Skip to content

Commit

Permalink
fix(es/parser): Fix detection of use strict directive (#8617)
Browse files Browse the repository at this point in the history
**Description:**

Directives should be at the start of the file, and this PR changes the parser to match only at the start of a file.

**Related issue:**

 - Closes #8616.
  • Loading branch information
kdy1 committed Feb 7, 2024
1 parent f7baf24 commit 95236e9
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 33 deletions.
18 changes: 18 additions & 0 deletions crates/swc/tests/fixture/issues-8xxx/8616/input/1.js
@@ -0,0 +1,18 @@

var Module = (() => {
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
if (typeof __filename !== 'undefined') _scriptDir ||= __filename;
return (
function (moduleArg = {}) {
var Module = moduleArg;
var readyPromiseResolve, readyPromiseReject; Module["ready"] = new Promise((resolve, reject) => { readyPromiseResolve = resolve; readyPromiseReject = reject }); "use strict";


return moduleArg
}
);
})()();
if (typeof exports === 'object' && typeof module === 'object')
module.exports = Module;
else if (typeof define === 'function' && define['amd'])
define([], () => Module);
19 changes: 19 additions & 0 deletions crates/swc/tests/fixture/issues-8xxx/8616/output/1.js
@@ -0,0 +1,19 @@
var Module = (function() {
var _scriptDir = typeof document !== "undefined" && document.currentScript ? document.currentScript.src : undefined;
if (typeof __filename !== "undefined") _scriptDir || (_scriptDir = __filename);
return function() {
var moduleArg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
var Module = moduleArg;
var readyPromiseResolve, readyPromiseReject;
Module["ready"] = new Promise(function(resolve, reject) {
readyPromiseResolve = resolve;
readyPromiseReject = reject;
});
"use strict";
return moduleArg;
};
})()();
if (typeof exports === "object" && typeof module === "object") module.exports = Module;
else if (typeof define === "function" && define["amd"]) define([], function() {
return Module;
});
25 changes: 24 additions & 1 deletion crates/swc_ecma_ast/src/stmt.rs
Expand Up @@ -6,7 +6,7 @@ use crate::{
expr::Expr,
ident::Ident,
pat::Pat,
UsingDecl,
Lit, Str, UsingDecl,
};

/// Use when only block statements are allowed.
Expand Down Expand Up @@ -106,6 +106,29 @@ pub enum Stmt {
Expr(ExprStmt),
}

impl Stmt {
pub fn is_use_strict(&self) -> bool {
match self {
Stmt::Expr(expr) => match *expr.expr {
Expr::Lit(Lit::Str(Str { ref raw, .. })) => {
matches!(raw, Some(value) if value == "\"use strict\"" || value == "'use strict'")
}
_ => false,
},
_ => false,
}
}

/// Returns true if the statement does not prevent the directives below
/// `self` from being directives.
pub fn can_precede_directive(&self) -> bool {
match self {
Stmt::Expr(expr) => matches!(*expr.expr, Expr::Lit(Lit::Str(_))),
_ => false,
}
}
}

// Memory layout depedns on the version of rustc.
// #[cfg(target_pointer_width = "64")]
// assert_eq_size!(Stmt, [u8; 56]);
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_compat_es2015/src/classes/mod.rs
Expand Up @@ -12,7 +12,7 @@ use swc_ecma_transforms_macros::fast_path;
use swc_ecma_utils::{
alias_if_required, contains_this_expr, default_constructor, is_valid_ident,
is_valid_prop_ident, prepend_stmt, private_ident, prop_name_to_expr, quote_expr, quote_ident,
quote_str, replace_ident, ExprFactory, IdentExt, IsDirective, ModuleItemLike, StmtLike,
quote_str, replace_ident, ExprFactory, IdentExt, ModuleItemLike, StmtLike,
};
use swc_ecma_visit::{
as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith,
Expand Down
17 changes: 12 additions & 5 deletions crates/swc_ecma_parser/src/parser/class_and_fn.rs
@@ -1,7 +1,7 @@
use swc_common::{Spanned, SyntaxContext};

use super::*;
use crate::{error::SyntaxError, lexer::TokenContext, parser::stmt::IsDirective, Tokens};
use crate::{error::SyntaxError, lexer::TokenContext, Tokens};

/// Parser for function expression and function declaration.
impl<I: Tokens> Parser<I> {
Expand Down Expand Up @@ -1615,10 +1615,17 @@ pub(super) trait FnBodyParser<Body> {
fn parse_fn_body_inner(&mut self, is_simple_parameter_list: bool) -> PResult<Body>;
}
fn has_use_strict(block: &BlockStmt) -> Option<Span> {
match block.stmts.iter().find(|stmt| stmt.is_use_strict()) {
Some(Stmt::Expr(ExprStmt { span, expr: _ })) => Some(*span),
_ => None,
}
block
.stmts
.iter()
.take_while(|s| s.can_precede_directive())
.find_map(|s| {
if s.is_use_strict() {
Some(s.span())
} else {
None
}
})
}
impl<I: Tokens> FnBodyParser<Box<BlockStmtOrExpr>> for Parser<I> {
fn parse_fn_body_inner(
Expand Down
10 changes: 1 addition & 9 deletions crates/swc_ecma_parser/src/parser/stmt.rs
Expand Up @@ -1394,15 +1394,7 @@ enum TempForHead {
pub(super) trait IsDirective {
fn as_ref(&self) -> Option<&Stmt>;
fn is_use_strict(&self) -> bool {
match self.as_ref() {
Some(Stmt::Expr(expr)) => match *expr.expr {
Expr::Lit(Lit::Str(Str { ref raw, .. })) => {
matches!(raw, Some(value) if value == "\"use strict\"" || value == "'use strict'")
}
_ => false,
},
_ => false,
}
self.as_ref().map_or(false, Stmt::is_use_strict)
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_transforms_base/src/resolver/mod.rs
Expand Up @@ -5,7 +5,7 @@ use swc_common::{
Mark, Span, SyntaxContext,
};
use swc_ecma_ast::*;
use swc_ecma_utils::{find_pat_ids, IsDirective};
use swc_ecma_utils::find_pat_ids;
use swc_ecma_visit::{
as_folder, noop_visit_mut_type, visit_mut_obj_and_computed, Fold, VisitMut, VisitMutWith,
};
Expand Down
18 changes: 2 additions & 16 deletions crates/swc_ecma_utils/src/lib.rs
Expand Up @@ -2306,24 +2306,10 @@ pub trait IsDirective {
}
}
fn directive_continue(&self) -> bool {
match self.as_ref() {
Some(Stmt::Expr(expr)) => match &*expr.expr {
Expr::Lit(Lit::Str(..)) => true,
_ => false,
},
_ => false,
}
self.as_ref().map_or(false, Stmt::can_precede_directive)
}
fn is_use_strict(&self) -> bool {
match self.as_ref() {
Some(Stmt::Expr(expr)) => match *expr.expr {
Expr::Lit(Lit::Str(Str { ref raw, .. })) => {
matches!(raw, Some(value) if value == "\"use strict\"" || value == "'use strict'")
}
_ => false,
},
_ => false,
}
self.as_ref().map_or(false, Stmt::is_use_strict)
}
}

Expand Down

0 comments on commit 95236e9

Please sign in to comment.