File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -192,6 +192,11 @@ impl Mangler {
192192
193193 assert ! ( scope_tree. has_child_ids( ) , "child_id needs to be generated" ) ;
194194
195+ // TODO: implement opt-out of direct-eval in a branch of scopes.
196+ if scope_tree. root_flags ( ) . contains_direct_eval ( ) {
197+ return symbol_table;
198+ }
199+
195200 let ( exported_names, exported_symbols) = if self . options . top_level {
196201 Mangler :: collect_exported_symbols ( program)
197202 } else {
Original file line number Diff line number Diff line change @@ -16,6 +16,13 @@ fn mangle(source_text: &str, top_level: bool) -> String {
1616 CodeGenerator :: new ( ) . with_symbol_table ( Some ( symbol_table) ) . build ( & program) . code
1717}
1818
19+ #[ test]
20+ fn direct_eval ( ) {
21+ let source_text = "function foo() { let NO_MANGLE; eval('') }" ;
22+ let mangled = mangle ( source_text, false ) ;
23+ assert_eq ! ( mangled, "function foo() {\n \t let NO_MANGLE;\n \t eval(\" \" );\n }\n " ) ;
24+ }
25+
1926#[ test]
2027fn mangler ( ) {
2128 let cases = [
Original file line number Diff line number Diff line change @@ -82,3 +82,24 @@ fn tagged_template() {
8282 test ( "foo(true && o.f)" , "foo(o.f)" ) ;
8383 test ( "foo(true ? o.f : false)" , "foo(o.f)" ) ;
8484}
85+
86+ #[ test]
87+ fn eval ( ) {
88+ // Keep indirect-eval syntaxes
89+ test_same ( "(!0 && eval)(x)" ) ;
90+ test_same ( "(1 ? eval : 2)(x)" ) ;
91+ test_same ( "(1 ? eval : 2)?.(x)" ) ;
92+ test_same ( "(1, eval)(x)" ) ;
93+ test_same ( "(1, eval)?.(x)" ) ;
94+ test_same ( "(3, eval)(x)" ) ;
95+ test_same ( "(4, eval)?.(x)" ) ;
96+ test_same ( "(eval)(x)" ) ;
97+ test_same ( "(eval)?.(x)" ) ;
98+ test_same ( "eval(x)" ) ;
99+ test_same ( "eval(x, y)" ) ;
100+ test_same ( "eval(x,y)" ) ;
101+ test_same ( "eval?.(x)" ) ;
102+ test_same ( "eval?.(x)" ) ;
103+ test_same ( "eval?.(x, y)" ) ;
104+ test_same ( "eval?.(x,y)" ) ;
105+ }
Original file line number Diff line number Diff line change @@ -564,8 +564,12 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
564564 // So we could `.unwrap()` here. But that seems to produce a small perf impact, probably because
565565 // `leave_scope` then doesn't get inlined because of its larger size due to the panic code.
566566 let parent_id = self . scope . get_parent_id ( self . current_scope_id ) ;
567+
567568 debug_assert ! ( parent_id. is_some( ) ) ;
568569 if let Some ( parent_id) = parent_id {
570+ if self . scope . get_flags ( self . current_scope_id ) . contains_direct_eval ( ) {
571+ self . scope . get_flags_mut ( parent_id) . insert ( ScopeFlags :: DirectEval ) ;
572+ }
569573 self . current_scope_id = parent_id;
570574 }
571575
@@ -2069,6 +2073,11 @@ impl<'a> SemanticBuilder<'a> {
20692073 AstKind :: YieldExpression ( _) => {
20702074 self . set_function_node_flags ( NodeFlags :: HasYield ) ;
20712075 }
2076+ AstKind :: CallExpression ( call_expr) => {
2077+ if !call_expr. optional && call_expr. callee . is_specific_id ( "eval" ) {
2078+ self . scope . get_flags_mut ( self . current_scope_id ) . insert ( ScopeFlags :: DirectEval ) ;
2079+ }
2080+ }
20722081 _ => { }
20732082 }
20742083 }
Original file line number Diff line number Diff line change @@ -261,3 +261,25 @@ fn test_ts_conditional_types() {
261261 . has_number_of_references ( 0 )
262262 . test ( ) ;
263263}
264+
265+ #[ test]
266+ fn test_eval ( ) {
267+ let direct_evals = [ "eval('')" , "function foo() { eval('') }" ] ;
268+ for code in direct_evals {
269+ let tester = SemanticTester :: js ( code) ;
270+ let semantic = tester. build ( ) ;
271+ assert ! ( semantic. scopes( ) . root_flags( ) . contains_direct_eval( ) ) ;
272+ }
273+
274+ let indirect_evals = [
275+ "eval?.('')" ,
276+ "(0, eval)('')" ,
277+ "const myEval = eval; myEval('')" ,
278+ "const obj = { eval }; obj.eval('x + y')" ,
279+ ] ;
280+ for code in indirect_evals {
281+ let tester = SemanticTester :: js ( code) ;
282+ let semantic = tester. build ( ) ;
283+ assert ! ( !semantic. scopes( ) . root_flags( ) . contains_direct_eval( ) ) ;
284+ }
285+ }
Original file line number Diff line number Diff line change @@ -79,6 +79,7 @@ bitflags! {
7979 const GetAccessor = 1 << 7 ;
8080 const SetAccessor = 1 << 8 ;
8181 const CatchClause = 1 << 9 ;
82+ const DirectEval = 1 << 10 ; // <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#direct_and_indirect_eval>
8283 const Var = Self :: Top . bits( ) | Self :: Function . bits( ) | Self :: ClassStaticBlock . bits( ) | Self :: TsModuleBlock . bits( ) ;
8384 }
8485}
@@ -153,4 +154,9 @@ impl ScopeFlags {
153154 pub fn is_catch_clause ( & self ) -> bool {
154155 self . contains ( Self :: CatchClause )
155156 }
157+
158+ #[ inline]
159+ pub fn contains_direct_eval ( & self ) -> bool {
160+ self . contains ( Self :: DirectEval )
161+ }
156162}
Load Diff Large diffs are not rendered by default.
Load Diff Large diffs are not rendered by default.
Original file line number Diff line number Diff line change @@ -65,7 +65,6 @@ static SKIP_INCLUDES: &[&str] = &[
6565
6666static SKIP_TEST_CASES : & [ & str ] = & [
6767 // node.js runtime error
68- "language/eval-code" ,
6968 "language/expressions/dynamic-import" ,
7069 "language/global-code/decl-func.js" ,
7170 "language/module-code" ,
@@ -77,12 +76,7 @@ static SKIP_TEST_CASES: &[&str] = &[
7776 "language/expressions/prefix-decrement/operator-prefix-decrement-x-calls-putvalue-lhs-newvalue" ,
7877] ;
7978
80- static SKIP_ESID : & [ & str ] = & [
81- // Always fail because they need to perform `eval`
82- "sec-performeval-rules-in-initializer" ,
83- "sec-privatefieldget" ,
84- "sec-privatefieldset" ,
85- ] ;
79+ static SKIP_ESID : & [ & str ] = & [ "sec-privatefieldget" , "sec-privatefieldset" ] ;
8680
8781pub struct Test262RuntimeCase {
8882 base : Test262Case ,
@@ -113,13 +107,7 @@ impl Case for Test262RuntimeCase {
113107 let features = & self . base . meta ( ) . features ;
114108 self . base . should_fail ( )
115109 || self . base . skip_test_case ( )
116- || ( self
117- . base
118- . meta ( )
119- . esid
120- . as_ref ( )
121- . is_some_and ( |esid| SKIP_ESID . contains ( & esid. as_ref ( ) ) )
122- && test262_path. contains ( "direct-eval" ) )
110+ || self . base . meta ( ) . esid . as_ref ( ) . is_some_and ( |esid| SKIP_ESID . contains ( & esid. as_ref ( ) ) )
123111 || base_path. contains ( "built-ins" )
124112 || base_path. contains ( "staging" )
125113 || base_path. contains ( "intl402" )
You can’t perform that action at this time.
0 commit comments