From 7eb2d4e4082e3082ce85541f65d4947b2fd24997 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Wed, 26 Apr 2023 17:55:43 +0000 Subject: [PATCH] Initialize `var` bindings in runtime environments with `undefined` (#2860) Currently in draft, because it breaks some of the annexB [`Block-Level Function Declarations Web Legacy Compatibility Semantics`](https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics) tests. This Pull Request fixes #2795, fixes #2779 and fixes #2760. It changes the following: - Initialize `var` bindings in runtime environments with `undefined` - Add a missing environment at class construction time. --- boa_engine/src/bytecompiler/class.rs | 16 ++++++++++++++++ boa_engine/src/environments/compile.rs | 14 ++++++++++++++ boa_engine/src/environments/runtime.rs | 14 ++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/boa_engine/src/bytecompiler/class.rs b/boa_engine/src/bytecompiler/class.rs index e571b1e3b1..78d2042ab6 100644 --- a/boa_engine/src/bytecompiler/class.rs +++ b/boa_engine/src/bytecompiler/class.rs @@ -113,6 +113,15 @@ impl ByteCompiler<'_, '_> { self.emit(Opcode::GetFunction, &[index]); self.emit_u8(0); + let class_env: Option<(super::Label, super::Label)> = match class.name() { + Some(name) if class.has_binding_identifier() => { + self.push_compile_environment(false); + self.create_immutable_binding(name, true); + Some(self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment)) + } + _ => None, + }; + self.emit_opcode(Opcode::Dup); if let Some(node) = class.super_ref() { self.compile_expr(node, true); @@ -544,6 +553,13 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::Pop); + if let Some(class_env) = class_env { + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(class_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(class_env.1, env_info.index as u32); + self.emit_opcode(Opcode::PopEnvironment); + } + if !expression { self.emit_binding( BindingOpcode::InitVar, diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 18771b54df..13ecdf2f88 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -217,4 +217,18 @@ impl CompileTimeEnvironment { pub(crate) const fn environment_index(&self) -> usize { self.environment_index } + + /// Gets the indices of all `var` bindings in this environment. + pub(crate) fn var_binding_indices(&self) -> Vec { + self.bindings + .iter() + .filter_map(|(_, binding)| { + if binding.lex { + None + } else { + Some(binding.index) + } + }) + .collect() + } } diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 108c89033e..ac7dc8452b 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -481,9 +481,14 @@ impl DeclarativeEnvironmentStack { let this = this.unwrap_or(JsValue::Null); + let mut bindings = vec![None; num_bindings]; + for index in compile_environment.borrow().var_binding_indices() { + bindings[index] = Some(JsValue::Undefined); + } + self.stack .push(Environment::Declarative(Gc::new(DeclarativeEnvironment { - bindings: GcRefCell::new(vec![None; num_bindings]), + bindings: GcRefCell::new(bindings), compile: compile_environment, poisoned: Cell::new(poisoned), with: Cell::new(with), @@ -534,9 +539,14 @@ impl DeclarativeEnvironmentStack { ) }; + let mut bindings = vec![None; num_bindings]; + for index in compile_environment.borrow().var_binding_indices() { + bindings[index] = Some(JsValue::Undefined); + } + self.stack .push(Environment::Declarative(Gc::new(DeclarativeEnvironment { - bindings: GcRefCell::new(vec![None; num_bindings]), + bindings: GcRefCell::new(bindings), compile: compile_environment, poisoned: Cell::new(poisoned), with: Cell::new(with),