From 27ab91af5682db26fbaf1c60cfa3e9ef9d94f85f Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Wed, 18 Sep 2024 02:21:06 +0200 Subject: [PATCH] Skip environment creation when possible for arrow functions --- core/ast/src/expression/literal/object.rs | 7 + core/ast/src/function/arrow_function.rs | 7 + core/ast/src/function/async_arrow_function.rs | 7 + core/ast/src/function/async_function.rs | 14 ++ core/ast/src/function/async_generator.rs | 14 ++ core/ast/src/function/class.rs | 7 + core/ast/src/function/generator.rs | 14 ++ core/ast/src/function/ordinary_function.rs | 14 ++ core/ast/src/scope_analyzer.rs | 78 +++++++-- core/engine/src/builtins/function/mod.rs | 11 +- core/engine/src/bytecompiler/class.rs | 7 + core/engine/src/bytecompiler/declarations.rs | 156 ++++++++++-------- core/engine/src/bytecompiler/function.rs | 12 +- core/engine/src/bytecompiler/mod.rs | 16 ++ core/engine/src/vm/code_block.rs | 10 ++ 15 files changed, 281 insertions(+), 93 deletions(-) diff --git a/core/ast/src/expression/literal/object.rs b/core/ast/src/expression/literal/object.rs index 88355918b9..3191f20ae7 100644 --- a/core/ast/src/expression/literal/object.rs +++ b/core/ast/src/expression/literal/object.rs @@ -469,6 +469,13 @@ impl ObjectMethodDefinition { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the object method definition contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for ObjectMethodDefinition { diff --git a/core/ast/src/function/arrow_function.rs b/core/ast/src/function/arrow_function.rs index 26bf82437f..9272febeb1 100644 --- a/core/ast/src/function/arrow_function.rs +++ b/core/ast/src/function/arrow_function.rs @@ -86,6 +86,13 @@ impl ArrowFunction { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the arrow function contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for ArrowFunction { diff --git a/core/ast/src/function/async_arrow_function.rs b/core/ast/src/function/async_arrow_function.rs index a6a360dc67..a800db5bc4 100644 --- a/core/ast/src/function/async_arrow_function.rs +++ b/core/ast/src/function/async_arrow_function.rs @@ -86,6 +86,13 @@ impl AsyncArrowFunction { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the function declaration contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for AsyncArrowFunction { diff --git a/core/ast/src/function/async_function.rs b/core/ast/src/function/async_function.rs index f80be21962..401a1694a0 100644 --- a/core/ast/src/function/async_function.rs +++ b/core/ast/src/function/async_function.rs @@ -78,6 +78,13 @@ impl AsyncFunctionDeclaration { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the async function declaration contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for AsyncFunctionDeclaration { @@ -207,6 +214,13 @@ impl AsyncFunctionExpression { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the async function expression contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for AsyncFunctionExpression { diff --git a/core/ast/src/function/async_generator.rs b/core/ast/src/function/async_generator.rs index b7a046e5fc..72edcb1ef3 100644 --- a/core/ast/src/function/async_generator.rs +++ b/core/ast/src/function/async_generator.rs @@ -77,6 +77,13 @@ impl AsyncGeneratorDeclaration { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the async generator declaration contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for AsyncGeneratorDeclaration { @@ -206,6 +213,13 @@ impl AsyncGeneratorExpression { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the async generator expression contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for AsyncGeneratorExpression { diff --git a/core/ast/src/function/class.rs b/core/ast/src/function/class.rs index 162b1b6933..ad5a45d066 100644 --- a/core/ast/src/function/class.rs +++ b/core/ast/src/function/class.rs @@ -748,6 +748,13 @@ impl ClassMethodDefinition { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the class method definition contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for ClassMethodDefinition { diff --git a/core/ast/src/function/generator.rs b/core/ast/src/function/generator.rs index 7911b922a6..5ddb3a876d 100644 --- a/core/ast/src/function/generator.rs +++ b/core/ast/src/function/generator.rs @@ -76,6 +76,13 @@ impl GeneratorDeclaration { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the generator declaration contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for GeneratorDeclaration { @@ -205,6 +212,13 @@ impl GeneratorExpression { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the generator expression contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for GeneratorExpression { diff --git a/core/ast/src/function/ordinary_function.rs b/core/ast/src/function/ordinary_function.rs index 84f8c4c6e3..bd4c456a15 100644 --- a/core/ast/src/function/ordinary_function.rs +++ b/core/ast/src/function/ordinary_function.rs @@ -77,6 +77,13 @@ impl FunctionDeclaration { pub const fn scopes(&self) -> &FunctionScopes { &self.scopes } + + /// Returns `true` if the function declaration contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } } impl ToIndentedString for FunctionDeclaration { @@ -207,6 +214,13 @@ impl FunctionExpression { &self.scopes } + /// Returns `true` if the function expression contains a direct call to `eval`. + #[inline] + #[must_use] + pub const fn contains_direct_eval(&self) -> bool { + self.contains_direct_eval + } + /// Analyze the scope of the function expression. pub fn analyze_scope(&mut self, strict: bool, scope: &Scope, interner: &Interner) -> bool { if !collect_bindings(self, strict, false, scope, interner) { diff --git a/core/ast/src/scope_analyzer.rs b/core/ast/src/scope_analyzer.rs index 78c65256e5..a503046a67 100644 --- a/core/ast/src/scope_analyzer.rs +++ b/core/ast/src/scope_analyzer.rs @@ -16,8 +16,9 @@ use crate::{ FunctionExpression, GeneratorDeclaration, GeneratorExpression, }, operations::{ - bound_names, lexically_declared_names, lexically_scoped_declarations, var_declared_names, - var_scoped_declarations, LexicallyScopedDeclaration, VarScopedDeclaration, + bound_names, contains, lexically_declared_names, lexically_scoped_declarations, + var_declared_names, var_scoped_declarations, ContainsSymbol, LexicallyScopedDeclaration, + VarScopedDeclaration, }, property::PropertyName, scope::{FunctionScopes, IdentifierReference, Scope}, @@ -1171,11 +1172,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut FunctionDeclaration, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + false, + contains_direct_eval, ) } @@ -1183,11 +1187,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut GeneratorDeclaration, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + false, + contains_direct_eval, ) } @@ -1195,11 +1202,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut AsyncFunctionDeclaration, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + false, + contains_direct_eval, ) } @@ -1207,11 +1217,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut AsyncGeneratorDeclaration, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + false, + contains_direct_eval, ) } @@ -1219,11 +1232,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut FunctionExpression, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut node.name_scope, + false, + contains_direct_eval, ) } @@ -1231,11 +1247,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut GeneratorExpression, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut node.name_scope, + false, + contains_direct_eval, ) } @@ -1243,11 +1262,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut AsyncFunctionExpression, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut node.name_scope, + false, + contains_direct_eval, ) } @@ -1255,11 +1277,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut AsyncGeneratorExpression, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut node.name_scope, + false, + contains_direct_eval, ) } @@ -1267,11 +1292,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut ArrowFunction, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + true, + contains_direct_eval, ) } @@ -1279,11 +1307,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { &mut self, node: &'ast mut AsyncArrowFunction, ) -> ControlFlow { + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + true, + contains_direct_eval, ) } @@ -1338,12 +1369,17 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { node: &'ast mut ClassElement, ) -> ControlFlow { match node { - ClassElement::MethodDefinition(node) => self.visit_function_like( - &mut node.body, - &mut node.parameters, - &mut node.scopes, - &mut None, - ), + ClassElement::MethodDefinition(node) => { + let contains_direct_eval = node.contains_direct_eval(); + self.visit_function_like( + &mut node.body, + &mut node.parameters, + &mut node.scopes, + &mut None, + false, + contains_direct_eval, + ) + } ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => { try_break!(self.visit_property_name_mut(&mut field.name)); let index = self.index; @@ -1371,12 +1407,17 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { } ControlFlow::Continue(()) } - ClassElement::StaticBlock(node) => self.visit_function_like( - &mut node.body, - &mut FormalParameterList::default(), - &mut node.scopes, - &mut None, - ), + ClassElement::StaticBlock(node) => { + let contains_direct_eval = contains(node.statements(), ContainsSymbol::DirectEval); + self.visit_function_like( + &mut node.body, + &mut FormalParameterList::default(), + &mut node.scopes, + &mut None, + false, + contains_direct_eval, + ) + } } } @@ -1390,11 +1431,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor { try_break!(self.visit_expression_mut(name)); } } + let contains_direct_eval = node.contains_direct_eval(); self.visit_function_like( &mut node.body, &mut node.parameters, &mut node.scopes, &mut None, + false, + contains_direct_eval, ) } @@ -1531,6 +1575,8 @@ impl ScopeIndexVisitor { parameters: &mut FormalParameterList, scopes: &mut FunctionScopes, name_scope: &mut Option, + arrow: bool, + contains_direct_eval: bool, ) -> ControlFlow<()> { let index = self.index; if let Some(scope) = name_scope { @@ -1539,7 +1585,9 @@ impl ScopeIndexVisitor { } scope.set_index(self.index); } - self.index += 1; + if !(arrow && scopes.function_scope.all_bindings_local() && !contains_direct_eval) { + self.index += 1; + } scopes.function_scope.set_index(self.index); if let Some(scope) = &scopes.parameters_eval_scope { if !scope.all_bindings_local() { diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 8c4967e564..19b032f88b 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -655,6 +655,7 @@ impl BuiltInFunctionObject { context.realm().scope().clone(), context.realm().scope().clone(), function.scopes(), + function.contains_direct_eval(), context.interner_mut(), ); @@ -1026,10 +1027,12 @@ pub(crate) fn function_call( last_env += 1; } - context.vm.environments.push_function( - code.constant_scope(last_env), - FunctionSlots::new(this, function_object.clone(), None), - ); + if code.has_function_scope() { + context.vm.environments.push_function( + code.constant_scope(last_env), + FunctionSlots::new(this, function_object.clone(), None), + ); + } Ok(CallValue::Ready) } diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index 4e9795c9d1..ab489974d4 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -96,6 +96,7 @@ impl ByteCompiler<'_> { compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; if let Some(expr) = &class.constructor { + compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(expr.scopes().function_scope()); compiler.length = expr.parameters().length(); @@ -115,11 +116,13 @@ impl ByteCompiler<'_> { compiler.emit_opcode(Opcode::PushUndefined); } else if class.super_ref.is_some() { // We push an empty, unused function scope since the compiler expects a function scope. + compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true)); compiler.emit_opcode(Opcode::SuperCallDerived); compiler.emit_opcode(Opcode::BindThisValue); } else { // We push an empty, unused function scope since the compiler expects a function scope. + compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true)); compiler.emit_opcode(Opcode::PushUndefined); } @@ -288,6 +291,7 @@ impl ByteCompiler<'_> { ); // Function environment + field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = field_compiler.push_scope(field.scope()); let is_anonymous_function = if let Some(node) = &field.field() { field_compiler.compile_expr(node, true); @@ -322,6 +326,7 @@ impl ByteCompiler<'_> { self.interner, self.in_with, ); + field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = field_compiler.push_scope(field.scope()); if let Some(node) = field.field() { field_compiler.compile_expr(node, true); @@ -363,6 +368,7 @@ impl ByteCompiler<'_> { self.interner, self.in_with, ); + field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = field_compiler.push_scope(field.scope()); let is_anonymous_function = if let Some(node) = &field.field() { field_compiler.compile_expr(node, true); @@ -406,6 +412,7 @@ impl ByteCompiler<'_> { self.interner, self.in_with, ); + compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(block.scopes().function_scope()); compiler.function_declaration_instantiation( diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index fcfce89fcc..06b91d9293 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -479,41 +479,46 @@ impl ByteCompiler<'_> { // 16. For each Parse Node f of functionsToInitialize, do for function in functions_to_initialize { // a. Let fn be the sole element of the BoundNames of f. - let (name, generator, r#async, parameters, body, scopes) = match &function { - VarScopedDeclaration::FunctionDeclaration(f) => ( - f.name(), - false, - false, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::GeneratorDeclaration(f) => ( - f.name(), - true, - false, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::AsyncFunctionDeclaration(f) => ( - f.name(), - false, - true, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::AsyncGeneratorDeclaration(f) => ( - f.name(), - true, - true, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::VariableDeclaration(_) => continue, - }; + let (name, generator, r#async, parameters, body, scopes, contains_direct_eval) = + match &function { + VarScopedDeclaration::FunctionDeclaration(f) => ( + f.name(), + false, + false, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::GeneratorDeclaration(f) => ( + f.name(), + true, + false, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::AsyncFunctionDeclaration(f) => ( + f.name(), + false, + true, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::AsyncGeneratorDeclaration(f) => ( + f.name(), + true, + true, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::VariableDeclaration(_) => continue, + }; let code = FunctionCompiler::new() .name(name.sym().to_js_string(self.interner())) @@ -527,6 +532,7 @@ impl ByteCompiler<'_> { self.variable_scope.clone(), self.lexical_scope.clone(), &scopes, + contains_direct_eval, self.interner, ); @@ -737,43 +743,48 @@ impl ByteCompiler<'_> { // 17. For each Parse Node f of functionsToInitialize, do for function in functions_to_initialize { // a. Let fn be the sole element of the BoundNames of f. - let (name, generator, r#async, parameters, body, scopes) = match &function { - VarScopedDeclaration::FunctionDeclaration(f) => ( - f.name(), - false, - false, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::GeneratorDeclaration(f) => ( - f.name(), - true, - false, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::AsyncFunctionDeclaration(f) => ( - f.name(), - false, - true, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::AsyncGeneratorDeclaration(f) => ( - f.name(), - true, - true, - f.parameters(), - f.body(), - f.scopes().clone(), - ), - VarScopedDeclaration::VariableDeclaration(_) => { - continue; - } - }; + let (name, generator, r#async, parameters, body, scopes, contains_direct_eval) = + match &function { + VarScopedDeclaration::FunctionDeclaration(f) => ( + f.name(), + false, + false, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::GeneratorDeclaration(f) => ( + f.name(), + true, + false, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::AsyncFunctionDeclaration(f) => ( + f.name(), + false, + true, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::AsyncGeneratorDeclaration(f) => ( + f.name(), + true, + true, + f.parameters(), + f.body(), + f.scopes().clone(), + f.contains_direct_eval(), + ), + VarScopedDeclaration::VariableDeclaration(_) => { + continue; + } + }; let code = FunctionCompiler::new() .name(name.sym().to_js_string(self.interner())) @@ -788,6 +799,7 @@ impl ByteCompiler<'_> { self.variable_scope.clone(), self.lexical_scope.clone(), &scopes, + contains_direct_eval, self.interner, ); diff --git a/core/engine/src/bytecompiler/function.rs b/core/engine/src/bytecompiler/function.rs index 5a2db2ce6a..d66d68c7b9 100644 --- a/core/engine/src/bytecompiler/function.rs +++ b/core/engine/src/bytecompiler/function.rs @@ -94,6 +94,7 @@ impl FunctionCompiler { } /// Compile a function statement list and it's parameters into bytecode. + #[allow(clippy::too_many_arguments)] pub(crate) fn compile( mut self, parameters: &FormalParameterList, @@ -101,6 +102,7 @@ impl FunctionCompiler { variable_environment: Scope, lexical_environment: Scope, scopes: &FunctionScopes, + contains_direct_eval: bool, interner: &mut Interner, ) -> Gc { self.strict = self.strict || body.strict(); @@ -134,8 +136,14 @@ impl FunctionCompiler { let _ = compiler.push_scope(&scope); } } - // Function environment - let _ = compiler.push_scope(scopes.function_scope()); + + if self.arrow && scopes.function_scope().all_bindings_local() && !contains_direct_eval { + compiler.variable_scope = scopes.function_scope().clone(); + compiler.lexical_scope = scopes.function_scope().clone(); + } else { + compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; + let _ = compiler.push_scope(scopes.function_scope()); + } // Taken from: // - 15.9.3 Runtime Semantics: EvaluateAsyncConciseBody: diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index da428e4e2d..a74310402b 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -122,6 +122,7 @@ pub(crate) struct FunctionSpec<'a> { body: &'a FunctionBody, pub(crate) scopes: &'a FunctionScopes, pub(crate) name_scope: Option<&'a Scope>, + pub(crate) contains_direct_eval: bool, } impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> { @@ -133,6 +134,7 @@ impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: None, + contains_direct_eval: function.contains_direct_eval(), } } } @@ -146,6 +148,7 @@ impl<'a> From<&'a GeneratorDeclaration> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: None, + contains_direct_eval: function.contains_direct_eval(), } } } @@ -159,6 +162,7 @@ impl<'a> From<&'a AsyncFunctionDeclaration> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: None, + contains_direct_eval: function.contains_direct_eval(), } } } @@ -172,6 +176,7 @@ impl<'a> From<&'a AsyncGeneratorDeclaration> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: None, + contains_direct_eval: function.contains_direct_eval(), } } } @@ -185,6 +190,7 @@ impl<'a> From<&'a FunctionExpression> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: function.name_scope(), + contains_direct_eval: function.contains_direct_eval(), } } } @@ -198,6 +204,7 @@ impl<'a> From<&'a ArrowFunction> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: None, + contains_direct_eval: function.contains_direct_eval(), } } } @@ -211,6 +218,7 @@ impl<'a> From<&'a AsyncArrowFunction> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: None, + contains_direct_eval: function.contains_direct_eval(), } } } @@ -224,6 +232,7 @@ impl<'a> From<&'a AsyncFunctionExpression> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: function.name_scope(), + contains_direct_eval: function.contains_direct_eval(), } } } @@ -237,6 +246,7 @@ impl<'a> From<&'a GeneratorExpression> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: function.name_scope(), + contains_direct_eval: function.contains_direct_eval(), } } } @@ -250,6 +260,7 @@ impl<'a> From<&'a AsyncGeneratorExpression> for FunctionSpec<'a> { body: function.body(), scopes: function.scopes(), name_scope: function.name_scope(), + contains_direct_eval: function.contains_direct_eval(), } } } @@ -270,6 +281,7 @@ impl<'a> From<&'a ClassMethodDefinition> for FunctionSpec<'a> { body: method.body(), scopes: method.scopes(), name_scope: None, + contains_direct_eval: method.contains_direct_eval(), } } } @@ -290,6 +302,7 @@ impl<'a> From<&'a ObjectMethodDefinition> for FunctionSpec<'a> { body: method.body(), scopes: method.scopes(), name_scope: None, + contains_direct_eval: method.contains_direct_eval(), } } } @@ -1518,6 +1531,7 @@ impl<'ctx> ByteCompiler<'ctx> { self.variable_scope.clone(), self.lexical_scope.clone(), scopes, + function.contains_direct_eval, self.interner, ); @@ -1595,6 +1609,7 @@ impl<'ctx> ByteCompiler<'ctx> { self.variable_scope.clone(), self.lexical_scope.clone(), scopes, + function.contains_direct_eval, self.interner, ); @@ -1638,6 +1653,7 @@ impl<'ctx> ByteCompiler<'ctx> { self.variable_scope.clone(), self.lexical_scope.clone(), scopes, + function.contains_direct_eval, self.interner, ); diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index 6c461b1b21..dd44daf1e8 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -66,6 +66,9 @@ bitflags! { /// Arrow and method functions don't have `"prototype"` property. const HAS_PROTOTYPE_PROPERTY = 0b1000_0000; + /// If the function requires a function scope. + const HAS_FUNCTION_SCOPE = 0b1_0000_0000; + /// Trace instruction execution to `stdout`. #[cfg(feature = "trace")] const TRACEABLE = 0b1000_0000_0000_0000; @@ -271,6 +274,13 @@ impl CodeBlock { .contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY) } + /// Returns true if this function requires a function scope. + pub(crate) fn has_function_scope(&self) -> bool { + self.flags + .get() + .contains(CodeBlockFlags::HAS_FUNCTION_SCOPE) + } + /// Find exception [`Handler`] in the code block given the current program counter (`pc`). #[inline] pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> {