From 89c02a244b06d1eef6651c387ea59c570a6440f0 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Tue, 10 Oct 2023 22:27:35 +0200 Subject: [PATCH] Refactor compile time environment handling (#3365) * Refactor compile time environment handling * Apply review * Apply review --- boa_engine/src/builtins/eval/mod.rs | 22 +- boa_engine/src/builtins/function/mod.rs | 3 + boa_engine/src/builtins/json/mod.rs | 1 + boa_engine/src/bytecompiler/class.rs | 60 ++-- boa_engine/src/bytecompiler/declarations.rs | 271 ++++++++++-------- boa_engine/src/bytecompiler/env.rs | 110 +------ .../src/bytecompiler/expression/assign.rs | 11 +- .../src/bytecompiler/expression/unary.rs | 6 +- .../src/bytecompiler/expression/update.rs | 11 +- boa_engine/src/bytecompiler/function.rs | 33 +-- boa_engine/src/bytecompiler/mod.rs | 102 ++++--- boa_engine/src/bytecompiler/module.rs | 6 +- .../src/bytecompiler/statement/block.rs | 5 +- boa_engine/src/bytecompiler/statement/loop.rs | 79 +++-- .../src/bytecompiler/statement/switch.rs | 5 +- boa_engine/src/bytecompiler/statement/try.rs | 11 +- boa_engine/src/bytecompiler/statement/with.rs | 2 + boa_engine/src/environments/compile.rs | 207 +++++-------- .../environments/runtime/declarative/mod.rs | 11 + boa_engine/src/environments/runtime/mod.rs | 62 +--- boa_engine/src/module/source.rs | 36 +-- boa_engine/src/script.rs | 6 +- boa_engine/src/vm/opcode/define/mod.rs | 2 - boa_engine/src/vm/opcode/mod.rs | 3 +- 24 files changed, 456 insertions(+), 609 deletions(-) diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 6023687351e..9fad389b595 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -224,31 +224,35 @@ impl Eval { } }); + let var_environment = context.vm.environments.outer_function_environment(); + let mut var_env = var_environment.compile_env(); + let mut compiler = ByteCompiler::new( Sym::MAIN, body.strict(), false, + var_env.clone(), context.vm.environments.current_compile_environment(), context, ); let env_index = compiler.push_compile_environment(strict); compiler.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); + let lex_env = compiler.lexical_environment.clone(); + if strict { + var_env = lex_env.clone(); + compiler.variable_environment = lex_env.clone(); + } - compiler.eval_declaration_instantiation(&body, strict)?; + compiler.eval_declaration_instantiation(&body, strict, &var_env, &lex_env)?; compiler.compile_statement_list(body.statements(), true, false); - compiler.pop_compile_environment(); - compiler.emit_opcode(Opcode::PopEnvironment); - let code_block = Gc::new(compiler.finish()); - // Indirect calls don't need extensions, because a non-strict indirect call modifies only - // the global object. - // Strict direct calls also don't need extensions, since all strict eval calls push a new + // Strict calls don't need extensions, since all strict eval calls push a new // function environment before evaluating. - if direct && !strict { - context.vm.environments.extend_outer_function_environment(); + if !strict { + var_environment.extend_from_compile(); } let env_fp = context.vm.environments.len() as u32; diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 5dbe23c412f..085834c7c2d 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -636,6 +636,7 @@ impl BuiltInFunctionObject { ¶meters, &body, context.realm().environment().compile_env(), + context.realm().environment().compile_env(), context, ); @@ -659,6 +660,7 @@ impl BuiltInFunctionObject { &FormalParameterList::default(), &FunctionBody::default(), context.realm().environment().compile_env(), + context.realm().environment().compile_env(), context, ); @@ -680,6 +682,7 @@ impl BuiltInFunctionObject { &FormalParameterList::default(), &FunctionBody::default(), context.realm().environment().compile_env(), + context.realm().environment().compile_env(), context, ); diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index a415d60e597..6b94df63d64 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -122,6 +122,7 @@ impl Json { script.strict(), true, context.realm().environment().compile_env(), + context.realm().environment().compile_env(), context, ); compiler.compile_statement_list(script.statements(), true, false); diff --git a/boa_engine/src/bytecompiler/class.rs b/boa_engine/src/bytecompiler/class.rs index 1bc57de76ca..7ee875ad16c 100644 --- a/boa_engine/src/bytecompiler/class.rs +++ b/boa_engine/src/bytecompiler/class.rs @@ -26,24 +26,29 @@ impl ByteCompiler<'_, '_> { pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) { let class_name = class.name().map_or(Sym::EMPTY_STRING, Identifier::sym); - let class_env = match class.name() { + let old_lex_env = match class.name() { Some(name) if class.has_binding_identifier() => { + let old_lex_env = self.lexical_environment.clone(); let env_index = self.push_compile_environment(false); - self.create_immutable_binding(name, true); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); - true + self.lexical_environment + .create_immutable_binding(name, true); + Some(old_lex_env) } - _ => false, + _ => None, }; let mut compiler = ByteCompiler::new( class_name, true, self.json_parse, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); + compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; + // Function environment let _ = compiler.push_compile_environment(true); @@ -51,7 +56,7 @@ impl ByteCompiler<'_, '_> { compiler.length = expr.parameters().length(); compiler.params = expr.parameters().clone(); - let (env_label, _) = compiler.function_declaration_instantiation( + compiler.function_declaration_instantiation( expr.body(), expr.parameters(), false, @@ -61,23 +66,14 @@ impl ByteCompiler<'_, '_> { compiler.compile_statement_list(expr.body().statements(), false, false); - if env_label { - compiler.pop_compile_environment(); - } else { - compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; - } compiler.emit_opcode(Opcode::PushUndefined); + } else if class.super_ref().is_some() { + compiler.emit_opcode(Opcode::SuperCallDerived); } else { - if class.super_ref().is_some() { - compiler.emit_opcode(Opcode::SuperCallDerived); - } else { - compiler.emit_opcode(Opcode::RestParameterPop); - compiler.emit_opcode(Opcode::PushUndefined); - } - compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; + compiler.emit_opcode(Opcode::RestParameterPop); + compiler.emit_opcode(Opcode::PushUndefined); } compiler.emit_opcode(Opcode::SetReturnValue); - compiler.pop_compile_environment(); let code = Gc::new(compiler.finish()); let index = self.functions.len() as u32; @@ -117,9 +113,9 @@ impl ByteCompiler<'_, '_> { let mut static_elements = Vec::new(); let mut static_field_name_count = 0; - if class_env { + if old_lex_env.is_some() { self.emit_opcode(Opcode::Dup); - self.emit_binding(BindingOpcode::InitConst, class_name.into()); + self.emit_binding(BindingOpcode::InitLexical, class_name.into()); } // TODO: set function name for getter and setters @@ -277,7 +273,8 @@ impl ByteCompiler<'_, '_> { Sym::EMPTY_STRING, true, self.json_parse, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); @@ -290,8 +287,6 @@ impl ByteCompiler<'_, '_> { } field_compiler.emit_opcode(Opcode::SetReturnValue); - field_compiler.pop_compile_environment(); - field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; let code = field_compiler.finish(); @@ -311,7 +306,8 @@ impl ByteCompiler<'_, '_> { class_name, true, self.json_parse, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); let _ = field_compiler.push_compile_environment(true); @@ -320,8 +316,6 @@ impl ByteCompiler<'_, '_> { } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - field_compiler.pop_compile_environment(); - field_compiler.emit_opcode(Opcode::SetReturnValue); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; @@ -355,7 +349,8 @@ impl ByteCompiler<'_, '_> { class_name, true, self.json_parse, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); let _ = field_compiler.push_compile_environment(true); @@ -366,8 +361,6 @@ impl ByteCompiler<'_, '_> { } field_compiler.emit_opcode(Opcode::SetReturnValue); - field_compiler.pop_compile_environment(); - field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; let code = field_compiler.finish(); @@ -390,7 +383,8 @@ impl ByteCompiler<'_, '_> { Sym::EMPTY_STRING, true, false, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); let _ = compiler.push_compile_environment(true); @@ -404,7 +398,6 @@ impl ByteCompiler<'_, '_> { ); compiler.compile_statement_list(body.statements(), false, false); - compiler.pop_compile_environment(); let code = Gc::new(compiler.finish()); static_elements.push(StaticElement::StaticBlock(code)); @@ -589,8 +582,9 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::Swap); self.emit_opcode(Opcode::Pop); - if class_env { + if let Some(old_lex_env) = old_lex_env { self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } diff --git a/boa_engine/src/bytecompiler/declarations.rs b/boa_engine/src/bytecompiler/declarations.rs index 2ebfdd7a405..5e403923b00 100644 --- a/boa_engine/src/bytecompiler/declarations.rs +++ b/boa_engine/src/bytecompiler/declarations.rs @@ -1,6 +1,8 @@ +use std::rc::Rc; + use crate::{ bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind}, - environments::BindingLocatorError, + environments::CompileTimeEnvironment, vm::{ create_function_object_fast, create_generator_function_object, BindingOpcode, CodeBlockFlags, Opcode, @@ -32,7 +34,11 @@ impl ByteCompiler<'_, '_> { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation - pub(crate) fn global_declaration_instantiation(&mut self, script: &Script) -> JsResult<()> { + pub(crate) fn global_declaration_instantiation( + &mut self, + script: &Script, + env: &Rc, + ) -> JsResult<()> { // 1. Let lexNames be the LexicallyDeclaredNames of script. let lex_names = lexically_declared_names(script); @@ -43,9 +49,8 @@ impl ByteCompiler<'_, '_> { for name in lex_names { // Note: Our implementation differs from the spec here. // a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception. - // b. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. - if self.has_binding(name) { + if env.has_binding(name) { return Err(JsNativeError::syntax() .with_message("duplicate lexical declaration") .into()); @@ -65,7 +70,7 @@ impl ByteCompiler<'_, '_> { // 4. For each element name of varNames, do for name in var_names { // a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. - if self.has_binding(name) { + if env.has_lex_binding(name) { return Err(JsNativeError::syntax() .with_message("duplicate lexical declaration") .into()); @@ -176,7 +181,7 @@ impl ByteCompiler<'_, '_> { // would not produce any Early Errors for script, then if !lex_names.contains(&f) { // a. If env.HasLexicalDeclaration(F) is false, then - if !self.current_environment.has_lex_binding(f) { + if !env.has_lex_binding(f) { // i. Let fnDefinable be ? env.CanDeclareGlobalVar(F). let fn_definable = self.context.can_declare_global_function(f)?; @@ -222,17 +227,17 @@ impl ByteCompiler<'_, '_> { match declaration { Declaration::Class(class) => { for name in bound_names(class) { - self.create_mutable_binding(name, false); + env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Let(declaration)) => { for name in bound_names(declaration) { - self.create_mutable_binding(name, false); + env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Const(declaration)) => { for name in bound_names(declaration) { - self.create_immutable_binding(name, true); + env.create_immutable_binding(name, true); } } _ => {} @@ -271,7 +276,8 @@ impl ByteCompiler<'_, '_> { .compile( parameters, body, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); @@ -306,12 +312,13 @@ impl ByteCompiler<'_, '_> { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-blockdeclarationinstantiation - pub(crate) fn block_declaration_instantiation<'a, N>(&mut self, block: &'a N) - where + pub(crate) fn block_declaration_instantiation<'a, N>( + &mut self, + block: &'a N, + env: &Rc, + ) where &'a N: Into>, { - let env = &self.current_environment; - // 1. Let declarations be the LexicallyScopedDeclarations of code. let declarations = lexically_scoped_declarations(block); @@ -386,14 +393,9 @@ impl ByteCompiler<'_, '_> { &mut self, body: &Script, strict: bool, + var_env: &Rc, + lex_env: &Rc, ) -> JsResult<()> { - let var_environment_is_global = self - .context - .vm - .environments - .is_next_outer_function_environment_global() - && !strict; - // 2. Let varDeclarations be the VarScopedDeclarations of body. let var_declarations = var_scoped_declarations(body); @@ -403,30 +405,47 @@ impl ByteCompiler<'_, '_> { let var_names = var_declared_names(body); // a. If varEnv is a Global Environment Record, then - // i. For each element name of varNames, do - // 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. - // 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration. + if var_env.is_global() { + // i. For each element name of varNames, do + for name in &var_names { + // 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. + // 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration. + if var_env.has_lex_binding(*name) { + return Err(JsNativeError::syntax() + .with_message("duplicate lexical declaration") + .into()); + } + } + } + // b. Let thisEnv be lexEnv. + let mut this_env = lex_env.clone(); + // c. Assert: The following loop will terminate. // d. Repeat, while thisEnv is not varEnv, - // i. If thisEnv is not an Object Environment Record, then - // 1. NOTE: The environment of with statements cannot contain any lexical - // declaration so it doesn't need to be checked for var/let hoisting conflicts. - // 2. For each element name of varNames, do - // a. If ! thisEnv.HasBinding(name) is true, then - // i. Throw a SyntaxError exception. - // ii. NOTE: Annex B.3.4 defines alternate semantics for the above step. - // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration. - // ii. Set thisEnv to thisEnv.[[OuterEnv]]. - if let Some(name) = self - .context - .vm - .environments - .has_lex_binding_until_function_environment(&var_names) - { - let name = self.context.interner().resolve_expect(name.sym()); - let msg = format!("variable declaration {name} in eval function already exists as a lexical variable"); - return Err(JsNativeError::syntax().with_message(msg).into()); + while this_env.environment_index() != var_env.environment_index() { + // i. If thisEnv is not an Object Environment Record, then + // 1. NOTE: The environment of with statements cannot contain any lexical + // declaration so it doesn't need to be checked for var/let hoisting conflicts. + // 2. For each element name of varNames, do + for name in &var_names { + // a. If ! thisEnv.HasBinding(name) is true, then + if this_env.has_binding(*name) { + // i. Throw a SyntaxError exception. + // ii. NOTE: Annex B.3.4 defines alternate semantics for the above step. + let name = self.context.interner().resolve_expect(name.sym()); + let msg = format!("variable declaration {name} in eval function already exists as a lexical variable"); + return Err(JsNativeError::syntax().with_message(msg).into()); + } + // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration. + } + + // ii. Set thisEnv to thisEnv.[[OuterEnv]]. + if let Some(outer) = this_env.outer() { + this_env = outer; + } else { + break; + } } } @@ -482,7 +501,7 @@ impl ByteCompiler<'_, '_> { // a.iv. If declaredFunctionNames does not contain fn, then if !declared_function_names.contains(&name) { // 1. If varEnv is a Global Environment Record, then - if var_environment_is_global { + if var_env.is_global() { // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn). let fn_definable = self.context.can_declare_global_function(name)?; @@ -519,20 +538,35 @@ impl ByteCompiler<'_, '_> { // as a BindingIdentifier would not produce any Early Errors for body, then if !lexically_declared_names.contains(&f) { // 1. Let bindingExists be false. + let mut binding_exists = false; + // 2. Let thisEnv be lexEnv. + let mut this_env = lex_env.clone(); + // 3. Assert: The following loop will terminate. // 4. Repeat, while thisEnv is not varEnv, - // a. If thisEnv is not an Object Environment Record, then - // i. If ! thisEnv.HasBinding(F) is true, then - // i. Let bindingExists be true. - // b. Set thisEnv to thisEnv.[[OuterEnv]]. - let binding_exists = self.has_binding_until_var(f); + while this_env.environment_index() != lex_env.environment_index() { + // a. If thisEnv is not an Object Environment Record, then + // i. If ! thisEnv.HasBinding(F) is true, then + if this_env.has_binding(f) { + // i. Let bindingExists be true. + binding_exists = true; + break; + } + + // b. Set thisEnv to thisEnv.[[OuterEnv]]. + if let Some(outer) = this_env.outer() { + this_env = outer; + } else { + break; + } + } // 5. If bindingExists is false and varEnv is a Global Environment Record, then - let fn_definable = if !binding_exists && var_environment_is_global { + let fn_definable = if !binding_exists && var_env.is_global() { // a. If varEnv.HasLexicalDeclaration(F) is false, then // b. Else, - if self.current_environment.has_lex_binding(f) { + if self.variable_environment.has_lex_binding(f) { // i. Let fnDefinable be false. false } else { @@ -556,7 +590,7 @@ impl ByteCompiler<'_, '_> { && !function_names.contains(&f) { // i. If varEnv is a Global Environment Record, then - if var_environment_is_global { + if var_env.is_global() { // i. Perform ? varEnv.CreateGlobalVarBinding(F, true). self.context.create_global_var_binding(f, true)?; } @@ -564,12 +598,10 @@ impl ByteCompiler<'_, '_> { else { // i. Let bindingExists be ! varEnv.HasBinding(F). // ii. If bindingExists is false, then - if !self.has_binding(f) { + if !var_env.has_binding(f) { // i. Perform ! varEnv.CreateMutableBinding(F, true). - self.create_mutable_binding(f, true); - // ii. Perform ! varEnv.InitializeBinding(F, undefined). - let binding = self.initialize_mutable_binding(f, true); + let binding = var_env.create_mutable_binding(f, true); let index = self.get_or_insert_binding(binding); self.emit_opcode(Opcode::PushUndefined); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -608,7 +640,7 @@ impl ByteCompiler<'_, '_> { // 1. If declaredFunctionNames does not contain vn, then if !declared_function_names.contains(&name) { // a. If varEnv is a Global Environment Record, then - if var_environment_is_global { + if var_env.is_global() { // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn). let vn_definable = self.context.can_declare_global_var(name)?; @@ -645,17 +677,17 @@ impl ByteCompiler<'_, '_> { match declaration { Declaration::Class(class) => { for name in bound_names(class) { - self.create_mutable_binding(name, false); + lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Let(declaration)) => { for name in bound_names(declaration) { - self.create_mutable_binding(name, false); + lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Const(declaration)) => { for name in bound_names(declaration) { - self.create_immutable_binding(name, true); + lex_env.create_immutable_binding(name, true); } } _ => {} @@ -693,12 +725,13 @@ impl ByteCompiler<'_, '_> { .compile( parameters, body, - self.context.vm.environments.current_compile_environment(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); // c. If varEnv is a Global Environment Record, then - if var_environment_is_global { + if var_env.is_global() { // Ensures global functions are printed when generating the global flowgraph. self.functions.push(code.clone()); @@ -735,31 +768,20 @@ impl ByteCompiler<'_, '_> { } // i. Let bindingExists be ! varEnv.HasBinding(fn). - let binding_exists = self.has_binding_eval(name, strict); + let binding_exists = var_env.has_binding(name); // ii. If bindingExists is false, then // iii. Else, if binding_exists { // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false). - match self.set_mutable_binding(name) { - Ok(binding) => { - let index = self.get_or_insert_binding(binding); - self.emit_with_varying_operand(Opcode::SetName, index); - } - Err(BindingLocatorError::MutateImmutable) => { - let index = self.get_or_insert_name(name); - self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); - } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } - } + let binding = var_env.set_mutable_binding(name).expect("must not fail"); + let index = self.get_or_insert_binding(binding); + self.emit_with_varying_operand(Opcode::SetName, index); } else { // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14. // 2. Perform ! varEnv.CreateMutableBinding(fn, true). // 3. Perform ! varEnv.InitializeBinding(fn, fo). - self.create_mutable_binding(name, !strict); - let binding = self.initialize_mutable_binding(name, !strict); + let binding = var_env.create_mutable_binding(name, !strict); let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::DefInitVar, index); } @@ -769,22 +791,21 @@ impl ByteCompiler<'_, '_> { // 18. For each String vn of declaredVarNames, do for name in declared_var_names { // a. If varEnv is a Global Environment Record, then - if var_environment_is_global { + if var_env.is_global() { // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true). self.context.create_global_var_binding(name, true)?; } // b. Else, else { // i. Let bindingExists be ! varEnv.HasBinding(vn). - let binding_exists = self.has_binding_eval(name, strict); + let binding_exists = var_env.has_binding(name); // ii. If bindingExists is false, then if !binding_exists { // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14. // 2. Perform ! varEnv.CreateMutableBinding(vn, true). // 3. Perform ! varEnv.InitializeBinding(vn, undefined). - self.create_mutable_binding(name, !strict); - let binding = self.initialize_mutable_binding(name, !strict); + let binding = var_env.create_mutable_binding(name, true); let index = self.get_or_insert_binding(binding); self.emit_opcode(Opcode::PushUndefined); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -809,10 +830,7 @@ impl ByteCompiler<'_, '_> { arrow: bool, strict: bool, generator: bool, - ) -> (bool, bool) { - let mut env_label = false; - let mut additional_env = false; - + ) { // 1. Let calleeContext be the running execution context. // 2. Let code be func.[[ECMAScriptCode]]. // 3. Let strict be func.[[Strict]]. @@ -899,11 +917,13 @@ impl ByteCompiler<'_, '_> { } // 19. If strict is true or hasParameterExpressions is false, then - // a. NOTE: Only a single Environment Record is needed for the parameters, - // since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval. - // b. Let env be the LexicalEnvironment of calleeContext. + if strict || !has_parameter_expressions { + // a. NOTE: Only a single Environment Record is needed for the parameters, + // since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval. + // b. Let env be the LexicalEnvironment of calleeContext. + } // 20. Else, - if !strict && has_parameter_expressions { + else { // a. NOTE: A separate Environment Record is needed to ensure that bindings created by // direct eval calls in the formal parameter list are outside the environment where parameters are declared. // b. Let calleeEnv be the LexicalEnvironment of calleeContext. @@ -911,8 +931,10 @@ impl ByteCompiler<'_, '_> { // d. Assert: The VariableEnvironment of calleeContext is calleeEnv. // e. Set the LexicalEnvironment of calleeContext to env. let _ = self.push_compile_environment(false); - additional_env = true; - } + self.code_block_flags |= CodeBlockFlags::PARAMETERS_ENV_BINDINGS; + }; + + let env = self.lexical_environment.clone(); // 22. If argumentsObjectNeeded is true, then // @@ -933,12 +955,12 @@ impl ByteCompiler<'_, '_> { // i. Perform ! env.CreateImmutableBinding("arguments", false). // ii. NOTE: In strict mode code early errors prevent attempting to assign // to this binding, so its mutability is not observable. - self.create_immutable_binding(arguments, false); + env.create_immutable_binding(arguments, false); } // d. Else, else { // i. Perform ! env.CreateMutableBinding("arguments", false). - self.create_mutable_binding(arguments, false); + env.create_mutable_binding(arguments, false); } self.code_block_flags |= CodeBlockFlags::NEEDS_ARGUMENTS_OBJECT; @@ -947,7 +969,7 @@ impl ByteCompiler<'_, '_> { // 21. For each String paramName of parameterNames, do for param_name in ¶meter_names { // a. Let alreadyDeclared be ! env.HasBinding(paramName). - let already_declared = self.has_binding(*param_name); + let already_declared = env.has_binding(*param_name); // b. NOTE: Early errors ensure that duplicate parameter names can only occur in non-strict // functions that do not have parameter default values or rest parameters. @@ -955,7 +977,7 @@ impl ByteCompiler<'_, '_> { // c. If alreadyDeclared is false, then if !already_declared { // i. Perform ! env.CreateMutableBinding(paramName, false). - self.create_mutable_binding(*param_name, false); + env.create_mutable_binding(*param_name, false); // Note: These steps are not necessary in our implementation. // ii. If hasDuplicates is true, then @@ -991,24 +1013,20 @@ impl ByteCompiler<'_, '_> { } match parameter.variable().binding() { Binding::Identifier(ident) => { - self.create_mutable_binding(*ident, false); if let Some(init) = parameter.variable().init() { let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); self.compile_expr(init, true); self.patch_jump(skip); } - self.emit_binding(BindingOpcode::InitLet, *ident); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { - for ident in bound_names(pattern) { - self.create_mutable_binding(ident, false); - } if let Some(init) = parameter.variable().init() { let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); self.compile_expr(init, true); self.patch_jump(skip); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } } } @@ -1024,7 +1042,7 @@ impl ByteCompiler<'_, '_> { // 27. If hasParameterExpressions is false, then // 28. Else, #[allow(unused_variables, unused_mut)] - let mut instantiated_var_names = if has_parameter_expressions { + let (mut instantiated_var_names, mut var_env) = if has_parameter_expressions { // a. NOTE: A separate Environment Record is needed to ensure that closures created by // expressions in the formal parameter list do not have // visibility of declarations in the function body. @@ -1032,7 +1050,8 @@ impl ByteCompiler<'_, '_> { // c. Set the VariableEnvironment of calleeContext to varEnv. let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); - env_label = true; + + let mut var_env = self.lexical_environment.clone(); // d. Let instantiatedVarNames be a new empty List. let mut instantiated_var_names = Vec::new(); @@ -1045,7 +1064,7 @@ impl ByteCompiler<'_, '_> { instantiated_var_names.push(n); // 2. Perform ! varEnv.CreateMutableBinding(n, false). - self.create_mutable_binding(n, true); + let binding = var_env.create_mutable_binding(n, false); // 3. If parameterBindings does not contain n, or if functionNames contains n, then if !parameter_bindings.contains(&n) || function_names.contains(&n) { @@ -1055,13 +1074,12 @@ impl ByteCompiler<'_, '_> { // 4. Else, else { // a. Let initialValue be ! env.GetBindingValue(n, false). - let binding = self.get_binding_value(n); + let binding = env.get_binding(n).expect("must have binding"); let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::GetName, index); } // 5. Perform ! varEnv.InitializeBinding(n, initialValue). - let binding = self.initialize_mutable_binding(n, true); let index = self.get_or_insert_binding(binding); self.emit_opcode(Opcode::PushUndefined); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -1071,7 +1089,7 @@ impl ByteCompiler<'_, '_> { } } - instantiated_var_names + (instantiated_var_names, var_env) } else { // a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars. // b. Let instantiatedVarNames be a copy of the List parameterBindings. @@ -1085,10 +1103,8 @@ impl ByteCompiler<'_, '_> { instantiated_var_names.push(n); // 2. Perform ! env.CreateMutableBinding(n, false). - self.create_mutable_binding(n, true); - // 3. Perform ! env.InitializeBinding(n, undefined). - let binding = self.initialize_mutable_binding(n, true); + let binding = env.create_mutable_binding(n, true); let index = self.get_or_insert_binding(binding); self.emit_opcode(Opcode::PushUndefined); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -1096,7 +1112,7 @@ impl ByteCompiler<'_, '_> { } // d. Let varEnv be env. - instantiated_var_names + (instantiated_var_names, env) }; // 29. NOTE: Annex B.3.2.1 adds additional steps at this point. @@ -1117,10 +1133,8 @@ impl ByteCompiler<'_, '_> { // 2. If initializedBindings does not contain F and F is not "arguments", then if !instantiated_var_names.contains(&f) && f != arguments { // a. Perform ! varEnv.CreateMutableBinding(F, false). - self.create_mutable_binding(f, true); - // b. Perform ! varEnv.InitializeBinding(F, undefined). - let binding = self.initialize_mutable_binding(f, true); + let binding = var_env.create_mutable_binding(f, false); let index = self.get_or_insert_binding(binding); self.emit_opcode(Opcode::PushUndefined); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -1142,14 +1156,22 @@ impl ByteCompiler<'_, '_> { } // 30. If strict is false, then - // 30.a. Let lexEnv be NewDeclarativeEnvironment(varEnv). - // 30.b. NOTE: Non-strict functions use a separate Environment Record for top-level lexical - // declarations so that a direct eval can determine whether any var scoped declarations - // introduced by the eval code conflict with pre-existing top-level lexically scoped declarations. - // This is not needed for strict functions because a strict direct eval always - // places all declarations into a new Environment Record. // 31. Else, - // a. Let lexEnv be varEnv. + let lex_env = if strict { + // a. Let lexEnv be varEnv. + var_env + } else { + // a. Let lexEnv be NewDeclarativeEnvironment(varEnv). + // b. NOTE: Non-strict functions use a separate Environment Record for top-level lexical + // declarations so that a direct eval can determine whether any var scoped declarations + // introduced by the eval code conflict with pre-existing top-level lexically scoped declarations. + // This is not needed for strict functions because a strict direct eval always + // places all declarations into a new Environment Record. + let env_index = self.push_compile_environment(false); + self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); + self.lexical_environment.clone() + }; + // 32. Set the LexicalEnvironment of calleeContext to lexEnv. // 33. Let lexDeclarations be the LexicallyScopedDeclarations of code. @@ -1166,17 +1188,17 @@ impl ByteCompiler<'_, '_> { match declaration { Declaration::Class(class) => { for name in bound_names(class) { - self.create_mutable_binding(name, false); + lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Let(declaration)) => { for name in bound_names(declaration) { - self.create_mutable_binding(name, false); + lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Const(declaration)) => { for name in bound_names(declaration) { - self.create_immutable_binding(name, true); + lex_env.create_immutable_binding(name, true); } } _ => {} @@ -1194,6 +1216,5 @@ impl ByteCompiler<'_, '_> { } // 37. Return unused. - (env_label, additional_env) } } diff --git a/boa_engine/src/bytecompiler/env.rs b/boa_engine/src/bytecompiler/env.rs index e85431b3ac1..ae2137c54f0 100644 --- a/boa_engine/src/bytecompiler/env.rs +++ b/boa_engine/src/bytecompiler/env.rs @@ -1,8 +1,6 @@ -use std::rc::Rc; - use super::ByteCompiler; -use crate::environments::{BindingLocator, BindingLocatorError, CompileTimeEnvironment}; -use boa_ast::expression::Identifier; +use crate::environments::CompileTimeEnvironment; +use std::rc::Rc; impl ByteCompiler<'_, '_> { /// Push either a new declarative or function environment on the compile time environment stack. @@ -10,111 +8,25 @@ impl ByteCompiler<'_, '_> { pub(crate) fn push_compile_environment(&mut self, function_scope: bool) -> u32 { self.current_open_environments_count += 1; - self.current_environment = Rc::new(CompileTimeEnvironment::new( - self.current_environment.clone(), + let env = Rc::new(CompileTimeEnvironment::new( + self.lexical_environment.clone(), function_scope, )); let index = self.compile_environments.len() as u32; - self.compile_environments - .push(self.current_environment.clone()); + self.compile_environments.push(env.clone()); + + if function_scope { + self.variable_environment = env.clone(); + } + + self.lexical_environment = env; index } /// Pops the top compile time environment and returns its index in the compile time environments array. - #[track_caller] pub(crate) fn pop_compile_environment(&mut self) { self.current_open_environments_count -= 1; - - let outer = self - .current_environment - .outer() - .expect("cannot pop the global environment"); - self.current_environment = outer; - } - - /// Get the binding locator of the binding at bytecode compile time. - pub(crate) fn get_binding_value(&self, name: Identifier) -> BindingLocator { - self.current_environment.get_binding_recursive(name) - } - - /// Return if a declarative binding exists at bytecode compile time. - /// This does not include bindings on the global object. - pub(crate) fn has_binding(&self, name: Identifier) -> bool { - self.current_environment.has_binding_recursive(name) - } - - /// Check if a binding name exists in a environment. - /// If strict is `false` check until a function scope is reached. - pub(crate) fn has_binding_eval(&self, name: Identifier, strict: bool) -> bool { - self.current_environment.has_binding_eval(name, strict) - } - - #[cfg(feature = "annex-b")] - /// Check if a binding name exists in a environment. - /// Stop when a function scope is reached. - pub(crate) fn has_binding_until_var(&self, name: Identifier) -> bool { - self.current_environment.has_binding_until_var(name) - } - - /// Create a mutable binding at bytecode compile time. - /// This function returns a syntax error, if the binding is a redeclaration. - /// - /// # Panics - /// - /// Panics if the global environment is not function scoped. - pub(crate) fn create_mutable_binding(&mut self, name: Identifier, function_scope: bool) { - assert!(self - .current_environment - .create_mutable_binding(name, function_scope)); - } - - /// Initialize a mutable binding at bytecode compile time and return its binding locator. - pub(crate) fn initialize_mutable_binding( - &self, - name: Identifier, - function_scope: bool, - ) -> BindingLocator { - self.current_environment - .initialize_mutable_binding(name, function_scope) - } - - /// Create an immutable binding at bytecode compile time. - /// This function returns a syntax error, if the binding is a redeclaration. - /// - /// # Panics - /// - /// Panics if the global environment does not exist. - pub(crate) fn create_immutable_binding(&mut self, name: Identifier, strict: bool) { - self.current_environment - .create_immutable_binding(name, strict); - } - - /// Initialize an immutable binding at bytecode compile time and return it's binding locator. - /// - /// # Panics - /// - /// Panics if the global environment does not exist or a the binding was not created on the current environment. - pub(crate) fn initialize_immutable_binding(&self, name: Identifier) -> BindingLocator { - self.current_environment.initialize_immutable_binding(name) - } - - /// Return the binding locator for a set operation on an existing binding. - pub(crate) fn set_mutable_binding( - &self, - name: Identifier, - ) -> Result { - self.current_environment.set_mutable_binding_recursive(name) - } - - #[cfg(feature = "annex-b")] - /// Return the binding locator for a set operation on an existing var binding. - pub(crate) fn set_mutable_binding_var( - &self, - name: Identifier, - ) -> Result { - self.current_environment - .set_mutable_binding_var_recursive(name) } } diff --git a/boa_engine/src/bytecompiler/expression/assign.rs b/boa_engine/src/bytecompiler/expression/assign.rs index 9efc49e2b23..0bf4c403749 100644 --- a/boa_engine/src/bytecompiler/expression/assign.rs +++ b/boa_engine/src/bytecompiler/expression/assign.rs @@ -55,11 +55,10 @@ impl ByteCompiler<'_, '_> { match access { Access::Variable { name } => { - let binding = self.get_binding_value(name); - let index = self.get_or_insert_binding(binding); - let lex = self.current_environment.is_lex_binding(name); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); - if lex { + if binding.is_lexical() { self.emit_with_varying_operand(Opcode::GetName, index); } else { self.emit_with_varying_operand(Opcode::GetNameAndLocator, index); @@ -75,8 +74,8 @@ impl ByteCompiler<'_, '_> { if use_expr { self.emit_opcode(Opcode::Dup); } - if lex { - match self.set_mutable_binding(name) { + if binding.is_lexical() { + match self.lexical_environment.set_mutable_binding(name) { Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::SetName, index); diff --git a/boa_engine/src/bytecompiler/expression/unary.rs b/boa_engine/src/bytecompiler/expression/unary.rs index 662d403afc5..55715fea5ee 100644 --- a/boa_engine/src/bytecompiler/expression/unary.rs +++ b/boa_engine/src/bytecompiler/expression/unary.rs @@ -27,8 +27,10 @@ impl ByteCompiler<'_, '_> { UnaryOp::TypeOf => { match unary.target().flatten() { Expression::Identifier(identifier) => { - let binding = self.get_binding_value(*identifier); - let index = self.get_or_insert_binding(binding); + let binding = self + .lexical_environment + .get_identifier_reference(*identifier); + let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::GetNameOrUndefined, index); } expr => self.compile_expr(expr, true), diff --git a/boa_engine/src/bytecompiler/expression/update.rs b/boa_engine/src/bytecompiler/expression/update.rs index 110cc6b4400..6a6e69acff1 100644 --- a/boa_engine/src/bytecompiler/expression/update.rs +++ b/boa_engine/src/bytecompiler/expression/update.rs @@ -23,11 +23,10 @@ impl ByteCompiler<'_, '_> { match Access::from_update_target(update.target()) { Access::Variable { name } => { - let binding = self.get_binding_value(name); - let index = self.get_or_insert_binding(binding); - let lex = self.current_environment.is_lex_binding(name); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); - if lex { + if binding.is_lexical() { self.emit_with_varying_operand(Opcode::GetName, index); } else { self.emit_with_varying_operand(Opcode::GetNameAndLocator, index); @@ -40,8 +39,8 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::Dup); } - if lex { - match self.set_mutable_binding(name) { + if binding.is_lexical() { + match self.lexical_environment.set_mutable_binding(name) { Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::SetName, index); diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index be072a6198a..e955df62627 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -82,14 +82,22 @@ impl FunctionCompiler { mut self, parameters: &FormalParameterList, body: &FunctionBody, - outer_env: Rc, + variable_environment: Rc, + lexical_environment: Rc, context: &mut Context<'_>, ) -> Gc { self.strict = self.strict || body.strict(); let length = parameters.length(); - let mut compiler = ByteCompiler::new(self.name, self.strict, false, outer_env, context); + let mut compiler = ByteCompiler::new( + self.name, + self.strict, + false, + variable_environment, + lexical_environment, + context, + ); compiler.length = length; compiler.in_async = self.r#async; compiler.in_generator = self.generator; @@ -101,7 +109,9 @@ impl FunctionCompiler { if let Some(binding_identifier) = self.binding_identifier { compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER; let _ = compiler.push_compile_environment(false); - compiler.create_immutable_binding(binding_identifier.into(), self.strict); + compiler + .lexical_environment + .create_immutable_binding(binding_identifier.into(), self.strict); } // Function environment @@ -132,7 +142,7 @@ impl FunctionCompiler { compiler.async_handler = Some(compiler.push_handler()); } - let (env_label, additional_env) = compiler.function_declaration_instantiation( + compiler.function_declaration_instantiation( body, parameters, self.arrow, @@ -155,21 +165,6 @@ impl FunctionCompiler { compiler.compile_statement_list(body.statements(), false, false); - if env_label { - compiler.pop_compile_environment(); - } - - if additional_env { - compiler.pop_compile_environment(); - compiler.code_block_flags |= CodeBlockFlags::PARAMETERS_ENV_BINDINGS; - } - - compiler.pop_compile_environment(); - - if self.binding_identifier.is_some() { - compiler.pop_compile_environment(); - } - compiler.params = parameters.clone(); Gc::new(compiler.finish()) diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 4ed65fcb329..b61caf514db 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -263,8 +263,11 @@ pub struct ByteCompiler<'ctx, 'host> { /// Compile time environments in this function. pub(crate) compile_environments: Vec>, - /// The environment that is currently active. - pub(crate) current_environment: Rc, + /// The current variable environment. + pub(crate) variable_environment: Rc, + + /// The current lexical environment. + pub(crate) lexical_environment: Rc, current_open_environments_count: u32, current_stack_value_count: u32, @@ -301,7 +304,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { name: Sym, strict: bool, json_parse: bool, - current_environment: Rc, + variable_environment: Rc, + lexical_environment: Rc, // TODO: remove when we separate scripts from the context context: &'ctx mut Context<'host>, ) -> ByteCompiler<'ctx, 'host> { @@ -332,7 +336,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { in_generator: false, async_handler: None, json_parse, - current_environment, + variable_environment, + lexical_environment, context, #[cfg(feature = "annex-b")] @@ -408,42 +413,29 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) { match opcode { BindingOpcode::Var => { - let binding = self.initialize_mutable_binding(name, true); - let index = self.get_or_insert_binding(binding); + let binding = self.variable_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::DefVar, index); } - BindingOpcode::InitVar => { - if self.has_binding(name) { - match self.set_mutable_binding(name) { - Ok(binding) => { - let index = self.get_or_insert_binding(binding); - self.emit_with_varying_operand(Opcode::DefInitVar, index); - } - Err(BindingLocatorError::MutateImmutable) => { - let index = self.get_or_insert_name(name); - self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); - } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } - } - } else { - let binding = self.initialize_mutable_binding(name, true); + BindingOpcode::InitVar => match self.lexical_environment.set_mutable_binding(name) { + Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::DefInitVar, index); - }; - } - BindingOpcode::InitLet => { - let binding = self.initialize_mutable_binding(name, false); - let index = self.get_or_insert_binding(binding); - self.emit_with_varying_operand(Opcode::PutLexicalValue, index); - } - BindingOpcode::InitConst => { - let binding = self.initialize_immutable_binding(name); - let index = self.get_or_insert_binding(binding); + } + Err(BindingLocatorError::MutateImmutable) => { + let index = self.get_or_insert_name(name); + self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); + } + Err(BindingLocatorError::Silent) => { + self.emit_opcode(Opcode::Pop); + } + }, + BindingOpcode::InitLexical => { + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::PutLexicalValue, index); } - BindingOpcode::SetName => match self.set_mutable_binding(name) { + BindingOpcode::SetName => match self.lexical_environment.set_mutable_binding(name) { Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::SetName, index); @@ -698,8 +690,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { fn access_get(&mut self, access: Access<'_>, use_expr: bool) { match access { Access::Variable { name } => { - let binding = self.get_binding_value(name); - let index = self.get_or_insert_binding(binding); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::GetName, index); } Access::Property { access } => match access { @@ -763,11 +755,10 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { { match access { Access::Variable { name } => { - let binding = self.get_binding_value(name); - let index = self.get_or_insert_binding(binding); - let lex = self.current_environment.is_lex_binding(name); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); - if !lex { + if !binding.is_lexical() { self.emit_with_varying_operand(Opcode::GetLocator, index); } @@ -776,8 +767,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { self.emit(Opcode::Dup, &[]); } - if lex { - match self.set_mutable_binding(name) { + if binding.is_lexical() { + match self.lexical_environment.set_mutable_binding(name) { Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::SetName, index); @@ -876,8 +867,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { } }, Access::Variable { name } => { - let binding = self.get_binding_value(name); - let index = self.get_or_insert_binding(binding); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::DeleteName, index); } Access::This => { @@ -1130,7 +1121,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { } else { self.emit_opcode(Opcode::PushUndefined); } - self.emit_binding(BindingOpcode::InitLet, *ident); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { if let Some(init) = variable.init() { @@ -1139,7 +1130,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { self.emit_opcode(Opcode::PushUndefined); }; - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } } } @@ -1152,7 +1143,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { .init() .expect("const declaration must have initializer"); self.compile_expr(init, true); - self.emit_binding(BindingOpcode::InitConst, *ident); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { if let Some(init) = variable.init() { @@ -1161,7 +1152,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { self.emit_opcode(Opcode::PushUndefined); }; - self.compile_declaration_pattern(pattern, BindingOpcode::InitConst); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } } } @@ -1189,11 +1180,11 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { .name() .expect("function declaration must have name"); if self.annex_b_function_names.contains(&name) { - let binding = self.get_binding_value(name); - let index = self.get_or_insert_binding(binding); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::GetName, index); - match self.set_mutable_binding_var(name) { + match self.variable_environment.set_mutable_binding_var(name) { Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::SetName, index); @@ -1250,7 +1241,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { .compile( parameters, body, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); @@ -1347,7 +1339,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { .compile( parameters, body, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); @@ -1410,7 +1403,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { .compile( parameters, body, - self.current_environment.clone(), + self.variable_environment.clone(), + self.lexical_environment.clone(), self.context, ); diff --git a/boa_engine/src/bytecompiler/module.rs b/boa_engine/src/bytecompiler/module.rs index a5312a4290f..c417c6b1a7b 100644 --- a/boa_engine/src/bytecompiler/module.rs +++ b/boa_engine/src/bytecompiler/module.rs @@ -46,14 +46,14 @@ impl ByteCompiler<'_, '_> { self.class(cl, cl.name().is_none()); if cl.name().is_none() { self.emit_binding( - BindingOpcode::InitLet, + BindingOpcode::InitLexical, Identifier::from(Sym::DEFAULT_EXPORT), ); } } ExportDeclaration::DefaultAssignmentExpression(expr) => { let name = Identifier::from(Sym::DEFAULT_EXPORT); - self.create_mutable_binding(name, false); + self.lexical_environment.create_mutable_binding(name, false); self.compile_expr(expr, true); if expr.is_anonymous_function_definition() { @@ -66,7 +66,7 @@ impl ByteCompiler<'_, '_> { self.emit(Opcode::SetFunctionName, &[Operand::U8(0)]); } - self.emit_binding(BindingOpcode::InitLet, name); + self.emit_binding(BindingOpcode::InitLexical, name); } } } diff --git a/boa_engine/src/bytecompiler/statement/block.rs b/boa_engine/src/bytecompiler/statement/block.rs index 5982dfd0731..e96096edbdf 100644 --- a/boa_engine/src/bytecompiler/statement/block.rs +++ b/boa_engine/src/bytecompiler/statement/block.rs @@ -4,13 +4,16 @@ use boa_ast::statement::Block; impl ByteCompiler<'_, '_> { /// Compile a [`Block`] `boa_ast` node pub(crate) fn compile_block(&mut self, block: &Block, use_expr: bool) { + let old_lex_env = self.lexical_environment.clone(); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); + let env = self.lexical_environment.clone(); - self.block_declaration_instantiation(block); + self.block_declaration_instantiation(block, &env); self.compile_statement_list(block.statement_list(), use_expr, true); self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } } diff --git a/boa_engine/src/bytecompiler/statement/loop.rs b/boa_engine/src/bytecompiler/statement/loop.rs index 4088a7e3b1c..ada2c3c3f42 100644 --- a/boa_engine/src/bytecompiler/statement/loop.rs +++ b/boa_engine/src/bytecompiler/statement/loop.rs @@ -22,7 +22,7 @@ impl ByteCompiler<'_, '_> { use_expr: bool, ) { let mut let_binding_indices = None; - let mut has_lexical_environment_binding = false; + let mut old_lex_env = None; if let Some(init) = for_loop.init() { match init { @@ -31,20 +31,22 @@ impl ByteCompiler<'_, '_> { self.compile_var_decl(decl); } ForLoopInitializer::Lexical(decl) => { + old_lex_env = Some(self.lexical_environment.clone()); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); - has_lexical_environment_binding = true; let names = bound_names(decl); if decl.is_const() { for name in &names { - self.create_immutable_binding(*name, true); + self.lexical_environment + .create_immutable_binding(*name, true); } } else { let mut indices = Vec::new(); for name in &names { - self.create_mutable_binding(*name, false); - let binding = self.initialize_mutable_binding(*name, false); + let binding = self + .lexical_environment + .create_mutable_binding(*name, false); let index = self.get_or_insert_binding(binding); indices.push(index); } @@ -101,8 +103,9 @@ impl ByteCompiler<'_, '_> { self.patch_jump(exit); self.pop_loop_control_info(); - if has_lexical_environment_binding { + if let Some(old_lex_env) = old_lex_env { self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } } @@ -130,15 +133,18 @@ impl ByteCompiler<'_, '_> { if initializer_bound_names.is_empty() { self.compile_expr(for_in_loop.target(), true); } else { + let old_lex_env = self.lexical_environment.clone(); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); for name in &initializer_bound_names { - self.create_mutable_binding(*name, false); + self.lexical_environment + .create_mutable_binding(*name, false); } self.compile_expr(for_in_loop.target(), true); self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } @@ -155,7 +161,10 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::IteratorValue); + let mut old_lex_env = None; + if !initializer_bound_names.is_empty() { + old_lex_env = Some(self.lexical_environment.clone()); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); } @@ -181,26 +190,30 @@ impl ByteCompiler<'_, '_> { }, IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { - self.create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLet, *ident); + self.lexical_environment + .create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.create_mutable_binding(ident, false); + self.lexical_environment + .create_mutable_binding(ident, false); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.create_immutable_binding(*ident, true); - self.emit_binding(BindingOpcode::InitConst, *ident); + self.lexical_environment + .create_immutable_binding(*ident, true); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.create_immutable_binding(ident, true); + self.lexical_environment + .create_immutable_binding(ident, true); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitConst); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } }, IterableLoopInitializer::Pattern(pattern) => { @@ -210,8 +223,9 @@ impl ByteCompiler<'_, '_> { self.compile_stmt(for_in_loop.body(), use_expr, true); - if !initializer_bound_names.is_empty() { + if let Some(old_lex_env) = old_lex_env { self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } @@ -242,15 +256,18 @@ impl ByteCompiler<'_, '_> { if initializer_bound_names.is_empty() { self.compile_expr(for_of_loop.iterable(), true); } else { + let old_lex_env = self.lexical_environment.clone(); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); for name in &initializer_bound_names { - self.create_mutable_binding(*name, false); + self.lexical_environment + .create_mutable_binding(*name, false); } self.compile_expr(for_of_loop.iterable(), true); self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } @@ -279,7 +296,10 @@ impl ByteCompiler<'_, '_> { let exit = self.jump_if_true(); self.emit_opcode(Opcode::IteratorValue); + let mut old_lex_env = None; + if !initializer_bound_names.is_empty() { + old_lex_env = Some(self.lexical_environment.clone()); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); }; @@ -287,7 +307,7 @@ impl ByteCompiler<'_, '_> { let mut handler_index = None; match for_of_loop.initializer() { IterableLoopInitializer::Identifier(ref ident) => { - match self.set_mutable_binding(*ident) { + match self.lexical_environment.set_mutable_binding(*ident) { Ok(binding) => { let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -323,26 +343,30 @@ impl ByteCompiler<'_, '_> { } IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { - self.create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLet, *ident); + self.lexical_environment + .create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.create_mutable_binding(ident, false); + self.lexical_environment + .create_mutable_binding(ident, false); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.create_immutable_binding(*ident, true); - self.emit_binding(BindingOpcode::InitConst, *ident); + self.lexical_environment + .create_immutable_binding(*ident, true); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.create_immutable_binding(ident, true); + self.lexical_environment + .create_immutable_binding(ident, true); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitConst); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } }, IterableLoopInitializer::Pattern(pattern) => { @@ -375,8 +399,9 @@ impl ByteCompiler<'_, '_> { self.compile_stmt(for_of_loop.body(), use_expr, true); - if !initializer_bound_names.is_empty() { + if let Some(old_lex_env) = old_lex_env { self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } diff --git a/boa_engine/src/bytecompiler/statement/switch.rs b/boa_engine/src/bytecompiler/statement/switch.rs index fd007840671..2540e26ca2e 100644 --- a/boa_engine/src/bytecompiler/statement/switch.rs +++ b/boa_engine/src/bytecompiler/statement/switch.rs @@ -6,10 +6,12 @@ impl ByteCompiler<'_, '_> { pub(crate) fn compile_switch(&mut self, switch: &Switch, use_expr: bool) { self.compile_expr(switch.val(), true); + let old_lex_env = self.lexical_environment.clone(); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); + let env = self.lexical_environment.clone(); - self.block_declaration_instantiation(switch); + self.block_declaration_instantiation(switch, &env); let start_address = self.next_opcode_location(); self.push_switch_control_info(None, start_address, use_expr); @@ -51,6 +53,7 @@ impl ByteCompiler<'_, '_> { self.pop_switch_control_info(); self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } } diff --git a/boa_engine/src/bytecompiler/statement/try.rs b/boa_engine/src/bytecompiler/statement/try.rs index 07a6bb1b1c4..65486a1e43d 100644 --- a/boa_engine/src/bytecompiler/statement/try.rs +++ b/boa_engine/src/bytecompiler/statement/try.rs @@ -111,20 +111,22 @@ impl ByteCompiler<'_, '_> { pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, _has_finally: bool, use_expr: bool) { // stack: exception + let old_lex_env = self.lexical_environment.clone(); let env_index = self.push_compile_environment(false); self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); + let env = self.lexical_environment.clone(); if let Some(binding) = catch.parameter() { match binding { Binding::Identifier(ident) => { - self.create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLet, *ident); + env.create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLexical, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.create_mutable_binding(ident, false); + env.create_mutable_binding(ident, false); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); } } } else { @@ -134,6 +136,7 @@ impl ByteCompiler<'_, '_> { self.compile_catch_finally_block(catch.block(), use_expr); self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } diff --git a/boa_engine/src/bytecompiler/statement/with.rs b/boa_engine/src/bytecompiler/statement/with.rs index db804296776..a2a55f89ad3 100644 --- a/boa_engine/src/bytecompiler/statement/with.rs +++ b/boa_engine/src/bytecompiler/statement/with.rs @@ -6,12 +6,14 @@ impl ByteCompiler<'_, '_> { pub(crate) fn compile_with(&mut self, with: &With, use_expr: bool) { self.compile_expr(with.expression(), true); + let old_lex_env = self.lexical_environment.clone(); let _ = self.push_compile_environment(false); self.emit_opcode(Opcode::PushObjectEnvironment); self.compile_stmt(with.statement(), use_expr, true); self.pop_compile_environment(); + self.lexical_environment = old_lex_env; self.emit_opcode(Opcode::PopEnvironment); } } diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index f0dad91db2a..deb7c97a10c 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -65,20 +65,23 @@ impl CompileTimeEnvironment { .map_or(false, |binding| binding.lex) } - #[cfg(feature = "annex-b")] /// Check if the environment has a binding with the given name. pub(crate) fn has_binding(&self, name: Identifier) -> bool { self.bindings.borrow().contains_key(&name) } - /// Checks if `name` is a lexical binding. - pub(crate) fn is_lex_binding(&self, name: Identifier) -> bool { + /// Get the binding locator for a binding with the given name. + /// Fall back to the global environment if the binding is not found. + pub(crate) fn get_identifier_reference(&self, name: Identifier) -> IdentifierReference { if let Some(binding) = self.bindings.borrow().get(&name) { - binding.lex + IdentifierReference::new( + BindingLocator::declarative(name, self.environment_index, binding.index), + binding.lex, + ) } else if let Some(outer) = &self.outer { - outer.is_lex_binding(name) + outer.get_identifier_reference(name) } else { - false + IdentifierReference::new(BindingLocator::global(name), false) } } @@ -87,11 +90,21 @@ impl CompileTimeEnvironment { self.bindings.borrow().len() as u32 } + /// Returns the index of this environment. + pub(crate) fn environment_index(&self) -> u32 { + self.environment_index + } + /// Check if the environment is a function environment. pub(crate) const fn is_function(&self) -> bool { self.function_scope } + /// Check if the environment is a global environment. + pub(crate) const fn is_global(&self) -> bool { + self.outer.is_none() + } + /// Get the locator for a binding name. pub(crate) fn get_binding(&self, name: Identifier) -> Option { self.bindings @@ -100,105 +113,31 @@ impl CompileTimeEnvironment { .map(|binding| BindingLocator::declarative(name, self.environment_index, binding.index)) } - /// Get the locator for a binding name in this and all outer environments. - pub(crate) fn get_binding_recursive(&self, name: Identifier) -> BindingLocator { - if let Some(binding) = self.bindings.borrow().get(&name) { - BindingLocator::declarative(name, self.environment_index, binding.index) - } else if let Some(outer) = &self.outer { - outer.get_binding_recursive(name) - } else { - BindingLocator::global(name) - } - } - - /// Check if a binding name exists in this and all outer environments. - pub(crate) fn has_binding_recursive(&self, name: Identifier) -> bool { - if self.bindings.borrow().contains_key(&name) { - true - } else if let Some(outer) = &self.outer { - outer.has_binding_recursive(name) - } else { - false - } - } - - /// Check if a binding name exists in a environment. - /// If strict is `false` check until a function scope is reached. - pub(crate) fn has_binding_eval(&self, name: Identifier, strict: bool) -> bool { - let exists = self.bindings.borrow().contains_key(&name); - if exists || strict { - return exists; - } - if self.function_scope { - return false; - } - if let Some(outer) = &self.outer { - outer.has_binding_eval(name, false) - } else { - false - } - } - - #[cfg(feature = "annex-b")] - /// Check if a binding name exists in a environment. - /// Stop when a function scope is reached. - pub(crate) fn has_binding_until_var(&self, name: Identifier) -> bool { - if self.function_scope { - return false; - } - if self.bindings.borrow().contains_key(&name) { - return true; - } - if let Some(outer) = &self.outer { - outer.has_binding_until_var(name) - } else { - false - } - } - /// Create a mutable binding. - /// - /// If the binding is a function scope binding and this is a declarative environment, try the outer environment. - pub(crate) fn create_mutable_binding(&self, name: Identifier, function_scope: bool) -> bool { - if let Some(outer) = &self.outer { - if !function_scope || self.function_scope { - if !self.bindings.borrow().contains_key(&name) { - let binding_index = self.bindings.borrow().len() as u32; - self.bindings.borrow_mut().insert( - name, - CompileTimeBinding { - index: binding_index, - mutable: true, - lex: !function_scope, - strict: false, - }, - ); - } - true - } else { - outer.create_mutable_binding(name, function_scope) - } - } else if function_scope { - false - } else { - if !self.bindings.borrow().contains_key(&name) { - let binding_index = self.bindings.borrow().len() as u32; - self.bindings.borrow_mut().insert( - name, - CompileTimeBinding { - index: binding_index, - mutable: true, - lex: !function_scope, - strict: false, - }, - ); - } - true - } + pub(crate) fn create_mutable_binding( + &self, + name: Identifier, + function_scope: bool, + ) -> BindingLocator { + let binding_index = self.bindings.borrow().len() as u32; + self.bindings.borrow_mut().insert( + name, + CompileTimeBinding { + index: binding_index, + mutable: true, + lex: !function_scope, + strict: false, + }, + ); + BindingLocator::declarative(name, self.environment_index, binding_index) } /// Crate an immutable binding. - pub(crate) fn create_immutable_binding(&self, name: Identifier, strict: bool) { + pub(crate) fn create_immutable_binding( + &self, + name: Identifier, + strict: bool, + ) -> BindingLocator { let binding_index = self.bindings.borrow().len() as u32; self.bindings.borrow_mut().insert( name, @@ -209,42 +148,11 @@ impl CompileTimeEnvironment { strict, }, ); - } - - /// Return the binding locator for a mutable binding with the given binding name and scope. - pub(crate) fn initialize_mutable_binding( - &self, - name: Identifier, - function_scope: bool, - ) -> BindingLocator { - if let Some(outer) = &self.outer { - if function_scope && !self.function_scope { - return outer.initialize_mutable_binding(name, function_scope); - } - self.bindings.borrow().get(&name).map_or_else( - || outer.initialize_mutable_binding(name, function_scope), - |binding| BindingLocator::declarative(name, self.environment_index, binding.index), - ) - } else if let Some(binding) = self.bindings.borrow().get(&name) { - BindingLocator::declarative(name, self.environment_index, binding.index) - } else { - BindingLocator::global(name) - } - } - - /// Return the binding locator for an immutable binding. - /// - /// # Panics - /// - /// Panics if the binding is not in the current environment. - pub(crate) fn initialize_immutable_binding(&self, name: Identifier) -> BindingLocator { - let bindings = self.bindings.borrow(); - let binding = bindings.get(&name).expect("binding must exist"); - BindingLocator::declarative(name, self.environment_index, binding.index) + BindingLocator::declarative(name, self.environment_index, binding_index) } /// Return the binding locator for a mutable binding. - pub(crate) fn set_mutable_binding_recursive( + pub(crate) fn set_mutable_binding( &self, name: Identifier, ) -> Result { @@ -256,21 +164,21 @@ impl CompileTimeEnvironment { Some(_) => return Err(BindingLocatorError::Silent), None => self.outer.as_ref().map_or_else( || Ok(BindingLocator::global(name)), - |outer| outer.set_mutable_binding_recursive(name), + |outer| outer.set_mutable_binding(name), )?, }) } #[cfg(feature = "annex-b")] /// Return the binding locator for a set operation on an existing var binding. - pub(crate) fn set_mutable_binding_var_recursive( + pub(crate) fn set_mutable_binding_var( &self, name: Identifier, ) -> Result { if !self.is_function() { return self.outer.as_ref().map_or_else( || Ok(BindingLocator::global(name)), - |outer| outer.set_mutable_binding_var_recursive(name), + |outer| outer.set_mutable_binding_var(name), ); } @@ -282,7 +190,7 @@ impl CompileTimeEnvironment { Some(_) => return Err(BindingLocatorError::Silent), None => self.outer.as_ref().map_or_else( || Ok(BindingLocator::global(name)), - |outer| outer.set_mutable_binding_var_recursive(name), + |outer| outer.set_mutable_binding_var(name), )?, }) } @@ -292,3 +200,26 @@ impl CompileTimeEnvironment { self.outer.clone() } } + +/// A reference to an identifier in a compile time environment. +pub(crate) struct IdentifierReference { + locator: BindingLocator, + lexical: bool, +} + +impl IdentifierReference { + /// Create a new identifier reference. + pub(crate) fn new(locator: BindingLocator, lexical: bool) -> Self { + Self { locator, lexical } + } + + /// Get the binding locator for this identifier reference. + pub(crate) fn locator(&self) -> BindingLocator { + self.locator + } + + /// Check if this identifier reference is lexical. + pub(crate) fn is_lexical(&self) -> bool { + self.lexical + } +} diff --git a/boa_engine/src/environments/runtime/declarative/mod.rs b/boa_engine/src/environments/runtime/declarative/mod.rs index ffa2d991902..8da2a01a9ab 100644 --- a/boa_engine/src/environments/runtime/declarative/mod.rs +++ b/boa_engine/src/environments/runtime/declarative/mod.rs @@ -127,6 +127,17 @@ impl DeclarativeEnvironment { pub(crate) fn poison(&self) { self.kind.poison(); } + + /// Extends the environment with the bindings from the compile time environment. + pub(crate) fn extend_from_compile(&self) { + if let Some(env) = self.kind().as_function() { + let compile_bindings_number = self.compile_env().num_bindings() as usize; + let mut bindings = env.poisonable_environment().bindings().borrow_mut(); + if compile_bindings_number > bindings.len() { + bindings.resize(compile_bindings_number, None); + } + } + } } /// The kind of the declarative environment. diff --git a/boa_engine/src/environments/runtime/mod.rs b/boa_engine/src/environments/runtime/mod.rs index d89a01a6b58..1c2b284b87f 100644 --- a/boa_engine/src/environments/runtime/mod.rs +++ b/boa_engine/src/environments/runtime/mod.rs @@ -7,7 +7,6 @@ use crate::{ }; use boa_ast::expression::Identifier; use boa_gc::{empty_trace, Finalize, Gc, Trace}; -use rustc_hash::FxHashSet; mod declarative; mod private; @@ -90,69 +89,19 @@ impl EnvironmentStack { } } - /// Extends the length of the next outer function environment to the number of compiled bindings. - /// - /// This is only useful when compiled bindings are added after the initial compilation (eval). - pub(crate) fn extend_outer_function_environment(&mut self) { - for env in self - .stack - .iter() - .filter_map(Environment::as_declarative) - .rev() - { - if let DeclarativeEnvironmentKind::Function(fun) = &env.kind() { - let compile_bindings_number = env.compile_env().num_bindings() as usize; - let mut bindings = fun.poisonable_environment().bindings().borrow_mut(); - - if compile_bindings_number > bindings.len() { - bindings.resize(compile_bindings_number, None); - } - break; - } - } - } - - /// Check if any of the provided binding names are defined as lexical bindings. - /// - /// Start at the current environment. - /// Stop at the next outer function environment. - pub(crate) fn has_lex_binding_until_function_environment( - &self, - names: &FxHashSet, - ) -> Option { + /// Gets the next outer function environment. + pub(crate) fn outer_function_environment(&self) -> Gc { for env in self .stack .iter() .filter_map(Environment::as_declarative) .rev() { - let compile = env.compile_env(); - for name in names { - if compile.has_lex_binding(*name) { - return Some(*name); - } - } - if compile.is_function() { - break; - } - } - None - } - - /// Check if the next outer function environment is the global environment. - pub(crate) fn is_next_outer_function_environment_global(&self) -> bool { - for env in self - .stack - .iter() - .rev() - .filter_map(Environment::as_declarative) - { - let compile = env.compile_env(); - if compile.is_function() { - return compile.outer().is_none(); + if let DeclarativeEnvironmentKind::Function(_) = &env.kind() { + return env.clone(); } } - true + self.global() } /// Pop all current environments except the global environment. @@ -527,6 +476,7 @@ impl BindingLocator { } /// Action that is returned when a fallible binding operation. +#[derive(Debug)] pub(crate) enum BindingLocatorError { /// Trying to mutate immutable binding, MutateImmutable, diff --git a/boa_engine/src/module/source.rs b/boa_engine/src/module/source.rs index 068ba259efa..3c0714d5682 100644 --- a/boa_engine/src/module/source.rs +++ b/boa_engine/src/module/source.rs @@ -1410,10 +1410,10 @@ impl SourceTextModule { // 6. Set module.[[Environment]] to env. let global_env = realm.environment().clone(); let global_compile_env = global_env.compile_env(); - let module_compile_env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true)); + let env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true)); let mut compiler = - ByteCompiler::new(Sym::MAIN, true, false, module_compile_env.clone(), context); + ByteCompiler::new(Sym::MAIN, true, false, env.clone(), env.clone(), context); compiler.in_async = true; compiler.async_handler = Some(compiler.push_handler()); @@ -1449,8 +1449,7 @@ impl SourceTextModule { // 2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true). // 3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace). - compiler.create_immutable_binding(entry.local_name(), true); - let locator = compiler.initialize_immutable_binding(entry.local_name()); + let locator = env.create_immutable_binding(entry.local_name(), true); if let BindingName::Name(_) = resolution.binding_name { // 1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], @@ -1471,9 +1470,8 @@ impl SourceTextModule { } else { // b. If in.[[ImportName]] is namespace-object, then // ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true). - compiler.create_immutable_binding(entry.local_name(), true); // iii. Perform ! env.InitializeBinding(in.[[LocalName]], namespace). - let locator = compiler.initialize_immutable_binding(entry.local_name()); + let locator = env.create_immutable_binding(entry.local_name(), true); // i. Let namespace be GetModuleNamespace(importedModule). // deferred to initialization below @@ -1496,12 +1494,12 @@ impl SourceTextModule { // i. If declaredVarNames does not contain dn, then if !declared_var_names.contains(&name) { // 1. Perform ! env.CreateMutableBinding(dn, false). - compiler.create_mutable_binding(name, false); // 2. Perform ! env.InitializeBinding(dn, undefined). - let binding = compiler.initialize_mutable_binding(name, false); + let binding = env.create_mutable_binding(name, false); let index = compiler.get_or_insert_binding(binding); compiler.emit_opcode(Opcode::PushUndefined); compiler.emit_with_varying_operand(Opcode::DefInitVar, index); + // 3. Append dn to declaredVarNames. declared_var_names.push(name); } @@ -1527,35 +1525,31 @@ impl SourceTextModule { let (spec, locator): (FunctionSpec<'_>, _) = match declaration { LexicallyScopedDeclaration::Function(f) => { let name = bound_names(f)[0]; - compiler.create_mutable_binding(name, false); - let locator = compiler.initialize_mutable_binding(name, false); + let locator = env.create_mutable_binding(name, false); (f.into(), locator) } LexicallyScopedDeclaration::Generator(g) => { let name = bound_names(g)[0]; - compiler.create_mutable_binding(name, false); - let locator = compiler.initialize_mutable_binding(name, false); + let locator = env.create_mutable_binding(name, false); (g.into(), locator) } LexicallyScopedDeclaration::AsyncFunction(af) => { let name = bound_names(af)[0]; - compiler.create_mutable_binding(name, false); - let locator = compiler.initialize_mutable_binding(name, false); + let locator = env.create_mutable_binding(name, false); (af.into(), locator) } LexicallyScopedDeclaration::AsyncGenerator(ag) => { let name = bound_names(ag)[0]; - compiler.create_mutable_binding(name, false); - let locator = compiler.initialize_mutable_binding(name, false); + let locator = env.create_mutable_binding(name, false); (ag.into(), locator) } LexicallyScopedDeclaration::Class(class) => { for name in bound_names(class) { - compiler.create_mutable_binding(name, false); + env.create_mutable_binding(name, false); } continue; } @@ -1566,19 +1560,19 @@ impl SourceTextModule { // a. For each element dn of the BoundNames of d, do for name in bound_names(c) { // 1. Perform ! env.CreateImmutableBinding(dn, true). - compiler.create_immutable_binding(name, true); + env.create_immutable_binding(name, true); } continue; } LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Let(l)) => { for name in bound_names(l) { - compiler.create_mutable_binding(name, false); + env.create_mutable_binding(name, false); } continue; } LexicallyScopedDeclaration::AssignmentExpression(expr) => { for name in bound_names(expr) { - compiler.create_mutable_binding(name, false); + env.create_mutable_binding(name, false); } continue; } @@ -1605,7 +1599,7 @@ impl SourceTextModule { // 8. Let moduleContext be a new ECMAScript code execution context. let mut envs = EnvironmentStack::new(global_env); - envs.push_module(module_compile_env); + envs.push_module(env); // 9. Set the Function of moduleContext to null. // 12. Set the ScriptOrModule of moduleContext to module. diff --git a/boa_engine/src/script.rs b/boa_engine/src/script.rs index 21b63def322..e69b22713eb 100644 --- a/boa_engine/src/script.rs +++ b/boa_engine/src/script.rs @@ -113,10 +113,14 @@ impl Script { self.inner.source.strict(), false, self.inner.realm.environment().compile_env(), + self.inner.realm.environment().compile_env(), context, ); // TODO: move to `Script::evaluate` to make this operation infallible. - compiler.global_declaration_instantiation(&self.inner.source)?; + compiler.global_declaration_instantiation( + &self.inner.source, + &self.inner.realm.environment().compile_env(), + )?; compiler.compile_statement_list(self.inner.source.statements(), true, false); let cb = Gc::new(compiler.finish()); diff --git a/boa_engine/src/vm/opcode/define/mod.rs b/boa_engine/src/vm/opcode/define/mod.rs index cf77d02dce3..1544ed82d1e 100644 --- a/boa_engine/src/vm/opcode/define/mod.rs +++ b/boa_engine/src/vm/opcode/define/mod.rs @@ -66,9 +66,7 @@ impl DefInitVar { fn operation(context: &mut Context<'_>, index: usize) -> JsResult { let value = context.vm.pop(); let mut binding_locator = context.vm.frame().code_block.bindings[index]; - context.find_runtime_binding(&mut binding_locator)?; - context.set_binding( binding_locator, value, diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index c7279be5fb1..e6633663334 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -2182,8 +2182,7 @@ generate_opcodes! { pub(crate) enum BindingOpcode { Var, InitVar, - InitLet, - InitConst, + InitLexical, SetName, }