Browse Source

Skip environment creation when possible for arrow functions

reduce-environment-allocations
raskad 2 months ago
parent
commit
27ab91af56
No known key found for this signature in database
  1. 7
      core/ast/src/expression/literal/object.rs
  2. 7
      core/ast/src/function/arrow_function.rs
  3. 7
      core/ast/src/function/async_arrow_function.rs
  4. 14
      core/ast/src/function/async_function.rs
  5. 14
      core/ast/src/function/async_generator.rs
  6. 7
      core/ast/src/function/class.rs
  7. 14
      core/ast/src/function/generator.rs
  8. 14
      core/ast/src/function/ordinary_function.rs
  9. 78
      core/ast/src/scope_analyzer.rs
  10. 11
      core/engine/src/builtins/function/mod.rs
  11. 7
      core/engine/src/bytecompiler/class.rs
  12. 156
      core/engine/src/bytecompiler/declarations.rs
  13. 12
      core/engine/src/bytecompiler/function.rs
  14. 16
      core/engine/src/bytecompiler/mod.rs
  15. 10
      core/engine/src/vm/code_block.rs

7
core/ast/src/expression/literal/object.rs

@ -469,6 +469,13 @@ impl ObjectMethodDefinition {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for ObjectMethodDefinition {

7
core/ast/src/function/arrow_function.rs

@ -86,6 +86,13 @@ impl ArrowFunction {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for ArrowFunction {

7
core/ast/src/function/async_arrow_function.rs

@ -86,6 +86,13 @@ impl AsyncArrowFunction {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for AsyncArrowFunction {

14
core/ast/src/function/async_function.rs

@ -78,6 +78,13 @@ impl AsyncFunctionDeclaration {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for AsyncFunctionDeclaration {
@ -207,6 +214,13 @@ impl AsyncFunctionExpression {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for AsyncFunctionExpression {

14
core/ast/src/function/async_generator.rs

@ -77,6 +77,13 @@ impl AsyncGeneratorDeclaration {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for AsyncGeneratorDeclaration {
@ -206,6 +213,13 @@ impl AsyncGeneratorExpression {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for AsyncGeneratorExpression {

7
core/ast/src/function/class.rs

@ -748,6 +748,13 @@ impl ClassMethodDefinition {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for ClassMethodDefinition {

14
core/ast/src/function/generator.rs

@ -76,6 +76,13 @@ impl GeneratorDeclaration {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for GeneratorDeclaration {
@ -205,6 +212,13 @@ impl GeneratorExpression {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for GeneratorExpression {

14
core/ast/src/function/ordinary_function.rs

@ -77,6 +77,13 @@ impl FunctionDeclaration {
pub const fn scopes(&self) -> &FunctionScopes { pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes &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 { impl ToIndentedString for FunctionDeclaration {
@ -207,6 +214,13 @@ impl FunctionExpression {
&self.scopes &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. /// Analyze the scope of the function expression.
pub fn analyze_scope(&mut self, strict: bool, scope: &Scope, interner: &Interner) -> bool { pub fn analyze_scope(&mut self, strict: bool, scope: &Scope, interner: &Interner) -> bool {
if !collect_bindings(self, strict, false, scope, interner) { if !collect_bindings(self, strict, false, scope, interner) {

78
core/ast/src/scope_analyzer.rs

@ -16,8 +16,9 @@ use crate::{
FunctionExpression, GeneratorDeclaration, GeneratorExpression, FunctionExpression, GeneratorDeclaration, GeneratorExpression,
}, },
operations::{ operations::{
bound_names, lexically_declared_names, lexically_scoped_declarations, var_declared_names, bound_names, contains, lexically_declared_names, lexically_scoped_declarations,
var_scoped_declarations, LexicallyScopedDeclaration, VarScopedDeclaration, var_declared_names, var_scoped_declarations, ContainsSymbol, LexicallyScopedDeclaration,
VarScopedDeclaration,
}, },
property::PropertyName, property::PropertyName,
scope::{FunctionScopes, IdentifierReference, Scope}, scope::{FunctionScopes, IdentifierReference, Scope},
@ -1171,11 +1172,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut FunctionDeclaration, node: &'ast mut FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
false,
contains_direct_eval,
) )
} }
@ -1183,11 +1187,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut GeneratorDeclaration, node: &'ast mut GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
false,
contains_direct_eval,
) )
} }
@ -1195,11 +1202,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut AsyncFunctionDeclaration, node: &'ast mut AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
false,
contains_direct_eval,
) )
} }
@ -1207,11 +1217,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut AsyncGeneratorDeclaration, node: &'ast mut AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
false,
contains_direct_eval,
) )
} }
@ -1219,11 +1232,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut FunctionExpression, node: &'ast mut FunctionExpression,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut node.name_scope, &mut node.name_scope,
false,
contains_direct_eval,
) )
} }
@ -1231,11 +1247,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut GeneratorExpression, node: &'ast mut GeneratorExpression,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut node.name_scope, &mut node.name_scope,
false,
contains_direct_eval,
) )
} }
@ -1243,11 +1262,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut AsyncFunctionExpression, node: &'ast mut AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut node.name_scope, &mut node.name_scope,
false,
contains_direct_eval,
) )
} }
@ -1255,11 +1277,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut AsyncGeneratorExpression, node: &'ast mut AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut node.name_scope, &mut node.name_scope,
false,
contains_direct_eval,
) )
} }
@ -1267,11 +1292,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut ArrowFunction, node: &'ast mut ArrowFunction,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
true,
contains_direct_eval,
) )
} }
@ -1279,11 +1307,14 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
&mut self, &mut self,
node: &'ast mut AsyncArrowFunction, node: &'ast mut AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
true,
contains_direct_eval,
) )
} }
@ -1338,12 +1369,17 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
node: &'ast mut ClassElement, node: &'ast mut ClassElement,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
match node { match node {
ClassElement::MethodDefinition(node) => self.visit_function_like( ClassElement::MethodDefinition(node) => {
&mut node.body, let contains_direct_eval = node.contains_direct_eval();
&mut node.parameters, self.visit_function_like(
&mut node.scopes, &mut node.body,
&mut None, &mut node.parameters,
), &mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => { ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {
try_break!(self.visit_property_name_mut(&mut field.name)); try_break!(self.visit_property_name_mut(&mut field.name));
let index = self.index; let index = self.index;
@ -1371,12 +1407,17 @@ impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
ClassElement::StaticBlock(node) => self.visit_function_like( ClassElement::StaticBlock(node) => {
&mut node.body, let contains_direct_eval = contains(node.statements(), ContainsSymbol::DirectEval);
&mut FormalParameterList::default(), self.visit_function_like(
&mut node.scopes, &mut node.body,
&mut None, &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)); try_break!(self.visit_expression_mut(name));
} }
} }
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like( self.visit_function_like(
&mut node.body, &mut node.body,
&mut node.parameters, &mut node.parameters,
&mut node.scopes, &mut node.scopes,
&mut None, &mut None,
false,
contains_direct_eval,
) )
} }
@ -1531,6 +1575,8 @@ impl ScopeIndexVisitor {
parameters: &mut FormalParameterList, parameters: &mut FormalParameterList,
scopes: &mut FunctionScopes, scopes: &mut FunctionScopes,
name_scope: &mut Option<Scope>, name_scope: &mut Option<Scope>,
arrow: bool,
contains_direct_eval: bool,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let index = self.index; let index = self.index;
if let Some(scope) = name_scope { if let Some(scope) = name_scope {
@ -1539,7 +1585,9 @@ impl ScopeIndexVisitor {
} }
scope.set_index(self.index); 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); scopes.function_scope.set_index(self.index);
if let Some(scope) = &scopes.parameters_eval_scope { if let Some(scope) = &scopes.parameters_eval_scope {
if !scope.all_bindings_local() { if !scope.all_bindings_local() {

11
core/engine/src/builtins/function/mod.rs

@ -655,6 +655,7 @@ impl BuiltInFunctionObject {
context.realm().scope().clone(), context.realm().scope().clone(),
context.realm().scope().clone(), context.realm().scope().clone(),
function.scopes(), function.scopes(),
function.contains_direct_eval(),
context.interner_mut(), context.interner_mut(),
); );
@ -1026,10 +1027,12 @@ pub(crate) fn function_call(
last_env += 1; last_env += 1;
} }
context.vm.environments.push_function( if code.has_function_scope() {
code.constant_scope(last_env), context.vm.environments.push_function(
FunctionSlots::new(this, function_object.clone(), None), code.constant_scope(last_env),
); FunctionSlots::new(this, function_object.clone(), None),
);
}
Ok(CallValue::Ready) Ok(CallValue::Ready)
} }

7
core/engine/src/bytecompiler/class.rs

@ -96,6 +96,7 @@ impl ByteCompiler<'_> {
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
if let Some(expr) = &class.constructor { if let Some(expr) = &class.constructor {
compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;
let _ = compiler.push_scope(expr.scopes().function_scope()); let _ = compiler.push_scope(expr.scopes().function_scope());
compiler.length = expr.parameters().length(); compiler.length = expr.parameters().length();
@ -115,11 +116,13 @@ impl ByteCompiler<'_> {
compiler.emit_opcode(Opcode::PushUndefined); compiler.emit_opcode(Opcode::PushUndefined);
} else if class.super_ref.is_some() { } else if class.super_ref.is_some() {
// We push an empty, unused function scope since the compiler expects a function scope. // 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)); let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true));
compiler.emit_opcode(Opcode::SuperCallDerived); compiler.emit_opcode(Opcode::SuperCallDerived);
compiler.emit_opcode(Opcode::BindThisValue); compiler.emit_opcode(Opcode::BindThisValue);
} else { } else {
// We push an empty, unused function scope since the compiler expects a function scope. // 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)); let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true));
compiler.emit_opcode(Opcode::PushUndefined); compiler.emit_opcode(Opcode::PushUndefined);
} }
@ -288,6 +291,7 @@ impl ByteCompiler<'_> {
); );
// Function environment // Function environment
field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;
let _ = field_compiler.push_scope(field.scope()); let _ = field_compiler.push_scope(field.scope());
let is_anonymous_function = if let Some(node) = &field.field() { let is_anonymous_function = if let Some(node) = &field.field() {
field_compiler.compile_expr(node, true); field_compiler.compile_expr(node, true);
@ -322,6 +326,7 @@ impl ByteCompiler<'_> {
self.interner, self.interner,
self.in_with, self.in_with,
); );
field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;
let _ = field_compiler.push_scope(field.scope()); let _ = field_compiler.push_scope(field.scope());
if let Some(node) = field.field() { if let Some(node) = field.field() {
field_compiler.compile_expr(node, true); field_compiler.compile_expr(node, true);
@ -363,6 +368,7 @@ impl ByteCompiler<'_> {
self.interner, self.interner,
self.in_with, self.in_with,
); );
field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;
let _ = field_compiler.push_scope(field.scope()); let _ = field_compiler.push_scope(field.scope());
let is_anonymous_function = if let Some(node) = &field.field() { let is_anonymous_function = if let Some(node) = &field.field() {
field_compiler.compile_expr(node, true); field_compiler.compile_expr(node, true);
@ -406,6 +412,7 @@ impl ByteCompiler<'_> {
self.interner, self.interner,
self.in_with, self.in_with,
); );
compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;
let _ = compiler.push_scope(block.scopes().function_scope()); let _ = compiler.push_scope(block.scopes().function_scope());
compiler.function_declaration_instantiation( compiler.function_declaration_instantiation(

156
core/engine/src/bytecompiler/declarations.rs

@ -479,41 +479,46 @@ impl ByteCompiler<'_> {
// 16. For each Parse Node f of functionsToInitialize, do // 16. For each Parse Node f of functionsToInitialize, do
for function in functions_to_initialize { for function in functions_to_initialize {
// a. Let fn be the sole element of the BoundNames of f. // a. Let fn be the sole element of the BoundNames of f.
let (name, generator, r#async, parameters, body, scopes) = match &function { let (name, generator, r#async, parameters, body, scopes, contains_direct_eval) =
VarScopedDeclaration::FunctionDeclaration(f) => ( match &function {
f.name(), VarScopedDeclaration::FunctionDeclaration(f) => (
false, f.name(),
false, false,
f.parameters(), false,
f.body(), f.parameters(),
f.scopes().clone(), f.body(),
), f.scopes().clone(),
VarScopedDeclaration::GeneratorDeclaration(f) => ( f.contains_direct_eval(),
f.name(), ),
true, VarScopedDeclaration::GeneratorDeclaration(f) => (
false, f.name(),
f.parameters(), true,
f.body(), false,
f.scopes().clone(), f.parameters(),
), f.body(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => ( f.scopes().clone(),
f.name(), f.contains_direct_eval(),
false, ),
true, VarScopedDeclaration::AsyncFunctionDeclaration(f) => (
f.parameters(), f.name(),
f.body(), false,
f.scopes().clone(), true,
), f.parameters(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => ( f.body(),
f.name(), f.scopes().clone(),
true, f.contains_direct_eval(),
true, ),
f.parameters(), VarScopedDeclaration::AsyncGeneratorDeclaration(f) => (
f.body(), f.name(),
f.scopes().clone(), true,
), true,
VarScopedDeclaration::VariableDeclaration(_) => continue, f.parameters(),
}; f.body(),
f.scopes().clone(),
f.contains_direct_eval(),
),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
let code = FunctionCompiler::new() let code = FunctionCompiler::new()
.name(name.sym().to_js_string(self.interner())) .name(name.sym().to_js_string(self.interner()))
@ -527,6 +532,7 @@ impl ByteCompiler<'_> {
self.variable_scope.clone(), self.variable_scope.clone(),
self.lexical_scope.clone(), self.lexical_scope.clone(),
&scopes, &scopes,
contains_direct_eval,
self.interner, self.interner,
); );
@ -737,43 +743,48 @@ impl ByteCompiler<'_> {
// 17. For each Parse Node f of functionsToInitialize, do // 17. For each Parse Node f of functionsToInitialize, do
for function in functions_to_initialize { for function in functions_to_initialize {
// a. Let fn be the sole element of the BoundNames of f. // a. Let fn be the sole element of the BoundNames of f.
let (name, generator, r#async, parameters, body, scopes) = match &function { let (name, generator, r#async, parameters, body, scopes, contains_direct_eval) =
VarScopedDeclaration::FunctionDeclaration(f) => ( match &function {
f.name(), VarScopedDeclaration::FunctionDeclaration(f) => (
false, f.name(),
false, false,
f.parameters(), false,
f.body(), f.parameters(),
f.scopes().clone(), f.body(),
), f.scopes().clone(),
VarScopedDeclaration::GeneratorDeclaration(f) => ( f.contains_direct_eval(),
f.name(), ),
true, VarScopedDeclaration::GeneratorDeclaration(f) => (
false, f.name(),
f.parameters(), true,
f.body(), false,
f.scopes().clone(), f.parameters(),
), f.body(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => ( f.scopes().clone(),
f.name(), f.contains_direct_eval(),
false, ),
true, VarScopedDeclaration::AsyncFunctionDeclaration(f) => (
f.parameters(), f.name(),
f.body(), false,
f.scopes().clone(), true,
), f.parameters(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => ( f.body(),
f.name(), f.scopes().clone(),
true, f.contains_direct_eval(),
true, ),
f.parameters(), VarScopedDeclaration::AsyncGeneratorDeclaration(f) => (
f.body(), f.name(),
f.scopes().clone(), true,
), true,
VarScopedDeclaration::VariableDeclaration(_) => { f.parameters(),
continue; f.body(),
} f.scopes().clone(),
}; f.contains_direct_eval(),
),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
};
let code = FunctionCompiler::new() let code = FunctionCompiler::new()
.name(name.sym().to_js_string(self.interner())) .name(name.sym().to_js_string(self.interner()))
@ -788,6 +799,7 @@ impl ByteCompiler<'_> {
self.variable_scope.clone(), self.variable_scope.clone(),
self.lexical_scope.clone(), self.lexical_scope.clone(),
&scopes, &scopes,
contains_direct_eval,
self.interner, self.interner,
); );

12
core/engine/src/bytecompiler/function.rs

@ -94,6 +94,7 @@ impl FunctionCompiler {
} }
/// Compile a function statement list and it's parameters into bytecode. /// Compile a function statement list and it's parameters into bytecode.
#[allow(clippy::too_many_arguments)]
pub(crate) fn compile( pub(crate) fn compile(
mut self, mut self,
parameters: &FormalParameterList, parameters: &FormalParameterList,
@ -101,6 +102,7 @@ impl FunctionCompiler {
variable_environment: Scope, variable_environment: Scope,
lexical_environment: Scope, lexical_environment: Scope,
scopes: &FunctionScopes, scopes: &FunctionScopes,
contains_direct_eval: bool,
interner: &mut Interner, interner: &mut Interner,
) -> Gc<CodeBlock> { ) -> Gc<CodeBlock> {
self.strict = self.strict || body.strict(); self.strict = self.strict || body.strict();
@ -134,8 +136,14 @@ impl FunctionCompiler {
let _ = compiler.push_scope(&scope); 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: // Taken from:
// - 15.9.3 Runtime Semantics: EvaluateAsyncConciseBody: <https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncconcisebody> // - 15.9.3 Runtime Semantics: EvaluateAsyncConciseBody: <https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncconcisebody>

16
core/engine/src/bytecompiler/mod.rs

@ -122,6 +122,7 @@ pub(crate) struct FunctionSpec<'a> {
body: &'a FunctionBody, body: &'a FunctionBody,
pub(crate) scopes: &'a FunctionScopes, pub(crate) scopes: &'a FunctionScopes,
pub(crate) name_scope: Option<&'a Scope>, pub(crate) name_scope: Option<&'a Scope>,
pub(crate) contains_direct_eval: bool,
} }
impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> { impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> {
@ -133,6 +134,7 @@ impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> {
body: function.body(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: None, 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: None, 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: None, 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: None, 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: function.name_scope(), 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: None, 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: None, 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: function.name_scope(), 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: function.name_scope(), 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(), body: function.body(),
scopes: function.scopes(), scopes: function.scopes(),
name_scope: function.name_scope(), 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(), body: method.body(),
scopes: method.scopes(), scopes: method.scopes(),
name_scope: None, 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(), body: method.body(),
scopes: method.scopes(), scopes: method.scopes(),
name_scope: None, name_scope: None,
contains_direct_eval: method.contains_direct_eval(),
} }
} }
} }
@ -1518,6 +1531,7 @@ impl<'ctx> ByteCompiler<'ctx> {
self.variable_scope.clone(), self.variable_scope.clone(),
self.lexical_scope.clone(), self.lexical_scope.clone(),
scopes, scopes,
function.contains_direct_eval,
self.interner, self.interner,
); );
@ -1595,6 +1609,7 @@ impl<'ctx> ByteCompiler<'ctx> {
self.variable_scope.clone(), self.variable_scope.clone(),
self.lexical_scope.clone(), self.lexical_scope.clone(),
scopes, scopes,
function.contains_direct_eval,
self.interner, self.interner,
); );
@ -1638,6 +1653,7 @@ impl<'ctx> ByteCompiler<'ctx> {
self.variable_scope.clone(), self.variable_scope.clone(),
self.lexical_scope.clone(), self.lexical_scope.clone(),
scopes, scopes,
function.contains_direct_eval,
self.interner, self.interner,
); );

10
core/engine/src/vm/code_block.rs

@ -66,6 +66,9 @@ bitflags! {
/// Arrow and method functions don't have `"prototype"` property. /// Arrow and method functions don't have `"prototype"` property.
const HAS_PROTOTYPE_PROPERTY = 0b1000_0000; 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`. /// Trace instruction execution to `stdout`.
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
const TRACEABLE = 0b1000_0000_0000_0000; const TRACEABLE = 0b1000_0000_0000_0000;
@ -271,6 +274,13 @@ impl CodeBlock {
.contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY) .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`). /// Find exception [`Handler`] in the code block given the current program counter (`pc`).
#[inline] #[inline]
pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> { pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> {

Loading…
Cancel
Save