From c1b05f4c45a558ac9e67eb860e54c3f8ebb3eee2 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Sun, 4 Aug 2024 18:39:20 +0200 Subject: [PATCH] Remove compile time environments from every runtime DeclarativeEnvironment --- core/engine/src/builtins/eval/mod.rs | 18 +++-- core/engine/src/builtins/function/mod.rs | 20 ++--- core/engine/src/builtins/json/mod.rs | 4 +- core/engine/src/bytecompiler/mod.rs | 17 +++- .../runtime/declarative/function.rs | 25 +++++- .../environments/runtime/declarative/mod.rs | 29 ++----- .../runtime/declarative/module.rs | 21 ++++- core/engine/src/environments/runtime/mod.rs | 81 ++++++++----------- core/engine/src/module/namespace.rs | 10 ++- core/engine/src/module/source.rs | 2 +- core/engine/src/module/synthetic.rs | 20 +++-- core/engine/src/realm.rs | 21 ++++- core/engine/src/script.rs | 8 +- core/engine/src/vm/code_block.rs | 9 ++- core/engine/src/vm/flowgraph/mod.rs | 2 +- core/engine/src/vm/opcode/call/mod.rs | 74 +++++++++++++---- core/engine/src/vm/opcode/mod.rs | 4 +- core/engine/src/vm/opcode/push/environment.rs | 5 +- 18 files changed, 236 insertions(+), 134 deletions(-) diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 75b8448580a..d195c039ce1 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -62,7 +62,7 @@ impl Eval { /// [spec]: https://tc39.es/ecma262/#sec-eval-x fn eval(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Return ? PerformEval(x, false, false). - Self::perform_eval(args.get_or_undefined(0), false, false, context) + Self::perform_eval(args.get_or_undefined(0), false, None, false, context) } /// `19.2.1.1 PerformEval ( x, strictCaller, direct )` @@ -74,6 +74,7 @@ impl Eval { pub(crate) fn perform_eval( x: &JsValue, direct: bool, + lex_env: Option>, mut strict: bool, context: &mut Context, ) -> JsResult { @@ -229,10 +230,17 @@ impl Eval { } }); - let var_environment = context.vm.environments.outer_function_environment().clone(); - let mut var_env = var_environment.compile_env(); - - let lex_env = context.vm.environments.current_compile_environment(); + let (var_environment, mut var_env) = + if let Some(e) = context.vm.environments.outer_function_environment() { + (e.0, e.1) + } else { + ( + context.realm().environment().clone(), + context.realm().compile_environment(), + ) + }; + + let lex_env = lex_env.unwrap_or(context.realm().compile_environment()); let lex_env = Rc::new(CompileTimeEnvironment::new(lex_env, strict)); let mut annex_b_function_names = Vec::new(); diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index a94022b2ad5..3426fef911a 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -644,8 +644,8 @@ impl BuiltInFunctionObject { .compile( ¶meters, &body, - context.realm().environment().compile_env(), - context.realm().environment().compile_env(), + context.realm().compile_environment(), + context.realm().compile_environment(), context.interner_mut(), ); @@ -1008,10 +1008,10 @@ pub(crate) fn function_call( let mut last_env = 0; if code.has_binding_identifier() { - let index = context - .vm - .environments - .push_lexical(code.constant_compile_time_environment(last_env)); + let index = context.vm.environments.push_lexical( + code.constant_compile_time_environment(last_env) + .num_bindings(), + ); context.vm.environments.put_lexical_value( BindingLocatorEnvironment::Stack(index), 0, @@ -1101,10 +1101,10 @@ fn function_construct( let mut last_env = 0; if code.has_binding_identifier() { - let index = context - .vm - .environments - .push_lexical(code.constant_compile_time_environment(last_env)); + let index = context.vm.environments.push_lexical( + code.constant_compile_time_environment(last_env) + .num_bindings(), + ); context.vm.environments.put_lexical_value( BindingLocatorEnvironment::Stack(index), 0, diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index 08dcdd7d549..c73193cf11d 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -118,8 +118,8 @@ impl Json { js_string!("
"), script.strict(), true, - context.realm().environment().compile_env(), - context.realm().environment().compile_env(), + context.realm().compile_environment(), + context.realm().compile_environment(), false, false, context.interner_mut(), diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index 74073baf20c..b14adbdd9b4 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -1709,9 +1709,22 @@ impl<'ctx> ByteCompiler<'ctx> { } match kind { - CallKind::CallEval if contains_spread => self.emit_opcode(Opcode::CallEvalSpread), CallKind::CallEval => { - self.emit_with_varying_operand(Opcode::CallEval, call.args().len() as u32); + let env_index = self.constants.len() as u32; + self.constants.push(Constant::CompileTimeEnvironment( + self.lexical_environment.clone(), + )); + if contains_spread { + self.emit_with_varying_operand(Opcode::CallEvalSpread, env_index); + } else { + self.emit( + Opcode::CallEval, + &[ + Operand::Varying(call.args().len() as u32), + Operand::Varying(env_index), + ], + ); + } } CallKind::Call if contains_spread => self.emit_opcode(Opcode::CallSpread), CallKind::Call => { diff --git a/core/engine/src/environments/runtime/declarative/function.rs b/core/engine/src/environments/runtime/declarative/function.rs index b1a7275c978..b2a7fec93bc 100644 --- a/core/engine/src/environments/runtime/declarative/function.rs +++ b/core/engine/src/environments/runtime/declarative/function.rs @@ -1,6 +1,11 @@ +use std::rc::Rc; + use boa_gc::{custom_trace, Finalize, GcRefCell, Trace}; -use crate::{builtins::function::OrdinaryFunction, JsNativeError, JsObject, JsResult, JsValue}; +use crate::{ + builtins::function::OrdinaryFunction, environments::CompileTimeEnvironment, JsNativeError, + JsObject, JsResult, JsValue, +}; use super::PoisonableEnvironment; @@ -8,14 +13,25 @@ use super::PoisonableEnvironment; pub(crate) struct FunctionEnvironment { inner: PoisonableEnvironment, slots: Box, + + // Safety: Nothing in CompileTimeEnvironment needs tracing. + #[unsafe_ignore_trace] + compile: Rc, } impl FunctionEnvironment { /// Creates a new `FunctionEnvironment`. - pub(crate) fn new(bindings: u32, poisoned: bool, with: bool, slots: FunctionSlots) -> Self { + pub(crate) fn new( + bindings: u32, + poisoned: bool, + with: bool, + slots: FunctionSlots, + compile: Rc, + ) -> Self { Self { inner: PoisonableEnvironment::new(bindings, poisoned, with), slots: Box::new(slots), + compile, } } @@ -24,6 +40,11 @@ impl FunctionEnvironment { &self.slots } + /// Gets the compile time environment of this function environment. + pub(crate) const fn compile(&self) -> &Rc { + &self.compile + } + /// Gets the `poisonable_environment` of this function environment. pub(crate) const fn poisonable_environment(&self) -> &PoisonableEnvironment { &self.inner diff --git a/core/engine/src/environments/runtime/declarative/mod.rs b/core/engine/src/environments/runtime/declarative/mod.rs index f444e36adc1..2287341d2b5 100644 --- a/core/engine/src/environments/runtime/declarative/mod.rs +++ b/core/engine/src/environments/runtime/declarative/mod.rs @@ -8,9 +8,9 @@ pub(crate) use global::GlobalEnvironment; pub(crate) use lexical::LexicalEnvironment; pub(crate) use module::ModuleEnvironment; -use crate::{environments::CompileTimeEnvironment, JsResult, JsValue}; +use crate::{JsResult, JsValue}; use boa_gc::{Finalize, GcRefCell, Trace}; -use std::{cell::Cell, rc::Rc}; +use std::cell::Cell; /// A declarative environment holds binding values at runtime. /// @@ -35,10 +35,6 @@ use std::{cell::Cell, rc::Rc}; #[derive(Debug, Trace, Finalize)] pub(crate) struct DeclarativeEnvironment { kind: DeclarativeEnvironmentKind, - - // Safety: Nothing in CompileTimeEnvironment needs tracing. - #[unsafe_ignore_trace] - compile: Rc, } impl DeclarativeEnvironment { @@ -46,21 +42,12 @@ impl DeclarativeEnvironment { pub(crate) fn global() -> Self { Self { kind: DeclarativeEnvironmentKind::Global(GlobalEnvironment::new()), - compile: Rc::new(CompileTimeEnvironment::new_global()), } } /// Creates a new `DeclarativeEnvironment` from its kind and compile environment. - pub(crate) fn new( - kind: DeclarativeEnvironmentKind, - compile: Rc, - ) -> Self { - Self { kind, compile } - } - - /// Gets the compile time environment of this environment. - pub(crate) fn compile_env(&self) -> Rc { - self.compile.clone() + pub(crate) fn new(kind: DeclarativeEnvironmentKind) -> Self { + Self { kind } } /// Returns a reference to the the kind of the environment. @@ -70,11 +57,7 @@ impl DeclarativeEnvironment { /// Returns whether this environment is a function environment. pub(crate) fn is_function(&self) -> bool { - if let DeclarativeEnvironmentKind::Function(_) = self.kind() { - true - } else { - false - } + matches!(self.kind(), DeclarativeEnvironmentKind::Function(_)) } /// Gets the binding value from the environment by index. @@ -139,7 +122,7 @@ impl DeclarativeEnvironment { /// 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 compile_bindings_number = env.compile().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); diff --git a/core/engine/src/environments/runtime/declarative/module.rs b/core/engine/src/environments/runtime/declarative/module.rs index 6ffd88cb45b..e0dcd13d501 100644 --- a/core/engine/src/environments/runtime/declarative/module.rs +++ b/core/engine/src/environments/runtime/declarative/module.rs @@ -1,8 +1,8 @@ -use std::cell::RefCell; +use std::{cell::RefCell, rc::Rc}; use boa_gc::{Finalize, GcRefCell, Trace}; -use crate::{module::Module, JsString, JsValue}; +use crate::{environments::CompileTimeEnvironment, module::Module, JsString, JsValue}; /// Type of accessor used to access an indirect binding. #[derive(Debug, Clone)] @@ -36,16 +36,26 @@ enum BindingType { #[derive(Debug, Trace, Finalize)] pub(crate) struct ModuleEnvironment { bindings: GcRefCell>, + + // Safety: Nothing in CompileTimeEnvironment needs tracing. + #[unsafe_ignore_trace] + compile: Rc, } impl ModuleEnvironment { /// Creates a new `LexicalEnvironment`. - pub(crate) fn new(bindings: u32) -> Self { + pub(crate) fn new(bindings: u32, compile: Rc) -> Self { Self { bindings: GcRefCell::new(vec![BindingType::Direct(None); bindings as usize]), + compile, } } + /// Gets the compile time environment of this module environment. + pub(crate) const fn compile(&self) -> &Rc { + &self.compile + } + /// Get the binding value from the environment by it's index. /// /// # Panics @@ -63,7 +73,10 @@ impl ModuleEnvironment { match &*accessor.clone().borrow() { BindingAccessor::Identifier(name) => { let index = env - .compile_env() + .kind() + .as_module() + .expect("must be module environment") + .compile() .get_binding(name) .expect("linking must ensure the binding exists"); diff --git a/core/engine/src/environments/runtime/mod.rs b/core/engine/src/environments/runtime/mod.rs index 1ddc369d2a6..669a7ba7a9a 100644 --- a/core/engine/src/environments/runtime/mod.rs +++ b/core/engine/src/environments/runtime/mod.rs @@ -76,18 +76,20 @@ impl EnvironmentStack { } /// Gets the next outer function environment. - pub(crate) fn outer_function_environment(&self) -> &Gc { + pub(crate) fn outer_function_environment( + &self, + ) -> Option<(Gc, Rc)> { for env in self .stack .iter() .filter_map(Environment::as_declarative) .rev() { - if let DeclarativeEnvironmentKind::Function(_) = &env.kind() { - return env; + if let Some(function_env) = env.kind().as_function() { + return Some((env.clone(), function_env.compile().clone())); } } - self.global() + None } /// Pop all current environments except the global environment. @@ -157,9 +159,7 @@ impl EnvironmentStack { } /// Push a lexical environment on the environments stack and return it's index. - pub(crate) fn push_lexical(&mut self, compile_environment: Rc) -> u32 { - let num_bindings = compile_environment.num_bindings(); - + pub(crate) fn push_lexical(&mut self, bindings_count: u32) -> u32 { let (poisoned, with) = { // Check if the outer environment is a declarative environment. let with = if let Some(env) = self.stack.last() { @@ -180,14 +180,9 @@ impl EnvironmentStack { let index = self.stack.len() as u32; self.stack.push(Environment::Declarative(Gc::new( - DeclarativeEnvironment::new( - DeclarativeEnvironmentKind::Lexical(LexicalEnvironment::new( - num_bindings, - poisoned, - with, - )), - compile_environment, - ), + DeclarativeEnvironment::new(DeclarativeEnvironmentKind::Lexical( + LexicalEnvironment::new(bindings_count, poisoned, with), + )), ))); index @@ -219,15 +214,15 @@ impl EnvironmentStack { }; self.stack.push(Environment::Declarative(Gc::new( - DeclarativeEnvironment::new( - DeclarativeEnvironmentKind::Function(FunctionEnvironment::new( + DeclarativeEnvironment::new(DeclarativeEnvironmentKind::Function( + FunctionEnvironment::new( num_bindings, poisoned, with, function_slots, - )), - compile_environment, - ), + compile_environment, + ), + )), ))); } @@ -235,10 +230,9 @@ impl EnvironmentStack { pub(crate) fn push_module(&mut self, compile_environment: Rc) { let num_bindings = compile_environment.num_bindings(); self.stack.push(Environment::Declarative(Gc::new( - DeclarativeEnvironment::new( - DeclarativeEnvironmentKind::Module(ModuleEnvironment::new(num_bindings)), - compile_environment, - ), + DeclarativeEnvironment::new(DeclarativeEnvironmentKind::Module( + ModuleEnvironment::new(num_bindings, compile_environment), + )), ))); } @@ -258,16 +252,6 @@ impl EnvironmentStack { } } - /// Get the compile environment for the current runtime environment. - pub(crate) fn current_compile_environment(&self) -> Rc { - self.stack - .iter() - .filter_map(Environment::as_declarative) - .last() - .map(|env| env.compile_env()) - .unwrap_or(self.global().compile_env()) - } - /// Mark that there may be added bindings from the current environment to the next function /// environment. pub(crate) fn poison_until_last_function(&mut self) { @@ -513,9 +497,8 @@ impl Context { match self.environment_expect(index) { Environment::Declarative(env) => { if env.poisoned() { - if env.is_function() { - let compile = env.compile_env(); - if let Some(b) = compile.get_binding(locator.name()) { + if let Some(env) = env.kind().as_function() { + if let Some(b) = env.compile().get_binding(locator.name()) { locator.set_environment(b.environment()); locator.binding_index = b.binding_index(); return Ok(()); @@ -542,14 +525,14 @@ impl Context { } } - if global { - let env = self.vm.environments.global(); - if env.poisoned() { - let compile = env.compile_env(); - if let Some(b) = compile.get_binding(locator.name()) { - locator.set_environment(b.environment()); - locator.binding_index = b.binding_index(); - } + if global && self.realm().environment().poisoned() { + if let Some(b) = self + .realm() + .compile_environment() + .get_binding(locator.name()) + { + locator.set_environment(b.environment()); + locator.binding_index = b.binding_index(); } } @@ -578,10 +561,10 @@ impl Context { match self.environment_expect(index) { Environment::Declarative(env) => { if env.poisoned() { - if env.is_function() - && env.compile_env().get_binding(locator.name()).is_some() - { - break; + if let Some(env) = env.kind().as_function() { + if env.compile().get_binding(locator.name()).is_some() { + break; + } } } else if !env.with() { break; diff --git a/core/engine/src/module/namespace.rs b/core/engine/src/module/namespace.rs index 1874d481bb7..196e0dd0842 100644 --- a/core/engine/src/module/namespace.rs +++ b/core/engine/src/module/namespace.rs @@ -309,7 +309,10 @@ fn module_namespace_exotic_try_get( }; let locator = env - .compile_env() + .kind() + .as_module() + .expect("must be module environment") + .compile() .get_binding(&name) .expect("checked before that the name was reachable"); @@ -386,7 +389,10 @@ fn module_namespace_exotic_get( }; let locator = env - .compile_env() + .kind() + .as_module() + .expect("must be module environment") + .compile() .get_binding(&name) .expect("checked before that the name was reachable"); diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index d35a4ca5212..4fba9fbc166 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1423,7 +1423,7 @@ impl SourceTextModule { // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). // 6. Set module.[[Environment]] to env. let global_env = realm.environment().clone(); - let global_compile_env = global_env.compile_env(); + let global_compile_env = realm.compile_environment(); let env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true)); let mut compiler = ByteCompiler::new( diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index 381fe6a32cb..38d306d96dd 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -205,12 +205,18 @@ impl SyntheticModule { export_name.to_std_string_escaped() )) })?; - let locator = env.compile_env().get_binding(export_name).ok_or_else(|| { - JsNativeError::reference().with_message(format!( - "cannot set name `{}` which was not included in the list of exports", - export_name.to_std_string_escaped() - )) - })?; + let locator = env + .kind() + .as_module() + .expect("must be module environment") + .compile() + .get_binding(export_name) + .ok_or_else(|| { + JsNativeError::reference().with_message(format!( + "cannot set name `{}` which was not included in the list of exports", + export_name.to_std_string_escaped() + )) + })?; env.set(locator.binding_index(), export_value); Ok(()) @@ -275,7 +281,7 @@ impl SyntheticModule { // 2. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). // 3. Set module.[[Environment]] to env. let global_env = module_self.realm().environment().clone(); - let global_compile_env = global_env.compile_env(); + let global_compile_env = module_self.realm().compile_environment(); let module_compile_env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true)); // TODO: A bit of a hack to be able to pass the currently active runnable without an diff --git a/core/engine/src/realm.rs b/core/engine/src/realm.rs index 255d7130244..306e73c7910 100644 --- a/core/engine/src/realm.rs +++ b/core/engine/src/realm.rs @@ -6,7 +6,7 @@ //! //! A realm is represented in this implementation as a Realm struct with the fields specified from the spec. -use std::any::TypeId; +use std::{any::TypeId, rc::Rc}; use rustc_hash::FxHashMap; @@ -16,7 +16,7 @@ use crate::{ intrinsics::{Intrinsics, StandardConstructor}, HostHooks, }, - environments::DeclarativeEnvironment, + environments::{CompileTimeEnvironment, DeclarativeEnvironment}, module::Module, object::shape::RootShape, HostDefined, JsNativeError, JsObject, JsResult, JsString, @@ -54,7 +54,16 @@ impl std::fmt::Debug for Realm { #[derive(Trace, Finalize)] struct Inner { intrinsics: Intrinsics, + + /// The global declarative environment of this realm. environment: Gc, + + /// The global compile time environment of this realm. + /// This is directly related to the global declarative environment. + // Safety: Nothing in CompileTimeEnvironment needs tracing. + #[unsafe_ignore_trace] + compile_environment: Rc, + global_object: JsObject, global_this: JsObject, template_map: GcRefCell>, @@ -79,11 +88,13 @@ impl Realm { .create_global_this(&intrinsics) .unwrap_or_else(|| global_object.clone()); let environment = Gc::new(DeclarativeEnvironment::global()); + let compile_environment = Rc::new(CompileTimeEnvironment::new_global()); let realm = Self { inner: Gc::new(Inner { intrinsics, environment, + compile_environment, global_object, global_this, template_map: GcRefCell::default(), @@ -156,6 +167,10 @@ impl Realm { &self.inner.environment } + pub(crate) fn compile_environment(&self) -> Rc { + self.inner.compile_environment.clone() + } + pub(crate) fn global_object(&self) -> &JsObject { &self.inner.global_object } @@ -170,7 +185,7 @@ impl Realm { /// Resizes the number of bindings on the global environment. pub(crate) fn resize_global_env(&self) { - let binding_number = self.environment().compile_env().num_bindings(); + let binding_number = self.compile_environment().num_bindings(); let env = self .environment() .kind() diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index 17c76a45505..68b85beedd6 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -124,7 +124,7 @@ impl Script { global_declaration_instantiation_context( &mut annex_b_function_names, &self.inner.source, - &self.inner.realm.environment().compile_env(), + &self.inner.realm.compile_environment(), context, )?; @@ -132,8 +132,8 @@ impl Script { js_string!("
"), self.inner.source.strict(), false, - self.inner.realm.environment().compile_env(), - self.inner.realm.environment().compile_env(), + self.inner.realm.compile_environment(), + self.inner.realm.compile_environment(), false, false, context.interner_mut(), @@ -148,7 +148,7 @@ impl Script { // TODO: move to `Script::evaluate` to make this operation infallible. compiler.global_declaration_instantiation( &self.inner.source, - &self.inner.realm.environment().compile_env(), + &self.inner.realm.compile_environment(), ); compiler.compile_statement_list(self.inner.source.statements(), true, false); diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index 0a7c2871b88..cb32bcd0669 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -416,8 +416,11 @@ impl CodeBlock { | Instruction::Coalesce { exit: value } => value.to_string(), Instruction::CallEval { argument_count: value, + compile_environments_index, + } => { + format!("{}, {}", value.value(), compile_environments_index.value()) } - | Instruction::Call { + Instruction::Call { argument_count: value, } | Instruction::New { @@ -430,6 +433,9 @@ impl CodeBlock { | Instruction::GetArgument { index: value } => value.value().to_string(), Instruction::PushDeclarativeEnvironment { compile_environments_index, + } + | Instruction::CallEvalSpread { + compile_environments_index, } => compile_environments_index.value().to_string(), Instruction::CopyDataProperties { excluded_key_count: value1, @@ -657,7 +663,6 @@ impl CodeBlock { | Instruction::NewTarget | Instruction::ImportMeta | Instruction::SuperCallPrepare - | Instruction::CallEvalSpread | Instruction::CallSpread | Instruction::NewSpread | Instruction::SuperCallSpread diff --git a/core/engine/src/vm/flowgraph/mod.rs b/core/engine/src/vm/flowgraph/mod.rs index 05d83a7ab1b..31deafcc3e4 100644 --- a/core/engine/src/vm/flowgraph/mod.rs +++ b/core/engine/src/vm/flowgraph/mod.rs @@ -434,7 +434,7 @@ impl CodeBlock { | Instruction::Await | Instruction::NewTarget | Instruction::ImportMeta - | Instruction::CallEvalSpread + | Instruction::CallEvalSpread { .. } | Instruction::CallSpread | Instruction::NewSpread | Instruction::SuperCallSpread diff --git a/core/engine/src/vm/opcode/call/mod.rs b/core/engine/src/vm/opcode/call/mod.rs index 4519f589c9c..fa20ae9d617 100644 --- a/core/engine/src/vm/opcode/call/mod.rs +++ b/core/engine/src/vm/opcode/call/mod.rs @@ -15,7 +15,11 @@ use crate::{ pub(crate) struct CallEval; impl CallEval { - fn operation(context: &mut Context, argument_count: usize) -> JsResult { + fn operation( + context: &mut Context, + argument_count: usize, + env_index: usize, + ) -> JsResult { let at = context.vm.stack.len() - argument_count; let func = &context.vm.stack[at - 1]; @@ -45,7 +49,18 @@ impl CallEval { // let strictCaller be true. Otherwise let strictCaller be false. // v. Return ? PerformEval(evalArg, strictCaller, true). let strict = context.vm.frame().code_block.strict(); - let result = crate::builtins::eval::Eval::perform_eval(x, true, strict, context)?; + let compile_environment = context + .vm + .frame() + .code_block() + .constant_compile_time_environment(env_index); + let result = crate::builtins::eval::Eval::perform_eval( + x, + true, + Some(compile_environment), + strict, + context, + )?; context.vm.push(result); } else { // NOTE: This is a deviation from the spec, to optimize the case when we dont pass anything to `eval`. @@ -69,17 +84,20 @@ impl Operation for CallEval { fn execute(context: &mut Context) -> JsResult { let argument_count = context.vm.read::(); - Self::operation(context, argument_count as usize) + let env_index = context.vm.read::(); + Self::operation(context, argument_count as usize, env_index as usize) } fn execute_with_u16_operands(context: &mut Context) -> JsResult { let argument_count = context.vm.read::() as usize; - Self::operation(context, argument_count) + let env_index = context.vm.read::(); + Self::operation(context, argument_count, env_index as usize) } fn execute_with_u32_operands(context: &mut Context) -> JsResult { let argument_count = context.vm.read::(); - Self::operation(context, argument_count as usize) + let env_index = context.vm.read::(); + Self::operation(context, argument_count as usize, env_index as usize) } } @@ -90,14 +108,8 @@ impl Operation for CallEval { #[derive(Debug, Clone, Copy)] pub(crate) struct CallEvalSpread; -impl Operation for CallEvalSpread { - const NAME: &'static str = "CallEvalSpread"; - const INSTRUCTION: &'static str = "INST - CallEvalSpread"; - // TODO: Calls will require a big refactor in order to track - // the cost of the call. - const COST: u8 = 5; - - fn execute(context: &mut Context) -> JsResult { +impl CallEvalSpread { + fn operation(context: &mut Context, env_index: usize) -> JsResult { // Get the arguments that are stored as an array object on the stack. let arguments_array = context.vm.pop(); let arguments_array_object = arguments_array @@ -137,7 +149,18 @@ impl Operation for CallEvalSpread { // let strictCaller be true. Otherwise let strictCaller be false. // v. Return ? PerformEval(evalArg, strictCaller, true). let strict = context.vm.frame().code_block.strict(); - let result = crate::builtins::eval::Eval::perform_eval(x, true, strict, context)?; + let compile_environment = context + .vm + .frame() + .code_block() + .constant_compile_time_environment(env_index); + let result = crate::builtins::eval::Eval::perform_eval( + x, + true, + Some(compile_environment), + strict, + context, + )?; context.vm.push(result); } else { // NOTE: This is a deviation from the spec, to optimize the case when we dont pass anything to `eval`. @@ -155,6 +178,29 @@ impl Operation for CallEvalSpread { } } +impl Operation for CallEvalSpread { + const NAME: &'static str = "CallEvalSpread"; + const INSTRUCTION: &'static str = "INST - CallEvalSpread"; + // TODO: Calls will require a big refactor in order to track + // the cost of the call. + const COST: u8 = 5; + + fn execute(context: &mut Context) -> JsResult { + let env_index = context.vm.read::(); + Self::operation(context, env_index as usize) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let env_index = context.vm.read::(); + Self::operation(context, env_index as usize) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let env_index = context.vm.read::(); + Self::operation(context, env_index as usize) + } +} + /// `Call` implements the Opcode Operation for `Opcode::Call` /// /// Operation: diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index 92812e4642c..e7d74ffd653 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -1701,14 +1701,14 @@ generate_opcodes! { /// Operands: argument_count: `VaryingOperand` /// /// Stack: this, func, argument_1, ... argument_n **=>** result - CallEval { argument_count: VaryingOperand }, + CallEval { argument_count: VaryingOperand, compile_environments_index: VaryingOperand }, /// Call a function named "eval" where the arguments contain spreads. /// /// Operands: /// /// Stack: this, func, arguments_array **=>** result - CallEvalSpread, + CallEvalSpread { compile_environments_index: VaryingOperand }, /// Call a function. /// diff --git a/core/engine/src/vm/opcode/push/environment.rs b/core/engine/src/vm/opcode/push/environment.rs index ca2a5e2e643..34dc08b3670 100644 --- a/core/engine/src/vm/opcode/push/environment.rs +++ b/core/engine/src/vm/opcode/push/environment.rs @@ -24,7 +24,10 @@ impl PushDeclarativeEnvironment { .frame() .code_block() .constant_compile_time_environment(compile_environments_index); - context.vm.environments.push_lexical(compile_environment); + context + .vm + .environments + .push_lexical(compile_environment.num_bindings()); Ok(CompletionType::Normal) } }