From 4890082216a675f6eb435ee1f6b133cada753f6e Mon Sep 17 00:00:00 2001 From: Gengkun Date: Fri, 21 Jun 2024 15:55:11 +0800 Subject: [PATCH] fix: should not eval exports in harmony (#6883) --- .../src/parser_plugin/api_plugin.rs | 18 +++++-------- .../common_js_exports_parse_plugin.rs | 23 +++++++++------- .../common_js_imports_parse_plugin.rs | 26 ++++++++++--------- .../src/parser_plugin/drive.rs | 9 +++---- .../harmony_detection_parser_plugin.rs | 24 +++++++++++++++-- .../src/parser_plugin/trait.rs | 5 ++-- .../src/utils/eval/eval_unary_expr.rs | 17 ++++++------ .../src/visitors/dependency/parser/mod.rs | 26 +++++++++---------- .../parsing/eval-cjs-typeof/index.js | 6 ++--- .../index.js | 8 ++++++ .../rspack.config.js | 6 +++++ 11 files changed, 100 insertions(+), 68 deletions(-) create mode 100644 packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/index.js create mode 100644 packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/rspack.config.js diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs index 4f9c09c6599..0326a64e52c 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs @@ -1,6 +1,6 @@ use rspack_core::{ConstDependency, RuntimeGlobals, RuntimeRequirementsDependency, SpanExt}; use swc_core::common::Spanned; -use swc_core::ecma::ast::{CallExpr, Callee, Expr, Ident}; +use swc_core::ecma::ast::{CallExpr, Callee, Expr, Ident, UnaryExpr}; use crate::dependency::ModuleArgumentDependency; use crate::parser_plugin::JavascriptParserPlugin; @@ -63,17 +63,13 @@ fn get_typeof_evaluate_of_api(sym: &str) -> Option<&str> { impl JavascriptParserPlugin for APIPlugin { fn evaluate_typeof( &self, - parser: &mut JavascriptParser, - expression: &Ident, - start: u32, - end: u32, + _parser: &mut JavascriptParser, + expr: &UnaryExpr, + for_name: &str, ) -> Option { - if parser.is_unresolved_ident(expression.sym.as_str()) { - get_typeof_evaluate_of_api(expression.sym.as_str()) - .map(|res| eval::evaluate_to_string(res.to_string(), start, end)) - } else { - None - } + get_typeof_evaluate_of_api(for_name).map(|res| { + eval::evaluate_to_string(res.to_string(), expr.span.real_lo(), expr.span.real_hi()) + }) } fn identifier( diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs index 6f2d6f4b860..6c77aa92bc7 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs @@ -4,7 +4,9 @@ use rspack_core::{ }; use swc_core::atoms::Atom; use swc_core::common::Spanned; -use swc_core::ecma::ast::{AssignExpr, AssignTarget, CallExpr, PropOrSpread, SimpleAssignTarget}; +use swc_core::ecma::ast::{ + AssignExpr, AssignTarget, CallExpr, PropOrSpread, SimpleAssignTarget, UnaryExpr, +}; use swc_core::ecma::ast::{Callee, ExprOrSpread, Ident, MemberExpr, ObjectLit}; use swc_core::ecma::ast::{Expr, Lit, Prop, PropName, ThisExpr, UnaryOp}; @@ -553,15 +555,16 @@ impl JavascriptParserPlugin for CommonJsExportsParserPlugin { fn evaluate_typeof( &self, - parser: &mut JavascriptParser, - expression: &Ident, - start: u32, - end: u32, + _parser: &mut JavascriptParser, + expr: &UnaryExpr, + for_name: &str, ) -> Option { - if parser.is_exports_ident(expression) || parser.is_module_ident(expression) { - Some(eval::evaluate_to_string("object".to_string(), start, end)) - } else { - None - } + (for_name == "module" || for_name == "exports").then(|| { + eval::evaluate_to_string( + "object".to_string(), + expr.span.real_lo(), + expr.span.real_hi(), + ) + }) } } diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs index 431cc6157c4..a062c52cfac 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs @@ -5,7 +5,7 @@ use rspack_core::{ use rspack_core::{ContextNameSpaceObject, ContextOptions}; use rspack_error::Severity; use swc_core::common::{Span, Spanned}; -use swc_core::ecma::ast::{CallExpr, Expr, Ident, Lit, MemberExpr}; +use swc_core::ecma::ast::{CallExpr, Expr, Ident, Lit, MemberExpr, UnaryExpr}; use super::JavascriptParserPlugin; use crate::dependency::RequireHeaderDependency; @@ -272,18 +272,20 @@ impl JavascriptParserPlugin for CommonJsImportsParserPlugin { fn evaluate_typeof( &self, - parser: &mut JavascriptParser, - expression: &Ident, - start: u32, - end: u32, + _parser: &mut JavascriptParser, + expr: &UnaryExpr, + for_name: &str, ) -> Option { - if expression.sym.as_str() == expr_name::REQUIRE - && parser.is_unresolved_ident(expr_name::REQUIRE) - { - Some(eval::evaluate_to_string("function".to_string(), start, end)) - } else { - None - } + (for_name == expr_name::REQUIRE + || for_name == expr_name::REQUIRE_RESOLVE + || for_name == expr_name::REQUIRE_RESOLVE_WEAK) + .then(|| { + eval::evaluate_to_string( + "function".to_string(), + expr.span.real_lo(), + expr.span.real_hi(), + ) + }) } fn evaluate_identifier( diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs b/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs index 928969df716..6c1dc93ad9d 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs @@ -1,7 +1,7 @@ use swc_core::atoms::Atom; use swc_core::common::Span; use swc_core::ecma::ast::{ - BinExpr, CallExpr, Callee, CondExpr, ExportDecl, ExportDefaultDecl, Expr, OptChainExpr, + BinExpr, CallExpr, Callee, CondExpr, ExportDecl, ExportDefaultDecl, Expr, OptChainExpr, UnaryExpr, }; use swc_core::ecma::ast::{IfStmt, MemberExpr, Stmt, UnaryOp, VarDecl, VarDeclarator}; @@ -332,12 +332,11 @@ impl JavascriptParserPlugin for JavaScriptParserPluginDrive { fn evaluate_typeof( &self, parser: &mut JavascriptParser, - ident: &swc_core::ecma::ast::Ident, - start: u32, - end: u32, + expr: &UnaryExpr, + for_name: &str, ) -> Option { for plugin in &self.plugins { - let res = plugin.evaluate_typeof(parser, ident, start, end); + let res = plugin.evaluate_typeof(parser, expr, for_name); // `SyncBailHook` if res.is_some() { return res; diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/harmony_detection_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/harmony_detection_parser_plugin.rs index 8496aaf7158..d25a117737e 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/harmony_detection_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/harmony_detection_parser_plugin.rs @@ -1,12 +1,13 @@ use std::ops::Add; -use rspack_core::{BuildMetaExportsType, ExportsArgument, ModuleArgument, ModuleType}; +use rspack_core::{BuildMetaExportsType, ExportsArgument, ModuleArgument, ModuleType, SpanExt}; use swc_core::common::source_map::Pos; use swc_core::common::{BytePos, Span, Spanned}; -use swc_core::ecma::ast::{ModuleItem, Program}; +use swc_core::ecma::ast::{Ident, ModuleItem, Program, UnaryExpr}; use super::JavascriptParserPlugin; use crate::dependency::HarmonyCompatibilityDependency; +use crate::utils::eval::BasicEvaluatedExpression; use crate::visitors::{create_traceable_error, JavascriptParser}; impl<'parser> JavascriptParser<'parser> { @@ -90,6 +91,25 @@ impl JavascriptParserPlugin for HarmonyDetectionParserPlugin { let span = Span::new(lo, hi, stmt.span.ctxt); parser.handle_top_level_await(self.top_level_await, span); } + + fn evaluate_typeof( + &self, + parser: &mut JavascriptParser, + expr: &UnaryExpr, + for_name: &str, + ) -> Option { + (parser.is_esm && for_name == "exports") + .then(|| BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span_hi().0)) + } + + fn identifier( + &self, + parser: &mut JavascriptParser, + _ident: &Ident, + for_name: &str, + ) -> Option { + (parser.is_esm && for_name == "exports").then_some(true) + } } /// "await".len(); diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs b/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs index 8da26aaf30a..18907c537bb 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs @@ -55,9 +55,8 @@ pub trait JavascriptParserPlugin { fn evaluate_typeof( &self, _parser: &mut JavascriptParser, - _ident: &Ident, - _start: u32, - _end: u32, + _expr: &UnaryExpr, + _for_name: &str, ) -> Option { None } diff --git a/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs b/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs index 0e415de0366..8382b6479f1 100644 --- a/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs +++ b/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs @@ -4,7 +4,7 @@ use swc_core::ecma::ast::{UnaryExpr, UnaryOp}; use super::BasicEvaluatedExpression; use crate::parser_plugin::JavascriptParserPlugin; -use crate::visitors::JavascriptParser; +use crate::visitors::{CallHooksName, JavascriptParser}; #[inline] fn eval_typeof( @@ -13,15 +13,14 @@ fn eval_typeof( ) -> Option { assert!(expr.op == UnaryOp::TypeOf); if let Some(ident) = expr.arg.as_ident() - && /* FIXME: should use call hooks for name */ let res = parser.plugin_drive.clone().evaluate_typeof( - parser, - ident, - expr.span.real_lo(), - expr.span.hi().0, - ) - && res.is_some() + && let Some(res) = ident.sym.call_hooks_name(parser, |parser, for_name| { + parser + .plugin_drive + .clone() + .evaluate_typeof(parser, expr, for_name) + }) { - return res; + return Some(res); } // TODO: if let `MetaProperty`, `MemberExpression` ... diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs index b0f07cca3e2..6810f82fe6e 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs @@ -304,6 +304,19 @@ impl<'parser> JavascriptParser<'parser> { ))); plugins.push(Box::new(parser_plugin::CompatibilityPlugin)); + if module_type.is_js_auto() || module_type.is_js_esm() { + plugins.push(Box::new(parser_plugin::HarmonyTopLevelThisParserPlugin)); + plugins.push(Box::new(parser_plugin::HarmonyDetectionParserPlugin::new( + compiler_options.experiments.top_level_await, + ))); + plugins.push(Box::new( + parser_plugin::ImportMetaContextDependencyParserPlugin, + )); + plugins.push(Box::new(parser_plugin::ImportMetaPlugin)); + plugins.push(Box::new(parser_plugin::HarmonyImportDependencyParserPlugin)); + plugins.push(Box::new(parser_plugin::HarmonyExportDependencyParserPlugin)); + } + if module_type.is_js_auto() || module_type.is_js_dynamic() { plugins.push(Box::new(parser_plugin::CommonJsImportsParserPlugin)); plugins.push(Box::new(parser_plugin::CommonJsPlugin)); @@ -345,26 +358,13 @@ impl<'parser> JavascriptParser<'parser> { compiler_options.output.module, ))); plugins.push(Box::new(parser_plugin::ImportParserPlugin)); - } - - if module_type.is_js_auto() || module_type.is_js_esm() { let parse_url = javascript_options.url; if !matches!(parse_url, JavascriptParserUrl::Disable) { plugins.push(Box::new(parser_plugin::URLPlugin { relative: matches!(parse_url, JavascriptParserUrl::Relative), })); } - plugins.push(Box::new(parser_plugin::HarmonyTopLevelThisParserPlugin)); - plugins.push(Box::new(parser_plugin::HarmonyDetectionParserPlugin::new( - compiler_options.experiments.top_level_await, - ))); plugins.push(Box::new(parser_plugin::WorkerPlugin)); - plugins.push(Box::new( - parser_plugin::ImportMetaContextDependencyParserPlugin, - )); - plugins.push(Box::new(parser_plugin::ImportMetaPlugin)); - plugins.push(Box::new(parser_plugin::HarmonyImportDependencyParserPlugin)); - plugins.push(Box::new(parser_plugin::HarmonyExportDependencyParserPlugin)); } let plugin_drive = Rc::new(JavaScriptParserPluginDrive::new(plugins)); diff --git a/packages/rspack-test-tools/tests/configCases/parsing/eval-cjs-typeof/index.js b/packages/rspack-test-tools/tests/configCases/parsing/eval-cjs-typeof/index.js index 12001dbb12f..9f5d19f9a7b 100644 --- a/packages/rspack-test-tools/tests/configCases/parsing/eval-cjs-typeof/index.js +++ b/packages/rspack-test-tools/tests/configCases/parsing/eval-cjs-typeof/index.js @@ -1,9 +1,9 @@ const fs = require("fs"); -it("should compile", () => { +it("should compile", async () => { if (typeof exports !== "object" || typeof module !== "object") { throw new Error("wrong") } - const file = fs.promises.readFile(__filename); - expect(file).not.toContain("typeof") + const file = await fs.promises.readFile(__filename, 'utf-8'); + expect(file).not.toContain(["type", "of"].join("")) }); diff --git a/packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/index.js b/packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/index.js new file mode 100644 index 00000000000..a27a086528f --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/index.js @@ -0,0 +1,8 @@ +import fs from "fs"; // import make this module a detection harmony + +it("should compile", async () => { + const a = typeof exports === "object"; + const file = await fs.promises.readFile(__filename, 'utf-8'); + expect(file).not.toContain(["const", "a", "=", "true"].join(" ")) + expect(file).toContain(["const", "a", "=", "typeof", "exports", "===", "\"object\""].join(" ")) +}); diff --git a/packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/rspack.config.js b/packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/rspack.config.js new file mode 100644 index 00000000000..8fde9c7af79 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/parsing/eval-exports-in-detection-harmony/rspack.config.js @@ -0,0 +1,6 @@ +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + node: { + __filename: false, + } +};