From 6a91a85e0e47ad229cb538514493fdfcd04885a2 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Sat, 16 Sep 2023 22:37:34 +0200 Subject: [PATCH] Store active runnable and active function in `CallFrame` (#3197) * Move acrive runnable to CallFrame * Move active function to CallFrame * Add some code doc * Fix doc link * Apply review --- boa_engine/src/builtins/array/mod.rs | 4 +- boa_engine/src/builtins/async_function/mod.rs | 2 +- .../builtins/async_generator_function/mod.rs | 2 +- boa_engine/src/builtins/error/aggregate.rs | 4 +- boa_engine/src/builtins/error/eval.rs | 4 +- boa_engine/src/builtins/error/mod.rs | 4 +- boa_engine/src/builtins/error/range.rs | 4 +- boa_engine/src/builtins/error/reference.rs | 4 +- boa_engine/src/builtins/error/syntax.rs | 4 +- boa_engine/src/builtins/error/type.rs | 4 +- boa_engine/src/builtins/error/uri.rs | 4 +- boa_engine/src/builtins/eval/mod.rs | 2 +- boa_engine/src/builtins/function/mod.rs | 4 +- boa_engine/src/builtins/generator/mod.rs | 6 - .../src/builtins/generator_function/mod.rs | 2 +- boa_engine/src/builtins/intl/collator/mod.rs | 4 +- .../src/builtins/intl/date_time_format.rs | 4 +- boa_engine/src/builtins/json/mod.rs | 2 +- boa_engine/src/builtins/object/mod.rs | 4 +- boa_engine/src/builtins/regexp/mod.rs | 4 +- boa_engine/src/context/mod.rs | 37 +++- boa_engine/src/job.rs | 11 +- boa_engine/src/module/source.rs | 38 ++-- boa_engine/src/script.rs | 14 +- boa_engine/src/vm/call_frame/mod.rs | 15 +- boa_engine/src/vm/code_block.rs | 162 +++++++++--------- boa_engine/src/vm/mod.rs | 9 +- boa_engine/src/vm/opcode/call/mod.rs | 4 +- boa_engine/src/vm/opcode/generator/mod.rs | 14 +- boa_engine/src/vm/opcode/meta/mod.rs | 2 +- 30 files changed, 184 insertions(+), 194 deletions(-) diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 36bcf063094..46f87be67c4 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -174,9 +174,7 @@ impl BuiltInConstructor for Array { // If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| context.intrinsics().constructors().array().constructor()) .into() } else { diff --git a/boa_engine/src/builtins/async_function/mod.rs b/boa_engine/src/builtins/async_function/mod.rs index 5dbc7f2b58b..be9b448ab50 100644 --- a/boa_engine/src/builtins/async_function/mod.rs +++ b/boa_engine/src/builtins/async_function/mod.rs @@ -66,7 +66,7 @@ impl BuiltInConstructor for AsyncFunction { args: &[JsValue], context: &mut Context<'_>, ) -> JsResult { - let active_function = context.vm.active_function.clone().unwrap_or_else(|| { + let active_function = context.active_function_object().unwrap_or_else(|| { context .intrinsics() .constructors() diff --git a/boa_engine/src/builtins/async_generator_function/mod.rs b/boa_engine/src/builtins/async_generator_function/mod.rs index 97636b070e8..aa953957bb4 100644 --- a/boa_engine/src/builtins/async_generator_function/mod.rs +++ b/boa_engine/src/builtins/async_generator_function/mod.rs @@ -71,7 +71,7 @@ impl BuiltInConstructor for AsyncGeneratorFunction { args: &[JsValue], context: &mut Context<'_>, ) -> JsResult { - let active_function = context.vm.active_function.clone().unwrap_or_else(|| { + let active_function = context.active_function_object().unwrap_or_else(|| { context .intrinsics() .constructors() diff --git a/boa_engine/src/builtins/error/aggregate.rs b/boa_engine/src/builtins/error/aggregate.rs index f9ce1fd70fd..7ee61332a20 100644 --- a/boa_engine/src/builtins/error/aggregate.rs +++ b/boa_engine/src/builtins/error/aggregate.rs @@ -63,9 +63,7 @@ impl BuiltInConstructor for AggregateError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/error/eval.rs b/boa_engine/src/builtins/error/eval.rs index ac3d915ed5f..13cc33bb43e 100644 --- a/boa_engine/src/builtins/error/eval.rs +++ b/boa_engine/src/builtins/error/eval.rs @@ -65,9 +65,7 @@ impl BuiltInConstructor for EvalError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/error/mod.rs b/boa_engine/src/builtins/error/mod.rs index 0430ddd26f0..091a8b8abbc 100644 --- a/boa_engine/src/builtins/error/mod.rs +++ b/boa_engine/src/builtins/error/mod.rs @@ -165,9 +165,7 @@ impl BuiltInConstructor for Error { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| context.intrinsics().constructors().error().constructor()) .into() } else { diff --git a/boa_engine/src/builtins/error/range.rs b/boa_engine/src/builtins/error/range.rs index 36c657b9c68..04bfdf18d74 100644 --- a/boa_engine/src/builtins/error/range.rs +++ b/boa_engine/src/builtins/error/range.rs @@ -63,9 +63,7 @@ impl BuiltInConstructor for RangeError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/error/reference.rs b/boa_engine/src/builtins/error/reference.rs index e72dd09883b..f11a7de1b5b 100644 --- a/boa_engine/src/builtins/error/reference.rs +++ b/boa_engine/src/builtins/error/reference.rs @@ -62,9 +62,7 @@ impl BuiltInConstructor for ReferenceError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/error/syntax.rs b/boa_engine/src/builtins/error/syntax.rs index 444e32a3d78..7661a155422 100644 --- a/boa_engine/src/builtins/error/syntax.rs +++ b/boa_engine/src/builtins/error/syntax.rs @@ -65,9 +65,7 @@ impl BuiltInConstructor for SyntaxError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/error/type.rs b/boa_engine/src/builtins/error/type.rs index 87cdc6fae5e..5466c5ca534 100644 --- a/boa_engine/src/builtins/error/type.rs +++ b/boa_engine/src/builtins/error/type.rs @@ -73,9 +73,7 @@ impl BuiltInConstructor for TypeError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/error/uri.rs b/boa_engine/src/builtins/error/uri.rs index 34a7514d6cf..0d96ce8d331 100644 --- a/boa_engine/src/builtins/error/uri.rs +++ b/boa_engine/src/builtins/error/uri.rs @@ -64,9 +64,7 @@ impl BuiltInConstructor for UriError { // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index caa4baccbae..b2793322c9e 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -256,7 +256,7 @@ impl Eval { let env_fp = context.vm.environments.len() as u32; context .vm - .push_frame(CallFrame::new(code_block).with_env_fp(env_fp)); + .push_frame(CallFrame::new(code_block, None, None).with_env_fp(env_fp)); context.realm().resize_global_env(); let record = context.run(); diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 76d7473496c..ffe64b21531 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -532,9 +532,7 @@ impl BuiltInConstructor for BuiltInFunctionObject { context: &mut Context<'_>, ) -> JsResult { let active_function = context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| context.intrinsics().constructors().function().constructor()); Self::create_dynamic_function(active_function, new_target, args, false, false, context) .map(Into::into) diff --git a/boa_engine/src/builtins/generator/mod.rs b/boa_engine/src/builtins/generator/mod.rs index ea9a636e4d7..d877caea7f6 100644 --- a/boa_engine/src/builtins/generator/mod.rs +++ b/boa_engine/src/builtins/generator/mod.rs @@ -60,7 +60,6 @@ unsafe impl Trace for GeneratorState { pub(crate) struct GeneratorContext { pub(crate) environments: EnvironmentStack, pub(crate) stack: Vec, - pub(crate) active_function: Option, pub(crate) call_frame: Option, pub(crate) realm: Realm, } @@ -70,14 +69,12 @@ impl GeneratorContext { pub(crate) fn new( environments: EnvironmentStack, stack: Vec, - active_function: Option, call_frame: CallFrame, realm: Realm, ) -> Self { Self { environments, stack, - active_function, call_frame: Some(call_frame), realm, } @@ -90,7 +87,6 @@ impl GeneratorContext { environments: context.vm.environments.clone(), call_frame: Some(context.vm.frame().clone()), stack: context.vm.stack[fp..].to_vec(), - active_function: context.vm.active_function.clone(), realm: context.realm().clone(), }; @@ -108,7 +104,6 @@ impl GeneratorContext { ) -> CompletionRecord { std::mem::swap(&mut context.vm.environments, &mut self.environments); std::mem::swap(&mut context.vm.stack, &mut self.stack); - std::mem::swap(&mut context.vm.active_function, &mut self.active_function); context.swap_realm(&mut self.realm); context .vm @@ -125,7 +120,6 @@ impl GeneratorContext { std::mem::swap(&mut context.vm.environments, &mut self.environments); std::mem::swap(&mut context.vm.stack, &mut self.stack); - std::mem::swap(&mut context.vm.active_function, &mut self.active_function); context.swap_realm(&mut self.realm); self.call_frame = context.vm.pop_frame(); assert!(self.call_frame.is_some()); diff --git a/boa_engine/src/builtins/generator_function/mod.rs b/boa_engine/src/builtins/generator_function/mod.rs index 90d2fb951d4..f4b0c2a626e 100644 --- a/boa_engine/src/builtins/generator_function/mod.rs +++ b/boa_engine/src/builtins/generator_function/mod.rs @@ -76,7 +76,7 @@ impl BuiltInConstructor for GeneratorFunction { args: &[JsValue], context: &mut Context<'_>, ) -> JsResult { - let active_function = context.vm.active_function.clone().unwrap_or_else(|| { + let active_function = context.active_function_object().unwrap_or_else(|| { context .intrinsics() .constructors() diff --git a/boa_engine/src/builtins/intl/collator/mod.rs b/boa_engine/src/builtins/intl/collator/mod.rs index af88d1cb30c..ccbf1cd2dbe 100644 --- a/boa_engine/src/builtins/intl/collator/mod.rs +++ b/boa_engine/src/builtins/intl/collator/mod.rs @@ -216,9 +216,7 @@ impl BuiltInConstructor for Collator { // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| context.intrinsics().constructors().collator().constructor()) .into() } else { diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index 6bb4e8c5056..4b44d2f2c9b 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -99,9 +99,7 @@ impl BuiltInConstructor for DateTimeFormat { // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. let new_target = &if new_target.is_undefined() { context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| { context .intrinsics() diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index f2616c1e8e3..7e8fefbb6f8 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -131,7 +131,7 @@ impl Json { let env_fp = context.vm.environments.len() as u32; context .vm - .push_frame(CallFrame::new(code_block).with_env_fp(env_fp)); + .push_frame(CallFrame::new(code_block, None, None).with_env_fp(env_fp)); context.realm().resize_global_env(); let record = context.run(); context.vm.pop_frame(); diff --git a/boa_engine/src/builtins/object/mod.rs b/boa_engine/src/builtins/object/mod.rs index 0f40d5e43eb..40f35a5d51e 100644 --- a/boa_engine/src/builtins/object/mod.rs +++ b/boa_engine/src/builtins/object/mod.rs @@ -131,9 +131,7 @@ impl BuiltInConstructor for Object { if !new_target.is_undefined() && new_target != &context - .vm - .active_function - .clone() + .active_function_object() .unwrap_or_else(|| context.intrinsics().constructors().object().constructor()) .into() { diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 4111487f82d..38b46a57c9a 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -172,9 +172,7 @@ impl BuiltInConstructor for RegExp { if new_target.is_undefined() { // a. Let newTarget be the active function object. let new_target = context - .vm - .active_function - .clone() + .active_function_object() .map_or(JsValue::undefined(), JsValue::new); // b. If patternIsRegExp is true and flags is undefined, then diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index f57e9ef3c3f..96b9e06ddd0 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -27,7 +27,7 @@ use crate::{ property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, script::Script, - vm::{CallFrame, Vm}, + vm::{ActiveRunnable, CallFrame, Vm}, JsResult, JsValue, Source, }; use boa_ast::{expression::Identifier, StatementList}; @@ -706,6 +706,41 @@ impl Context<'_> { pub(crate) const fn is_strict(&self) -> bool { self.strict } + + /// `9.4.1 GetActiveScriptOrModule ( )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-getactivescriptormodule + pub(crate) fn get_active_script_or_module(&self) -> Option { + // 1. If the execution context stack is empty, return null. + // 2. Let ec be the topmost execution context on the execution context stack whose ScriptOrModule component is not null. + // 3. If no such execution context exists, return null. Otherwise, return ec's ScriptOrModule. + self.vm + .frames + .iter() + .rev() + .find_map(|frame| frame.active_runnable.clone()) + } + + /// Get `active function object` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#active-function-object + pub(crate) fn active_function_object(&self) -> Option { + if self.vm.native_active_function.is_some() { + return self.vm.native_active_function.clone(); + } + + if let Some(frame) = self.vm.frames.last() { + return frame.active_function.clone(); + } + + None + } } impl<'host> Context<'host> { diff --git a/boa_engine/src/job.rs b/boa_engine/src/job.rs index 34373bcaf8c..3242fb9508f 100644 --- a/boa_engine/src/job.rs +++ b/boa_engine/src/job.rs @@ -22,7 +22,6 @@ use std::{any::Any, cell::RefCell, collections::VecDeque, fmt::Debug, future::Fu use crate::{ object::{JsFunction, NativeObject}, realm::Realm, - vm::ActiveRunnable, Context, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -69,7 +68,6 @@ pub struct NativeJob { #[allow(clippy::type_complexity)] f: Box) -> JsResult>, realm: Option, - active_runnable: Option, } impl Debug for NativeJob { @@ -87,19 +85,17 @@ impl NativeJob { Self { f: Box::new(f), realm: None, - active_runnable: None, } } /// Creates a new `NativeJob` from a closure and an execution realm. - pub fn with_realm(f: F, realm: Realm, context: &mut Context<'_>) -> Self + pub fn with_realm(f: F, realm: Realm, _context: &mut Context<'_>) -> Self where F: FnOnce(&mut Context<'_>) -> JsResult + 'static, { Self { f: Box::new(f), realm: Some(realm), - active_runnable: context.vm.active_runnable.clone(), } } @@ -115,7 +111,7 @@ impl NativeJob { /// /// If the native job has an execution realm defined, this sets the running execution /// context to the realm's before calling the inner closure, and resets it after execution. - pub fn call(mut self, context: &mut Context<'_>) -> JsResult { + pub fn call(self, context: &mut Context<'_>) -> JsResult { // If realm is not null, each time job is invoked the implementation must perform // implementation-defined steps such that execution is prepared to evaluate ECMAScript // code at the time of job's invocation. @@ -126,12 +122,9 @@ impl NativeJob { // invoked. If realm is not null, each time job is invoked the implementation must // perform implementation-defined steps such that scriptOrModule is the active script or // module at the time of job's invocation. - std::mem::swap(&mut context.vm.active_runnable, &mut self.active_runnable); - let result = (self.f)(context); context.enter_realm(old_realm); - std::mem::swap(&mut context.vm.active_runnable, &mut self.active_runnable); result } else { diff --git a/boa_engine/src/module/source.rs b/boa_engine/src/module/source.rs index 16eb4ecfce7..3566d98462f 100644 --- a/boa_engine/src/module/source.rs +++ b/boa_engine/src/module/source.rs @@ -1607,20 +1607,19 @@ impl SourceTextModule { let mut envs = EnvironmentStack::new(global_env); envs.push_module(module_compile_env); + // 9. Set the Function of moduleContext to null. // 12. Set the ScriptOrModule of moduleContext to module. - let active_runnable = context - .vm - .active_runnable - .replace(ActiveRunnable::Module(parent.clone())); + let call_frame = CallFrame::new( + codeblock.clone(), + Some(ActiveRunnable::Module(parent.clone())), + None, + ); + context.vm.push_frame(call_frame); // 13. Set the VariableEnvironment of moduleContext to module.[[Environment]]. // 14. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. // 15. Set the PrivateEnvironment of moduleContext to null. std::mem::swap(&mut context.vm.environments, &mut envs); - let stack = std::mem::take(&mut context.vm.stack); - - // 9. Set the Function of moduleContext to null. - let active_function = context.vm.active_function.take(); // 10. Assert: module.[[Realm]] is not undefined. // 11. Set the Realm of moduleContext to module.[[Realm]]. @@ -1682,10 +1681,11 @@ impl SourceTextModule { } // 25. Remove moduleContext from the execution context stack. + context + .vm + .pop_frame() + .expect("There should be a call frame"); std::mem::swap(&mut context.vm.environments, &mut envs); - context.vm.stack = stack; - context.vm.active_function = active_function; - context.vm.active_runnable = active_runnable; context.swap_realm(&mut realm); debug_assert!(envs.current().as_declarative().is_some()); @@ -1733,22 +1733,18 @@ impl SourceTextModule { _ => unreachable!("`execute` should only be called for evaluating modules."), }; + // 2. Set the Function of moduleContext to null. + // 4. Set the ScriptOrModule of moduleContext to module. let env_fp = environments.len() as u32; - let mut callframe = CallFrame::new(codeblock).with_env_fp(env_fp); + let mut callframe = + CallFrame::new(codeblock, Some(ActiveRunnable::Module(self.parent())), None) + .with_env_fp(env_fp); callframe.promise_capability = capability; - // 4. Set the ScriptOrModule of moduleContext to module. - let active_runnable = context - .vm - .active_runnable - .replace(ActiveRunnable::Module(self.parent())); - // 5. Assert: module has been linked and declarations in its module environment have been instantiated. // 6. Set the VariableEnvironment of moduleContext to module.[[Environment]]. // 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. std::mem::swap(&mut context.vm.environments, &mut environments); - // 2. Set the Function of moduleContext to null. - let function = context.vm.active_function.take(); // 3. Set the Realm of moduleContext to module.[[Realm]]. context.swap_realm(&mut realm); // 8. Suspend the running execution context. @@ -1766,8 +1762,6 @@ impl SourceTextModule { let result = context.run(); std::mem::swap(&mut context.vm.environments, &mut environments); - context.vm.active_function = function; - context.vm.active_runnable = active_runnable; context.swap_realm(&mut realm); context.vm.pop_frame(); diff --git a/boa_engine/src/script.rs b/boa_engine/src/script.rs index 226b9306610..21b63def322 100644 --- a/boa_engine/src/script.rs +++ b/boa_engine/src/script.rs @@ -138,15 +138,11 @@ impl Script { let codeblock = self.codeblock(context)?; let old_realm = context.enter_realm(self.inner.realm.clone()); - let active_function = context.vm.active_function.take(); - let old_active = context - .vm - .active_runnable - .replace(ActiveRunnable::Script(self.clone())); let env_fp = context.vm.environments.len() as u32; - context - .vm - .push_frame(CallFrame::new(codeblock).with_env_fp(env_fp)); + context.vm.push_frame( + CallFrame::new(codeblock, Some(ActiveRunnable::Script(self.clone())), None) + .with_env_fp(env_fp), + ); // TODO: Here should be https://tc39.es/ecma262/#sec-globaldeclarationinstantiation @@ -154,8 +150,6 @@ impl Script { let record = context.run(); context.vm.pop_frame(); - context.vm.active_function = active_function; - context.vm.active_runnable = old_active; context.enter_realm(old_realm); context.clear_kept_objects(); diff --git a/boa_engine/src/vm/call_frame/mod.rs b/boa_engine/src/vm/call_frame/mod.rs index 123b966cfa7..3caa44a990c 100644 --- a/boa_engine/src/vm/call_frame/mod.rs +++ b/boa_engine/src/vm/call_frame/mod.rs @@ -12,6 +12,8 @@ use crate::{ use boa_gc::{Finalize, Gc, Trace}; use thin_vec::ThinVec; +use super::ActiveRunnable; + /// A `CallFrame` holds the state of a function call. #[derive(Clone, Debug, Finalize, Trace)] pub struct CallFrame { @@ -36,6 +38,11 @@ pub struct CallFrame { /// How many iterations a loop has done. pub(crate) loop_iteration_count: u64, + + /// \[\[ScriptOrModule\]\] + pub(crate) active_runnable: Option, + + pub(crate) active_function: Option, } /// ---- `CallFrame` public API ---- @@ -51,7 +58,11 @@ impl CallFrame { /// ---- `CallFrame` creation methods ---- impl CallFrame { /// Creates a new `CallFrame` with the provided `CodeBlock`. - pub(crate) fn new(code_block: Gc) -> Self { + pub(crate) fn new( + code_block: Gc, + active_runnable: Option, + active_function: Option, + ) -> Self { Self { code_block, pc: 0, @@ -63,6 +74,8 @@ impl CallFrame { iterators: ThinVec::new(), binding_stack: Vec::new(), loop_iteration_count: 0, + active_runnable, + active_function, } } diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index f4bee9bafe9..b52229e0b7c 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -758,7 +758,7 @@ pub(crate) fn create_function_object( let name: JsValue = code.name().clone().into(); let length: JsValue = code.length.into(); - let script_or_module = context.vm.active_runnable.clone(); + let script_or_module = context.get_active_script_or_module(); let function = if r#async { Function::new( @@ -838,7 +838,7 @@ pub(crate) fn create_function_object_fast( let name: JsValue = code.name().clone().into(); let length: JsValue = code.length.into(); - let script_or_module = context.vm.active_runnable.clone(); + let script_or_module = context.get_active_script_or_module(); let function = if r#async { FunctionKind::Async { @@ -943,7 +943,7 @@ pub(crate) fn create_generator_function_object( ObjectData::ordinary(), ); - let script_or_module = context.vm.active_runnable.clone(); + let script_or_module = context.get_active_script_or_module(); let constructor = if r#async { let function = Function::new( @@ -1007,87 +1007,89 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let old_realm = context.realm().clone(); - let old_active_fn = context.vm.active_function.clone(); let context = &mut context.guard(move |ctx| { ctx.enter_realm(old_realm); - ctx.vm.active_function = old_active_fn; }); let this_function_object = self.clone(); - let active_function = self.clone(); let object = self.borrow(); let function_object = object.as_function().expect("not a function"); let realm = function_object.realm().clone(); context.enter_realm(realm); - context.vm.active_function = Some(active_function); - - let (code, mut environments, class_object, mut script_or_module) = - match function_object.kind() { - FunctionKind::Native { - function, - constructor, - } => { - let function = function.clone(); - let constructor = *constructor; - drop(object); - - return if constructor.is_some() { - function.call(&JsValue::undefined(), args, context) - } else { - function.call(this, args, context) - } - .map_err(|err| err.inject_realm(context.realm().clone())); - } - FunctionKind::Ordinary { - code, - environments, - class_object, - script_or_module, - .. - } => { - let code = code.clone(); - if code.is_class_constructor() { - return Err(JsNativeError::typ() - .with_message("class constructor cannot be invoked without 'new'") - .with_realm(context.realm().clone()) - .into()); - } - ( - code, - environments.clone(), - class_object.clone(), - script_or_module.clone(), - ) - } - FunctionKind::Async { - code, - environments, - class_object, - script_or_module, - .. + + let (code, mut environments, class_object, script_or_module) = match function_object.kind() + { + FunctionKind::Native { + function, + constructor, + } => { + let function = function.clone(); + let constructor = *constructor; + drop(object); + + context.vm.native_active_function = Some(this_function_object); + + let result = if constructor.is_some() { + function.call(&JsValue::undefined(), args, context) + } else { + function.call(this, args, context) } - | FunctionKind::Generator { - code, - environments, - class_object, - script_or_module, - .. + .map_err(|err| err.inject_realm(context.realm().clone())); + + context.vm.native_active_function = None; + + return result; + } + FunctionKind::Ordinary { + code, + environments, + class_object, + script_or_module, + .. + } => { + let code = code.clone(); + if code.is_class_constructor() { + return Err(JsNativeError::typ() + .with_message("class constructor cannot be invoked without 'new'") + .with_realm(context.realm().clone()) + .into()); } - | FunctionKind::AsyncGenerator { + ( code, - environments, - class_object, - script_or_module, - .. - } => ( - code.clone(), environments.clone(), class_object.clone(), script_or_module.clone(), - ), - }; + ) + } + FunctionKind::Async { + code, + environments, + class_object, + script_or_module, + .. + } + | FunctionKind::Generator { + code, + environments, + class_object, + script_or_module, + .. + } + | FunctionKind::AsyncGenerator { + code, + environments, + class_object, + script_or_module, + .. + } => ( + code.clone(), + environments.clone(), + class_object.clone(), + script_or_module.clone(), + ), + }; drop(object); @@ -1185,12 +1187,10 @@ impl JsObject { let argument_count = args.len(); let parameters_count = code.params.as_ref().len(); - let frame = CallFrame::new(code) + let frame = CallFrame::new(code, script_or_module, Some(self.clone())) .with_argument_count(argument_count as u32) .with_env_fp(env_fp); - std::mem::swap(&mut context.vm.active_runnable, &mut script_or_module); - context.vm.push_frame(frame); // Push function arguments to the stack. @@ -1206,7 +1206,6 @@ impl JsObject { context.vm.pop_frame().expect("frame must exist"); std::mem::swap(&mut environments, &mut context.vm.environments); - std::mem::swap(&mut context.vm.active_runnable, &mut script_or_module); result } @@ -1218,20 +1217,16 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let old_realm = context.realm().clone(); - let old_active_fn = context.vm.active_function.clone(); let context = &mut context.guard(move |ctx| { ctx.enter_realm(old_realm); - ctx.vm.active_function = old_active_fn; }); let this_function_object = self.clone(); - let active_function = self.clone(); let object = self.borrow(); let function_object = object.as_function().expect("not a function"); let realm = function_object.realm().clone(); context.enter_realm(realm); - context.vm.active_function = Some(active_function); match function_object.kind() { FunctionKind::Native { @@ -1243,7 +1238,9 @@ impl JsObject { let constructor = *constructor; drop(object); - function + context.vm.native_active_function = Some(this_function_object); + + let result = function .call(this_target, args, context) .map_err(|err| err.inject_realm(context.realm().clone())) .and_then(|v| match v { @@ -1270,7 +1267,11 @@ impl JsObject { .into()) } } - }) + }); + + context.vm.native_active_function = None; + + result } FunctionKind::Ordinary { code, @@ -1281,7 +1282,7 @@ impl JsObject { } => { let code = code.clone(); let mut environments = environments.clone(); - let mut script_or_module = script_or_module.clone(); + let script_or_module = script_or_module.clone(); let constructor_kind = *constructor_kind; drop(object); @@ -1384,10 +1385,8 @@ impl JsObject { let has_binding_identifier = code.has_binding_identifier(); - std::mem::swap(&mut context.vm.active_runnable, &mut script_or_module); - context.vm.push_frame( - CallFrame::new(code) + CallFrame::new(code, script_or_module, Some(self.clone())) .with_argument_count(argument_count as u32) .with_env_fp(environments_len as u32), ); @@ -1403,7 +1402,6 @@ impl JsObject { context.vm.pop_frame(); std::mem::swap(&mut environments, &mut context.vm.environments); - std::mem::swap(&mut context.vm.active_runnable, &mut script_or_module); let environment = if has_binding_identifier { environments.truncate(environments_len + 2); diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 28589995e66..bbce9310e98 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -69,8 +69,10 @@ pub struct Vm { pub(crate) pending_exception: Option, pub(crate) environments: EnvironmentStack, pub(crate) runtime_limits: RuntimeLimits, - pub(crate) active_function: Option, - pub(crate) active_runnable: Option, + + /// This is used to assign a native (rust) function as the active function, + /// because we don't push a frame for them. + pub(crate) native_active_function: Option, #[cfg(feature = "trace")] pub(crate) trace: bool, @@ -102,8 +104,7 @@ impl Vm { environments: EnvironmentStack::new(global), pending_exception: None, runtime_limits: RuntimeLimits::default(), - active_function: None, - active_runnable: None, + native_active_function: None, #[cfg(feature = "trace")] trace: false, } diff --git a/boa_engine/src/vm/opcode/call/mod.rs b/boa_engine/src/vm/opcode/call/mod.rs index 4bdf52d7d94..c06faea4bc9 100644 --- a/boa_engine/src/vm/opcode/call/mod.rs +++ b/boa_engine/src/vm/opcode/call/mod.rs @@ -272,9 +272,7 @@ impl Operation for ImportCall { // 1. Let referrer be GetActiveScriptOrModule(). // 2. If referrer is null, set referrer to the current Realm Record. let referrer = context - .vm - .active_runnable - .clone() + .get_active_script_or_module() .map_or_else(|| Referrer::Realm(context.realm().clone()), Into::into); // 3. Let argRef be ? Evaluation of AssignmentExpression. diff --git a/boa_engine/src/vm/opcode/generator/mod.rs b/boa_engine/src/vm/opcode/generator/mod.rs index 41ce8dd0013..c3b5bc7e574 100644 --- a/boa_engine/src/vm/opcode/generator/mod.rs +++ b/boa_engine/src/vm/opcode/generator/mod.rs @@ -38,8 +38,11 @@ impl Operation for Generator { let r#async = context.vm.read::() != 0; let code_block = context.vm.frame().code_block().clone(); + let active_runnable = context.vm.frame().active_runnable.clone(); + let active_function = context.vm.frame().active_function.clone(); let pc = context.vm.frame().pc; - let mut dummy_call_frame = CallFrame::new(code_block); + let mut dummy_call_frame = + CallFrame::new(code_block, active_runnable, active_function.clone()); dummy_call_frame.pc = pc; let mut call_frame = std::mem::replace(context.vm.frame_mut(), dummy_call_frame); @@ -50,11 +53,8 @@ impl Operation for Generator { call_frame.fp = 0; - let this_function_object = context - .vm - .active_function - .clone() - .expect("active function should be set to the generator"); + let this_function_object = + active_function.expect("active function should be set to the generator"); let proto = this_function_object .get(PROTOTYPE, context) @@ -83,7 +83,6 @@ impl Operation for Generator { context: Some(GeneratorContext::new( environments, stack, - context.vm.active_function.clone(), call_frame, context.realm().clone(), )), @@ -95,7 +94,6 @@ impl Operation for Generator { context: GeneratorContext::new( environments, stack, - context.vm.active_function.clone(), call_frame, context.realm().clone(), ), diff --git a/boa_engine/src/vm/opcode/meta/mod.rs b/boa_engine/src/vm/opcode/meta/mod.rs index b097eb7978b..9fefa5d0d7d 100644 --- a/boa_engine/src/vm/opcode/meta/mod.rs +++ b/boa_engine/src/vm/opcode/meta/mod.rs @@ -54,7 +54,7 @@ impl Operation for ImportMeta { // 1. Let module be GetActiveScriptOrModule(). - let Some(ActiveRunnable::Module(module)) = context.vm.active_runnable.clone() else { + let Some(ActiveRunnable::Module(module)) = context.get_active_script_or_module() else { unreachable!("2. Assert: module is a Source Text Module Record."); };