Skip to content

Commit

Permalink
fix: should not eval exports in harmony (#6883)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahabhgk committed Jun 24, 2024
1 parent 95177b6 commit 4890082
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 68 deletions.
18 changes: 7 additions & 11 deletions crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<BasicEvaluatedExpression> {
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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<BasicEvaluatedExpression> {
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(),
)
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<BasicEvaluatedExpression> {
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(
Expand Down
9 changes: 4 additions & 5 deletions crates/rspack_plugin_javascript/src/parser_plugin/drive.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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<BasicEvaluatedExpression> {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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> {
Expand Down Expand Up @@ -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<BasicEvaluatedExpression> {
(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<bool> {
(parser.is_esm && for_name == "exports").then_some(true)
}
}

/// "await".len();
Expand Down
5 changes: 2 additions & 3 deletions crates/rspack_plugin_javascript/src/parser_plugin/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BasicEvaluatedExpression> {
None
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -13,15 +13,14 @@ fn eval_typeof(
) -> Option<BasicEvaluatedExpression> {
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` ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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(""))
});
Original file line number Diff line number Diff line change
@@ -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(" "))
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import("@rspack/core").Configuration} */
module.exports = {
node: {
__filename: false,
}
};

0 comments on commit 4890082

Please sign in to comment.