diff --git a/boa_engine/src/builtins/array/array_iterator.rs b/boa_engine/src/builtins/array/array_iterator.rs index c625478858..4f8cf2d3f5 100644 --- a/boa_engine/src/builtins/array/array_iterator.rs +++ b/boa_engine/src/builtins/array/array_iterator.rs @@ -13,6 +13,7 @@ use crate::{ error::JsNativeError, object::{JsObject, ObjectData}, property::{Attribute, PropertyNameKind}, + realm::Realm, symbol::JsSymbol, Context, JsResult, }; @@ -35,11 +36,17 @@ pub struct ArrayIterator { } impl IntrinsicObject for ArrayIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("ArrayIterator", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 0) .static_property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index a6e01c4a13..0e3d39a5ad 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -22,6 +22,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, property::{Attribute, PropertyDescriptor, PropertyNameKind}, + realm::Realm, symbol::JsSymbol, value::{IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, @@ -40,26 +41,28 @@ mod tests; pub(crate) struct Array; impl IntrinsicObject for Array { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let symbol_iterator = JsSymbol::iterator(); let symbol_unscopables = JsSymbol::unscopables(); - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); - let values_function = - BuiltInBuilder::with_object(intrinsics, intrinsics.objects().array_prototype_values()) - .callable(Self::values) - .name("values") - .build(); + let values_function = BuiltInBuilder::with_object( + realm, + realm.intrinsics().objects().array_prototype_values(), + ) + .callable(Self::values) + .name("values") + .build(); let unscopables_object = Self::unscopables_object(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_accessor( JsSymbol::species(), Some(get_species), @@ -369,12 +372,14 @@ impl Array { // 4. If IsConstructor(C) is true, then if let Some(c) = c.as_constructor() { // a. Let thisRealm be the current Realm Record. - let this_realm = &context.intrinsics().clone(); + let this_realm = context.realm().clone(); // b. Let realmC be ? GetFunctionRealm(C). - let realm_c = &c.get_function_realm(context)?; + let realm_c = c.get_function_realm(context)?; // c. If thisRealm and realmC are not the same Realm Record, then - if this_realm != realm_c && *c == realm_c.constructors().array().constructor() { + if this_realm != realm_c + && *c == realm_c.intrinsics().constructors().array().constructor() + { // i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined. // Note: fast path to step 6. return Self::array_create(length, None, context); diff --git a/boa_engine/src/builtins/array_buffer/mod.rs b/boa_engine/src/builtins/array_buffer/mod.rs index be5b99fedf..f19e0fc195 100644 --- a/boa_engine/src/builtins/array_buffer/mod.rs +++ b/boa_engine/src/builtins/array_buffer/mod.rs @@ -16,6 +16,7 @@ use crate::{ error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, value::{IntegerOrInfinity, Numeric}, @@ -47,22 +48,22 @@ impl ArrayBuffer { } impl IntrinsicObject for ArrayBuffer { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); - let get_byte_length = BuiltInBuilder::new(intrinsics) + let get_byte_length = BuiltInBuilder::new(realm) .callable(Self::get_byte_length) .name("get byteLength") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .accessor( utf16!("byteLength"), Some(get_byte_length), diff --git a/boa_engine/src/builtins/async_function/mod.rs b/boa_engine/src/builtins/async_function/mod.rs index 611092b87d..b796d3cbb6 100644 --- a/boa_engine/src/builtins/async_function/mod.rs +++ b/boa_engine/src/builtins/async_function/mod.rs @@ -11,6 +11,7 @@ use crate::{ builtins::{function::BuiltInFunctionObject, BuiltInObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, property::Attribute, + realm::Realm, symbol::JsSymbol, Context, JsResult, JsValue, }; @@ -23,12 +24,14 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; pub struct AsyncFunction; impl IntrinsicObject for AsyncFunction { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().function().constructor()) - .inherits(Some(intrinsics.constructors().function().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().function().constructor()) + .inherits(Some( + realm.intrinsics().constructors().function().prototype(), + )) .property( JsSymbol::to_string_tag(), Self::NAME, diff --git a/boa_engine/src/builtins/async_generator/mod.rs b/boa_engine/src/builtins/async_generator/mod.rs index 440783146c..1520db5a01 100644 --- a/boa_engine/src/builtins/async_generator/mod.rs +++ b/boa_engine/src/builtins/async_generator/mod.rs @@ -15,6 +15,7 @@ use crate::{ native_function::NativeFunction, object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR}, property::Attribute, + realm::Realm, symbol::JsSymbol, value::JsValue, vm::GeneratorResumeKind, @@ -66,11 +67,17 @@ pub struct AsyncGenerator { } impl IntrinsicObject for AsyncGenerator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().async_iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .async_iterator(), + ) .static_method(Self::next, "next", 1) .static_method(Self::r#return, "return", 1) .static_method(Self::throw, "throw", 1) @@ -81,7 +88,8 @@ impl IntrinsicObject for AsyncGenerator { ) .static_property( CONSTRUCTOR, - intrinsics + realm + .intrinsics() .constructors() .async_generator_function() .prototype(), @@ -399,6 +407,7 @@ impl AsyncGenerator { next: &AsyncGeneratorRequest, completion: JsResult, done: bool, + realm: Option, context: &mut Context<'_>, ) { // 1. Let queue be generator.[[AsyncGeneratorQueue]]. @@ -422,15 +431,24 @@ impl AsyncGenerator { Ok(value) => { // a. Assert: completion.[[Type]] is normal. - // TODO: Realm handling not implemented yet. // b. If realm is present, then - // i. Let oldRealm be the running execution context's Realm. - // ii. Set the running execution context's Realm to realm. - // iii. Let iteratorResult be CreateIterResultObject(value, done). - // iv. Set the running execution context's Realm to oldRealm. - // c. Else, - // i. Let iteratorResult be CreateIterResultObject(value, done). - let iterator_result = create_iter_result_object(value, done, context); + let iterator_result = if let Some(realm) = realm { + // i. Let oldRealm be the running execution context's Realm. + // ii. Set the running execution context's Realm to realm. + let old_realm = context.enter_realm(realm); + + // iii. Let iteratorResult be CreateIterResultObject(value, done). + let iterator_result = create_iter_result_object(value, done, context); + + // iv. Set the running execution context's Realm to oldRealm. + context.enter_realm(old_realm); + + iterator_result + } else { + // c. Else, + // i. Let iteratorResult be CreateIterResultObject(value, done). + create_iter_result_object(value, done, context) + }; // d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). promise_capability @@ -474,7 +492,7 @@ impl AsyncGenerator { // 6. Push genContext onto the execution context stack; genContext is now the running execution context. std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -482,10 +500,7 @@ impl AsyncGenerator { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + let old_realm = context.enter_realm(generator_context.realm.clone()); context.vm.push_frame(generator_context.call_frame.clone()); @@ -508,7 +523,7 @@ impl AsyncGenerator { let result = context.run(); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -517,10 +532,7 @@ impl AsyncGenerator { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + context.enter_realm(old_realm); generator .borrow_mut() @@ -575,7 +587,7 @@ impl AsyncGenerator { gen.context = None; let next = gen.queue.pop_front().expect("queue must not be empty"); drop(generator_borrow_mut); - Self::complete_step(&next, Err(value), true, context); + Self::complete_step(&next, Err(value), true, None, context); Self::drain_queue(&generator, context); return; } @@ -604,7 +616,7 @@ impl AsyncGenerator { let result = Ok(args.get_or_undefined(0).clone()); // c. Perform AsyncGeneratorCompleteStep(generator, result, true). - Self::complete_step(&next, result, true, context); + Self::complete_step(&next, result, true, None, context); // d. Perform AsyncGeneratorDrainQueue(generator). Self::drain_queue(generator, context); @@ -640,7 +652,7 @@ impl AsyncGenerator { // c. Perform AsyncGeneratorCompleteStep(generator, result, true). let next = gen.queue.pop_front().expect("must have one entry"); drop(generator_borrow_mut); - Self::complete_step(&next, result, true, context); + Self::complete_step(&next, result, true, None, context); // d. Perform AsyncGeneratorDrainQueue(generator). Self::drain_queue(generator, context); @@ -721,7 +733,7 @@ impl AsyncGenerator { // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true). let next = queue.pop_front().expect("must have entry"); - Self::complete_step(&next, completion, true, context); + Self::complete_step(&next, completion, true, None, context); // iii. If queue is empty, set done to true. if queue.is_empty() { diff --git a/boa_engine/src/builtins/async_generator_function/mod.rs b/boa_engine/src/builtins/async_generator_function/mod.rs index d108ac3e60..97636b070e 100644 --- a/boa_engine/src/builtins/async_generator_function/mod.rs +++ b/boa_engine/src/builtins/async_generator_function/mod.rs @@ -10,6 +10,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{JsObject, PROTOTYPE}, property::Attribute, + realm::Realm, symbol::JsSymbol, value::JsValue, Context, JsResult, @@ -23,15 +24,17 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; pub struct AsyncGeneratorFunction; impl IntrinsicObject for AsyncGeneratorFunction { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) - .inherits(Some(intrinsics.constructors().function().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .inherits(Some( + realm.intrinsics().constructors().function().prototype(), + )) .constructor_attributes(Attribute::CONFIGURABLE) .property( PROTOTYPE, - intrinsics.objects().async_generator(), + realm.intrinsics().objects().async_generator(), Attribute::CONFIGURABLE, ) .property( diff --git a/boa_engine/src/builtins/bigint/mod.rs b/boa_engine/src/builtins/bigint/mod.rs index 2d2ac22aec..d649bf6083 100644 --- a/boa_engine/src/builtins/bigint/mod.rs +++ b/boa_engine/src/builtins/bigint/mod.rs @@ -18,6 +18,7 @@ use crate::{ error::JsNativeError, object::JsObject, property::Attribute, + realm::Realm, symbol::JsSymbol, value::{IntegerOrInfinity, PreferredType}, Context, JsArgs, JsBigInt, JsResult, JsValue, @@ -35,10 +36,10 @@ mod tests; pub struct BigInt; impl IntrinsicObject for BigInt { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .method(Self::to_string, "toString", 0) .method(Self::value_of, "valueOf", 0) .static_method(Self::as_int_n, "asIntN", 2) diff --git a/boa_engine/src/builtins/boolean/mod.rs b/boa_engine/src/builtins/boolean/mod.rs index 18cdea1750..4adebf3c2e 100644 --- a/boa_engine/src/builtins/boolean/mod.rs +++ b/boa_engine/src/builtins/boolean/mod.rs @@ -17,6 +17,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, + realm::Realm, Context, JsResult, JsValue, }; use boa_profiler::Profiler; @@ -28,10 +29,10 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; pub(crate) struct Boolean; impl IntrinsicObject for Boolean { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .method(Self::to_string, "toString", 0) .method(Self::value_of, "valueOf", 0) .build(); diff --git a/boa_engine/src/builtins/dataview/mod.rs b/boa_engine/src/builtins/dataview/mod.rs index 0999c5a1c9..983fb0c755 100644 --- a/boa_engine/src/builtins/dataview/mod.rs +++ b/boa_engine/src/builtins/dataview/mod.rs @@ -13,6 +13,7 @@ use crate::{ error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, value::JsValue, @@ -31,25 +32,25 @@ pub struct DataView { } impl IntrinsicObject for DataView { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_buffer = BuiltInBuilder::new(intrinsics) + let get_buffer = BuiltInBuilder::new(realm) .callable(Self::get_buffer) .name("get buffer") .build(); - let get_byte_length = BuiltInBuilder::new(intrinsics) + let get_byte_length = BuiltInBuilder::new(realm) .callable(Self::get_byte_length) .name("get byteLength") .build(); - let get_byte_offset = BuiltInBuilder::new(intrinsics) + let get_byte_offset = BuiltInBuilder::new(realm) .callable(Self::get_byte_offset) .name("get byteOffset") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes) .accessor( utf16!("byteLength"), diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index aed1cbce60..bae0a3207c 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -19,6 +19,7 @@ use crate::{ error::JsNativeError, js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, + realm::Realm, string::utf16, symbol::JsSymbol, value::{IntegerOrNan, JsValue, PreferredType}, @@ -100,10 +101,10 @@ impl Default for Date { } impl IntrinsicObject for Date { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .method(Self::get_date::, "getDate", 0) .method(Self::get_day::, "getDay", 0) .method(Self::get_full_year::, "getFullYear", 0) diff --git a/boa_engine/src/builtins/error/aggregate.rs b/boa_engine/src/builtins/error/aggregate.rs index de71c6311d..7cdeb8d19f 100644 --- a/boa_engine/src/builtins/error/aggregate.rs +++ b/boa_engine/src/builtins/error/aggregate.rs @@ -15,6 +15,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::{Attribute, PropertyDescriptorBuilder}, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -26,13 +27,13 @@ use super::{Error, ErrorKind}; pub(crate) struct AggregateError; impl IntrinsicObject for AggregateError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); diff --git a/boa_engine/src/builtins/error/eval.rs b/boa_engine/src/builtins/error/eval.rs index 04b331d78f..50c862ad5b 100644 --- a/boa_engine/src/builtins/error/eval.rs +++ b/boa_engine/src/builtins/error/eval.rs @@ -16,6 +16,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -28,13 +29,13 @@ use super::{Error, ErrorKind}; pub(crate) struct EvalError; impl IntrinsicObject for EvalError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); diff --git a/boa_engine/src/builtins/error/mod.rs b/boa_engine/src/builtins/error/mod.rs index b8ab2ea321..9444c22aff 100644 --- a/boa_engine/src/builtins/error/mod.rs +++ b/boa_engine/src/builtins/error/mod.rs @@ -17,6 +17,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -127,11 +128,11 @@ pub enum ErrorKind { pub(crate) struct Error; impl IntrinsicObject for Error { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .method(Self::to_string, "toString", 0) diff --git a/boa_engine/src/builtins/error/range.rs b/boa_engine/src/builtins/error/range.rs index cddca0ac93..987dc1c36c 100644 --- a/boa_engine/src/builtins/error/range.rs +++ b/boa_engine/src/builtins/error/range.rs @@ -14,6 +14,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -26,13 +27,13 @@ use super::{Error, ErrorKind}; pub(crate) struct RangeError; impl IntrinsicObject for RangeError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); diff --git a/boa_engine/src/builtins/error/reference.rs b/boa_engine/src/builtins/error/reference.rs index aef5e9fe98..5b7b31f74e 100644 --- a/boa_engine/src/builtins/error/reference.rs +++ b/boa_engine/src/builtins/error/reference.rs @@ -14,6 +14,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -25,13 +26,13 @@ use super::{Error, ErrorKind}; pub(crate) struct ReferenceError; impl IntrinsicObject for ReferenceError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); diff --git a/boa_engine/src/builtins/error/syntax.rs b/boa_engine/src/builtins/error/syntax.rs index f093d92553..42f83997bc 100644 --- a/boa_engine/src/builtins/error/syntax.rs +++ b/boa_engine/src/builtins/error/syntax.rs @@ -16,6 +16,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -28,13 +29,13 @@ use super::{Error, ErrorKind}; pub(crate) struct SyntaxError; impl IntrinsicObject for SyntaxError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); diff --git a/boa_engine/src/builtins/error/type.rs b/boa_engine/src/builtins/error/type.rs index a18073d8b5..d480170e51 100644 --- a/boa_engine/src/builtins/error/type.rs +++ b/boa_engine/src/builtins/error/type.rs @@ -25,6 +25,7 @@ use crate::{ native_function::NativeFunction, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -37,13 +38,13 @@ use super::{Error, ErrorKind}; pub(crate) struct TypeError; impl IntrinsicObject for TypeError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); @@ -114,7 +115,7 @@ impl BuiltInConstructor for TypeError { pub(crate) struct ThrowTypeError; impl IntrinsicObject for ThrowTypeError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { fn throw_type_error(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { Err(JsNativeError::typ() .with_message( @@ -124,8 +125,8 @@ impl IntrinsicObject for ThrowTypeError { .into()) } - let obj = BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.constructors().function().prototype()) + let obj = BuiltInBuilder::with_intrinsic::(realm) + .prototype(realm.intrinsics().constructors().function().prototype()) .static_property(utf16!("name"), "ThrowTypeError", Attribute::empty()) .static_property(utf16!("length"), 0, Attribute::empty()) .build(); @@ -137,7 +138,7 @@ impl IntrinsicObject for ThrowTypeError { function: NativeFunction::from_fn_ptr(throw_type_error), constructor: None, }, - intrinsics.clone(), + realm.clone(), )); } diff --git a/boa_engine/src/builtins/error/uri.rs b/boa_engine/src/builtins/error/uri.rs index 0605e9fbc5..94794d5d13 100644 --- a/boa_engine/src/builtins/error/uri.rs +++ b/boa_engine/src/builtins/error/uri.rs @@ -15,6 +15,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -27,13 +28,13 @@ use super::{Error, ErrorKind}; pub(crate) struct UriError; impl IntrinsicObject for UriError { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().error().constructor()) - .inherits(Some(intrinsics.constructors().error().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) .property(utf16!("name"), Self::NAME, attribute) .property(utf16!("message"), "", attribute) .build(); diff --git a/boa_engine/src/builtins/escape/mod.rs b/boa_engine/src/builtins/escape/mod.rs index df5d1fdb6c..1d6632ab4f 100644 --- a/boa_engine/src/builtins/escape/mod.rs +++ b/boa_engine/src/builtins/escape/mod.rs @@ -11,7 +11,8 @@ //! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object use crate::{ - context::intrinsics::Intrinsics, js_string, Context, JsArgs, JsObject, JsResult, JsValue, + context::intrinsics::Intrinsics, js_string, realm::Realm, Context, JsArgs, JsObject, JsResult, + JsValue, }; use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject}; @@ -21,8 +22,8 @@ use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject}; pub(crate) struct Escape; impl IntrinsicObject for Escape { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(escape) .name(Self::NAME) .length(1) @@ -94,8 +95,8 @@ fn escape(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult< pub(crate) struct Unescape; impl IntrinsicObject for Unescape { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(unescape) .name(Self::NAME) .length(1) diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 981efaf896..080712b471 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -10,12 +10,15 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval use crate::{ - builtins::BuiltInObject, context::intrinsics::Intrinsics, environments::Environment, - error::JsNativeError, object::JsObject, Context, JsArgs, JsResult, JsString, JsValue, + builtins::BuiltInObject, bytecompiler::ByteCompiler, context::intrinsics::Intrinsics, + environments::Environment, error::JsNativeError, object::JsObject, realm::Realm, Context, + JsArgs, JsResult, JsString, JsValue, }; use boa_ast::operations::{ contains, contains_arguments, top_level_var_declared_names, ContainsSymbol, }; +use boa_gc::Gc; +use boa_interner::Sym; use boa_parser::{Parser, Source}; use boa_profiler::Profiler; @@ -25,10 +28,10 @@ use super::{BuiltInBuilder, IntrinsicObject}; pub(crate) struct Eval; impl IntrinsicObject for Eval { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .callable(Self::eval) .name(Self::NAME) .length(1) @@ -91,12 +94,12 @@ impl Eval { fn restore_environment(context: &mut Context<'_>, action: EnvStackAction) { match action { EnvStackAction::Truncate(size) => { - context.realm.environments.truncate(size); + context.vm.environments.truncate(size); } EnvStackAction::Restore(envs) => { // Pop all environments created during the eval execution and restore the original stack. - context.realm.environments.truncate(1); - context.realm.environments.extend(envs); + context.vm.environments.truncate(1); + context.vm.environments.extend(envs); } } } @@ -112,8 +115,13 @@ impl Eval { // Because of implementation details the following code differs from the spec. + // 3. Let evalRealm be the current Realm Record. + // 4. NOTE: In the case of a direct eval, evalRealm is the realm of both the caller of eval + // and of the eval function itself. // 5. Perform ? HostEnsureCanCompileStrings(evalRealm). - context.host_hooks().ensure_can_compile_strings(context)?; + context + .host_hooks() + .ensure_can_compile_strings(context.realm().clone(), context)?; // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection: // a. Let script be ParseText(StringToCodePoints(x), Script). @@ -132,7 +140,7 @@ impl Eval { // 9. Let inClassFieldInitializer be false. // a. Let thisEnvRec be GetThisEnvironment(). let flags = match context - .realm + .vm .environments .get_this_environment() .as_function_slots() @@ -203,28 +211,21 @@ impl Eval { let action = if direct { // If the call to eval is direct, the code is executed in the current environment. - // Poison the current environment, because it may contain new declarations after/during eval. + // Poison the last parent function environment, because it may contain new declarations after/during eval. if !strict { - context.realm.environments.poison_current(); + context.vm.environments.poison_last_function(); } // Set the compile time environment to the current running environment and save the number of current environments. - context.realm.compile_env = context.realm.environments.current_compile_environment(); - let environments_len = context.realm.environments.len(); + let environments_len = context.vm.environments.len(); // Pop any added runtime environments that were not removed during the eval execution. EnvStackAction::Truncate(environments_len) } else { // If the call to eval is indirect, the code is executed in the global environment. - // Poison all environments, because the global environment may contain new declarations after/during eval. - if !strict { - context.realm.environments.poison_all(); - } - // Pop all environments before the eval execution. - let environments = context.realm.environments.pop_to_global(); - context.realm.compile_env = context.realm.environments.current_compile_environment(); + let environments = context.vm.environments.pop_to_global(); // Restore all environments to the state from before the eval execution. EnvStackAction::Restore(environments) @@ -235,7 +236,7 @@ impl Eval { if !strict { // Error if any var declaration in the eval code already exists as a let/const declaration in the current running environment. if let Some(name) = context - .realm + .vm .environments .has_lex_binding_until_function_environment(&top_level_var_declared_names(&body)) { @@ -249,16 +250,23 @@ impl Eval { // TODO: check if private identifiers inside `eval` are valid. // Compile and execute the eval statement list. - let code_block = context.compile_with_new_declarative(&body, strict); + let code_block = { + let mut compiler = ByteCompiler::new( + Sym::MAIN, + body.strict(), + false, + context.vm.environments.current_compile_environment(), + context, + ); + compiler.compile_statement_list_with_new_declarative(&body, true, strict); + 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 // function environment before evaluating. if direct && !strict { - context - .realm - .environments - .extend_outer_function_environment(); + context.vm.environments.extend_outer_function_environment(); } let result = context.execute(code_block); diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 56e0a566ce..f204deed8c 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -22,6 +22,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, object::{JsFunction, PrivateElement}, property::{Attribute, PropertyDescriptor, PropertyKey}, + realm::Realm, string::utf16, symbol::JsSymbol, value::IntegerOrInfinity, @@ -296,7 +297,7 @@ unsafe impl Trace for FunctionKind { #[derive(Debug, Trace, Finalize)] pub struct Function { kind: FunctionKind, - realm_intrinsics: Intrinsics, + realm: Realm, } impl Function { @@ -323,11 +324,8 @@ impl Function { } /// Creates a new `Function`. - pub(crate) fn new(kind: FunctionKind, intrinsics: Intrinsics) -> Self { - Self { - kind, - realm_intrinsics: intrinsics, - } + pub(crate) fn new(kind: FunctionKind, realm: Realm) -> Self { + Self { kind, realm } } /// Returns true if the function object is a derived constructor. @@ -441,9 +439,9 @@ impl Function { } } - /// Gets the `Realm`'s intrinsic objects from where this function originates. - pub const fn realm_intrinsics(&self) -> &Intrinsics { - &self.realm_intrinsics + /// Gets the `Realm` from where this function originates. + pub const fn realm(&self) -> &Realm { + &self.realm } /// Gets a reference to the [`FunctionKind`] of the `Function`. @@ -462,24 +460,27 @@ impl Function { pub struct BuiltInFunctionObject; impl IntrinsicObject for BuiltInFunctionObject { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("function", "init"); - BuiltInBuilder::with_object(intrinsics, intrinsics.constructors().function().prototype()) - .callable(Self::prototype) - .name("") - .length(0) - .build(); + BuiltInBuilder::with_object( + realm, + realm.intrinsics().constructors().function().prototype(), + ) + .callable(Self::prototype) + .name("") + .length(0) + .build(); - let has_instance = BuiltInBuilder::new(intrinsics) + let has_instance = BuiltInBuilder::new(realm) .callable(Self::has_instance) .name("[Symbol.iterator]") .length(1) .build(); - let throw_type_error = intrinsics.objects().throw_type_error(); + let throw_type_error = realm.intrinsics().objects().throw_type_error(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .method(Self::apply, "apply", 2) .method(Self::bind, "bind", 1) .method(Self::call, "call", 1) @@ -558,7 +559,9 @@ impl BuiltInFunctionObject { ) -> JsResult { // 1. Let currentRealm be the current Realm Record. // 2. Perform ? HostEnsureCanCompileStrings(currentRealm). - context.host_hooks().ensure_can_compile_strings(context)?; + context + .host_hooks() + .ensure_can_compile_strings(context.realm().clone(), context)?; // 3. If newTarget is undefined, set newTarget to constructor. let new_target = if new_target.is_undefined() { @@ -731,9 +734,14 @@ impl BuiltInFunctionObject { .name(Sym::ANONYMOUS) .generator(generator) .r#async(r#async) - .compile(¶meters, &body, context); + .compile( + ¶meters, + &body, + context.realm().environment().compile_env(), + context, + ); - let environments = context.realm.environments.pop_to_global(); + let environments = context.vm.environments.pop_to_global(); let function_object = if generator { crate::vm::create_generator_function_object( @@ -754,7 +762,7 @@ impl BuiltInFunctionObject { ) }; - context.realm.environments.extend(environments); + context.vm.environments.extend(environments); Ok(function_object) } else if generator { @@ -764,10 +772,11 @@ impl BuiltInFunctionObject { .compile( &FormalParameterList::default(), &StatementList::default(), + context.realm().environment().compile_env(), context, ); - let environments = context.realm.environments.pop_to_global(); + let environments = context.vm.environments.pop_to_global(); let function_object = crate::vm::create_generator_function_object( code, r#async, @@ -775,17 +784,18 @@ impl BuiltInFunctionObject { Some(prototype), context, ); - context.realm.environments.extend(environments); + context.vm.environments.extend(environments); Ok(function_object) } else { let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile( &FormalParameterList::default(), &StatementList::default(), + context.realm().environment().compile_env(), context, ); - let environments = context.realm.environments.pop_to_global(); + let environments = context.vm.environments.pop_to_global(); let function_object = crate::vm::create_function_object( code, r#async, @@ -794,7 +804,7 @@ impl BuiltInFunctionObject { false, context, ); - context.realm.environments.extend(environments); + context.vm.environments.extend(environments); Ok(function_object) } diff --git a/boa_engine/src/builtins/generator/mod.rs b/boa_engine/src/builtins/generator/mod.rs index ebf5ccaea1..d0cbe860df 100644 --- a/boa_engine/src/builtins/generator/mod.rs +++ b/boa_engine/src/builtins/generator/mod.rs @@ -16,6 +16,7 @@ use crate::{ error::JsNativeError, object::{JsObject, CONSTRUCTOR}, property::Attribute, + realm::Realm, symbol::JsSymbol, value::JsValue, vm::{CallFrame, CompletionRecord, GeneratorResumeKind}, @@ -46,7 +47,7 @@ pub(crate) struct GeneratorContext { pub(crate) call_frame: CallFrame, pub(crate) stack: Vec, pub(crate) active_function: Option, - pub(crate) realm_intrinsics: Intrinsics, + pub(crate) realm: Realm, } /// The internal representation of a `Generator` object. @@ -61,11 +62,17 @@ pub struct Generator { } impl IntrinsicObject for Generator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 1) .static_method(Self::r#return, "return", 1) .static_method(Self::throw, "throw", 1) @@ -76,7 +83,11 @@ impl IntrinsicObject for Generator { ) .static_property( CONSTRUCTOR, - intrinsics.constructors().generator_function().prototype(), + realm + .intrinsics() + .constructors() + .generator_function() + .prototype(), Attribute::CONFIGURABLE, ) .build(); @@ -217,7 +228,7 @@ impl Generator { drop(generator_obj_mut); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -225,10 +236,7 @@ impl Generator { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + let old_realm = context.enter_realm(generator_context.realm.clone()); context.vm.push_frame(generator_context.call_frame.clone()); if !first_execution { context.vm.push(value.clone()); @@ -243,7 +251,7 @@ impl Generator { .pop_frame() .expect("generator call frame must exist"); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -251,10 +259,7 @@ impl Generator { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + context.enter_realm(old_realm); let mut generator_obj_mut = generator_obj.borrow_mut(); let generator = generator_obj_mut @@ -348,7 +353,7 @@ impl Generator { drop(generator_obj_mut); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -356,10 +361,7 @@ impl Generator { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + let old_realm = context.enter_realm(generator_context.realm.clone()); context.vm.push_frame(generator_context.call_frame.clone()); let completion_record = match abrupt_completion { @@ -380,7 +382,7 @@ impl Generator { .pop_frame() .expect("generator call frame must exist"); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -388,10 +390,7 @@ impl Generator { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + context.enter_realm(old_realm); let mut generator_obj_mut = generator_obj.borrow_mut(); let generator = generator_obj_mut diff --git a/boa_engine/src/builtins/generator_function/mod.rs b/boa_engine/src/builtins/generator_function/mod.rs index 68d393c451..90d2fb951d 100644 --- a/boa_engine/src/builtins/generator_function/mod.rs +++ b/boa_engine/src/builtins/generator_function/mod.rs @@ -15,6 +15,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::PROTOTYPE, property::Attribute, + realm::Realm, symbol::JsSymbol, value::JsValue, Context, JsResult, @@ -28,15 +29,17 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; pub struct GeneratorFunction; impl IntrinsicObject for GeneratorFunction { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) - .inherits(Some(intrinsics.constructors().function().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .inherits(Some( + realm.intrinsics().constructors().function().prototype(), + )) .constructor_attributes(Attribute::CONFIGURABLE) .property( PROTOTYPE, - intrinsics.objects().generator(), + realm.intrinsics().objects().generator(), Attribute::CONFIGURABLE, ) .property( diff --git a/boa_engine/src/builtins/intl/collator/mod.rs b/boa_engine/src/builtins/intl/collator/mod.rs index 64dfff821b..dcbed041fb 100644 --- a/boa_engine/src/builtins/intl/collator/mod.rs +++ b/boa_engine/src/builtins/intl/collator/mod.rs @@ -22,6 +22,7 @@ use crate::{ JsObject, ObjectData, }, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, @@ -159,15 +160,15 @@ impl Service for Collator { } impl IntrinsicObject for Collator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let compare = BuiltInBuilder::new(intrinsics) + let compare = BuiltInBuilder::new(realm) .callable(Self::compare) .name("get compare") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index 87772b0f85..eb4c4f8021 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -13,6 +13,7 @@ use crate::{ error::JsNativeError, js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, + realm::Realm, string::utf16, Context, JsResult, JsString, JsValue, }; @@ -62,10 +63,10 @@ pub struct DateTimeFormat { } impl IntrinsicObject for DateTimeFormat { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics).build(); + BuiltInBuilder::from_standard_constructor::(realm).build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/intl/list_format/mod.rs b/boa_engine/src/builtins/intl/list_format/mod.rs index af3575c49e..656699338a 100644 --- a/boa_engine/src/builtins/intl/list_format/mod.rs +++ b/boa_engine/src/builtins/intl/list_format/mod.rs @@ -10,6 +10,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, @@ -48,10 +49,10 @@ impl Service for ListFormat { } impl IntrinsicObject for ListFormat { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/intl/locale/mod.rs b/boa_engine/src/builtins/intl/locale/mod.rs index 264335c0ac..2449ca930e 100644 --- a/boa_engine/src/builtins/intl/locale/mod.rs +++ b/boa_engine/src/builtins/intl/locale/mod.rs @@ -1,4 +1,4 @@ -use crate::string::utf16; +use crate::{realm::Realm, string::utf16}; use boa_profiler::Profiler; use icu_collator::CaseFirst; use icu_datetime::options::preferences::HourCycle; @@ -32,60 +32,60 @@ use super::options::{coerce_options_to_object, get_option}; pub(crate) struct Locale; impl IntrinsicObject for Locale { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let base_name = BuiltInBuilder::new(intrinsics) + let base_name = BuiltInBuilder::new(realm) .callable(Self::base_name) .name("get baseName") .build(); - let calendar = BuiltInBuilder::new(intrinsics) + let calendar = BuiltInBuilder::new(realm) .callable(Self::calendar) .name("get calendar") .build(); - let case_first = BuiltInBuilder::new(intrinsics) + let case_first = BuiltInBuilder::new(realm) .callable(Self::case_first) .name("get caseFirst") .build(); - let collation = BuiltInBuilder::new(intrinsics) + let collation = BuiltInBuilder::new(realm) .callable(Self::collation) .name("get collation") .build(); - let hour_cycle = BuiltInBuilder::new(intrinsics) + let hour_cycle = BuiltInBuilder::new(realm) .callable(Self::hour_cycle) .name("get hourCycle") .build(); - let numeric = BuiltInBuilder::new(intrinsics) + let numeric = BuiltInBuilder::new(realm) .callable(Self::numeric) .name("get numeric") .build(); - let numbering_system = BuiltInBuilder::new(intrinsics) + let numbering_system = BuiltInBuilder::new(realm) .callable(Self::numbering_system) .name("get numberingSystem") .build(); - let language = BuiltInBuilder::new(intrinsics) + let language = BuiltInBuilder::new(realm) .callable(Self::language) .name("get language") .build(); - let script = BuiltInBuilder::new(intrinsics) + let script = BuiltInBuilder::new(realm) .callable(Self::script) .name("get script") .build(); - let region = BuiltInBuilder::new(intrinsics) + let region = BuiltInBuilder::new(realm) .callable(Self::region) .name("get region") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .property( JsSymbol::to_string_tag(), "Intl.Locale", diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 97284c2818..1cdabf97eb 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -14,6 +14,7 @@ use crate::{ context::{intrinsics::Intrinsics, BoaProvider}, object::JsObject, property::Attribute, + realm::Realm, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, }; @@ -39,10 +40,10 @@ mod options; pub(crate) struct Intl; impl IntrinsicObject for Intl { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .static_property( JsSymbol::to_string_tag(), Self::NAME, @@ -50,27 +51,35 @@ impl IntrinsicObject for Intl { ) .static_property( Collator::NAME, - intrinsics.constructors().collator().constructor(), + realm.intrinsics().constructors().collator().constructor(), Collator::ATTRIBUTE, ) .static_property( ListFormat::NAME, - intrinsics.constructors().list_format().constructor(), + realm + .intrinsics() + .constructors() + .list_format() + .constructor(), ListFormat::ATTRIBUTE, ) .static_property( Locale::NAME, - intrinsics.constructors().locale().constructor(), + realm.intrinsics().constructors().locale().constructor(), Locale::ATTRIBUTE, ) .static_property( Segmenter::NAME, - intrinsics.constructors().segmenter().constructor(), + realm.intrinsics().constructors().segmenter().constructor(), Segmenter::ATTRIBUTE, ) .static_property( DateTimeFormat::NAME, - intrinsics.constructors().date_time_format().constructor(), + realm + .intrinsics() + .constructors() + .date_time_format() + .constructor(), DateTimeFormat::ATTRIBUTE, ) .static_method(Self::get_canonical_locales, "getCanonicalLocales", 1) diff --git a/boa_engine/src/builtins/intl/segmenter/mod.rs b/boa_engine/src/builtins/intl/segmenter/mod.rs index 6684bc4d9f..bd32d380e9 100644 --- a/boa_engine/src/builtins/intl/segmenter/mod.rs +++ b/boa_engine/src/builtins/intl/segmenter/mod.rs @@ -6,6 +6,7 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::JsObject, + realm::Realm, Context, JsResult, JsValue, }; @@ -17,10 +18,10 @@ pub(crate) use options::*; pub(crate) struct Segmenter; impl IntrinsicObject for Segmenter { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics).build(); + BuiltInBuilder::from_standard_constructor::(realm).build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs b/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs index dd0202ce01..8f11666b1d 100644 --- a/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs +++ b/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs @@ -7,6 +7,7 @@ use crate::{ context::intrinsics::Intrinsics, native_function::NativeFunction, object::{FunctionObjectBuilder, JsObject, ObjectData}, + realm::Realm, string::utf16, Context, JsArgs, JsNativeError, JsResult, JsValue, }; @@ -26,11 +27,17 @@ pub struct AsyncFromSyncIterator { } impl IntrinsicObject for AsyncFromSyncIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("AsyncFromSyncIteratorPrototype", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().async_iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .async_iterator(), + ) .static_method(Self::next, "next", 1) .static_method(Self::r#return, "return", 1) .static_method(Self::throw, "throw", 1) diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index 28a5e96c4c..d54f5b760f 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/boa_engine/src/builtins/iterable/mod.rs @@ -5,6 +5,7 @@ use crate::{ context::intrinsics::Intrinsics, error::JsNativeError, object::JsObject, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsResult, JsValue, @@ -135,10 +136,10 @@ impl IteratorPrototypes { pub(crate) struct Iterator; impl IntrinsicObject for Iterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("Iterator Prototype", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .static_method( |v, _, _| Ok(v.clone()), (JsSymbol::iterator(), "[Symbol.iterator]"), @@ -161,10 +162,10 @@ impl IntrinsicObject for Iterator { pub(crate) struct AsyncIterator; impl IntrinsicObject for AsyncIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("AsyncIteratorPrototype", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .static_method( |v, _, _| Ok(v.clone()), (JsSymbol::async_iterator(), "[Symbol.asyncIterator]"), diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index f5145d301b..1cba7255ab 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -20,16 +20,20 @@ use std::{ use crate::{ builtins::BuiltInObject, + bytecompiler::ByteCompiler, context::intrinsics::Intrinsics, error::JsNativeError, js_string, object::{JsObject, RecursionLimiter}, property::{Attribute, PropertyNameKind}, + realm::Realm, string::{utf16, CodePoint}, symbol::JsSymbol, value::IntegerOrInfinity, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_gc::Gc; +use boa_interner::Sym; use boa_parser::{Parser, Source}; use boa_profiler::Profiler; @@ -137,13 +141,13 @@ where pub(crate) struct Json; impl IntrinsicObject for Json { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let to_string_tag = JsSymbol::to_string_tag(); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .static_method(Self::parse, "parse", 2) .static_method(Self::stringify, "stringify", 3) .static_property(to_string_tag, Self::NAME, attribute) @@ -206,7 +210,18 @@ impl Json { let mut parser = Parser::new(Source::from_bytes(&script_string)); parser.set_json_parse(); let statement_list = parser.parse_script(context.interner_mut())?; - let code_block = context.compile_json_parse(&statement_list); + let code_block = { + let mut compiler = ByteCompiler::new( + Sym::MAIN, + statement_list.strict(), + true, + context.realm().environment().compile_env(), + context, + ); + compiler.create_script_decls(&statement_list, false); + compiler.compile_statement_list(&statement_list, true, false); + Gc::new(compiler.finish()) + }; let unfiltered = context.execute(code_block)?; // 11. If IsCallable(reviver) is true, then diff --git a/boa_engine/src/builtins/map/map_iterator.rs b/boa_engine/src/builtins/map/map_iterator.rs index 67ae12ba73..1092d8c585 100644 --- a/boa_engine/src/builtins/map/map_iterator.rs +++ b/boa_engine/src/builtins/map/map_iterator.rs @@ -14,6 +14,7 @@ use crate::{ error::JsNativeError, object::{JsObject, ObjectData}, property::{Attribute, PropertyNameKind}, + realm::Realm, symbol::JsSymbol, Context, JsResult, }; @@ -36,11 +37,17 @@ pub struct MapIterator { } impl IntrinsicObject for MapIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("MapIterator", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 0) .static_property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/map/mod.rs b/boa_engine/src/builtins/map/mod.rs index b058f686cb..c321576c7c 100644 --- a/boa_engine/src/builtins/map/mod.rs +++ b/boa_engine/src/builtins/map/mod.rs @@ -16,6 +16,7 @@ use crate::{ error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::{Attribute, PropertyNameKind}, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, @@ -37,25 +38,25 @@ mod tests; pub(crate) struct Map; impl IntrinsicObject for Map { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); - let get_size = BuiltInBuilder::new(intrinsics) + let get_size = BuiltInBuilder::new(realm) .callable(Self::get_size) .name("get size") .build(); - let entries_function = BuiltInBuilder::new(intrinsics) + let entries_function = BuiltInBuilder::new(realm) .callable(Self::entries) .name("entries") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_accessor( JsSymbol::species(), Some(get_species), diff --git a/boa_engine/src/builtins/math/mod.rs b/boa_engine/src/builtins/math/mod.rs index d3d6672d05..406661c5ff 100644 --- a/boa_engine/src/builtins/math/mod.rs +++ b/boa_engine/src/builtins/math/mod.rs @@ -13,7 +13,8 @@ use crate::{ builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject, - property::Attribute, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, + property::Attribute, realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, + JsValue, }; use boa_profiler::Profiler; @@ -27,11 +28,11 @@ mod tests; pub(crate) struct Math; impl IntrinsicObject for Math { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .static_property(utf16!("E"), std::f64::consts::E, attribute) .static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute) .static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute) diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 0be9b38bf8..3855d8eab6 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -100,6 +100,7 @@ use crate::{ FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE, }, property::{Attribute, PropertyDescriptor, PropertyKey}, + realm::Realm, string::utf16, Context, JsResult, JsString, JsValue, }; @@ -118,7 +119,7 @@ pub(crate) trait IntrinsicObject { /// /// This is where the methods, properties, static methods and the constructor of a built-in must /// be initialized to be accessible from ECMAScript. - fn init(intrinsics: &Intrinsics); + fn init(realm: &Realm); /// Gets the intrinsic object. fn get(intrinsics: &Intrinsics) -> JsObject; @@ -183,97 +184,93 @@ fn global_binding(context: &mut Context<'_>) -> JsResult<()> { Ok(()) } -impl Intrinsics { +impl Realm { /// Abstract operation [`CreateIntrinsics ( realmRec )`][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-createintrinsics - pub(crate) fn new() -> Self { - let intrinsics = Self::default(); - - BuiltInFunctionObject::init(&intrinsics); - BuiltInObjectObject::init(&intrinsics); - Iterator::init(&intrinsics); - AsyncIterator::init(&intrinsics); - AsyncFromSyncIterator::init(&intrinsics); - ForInIterator::init(&intrinsics); - Math::init(&intrinsics); - Json::init(&intrinsics); - Array::init(&intrinsics); - ArrayIterator::init(&intrinsics); - Proxy::init(&intrinsics); - ArrayBuffer::init(&intrinsics); - BigInt::init(&intrinsics); - Boolean::init(&intrinsics); - Date::init(&intrinsics); - DataView::init(&intrinsics); - Map::init(&intrinsics); - MapIterator::init(&intrinsics); - IsFinite::init(&intrinsics); - IsNaN::init(&intrinsics); - ParseInt::init(&intrinsics); - ParseFloat::init(&intrinsics); - Number::init(&intrinsics); - Eval::init(&intrinsics); - Set::init(&intrinsics); - SetIterator::init(&intrinsics); - String::init(&intrinsics); - StringIterator::init(&intrinsics); - RegExp::init(&intrinsics); - RegExpStringIterator::init(&intrinsics); - TypedArray::init(&intrinsics); - Int8Array::init(&intrinsics); - Uint8Array::init(&intrinsics); - Uint8ClampedArray::init(&intrinsics); - Int16Array::init(&intrinsics); - Uint16Array::init(&intrinsics); - Int32Array::init(&intrinsics); - Uint32Array::init(&intrinsics); - BigInt64Array::init(&intrinsics); - BigUint64Array::init(&intrinsics); - Float32Array::init(&intrinsics); - Float64Array::init(&intrinsics); - Symbol::init(&intrinsics); - Error::init(&intrinsics); - RangeError::init(&intrinsics); - ReferenceError::init(&intrinsics); - TypeError::init(&intrinsics); - ThrowTypeError::init(&intrinsics); - SyntaxError::init(&intrinsics); - EvalError::init(&intrinsics); - UriError::init(&intrinsics); - AggregateError::init(&intrinsics); - Reflect::init(&intrinsics); - Generator::init(&intrinsics); - GeneratorFunction::init(&intrinsics); - Promise::init(&intrinsics); - AsyncFunction::init(&intrinsics); - AsyncGenerator::init(&intrinsics); - AsyncGeneratorFunction::init(&intrinsics); - EncodeUri::init(&intrinsics); - EncodeUriComponent::init(&intrinsics); - DecodeUri::init(&intrinsics); - DecodeUriComponent::init(&intrinsics); - WeakRef::init(&intrinsics); - WeakMap::init(&intrinsics); - WeakSet::init(&intrinsics); + pub(crate) fn initialize(&self) { + BuiltInFunctionObject::init(self); + BuiltInObjectObject::init(self); + Iterator::init(self); + AsyncIterator::init(self); + AsyncFromSyncIterator::init(self); + ForInIterator::init(self); + Math::init(self); + Json::init(self); + Array::init(self); + ArrayIterator::init(self); + Proxy::init(self); + ArrayBuffer::init(self); + BigInt::init(self); + Boolean::init(self); + Date::init(self); + DataView::init(self); + Map::init(self); + MapIterator::init(self); + IsFinite::init(self); + IsNaN::init(self); + ParseInt::init(self); + ParseFloat::init(self); + Number::init(self); + Eval::init(self); + Set::init(self); + SetIterator::init(self); + String::init(self); + StringIterator::init(self); + RegExp::init(self); + RegExpStringIterator::init(self); + TypedArray::init(self); + Int8Array::init(self); + Uint8Array::init(self); + Uint8ClampedArray::init(self); + Int16Array::init(self); + Uint16Array::init(self); + Int32Array::init(self); + Uint32Array::init(self); + BigInt64Array::init(self); + BigUint64Array::init(self); + Float32Array::init(self); + Float64Array::init(self); + Symbol::init(self); + Error::init(self); + RangeError::init(self); + ReferenceError::init(self); + TypeError::init(self); + ThrowTypeError::init(self); + SyntaxError::init(self); + EvalError::init(self); + UriError::init(self); + AggregateError::init(self); + Reflect::init(self); + Generator::init(self); + GeneratorFunction::init(self); + Promise::init(self); + AsyncFunction::init(self); + AsyncGenerator::init(self); + AsyncGeneratorFunction::init(self); + EncodeUri::init(self); + EncodeUriComponent::init(self); + DecodeUri::init(self); + DecodeUriComponent::init(self); + WeakRef::init(self); + WeakMap::init(self); + WeakSet::init(self); #[cfg(feature = "annex-b")] { - escape::Escape::init(&intrinsics); - escape::Unescape::init(&intrinsics); + escape::Escape::init(self); + escape::Unescape::init(self); } #[cfg(feature = "intl")] { - intl::Intl::init(&intrinsics); - intl::Collator::init(&intrinsics); - intl::ListFormat::init(&intrinsics); - intl::Locale::init(&intrinsics); - intl::DateTimeFormat::init(&intrinsics); - intl::Segmenter::init(&intrinsics); + intl::Intl::init(self); + intl::Collator::init(self); + intl::ListFormat::init(self); + intl::Locale::init(self); + intl::DateTimeFormat::init(self); + intl::Segmenter::init(self); } - - intrinsics } } @@ -286,7 +283,7 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult global_object.define_property_or_throw( utf16!("globalThis"), PropertyDescriptor::builder() - .value(context.realm.global_this().clone()) + .value(context.realm().global_this().clone()) .writable(true) .enumerable(false) .configurable(true), @@ -429,7 +426,7 @@ struct Callable { name: JsString, length: usize, kind: Kind, - intrinsics: Intrinsics, + realm: Realm, } /// Marker for an ordinary object. @@ -483,7 +480,7 @@ impl ApplyToObject for Callable { function: NativeFunction::from_fn_ptr(self.function), constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base), }, - self.intrinsics, + self.realm, ); let length = PropertyDescriptor::builder() @@ -517,42 +514,39 @@ impl ApplyToObject for OrdinaryObject { #[derive(Debug)] #[must_use = "You need to call the `build` method in order for this to correctly assign the inner data"] struct BuiltInBuilder<'ctx, Kind> { - intrinsics: &'ctx Intrinsics, + realm: &'ctx Realm, object: JsObject, kind: Kind, prototype: JsObject, } impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { - fn new(intrinsics: &'ctx Intrinsics) -> BuiltInBuilder<'ctx, OrdinaryObject> { + fn new(realm: &'ctx Realm) -> BuiltInBuilder<'ctx, OrdinaryObject> { BuiltInBuilder { - intrinsics, + realm, object: JsObject::with_null_proto(), kind: OrdinaryObject, - prototype: intrinsics.constructors().object().prototype(), + prototype: realm.intrinsics().constructors().object().prototype(), } } fn with_intrinsic( - intrinsics: &'ctx Intrinsics, + realm: &'ctx Realm, ) -> BuiltInBuilder<'ctx, OrdinaryObject> { BuiltInBuilder { - intrinsics, - object: I::get(intrinsics), + realm, + object: I::get(realm.intrinsics()), kind: OrdinaryObject, - prototype: intrinsics.constructors().object().prototype(), + prototype: realm.intrinsics().constructors().object().prototype(), } } - fn with_object( - intrinsics: &'ctx Intrinsics, - object: JsObject, - ) -> BuiltInBuilder<'ctx, OrdinaryObject> { + fn with_object(realm: &'ctx Realm, object: JsObject) -> BuiltInBuilder<'ctx, OrdinaryObject> { BuiltInBuilder { - intrinsics, + realm, object, kind: OrdinaryObject, - prototype: intrinsics.constructors().object().prototype(), + prototype: realm.intrinsics().constructors().object().prototype(), } } } @@ -563,27 +557,32 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { function: NativeFunctionPointer, ) -> BuiltInBuilder<'ctx, Callable> { BuiltInBuilder { - intrinsics: self.intrinsics, + realm: self.realm, object: self.object, kind: Callable { function, name: js_string!(""), length: 0, kind: OrdinaryFunction, - intrinsics: self.intrinsics.clone(), + realm: self.realm.clone(), }, - prototype: self.intrinsics.constructors().function().prototype(), + prototype: self + .realm + .intrinsics() + .constructors() + .function() + .prototype(), } } } impl<'ctx> BuiltInBuilder<'ctx, Callable> { fn from_standard_constructor( - intrinsics: &'ctx Intrinsics, + realm: &'ctx Realm, ) -> BuiltInBuilder<'ctx, Callable> { - let constructor = SC::STANDARD_CONSTRUCTOR(intrinsics.constructors()); + let constructor = SC::STANDARD_CONSTRUCTOR(realm.intrinsics().constructors()); BuiltInBuilder { - intrinsics, + realm, object: constructor.constructor(), kind: Callable { function: SC::constructor, @@ -591,25 +590,25 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable> { length: SC::LENGTH, kind: Constructor { prototype: constructor.prototype(), - inherits: Some(intrinsics.constructors().object().prototype()), + inherits: Some(realm.intrinsics().constructors().object().prototype()), attributes: Attribute::WRITABLE | Attribute::CONFIGURABLE, }, - intrinsics: intrinsics.clone(), + realm: realm.clone(), }, - prototype: intrinsics.constructors().function().prototype(), + prototype: realm.intrinsics().constructors().function().prototype(), } } fn no_proto(self) -> BuiltInBuilder<'ctx, Callable> { BuiltInBuilder { - intrinsics: self.intrinsics, + realm: self.realm, object: self.object, kind: Callable { function: self.kind.function, name: self.kind.name, length: self.kind.length, kind: ConstructorNoProto, - intrinsics: self.intrinsics.clone(), + realm: self.realm.clone(), }, prototype: self.prototype, } @@ -623,7 +622,7 @@ impl BuiltInBuilder<'_, T> { B: Into, { let binding = binding.into(); - let function = BuiltInBuilder::new(self.intrinsics) + let function = BuiltInBuilder::new(self.realm) .callable(function) .name(binding.name) .length(length) @@ -691,7 +690,7 @@ impl BuiltInBuilder<'_, Callable> { B: Into, { let binding = binding.into(); - let function = BuiltInBuilder::new(self.intrinsics) + let function = BuiltInBuilder::new(self.realm) .callable(function) .name(binding.name) .length(length) diff --git a/boa_engine/src/builtins/number/globals.rs b/boa_engine/src/builtins/number/globals.rs index 6ef92ba1a5..3dd405c121 100644 --- a/boa_engine/src/builtins/number/globals.rs +++ b/boa_engine/src/builtins/number/globals.rs @@ -4,6 +4,7 @@ use crate::{ builtins::{string::is_trimmable_whitespace, BuiltInBuilder, BuiltInObject, IntrinsicObject}, context::intrinsics::Intrinsics, object::JsObject, + realm::Realm, string::Utf16Trim, Context, JsArgs, JsResult, JsValue, }; @@ -36,8 +37,8 @@ fn is_finite(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResu pub(crate) struct IsFinite; impl IntrinsicObject for IsFinite { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(is_finite) .name(Self::NAME) .length(1) @@ -83,8 +84,8 @@ pub(crate) fn is_nan( pub(crate) struct IsNaN; impl IntrinsicObject for IsNaN { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(is_nan) .name(Self::NAME) .length(1) @@ -225,8 +226,8 @@ pub(crate) fn parse_int( pub(crate) struct ParseInt; impl IntrinsicObject for ParseInt { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(parse_int) .name(Self::NAME) .length(2) @@ -299,8 +300,8 @@ pub(crate) fn parse_float( pub(crate) struct ParseFloat; impl IntrinsicObject for ParseFloat { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(parse_float) .name(Self::NAME) .length(1) diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index bb6c8c489c..799c23e376 100644 --- a/boa_engine/src/builtins/number/mod.rs +++ b/boa_engine/src/builtins/number/mod.rs @@ -19,6 +19,7 @@ use crate::{ error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, value::{AbstractRelation, IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, @@ -45,12 +46,12 @@ const BUF_SIZE: usize = 2200; pub(crate) struct Number; impl IntrinsicObject for Number { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_property(utf16!("EPSILON"), f64::EPSILON, attribute) .static_property( utf16!("MAX_SAFE_INTEGER"), @@ -69,12 +70,12 @@ impl IntrinsicObject for Number { .static_property(utf16!("NaN"), f64::NAN, attribute) .static_property( utf16!("parseInt"), - intrinsics.objects().parse_int(), + realm.intrinsics().objects().parse_int(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .static_property( utf16!("parseFloat"), - intrinsics.objects().parse_float(), + realm.intrinsics().objects().parse_float(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .static_method(Self::number_is_finite, "isFinite", 1) diff --git a/boa_engine/src/builtins/object/for_in_iterator.rs b/boa_engine/src/builtins/object/for_in_iterator.rs index 91ff390807..a5665d7e5f 100644 --- a/boa_engine/src/builtins/object/for_in_iterator.rs +++ b/boa_engine/src/builtins/object/for_in_iterator.rs @@ -14,6 +14,7 @@ use crate::{ error::JsNativeError, object::{JsObject, ObjectData}, property::PropertyKey, + realm::Realm, Context, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -37,11 +38,17 @@ pub struct ForInIterator { } impl IntrinsicObject for ForInIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("ForInIterator", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 0) .build(); } diff --git a/boa_engine/src/builtins/object/mod.rs b/boa_engine/src/builtins/object/mod.rs index 151803fc95..fc97a94260 100644 --- a/boa_engine/src/builtins/object/mod.rs +++ b/boa_engine/src/builtins/object/mod.rs @@ -27,6 +27,7 @@ use crate::{ JsObject, ObjectData, ObjectKind, }, property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, + realm::Realm, string::utf16, symbol::JsSymbol, value::JsValue, @@ -44,20 +45,20 @@ mod tests; pub struct Object; impl IntrinsicObject for Object { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let legacy_proto_getter = BuiltInBuilder::new(intrinsics) + let legacy_proto_getter = BuiltInBuilder::new(realm) .callable(Self::legacy_proto_getter) .name("get __proto__") .build(); - let legacy_setter_proto = BuiltInBuilder::new(intrinsics) + let legacy_setter_proto = BuiltInBuilder::new(realm) .callable(Self::legacy_proto_setter) .name("set __proto__") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .inherits(None) .accessor( utf16!("__proto__"), diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index 1785868458..b4b1ce2099 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -15,6 +15,7 @@ use crate::{ JsObject, ObjectData, CONSTRUCTOR, }, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, value::JsValue, @@ -307,15 +308,15 @@ impl PromiseCapability { } impl IntrinsicObject for Promise { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_method(Self::all, "all", 1) .static_method(Self::all_settled, "allSettled", 1) .static_method(Self::any, "any", 1) @@ -1856,7 +1857,8 @@ impl Promise { // a. Let value be promise.[[PromiseResult]]. PromiseState::Fulfilled(ref value) => { // b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, value). - let fulfill_job = new_promise_reaction_job(fulfill_reaction, value.clone()); + let fulfill_job = + new_promise_reaction_job(fulfill_reaction, value.clone(), context); // c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]). context @@ -1878,7 +1880,7 @@ impl Promise { } // d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason). - let reject_job = new_promise_reaction_job(reject_reaction, reason.clone()); + let reject_job = new_promise_reaction_job(reject_reaction, reason.clone(), context); // e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]). context.job_queue().enqueue_promise_job(reject_job, context); @@ -1955,7 +1957,7 @@ impl Promise { // 1. For each element reaction of reactions, do for reaction in reactions { // a. Let job be NewPromiseReactionJob(reaction, argument). - let job = new_promise_reaction_job(reaction, argument.clone()); + let job = new_promise_reaction_job(reaction, argument.clone(), context); // b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). context.job_queue().enqueue_promise_job(job, context); @@ -2163,6 +2165,7 @@ impl Promise { promise.clone(), resolution.clone(), then_job_callback, + context ); // 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). @@ -2235,7 +2238,26 @@ impl Promise { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-newpromisereactionjob -fn new_promise_reaction_job(mut reaction: ReactionRecord, argument: JsValue) -> NativeJob { +fn new_promise_reaction_job( + mut reaction: ReactionRecord, + argument: JsValue, + context: &mut Context<'_>, +) -> NativeJob { + // Inverting order since `job` captures `reaction` by value. + + // 2. Let handlerRealm be null. + // 3. If reaction.[[Handler]] is not empty, then + // a. Let getHandlerRealmResult be Completion(GetFunctionRealm(reaction.[[Handler]].[[Callback]])). + // b. If getHandlerRealmResult is a normal completion, set handlerRealm to getHandlerRealmResult.[[Value]]. + // c. Else, set handlerRealm to the current Realm Record. + // d. NOTE: handlerRealm is never null unless the handler is undefined. When the handler is a + // revoked Proxy and no ECMAScript code runs, handlerRealm is used to create error objects. + let realm = reaction + .handler + .as_ref() + .and_then(|handler| handler.callback().get_function_realm(context).ok()) + .unwrap_or_else(|| context.realm().clone()); + // 1. Let job be a new Job Abstract Closure with no parameters that captures reaction and argument and performs the following steps when called: let job = move |context: &mut Context<'_>| { // a. Let promiseCapability be reaction.[[Capability]]. @@ -2301,16 +2323,8 @@ fn new_promise_reaction_job(mut reaction: ReactionRecord, argument: JsValue) -> } }; - // TODO: handle realms - // 2. Let handlerRealm be null. - // 3. If reaction.[[Handler]] is not empty, then - // a. Let getHandlerRealmResult be Completion(GetFunctionRealm(reaction.[[Handler]].[[Callback]])). - // b. If getHandlerRealmResult is a normal completion, set handlerRealm to getHandlerRealmResult.[[Value]]. - // c. Else, set handlerRealm to the current Realm Record. - // d. NOTE: handlerRealm is never null unless the handler is undefined. When the handler is a revoked Proxy and no ECMAScript code runs, handlerRealm is used to create error objects. - // 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }. - NativeJob::new(job) + NativeJob::with_realm(job, realm) } /// More information: @@ -2321,7 +2335,19 @@ fn new_promise_resolve_thenable_job( promise_to_resolve: JsObject, thenable: JsValue, then: JobCallback, + context: &mut Context<'_>, ) -> NativeJob { + // Inverting order since `job` captures variables by value. + + // 2. Let getThenRealmResult be Completion(GetFunctionRealm(then.[[Callback]])). + // 3. If getThenRealmResult is a normal completion, let thenRealm be getThenRealmResult.[[Value]]. + // 4. Else, let thenRealm be the current Realm Record. + // 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked Proxy and no code runs, thenRealm is used to create error objects. + let realm = then + .callback() + .get_function_realm(context) + .unwrap_or_else(|_| context.realm().clone()); + // 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve, thenable, and then and performs the following steps when called: let job = move |context: &mut Context<'_>| { // a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve). @@ -2351,12 +2377,6 @@ fn new_promise_resolve_thenable_job( then_call_result }; - // TODO: handle realms - // 2. Let getThenRealmResult be Completion(GetFunctionRealm(then.[[Callback]])). - // 3. If getThenRealmResult is a normal completion, let thenRealm be getThenRealmResult.[[Value]]. - // 4. Else, let thenRealm be the current Realm Record. - // 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked Proxy and no code runs, thenRealm is used to create error objects. - // 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }. - NativeJob::new(job) + NativeJob::with_realm(job, realm) } diff --git a/boa_engine/src/builtins/proxy/mod.rs b/boa_engine/src/builtins/proxy/mod.rs index e34d3a4f2e..37e678fba9 100644 --- a/boa_engine/src/builtins/proxy/mod.rs +++ b/boa_engine/src/builtins/proxy/mod.rs @@ -16,6 +16,7 @@ use crate::{ error::JsNativeError, native_function::NativeFunction, object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData}, + realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, }; @@ -31,10 +32,10 @@ pub struct Proxy { } impl IntrinsicObject for Proxy { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .no_proto() .static_method(Self::revocable, "revocable", 2) .build(); diff --git a/boa_engine/src/builtins/reflect/mod.rs b/boa_engine/src/builtins/reflect/mod.rs index 092af9a44c..a0b2807fe0 100644 --- a/boa_engine/src/builtins/reflect/mod.rs +++ b/boa_engine/src/builtins/reflect/mod.rs @@ -17,6 +17,7 @@ use crate::{ error::JsNativeError, object::JsObject, property::Attribute, + realm::Realm, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, }; @@ -30,12 +31,12 @@ mod tests; pub(crate) struct Reflect; impl IntrinsicObject for Reflect { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let to_string_tag = JsSymbol::to_string_tag(); - BuiltInBuilder::with_intrinsic::(intrinsics) + BuiltInBuilder::with_intrinsic::(realm) .static_method(Self::apply, "apply", 3) .static_method(Self::construct, "construct", 2) .static_method(Self::define_property, "defineProperty", 3) diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 77bdbe8c95..5eaaed5870 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -16,6 +16,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, property::{Attribute, PropertyDescriptorBuilder}, + realm::Realm, string::{utf16, CodePoint}, symbol::JsSymbol, value::JsValue, @@ -44,53 +45,53 @@ pub struct RegExp { } impl IntrinsicObject for RegExp { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_has_indices = BuiltInBuilder::new(intrinsics) + let get_has_indices = BuiltInBuilder::new(realm) .callable(Self::get_has_indices) .name("get hasIndices") .build(); - let get_global = BuiltInBuilder::new(intrinsics) + let get_global = BuiltInBuilder::new(realm) .callable(Self::get_global) .name("get global") .build(); - let get_ignore_case = BuiltInBuilder::new(intrinsics) + let get_ignore_case = BuiltInBuilder::new(realm) .callable(Self::get_ignore_case) .name("get ignoreCase") .build(); - let get_multiline = BuiltInBuilder::new(intrinsics) + let get_multiline = BuiltInBuilder::new(realm) .callable(Self::get_multiline) .name("get multiline") .build(); - let get_dot_all = BuiltInBuilder::new(intrinsics) + let get_dot_all = BuiltInBuilder::new(realm) .callable(Self::get_dot_all) .name("get dotAll") .build(); - let get_unicode = BuiltInBuilder::new(intrinsics) + let get_unicode = BuiltInBuilder::new(realm) .callable(Self::get_unicode) .name("get unicode") .build(); - let get_sticky = BuiltInBuilder::new(intrinsics) + let get_sticky = BuiltInBuilder::new(realm) .callable(Self::get_sticky) .name("get sticky") .build(); - let get_flags = BuiltInBuilder::new(intrinsics) + let get_flags = BuiltInBuilder::new(realm) .callable(Self::get_flags) .name("get flags") .build(); - let get_source = BuiltInBuilder::new(intrinsics) + let get_source = BuiltInBuilder::new(realm) .callable(Self::get_source) .name("get source") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_accessor( JsSymbol::species(), Some(get_species), diff --git a/boa_engine/src/builtins/regexp/regexp_string_iterator.rs b/boa_engine/src/builtins/regexp/regexp_string_iterator.rs index 6f07c1b6bf..f17bac9ee0 100644 --- a/boa_engine/src/builtins/regexp/regexp_string_iterator.rs +++ b/boa_engine/src/builtins/regexp/regexp_string_iterator.rs @@ -16,6 +16,7 @@ use crate::{ error::JsNativeError, object::{JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsResult, JsString, JsValue, @@ -40,11 +41,17 @@ pub struct RegExpStringIterator { } impl IntrinsicObject for RegExpStringIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("RegExpStringIterator", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 0) .static_property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/set/mod.rs b/boa_engine/src/builtins/set/mod.rs index 19418ebe73..a5e09a96bb 100644 --- a/boa_engine/src/builtins/set/mod.rs +++ b/boa_engine/src/builtins/set/mod.rs @@ -24,6 +24,7 @@ use crate::{ error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::{Attribute, PropertyNameKind}, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, @@ -40,25 +41,25 @@ impl IntrinsicObject for Set { fn get(intrinsics: &Intrinsics) -> JsObject { Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() } - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); - let size_getter = BuiltInBuilder::new(intrinsics) + let size_getter = BuiltInBuilder::new(realm) .callable(Self::size_getter) .name("get size") .build(); - let values_function = BuiltInBuilder::new(intrinsics) + let values_function = BuiltInBuilder::new(realm) .callable(Self::values) .name("values") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_accessor( JsSymbol::species(), Some(get_species), diff --git a/boa_engine/src/builtins/set/set_iterator.rs b/boa_engine/src/builtins/set/set_iterator.rs index 4fcf1812fa..56c60173a1 100644 --- a/boa_engine/src/builtins/set/set_iterator.rs +++ b/boa_engine/src/builtins/set/set_iterator.rs @@ -14,6 +14,7 @@ use crate::{ error::JsNativeError, object::{JsObject, ObjectData}, property::{Attribute, PropertyNameKind}, + realm::Realm, symbol::JsSymbol, Context, JsResult, }; @@ -36,11 +37,17 @@ pub struct SetIterator { } impl IntrinsicObject for SetIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("SetIterator", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 0) .static_property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index f9086266f9..36cf7226d9 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -16,6 +16,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor}, + realm::Realm, string::utf16, string::{CodePoint, Utf16Trim}, symbol::JsSymbol, @@ -64,13 +65,13 @@ pub(crate) const fn is_trimmable_whitespace(c: char) -> bool { pub(crate) struct String; impl IntrinsicObject for String { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let symbol_iterator = JsSymbol::iterator(); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .property(utf16!("length"), 0, attribute) .static_method(Self::raw, "raw", 1) .static_method(Self::from_char_code, "fromCharCode", 1) diff --git a/boa_engine/src/builtins/string/string_iterator.rs b/boa_engine/src/builtins/string/string_iterator.rs index a266fb8f56..f39aabd7df 100644 --- a/boa_engine/src/builtins/string/string_iterator.rs +++ b/boa_engine/src/builtins/string/string_iterator.rs @@ -12,6 +12,7 @@ use crate::{ js_string, object::{JsObject, ObjectData}, property::Attribute, + realm::Realm, symbol::JsSymbol, Context, JsResult, JsString, JsValue, }; @@ -31,11 +32,17 @@ pub struct StringIterator { } impl IntrinsicObject for StringIterator { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event("StringIterator", "init"); - BuiltInBuilder::with_intrinsic::(intrinsics) - .prototype(intrinsics.objects().iterator_prototypes().iterator()) + BuiltInBuilder::with_intrinsic::(realm) + .prototype( + realm + .intrinsics() + .objects() + .iterator_prototypes() + .iterator(), + ) .static_method(Self::next, "next", 0) .static_property( JsSymbol::to_string_tag(), diff --git a/boa_engine/src/builtins/symbol/mod.rs b/boa_engine/src/builtins/symbol/mod.rs index b1a0bc9b86..4747373e92 100644 --- a/boa_engine/src/builtins/symbol/mod.rs +++ b/boa_engine/src/builtins/symbol/mod.rs @@ -27,6 +27,7 @@ use crate::{ js_string, object::JsObject, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, value::JsValue, @@ -91,7 +92,7 @@ impl GlobalSymbolRegistry { pub struct Symbol; impl IntrinsicObject for Symbol { - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); let symbol_async_iterator = JsSymbol::async_iterator(); @@ -110,18 +111,18 @@ impl IntrinsicObject for Symbol { let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - let to_primitive = BuiltInBuilder::new(intrinsics) + let to_primitive = BuiltInBuilder::new(realm) .callable(Self::to_primitive) .name("[Symbol.toPrimitive]") .length(1) .build(); - let get_description = BuiltInBuilder::new(intrinsics) + let get_description = BuiltInBuilder::new(realm) .callable(Self::get_description) .name("get description") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_method(Self::for_, "for", 1) .static_method(Self::key_for, "keyFor", 1) .static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute) diff --git a/boa_engine/src/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index 32f4ba5e27..db3b97a743 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -25,6 +25,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::{Attribute, PropertyNameKind}, + realm::Realm, string::utf16, symbol::JsSymbol, value::{IntegerOrInfinity, JsValue}, @@ -47,17 +48,25 @@ macro_rules! typed_array { Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() } - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let get_species = BuiltInBuilder::new(intrinsics) + let get_species = BuiltInBuilder::new(realm) .callable(TypedArray::get_species) .name("get [Symbol.species]") .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) - .prototype(intrinsics.constructors().typed_array().constructor()) - .inherits(Some(intrinsics.constructors().typed_array().prototype())) + BuiltInBuilder::from_standard_constructor::(realm) + .prototype( + realm + .intrinsics() + .constructors() + .typed_array() + .constructor(), + ) + .inherits(Some( + realm.intrinsics().constructors().typed_array().prototype(), + )) .static_accessor( JsSymbol::species(), Some(get_species), @@ -231,44 +240,44 @@ macro_rules! typed_array { pub(crate) struct TypedArray; impl IntrinsicObject for TypedArray { - fn init(intrinsics: &Intrinsics) { - let get_species = BuiltInBuilder::new(intrinsics) + fn init(realm: &Realm) { + let get_species = BuiltInBuilder::new(realm) .callable(Self::get_species) .name("get [Symbol.species]") .build(); - let get_buffer = BuiltInBuilder::new(intrinsics) + let get_buffer = BuiltInBuilder::new(realm) .callable(Self::buffer) .name("get buffer") .build(); - let get_byte_length = BuiltInBuilder::new(intrinsics) + let get_byte_length = BuiltInBuilder::new(realm) .callable(Self::byte_length) .name("get byteLength") .build(); - let get_byte_offset = BuiltInBuilder::new(intrinsics) + let get_byte_offset = BuiltInBuilder::new(realm) .callable(Self::byte_offset) .name("get byteOffset") .build(); - let get_length = BuiltInBuilder::new(intrinsics) + let get_length = BuiltInBuilder::new(realm) .callable(Self::length) .name("get length") .build(); - let get_to_string_tag = BuiltInBuilder::new(intrinsics) + let get_to_string_tag = BuiltInBuilder::new(realm) .callable(Self::to_string_tag) .name("get [Symbol.toStringTag]") .build(); - let values_function = BuiltInBuilder::new(intrinsics) + let values_function = BuiltInBuilder::new(realm) .callable(Self::values) .name("values") .length(0) .build(); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .static_accessor( JsSymbol::species(), Some(get_species), diff --git a/boa_engine/src/builtins/uri/mod.rs b/boa_engine/src/builtins/uri/mod.rs index 1144140d6e..e1acdab88a 100644 --- a/boa_engine/src/builtins/uri/mod.rs +++ b/boa_engine/src/builtins/uri/mod.rs @@ -24,6 +24,7 @@ use crate::{ context::intrinsics::Intrinsics, js_string, object::{JsFunction, JsObject}, + realm::Realm, string::CodePoint, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; @@ -80,8 +81,8 @@ impl UriFunctions { pub(crate) struct DecodeUri; impl IntrinsicObject for DecodeUri { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(decode_uri) .name(Self::NAME) .length(1) @@ -99,8 +100,8 @@ impl BuiltInObject for DecodeUri { pub(crate) struct DecodeUriComponent; impl IntrinsicObject for DecodeUriComponent { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(decode_uri_component) .name(Self::NAME) .length(1) @@ -122,8 +123,8 @@ impl BuiltInObject for DecodeUriComponent { pub(crate) struct EncodeUri; impl IntrinsicObject for EncodeUri { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(encode_uri) .name(Self::NAME) .length(1) @@ -140,8 +141,8 @@ impl BuiltInObject for EncodeUri { pub(crate) struct EncodeUriComponent; impl IntrinsicObject for EncodeUriComponent { - fn init(intrinsics: &Intrinsics) { - BuiltInBuilder::with_intrinsic::(intrinsics) + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::(realm) .callable(encode_uri_component) .name(Self::NAME) .length(1) diff --git a/boa_engine/src/builtins/weak/weak_ref.rs b/boa_engine/src/builtins/weak/weak_ref.rs index ee4399c250..56f9e4cc5d 100644 --- a/boa_engine/src/builtins/weak/weak_ref.rs +++ b/boa_engine/src/builtins/weak/weak_ref.rs @@ -6,6 +6,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, }; @@ -28,9 +29,9 @@ impl IntrinsicObject for WeakRef { Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() } - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .property( JsSymbol::to_string_tag(), "WeakRef", diff --git a/boa_engine/src/builtins/weak_map/mod.rs b/boa_engine/src/builtins/weak_map/mod.rs index acb4a4ea6b..07155e4481 100644 --- a/boa_engine/src/builtins/weak_map/mod.rs +++ b/boa_engine/src/builtins/weak_map/mod.rs @@ -15,6 +15,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, @@ -30,9 +31,9 @@ impl IntrinsicObject for WeakMap { Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() } - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .property( JsSymbol::to_string_tag(), Self::NAME, diff --git a/boa_engine/src/builtins/weak_set/mod.rs b/boa_engine/src/builtins/weak_set/mod.rs index 1c5d087313..2294ddd5ec 100644 --- a/boa_engine/src/builtins/weak_set/mod.rs +++ b/boa_engine/src/builtins/weak_set/mod.rs @@ -12,6 +12,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, + realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, @@ -27,9 +28,9 @@ impl IntrinsicObject for WeakSet { Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() } - fn init(intrinsics: &Intrinsics) { + fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(intrinsics) + BuiltInBuilder::from_standard_constructor::(realm) .property( JsSymbol::to_string_tag(), Self::NAME, diff --git a/boa_engine/src/bytecompiler/class.rs b/boa_engine/src/bytecompiler/class.rs index 5ced760b7f..e571b1e3b1 100644 --- a/boa_engine/src/bytecompiler/class.rs +++ b/boa_engine/src/bytecompiler/class.rs @@ -19,29 +19,30 @@ 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 mut compiler = ByteCompiler::new(class_name, true, self.json_parse, self.context); + let mut compiler = ByteCompiler::new( + class_name, + true, + self.json_parse, + self.current_environment.clone(), + self.context, + ); if let Some(class_name) = class.name() { if class.has_binding_identifier() { compiler.has_binding_identifier = true; - compiler.context.push_compile_time_environment(false); - compiler.context.create_immutable_binding(class_name, true); + compiler.push_compile_environment(false); + compiler.create_immutable_binding(class_name, true); } } - compiler.context.push_compile_time_environment(true); + compiler.push_compile_environment(true); if let Some(expr) = class.constructor() { compiler.length = expr.parameters().length(); compiler.params = expr.parameters().clone(); - compiler - .context - .create_mutable_binding(Sym::ARGUMENTS.into(), false, false); - compiler.arguments_binding = Some( - compiler - .context - .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), - ); + compiler.create_mutable_binding(Sym::ARGUMENTS.into(), false, false); + compiler.arguments_binding = + Some(compiler.initialize_mutable_binding(Sym::ARGUMENTS.into(), false)); for parameter in expr.parameters().as_ref() { if parameter.is_rest_param() { compiler.emit_opcode(Opcode::RestParameterInit); @@ -49,9 +50,7 @@ impl ByteCompiler<'_, '_> { match parameter.variable().binding() { Binding::Identifier(ident) => { - compiler - .context - .create_mutable_binding(*ident, false, false); + compiler.create_mutable_binding(*ident, false, false); if let Some(init) = parameter.variable().init() { let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); @@ -62,7 +61,7 @@ impl ByteCompiler<'_, '_> { } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - compiler.context.create_mutable_binding(ident, false, false); + compiler.create_mutable_binding(ident, false, false); } compiler.compile_declaration_pattern(pattern, BindingOpcode::InitArg); } @@ -72,8 +71,8 @@ impl ByteCompiler<'_, '_> { compiler.emit_opcode(Opcode::RestParameterPop); } let env_label = if expr.parameters().has_expressions() { - compiler.num_bindings = compiler.context.get_binding_number(); - compiler.context.push_compile_time_environment(true); + compiler.num_bindings = compiler.current_environment.borrow().num_bindings(); + compiler.push_compile_environment(true); compiler.function_environment_push_location = compiler.next_opcode_location(); Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) } else { @@ -81,36 +80,28 @@ impl ByteCompiler<'_, '_> { }; compiler.create_script_decls(expr.body(), false); compiler.compile_statement_list(expr.body(), false, false); + + let env_info = compiler.pop_compile_environment(); + if let Some(env_label) = env_label { - let (num_bindings, compile_environment) = - compiler.context.pop_compile_time_environment(); - let index_compile_environment = - compiler.push_compile_environment(compile_environment); - compiler.patch_jump_with_target(env_label.0, num_bindings as u32); - compiler.patch_jump_with_target(env_label.1, index_compile_environment as u32); - let (_, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); + compiler.patch_jump_with_target(env_label.0, env_info.num_bindings as u32); + compiler.patch_jump_with_target(env_label.1, env_info.index as u32); + compiler.pop_compile_environment(); } else { - let (num_bindings, compile_environment) = - compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); - compiler.num_bindings = num_bindings; + compiler.num_bindings = env_info.num_bindings; compiler.is_class_constructor = true; } } else { if class.super_ref().is_some() { compiler.emit_opcode(Opcode::SuperCallDerived); } - let (num_bindings, compile_environment) = - compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); - compiler.num_bindings = num_bindings; + let env_info = compiler.pop_compile_environment(); + compiler.num_bindings = env_info.num_bindings; compiler.is_class_constructor = true; } if class.name().is_some() && class.has_binding_identifier() { - let (_, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); + compiler.pop_compile_environment(); } compiler.emit_opcode(Opcode::PushUndefined); @@ -266,25 +257,24 @@ impl ByteCompiler<'_, '_> { self.compile_expr(name, true); } } - let mut field_compiler = - ByteCompiler::new(Sym::EMPTY_STRING, true, self.json_parse, self.context); - field_compiler.context.push_compile_time_environment(false); - field_compiler - .context - .create_immutable_binding(class_name.into(), true); - field_compiler.context.push_compile_time_environment(true); + let mut field_compiler = ByteCompiler::new( + Sym::EMPTY_STRING, + true, + self.json_parse, + self.current_environment.clone(), + self.context, + ); + field_compiler.push_compile_environment(false); + field_compiler.create_immutable_binding(class_name.into(), true); + field_compiler.push_compile_environment(true); if let Some(node) = field { field_compiler.compile_expr(node, true); } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - let (num_bindings, compile_environment) = - field_compiler.context.pop_compile_time_environment(); - field_compiler.push_compile_environment(compile_environment); - let (_, compile_environment) = - field_compiler.context.pop_compile_time_environment(); - field_compiler.push_compile_environment(compile_environment); - field_compiler.num_bindings = num_bindings; + let env_info = field_compiler.pop_compile_environment(); + field_compiler.pop_compile_environment(); + field_compiler.num_bindings = env_info.num_bindings; field_compiler.emit_opcode(Opcode::Return); let mut code = field_compiler.finish(); @@ -299,25 +289,24 @@ impl ByteCompiler<'_, '_> { ClassElement::PrivateFieldDefinition(name, field) => { self.emit_opcode(Opcode::Dup); let name_index = self.get_or_insert_private_name(*name); - let mut field_compiler = - ByteCompiler::new(class_name, true, self.json_parse, self.context); - field_compiler.context.push_compile_time_environment(false); - field_compiler - .context - .create_immutable_binding(class_name.into(), true); - field_compiler.context.push_compile_time_environment(true); + let mut field_compiler = ByteCompiler::new( + class_name, + true, + self.json_parse, + self.current_environment.clone(), + self.context, + ); + field_compiler.push_compile_environment(false); + field_compiler.create_immutable_binding(class_name.into(), true); + field_compiler.push_compile_environment(true); if let Some(node) = field { field_compiler.compile_expr(node, true); } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - let (num_bindings, compile_environment) = - field_compiler.context.pop_compile_time_environment(); - field_compiler.push_compile_environment(compile_environment); - let (_, compile_environment) = - field_compiler.context.pop_compile_time_environment(); - field_compiler.push_compile_environment(compile_environment); - field_compiler.num_bindings = num_bindings; + let env_info = field_compiler.pop_compile_environment(); + field_compiler.pop_compile_environment(); + field_compiler.num_bindings = env_info.num_bindings; field_compiler.emit_opcode(Opcode::Return); let mut code = field_compiler.finish(); @@ -342,25 +331,24 @@ impl ByteCompiler<'_, '_> { None } }; - let mut field_compiler = - ByteCompiler::new(class_name, true, self.json_parse, self.context); - field_compiler.context.push_compile_time_environment(false); - field_compiler - .context - .create_immutable_binding(class_name.into(), true); - field_compiler.context.push_compile_time_environment(true); + let mut field_compiler = ByteCompiler::new( + class_name, + true, + self.json_parse, + self.current_environment.clone(), + self.context, + ); + field_compiler.push_compile_environment(false); + field_compiler.create_immutable_binding(class_name.into(), true); + field_compiler.push_compile_environment(true); if let Some(node) = field { field_compiler.compile_expr(node, true); } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - let (num_bindings, compile_environment) = - field_compiler.context.pop_compile_time_environment(); - field_compiler.push_compile_environment(compile_environment); - let (_, compile_environment) = - field_compiler.context.pop_compile_time_environment(); - field_compiler.push_compile_environment(compile_environment); - field_compiler.num_bindings = num_bindings; + let env_info = field_compiler.pop_compile_environment(); + field_compiler.pop_compile_environment(); + field_compiler.num_bindings = env_info.num_bindings; field_compiler.emit_opcode(Opcode::Return); let mut code = field_compiler.finish(); @@ -390,21 +378,21 @@ impl ByteCompiler<'_, '_> { } ClassElement::StaticBlock(statement_list) => { self.emit_opcode(Opcode::Dup); - let mut compiler = - ByteCompiler::new(Sym::EMPTY_STRING, true, false, self.context); - compiler.context.push_compile_time_environment(false); - compiler - .context - .create_immutable_binding(class_name.into(), true); - compiler.context.push_compile_time_environment(true); + let mut compiler = ByteCompiler::new( + Sym::EMPTY_STRING, + true, + false, + self.current_environment.clone(), + self.context, + ); + compiler.push_compile_environment(false); + compiler.create_immutable_binding(class_name.into(), true); + compiler.push_compile_environment(true); compiler.create_script_decls(statement_list, false); compiler.compile_statement_list(statement_list, false, false); - let (num_bindings, compile_environment) = - compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); - let (_, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); - compiler.num_bindings = num_bindings; + let env_info = compiler.pop_compile_environment(); + compiler.pop_compile_environment(); + compiler.num_bindings = env_info.num_bindings; let code = Gc::new(compiler.finish()); let index = self.functions.len() as u32; diff --git a/boa_engine/src/bytecompiler/env.rs b/boa_engine/src/bytecompiler/env.rs new file mode 100644 index 0000000000..fd169c28e8 --- /dev/null +++ b/boa_engine/src/bytecompiler/env.rs @@ -0,0 +1,150 @@ +use boa_ast::expression::Identifier; +use boa_gc::{Gc, GcRefCell}; + +use crate::{ + environments::{BindingLocator, CompileTimeEnvironment}, + property::PropertyDescriptor, + JsString, JsValue, +}; + +use super::ByteCompiler; + +/// Info returned by the [`ByteCompiler::pop_compile_environment`] method. +#[derive(Debug, Clone, Copy)] +pub(crate) struct PopEnvironmentInfo { + /// Number of bindings declared. + pub(crate) num_bindings: usize, + /// Index in the compile time envs array. + pub(crate) index: usize, +} + +impl ByteCompiler<'_, '_> { + /// Push either a new declarative or function environment on the compile time environment stack. + pub(crate) fn push_compile_environment(&mut self, function_scope: bool) { + self.current_environment = Gc::new(GcRefCell::new(CompileTimeEnvironment::new( + self.current_environment.clone(), + function_scope, + ))); + } + + /// Pops the top compile time environment and returns its index and number of bindings. + #[track_caller] + pub(crate) fn pop_compile_environment(&mut self) -> PopEnvironmentInfo { + let index = self.compile_environments.len(); + self.compile_environments + .push(self.current_environment.clone()); + + let (num_bindings, outer) = { + let env = self.current_environment.borrow(); + ( + env.num_bindings(), + env.outer().expect("cannot pop the global environment"), + ) + }; + self.current_environment = outer; + + PopEnvironmentInfo { + num_bindings, + index, + } + } + + /// Get the binding locator of the binding at bytecode compile time. + pub(crate) fn get_binding_value(&self, name: Identifier) -> BindingLocator { + self.current_environment + .borrow() + .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 + .borrow() + .has_binding_recursive(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, + configurable: bool, + ) { + if !self + .current_environment + .borrow_mut() + .create_mutable_binding(name, function_scope) + { + let name_str = self + .context + .interner() + .resolve_expect(name.sym()) + .into_common::(false); + + let global_obj = self.context.global_object(); + + // TODO: defer global initialization to execution time. + if !global_obj + .has_own_property(name_str.clone(), self.context) + .unwrap_or_default() + { + global_obj.borrow_mut().insert( + name_str, + PropertyDescriptor::builder() + .value(JsValue::Undefined) + .writable(true) + .enumerable(true) + .configurable(configurable) + .build(), + ); + } + } + } + + /// Initialize a mutable binding at bytecode compile time and return it's binding locator. + pub(crate) fn initialize_mutable_binding( + &self, + name: Identifier, + function_scope: bool, + ) -> BindingLocator { + self.current_environment + .borrow() + .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 + .borrow_mut() + .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 + .borrow() + .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) -> BindingLocator { + self.current_environment + .borrow() + .set_mutable_binding_recursive(name) + } +} diff --git a/boa_engine/src/bytecompiler/expression/assign.rs b/boa_engine/src/bytecompiler/expression/assign.rs index c046d67081..ac9a5fbaec 100644 --- a/boa_engine/src/bytecompiler/expression/assign.rs +++ b/boa_engine/src/bytecompiler/expression/assign.rs @@ -54,7 +54,7 @@ impl ByteCompiler<'_, '_> { match access { Access::Variable { name } => { - let binding = self.context.get_binding_value(name); + let binding = self.get_binding_value(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetName, &[index]); @@ -69,7 +69,7 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::Dup); } - let binding = self.context.set_mutable_binding(name); + let binding = self.set_mutable_binding(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::SetName, &[index]); } diff --git a/boa_engine/src/bytecompiler/expression/unary.rs b/boa_engine/src/bytecompiler/expression/unary.rs index 00cb395190..ff5d6708f8 100644 --- a/boa_engine/src/bytecompiler/expression/unary.rs +++ b/boa_engine/src/bytecompiler/expression/unary.rs @@ -27,7 +27,7 @@ impl ByteCompiler<'_, '_> { UnaryOp::TypeOf => { match unary.target().flatten() { Expression::Identifier(identifier) => { - let binding = self.context.get_binding_value(*identifier); + let binding = self.get_binding_value(*identifier); let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetNameOrUndefined, &[index]); } diff --git a/boa_engine/src/bytecompiler/expression/update.rs b/boa_engine/src/bytecompiler/expression/update.rs index 1249c6a372..b7becf9a11 100644 --- a/boa_engine/src/bytecompiler/expression/update.rs +++ b/boa_engine/src/bytecompiler/expression/update.rs @@ -22,7 +22,7 @@ impl ByteCompiler<'_, '_> { match Access::from_update_target(update.target()) { Access::Variable { name } => { - let binding = self.context.get_binding_value(name); + let binding = self.get_binding_value(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetName, &[index]); @@ -33,7 +33,7 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::Dup); } - let binding = self.context.set_mutable_binding(name); + let binding = self.set_mutable_binding(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::SetName, &[index]); } diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index c19226d5c0..85ec537ead 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -1,13 +1,14 @@ use crate::{ builtins::function::ThisMode, bytecompiler::ByteCompiler, + environments::CompileTimeEnvironment, vm::{BindingOpcode, CodeBlock, Opcode}, Context, }; use boa_ast::{ declaration::Binding, function::FormalParameterList, operations::bound_names, StatementList, }; -use boa_gc::Gc; +use boa_gc::{Gc, GcRefCell}; use boa_interner::Sym; /// `FunctionCompiler` is used to compile AST functions to bytecode. @@ -89,13 +90,14 @@ impl FunctionCompiler { mut self, parameters: &FormalParameterList, body: &StatementList, + outer_env: Gc>, context: &mut Context<'_>, ) -> Gc { self.strict = self.strict || body.strict(); let length = parameters.length(); - let mut compiler = ByteCompiler::new(self.name, self.strict, false, context); + let mut compiler = ByteCompiler::new(self.name, self.strict, false, outer_env, context); compiler.length = length; compiler.in_async_generator = self.generator && self.r#async; @@ -104,21 +106,23 @@ impl FunctionCompiler { } if let Some(class_name) = self.class_name { - compiler.context.push_compile_time_environment(false); - compiler - .context - .create_immutable_binding(class_name.into(), true); + compiler.push_compile_environment(false); + compiler.create_immutable_binding(class_name.into(), true); } if let Some(binding_identifier) = self.binding_identifier { compiler.has_binding_identifier = true; - compiler.context.push_compile_time_environment(false); - compiler - .context - .create_immutable_binding(binding_identifier.into(), self.strict); + compiler.push_compile_environment(false); + compiler.create_immutable_binding(binding_identifier.into(), self.strict); } - compiler.context.push_compile_time_environment(true); + // Function environment + compiler.push_compile_environment(true); + + // Only used to initialize bindings + if !self.strict && parameters.has_expressions() { + compiler.push_compile_environment(false); + }; // An arguments object is added when all of the following conditions are met // - If not in an arrow function (10.2.11.16) @@ -126,14 +130,14 @@ impl FunctionCompiler { // Note: This following just means, that we add an extra environment for the arguments. // - If there are default parameters or if lexical names and function names do not contain `arguments` (10.2.11.18) if !(self.arrow) && !parameters.has_arguments() { - compiler - .context - .create_mutable_binding(Sym::ARGUMENTS.into(), false, false); - compiler.arguments_binding = Some( - compiler - .context - .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), - ); + let arguments = Sym::ARGUMENTS.into(); + compiler.arguments_binding = Some(if self.strict { + compiler.create_immutable_binding(arguments, true); + compiler.initialize_immutable_binding(arguments) + } else { + compiler.create_mutable_binding(arguments, false, false); + compiler.initialize_mutable_binding(arguments, false) + }); } for parameter in parameters.as_ref() { @@ -143,20 +147,18 @@ impl FunctionCompiler { match parameter.variable().binding() { Binding::Identifier(ident) => { - compiler - .context - .create_mutable_binding(*ident, false, false); + compiler.create_mutable_binding(*ident, false, false); // TODO: throw custom error if ident is in init if let Some(init) = parameter.variable().init() { let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); compiler.compile_expr(init, true); compiler.patch_jump(skip); } - compiler.emit_binding(BindingOpcode::InitArg, *ident); + compiler.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - compiler.context.create_mutable_binding(ident, false, false); + compiler.create_mutable_binding(ident, false, false); } // TODO: throw custom error if ident is in init if let Some(init) = parameter.variable().init() { @@ -164,7 +166,7 @@ impl FunctionCompiler { compiler.compile_expr(init, true); compiler.patch_jump(skip); } - compiler.compile_declaration_pattern(pattern, BindingOpcode::InitArg); + compiler.compile_declaration_pattern(pattern, BindingOpcode::InitLet); } } } @@ -174,8 +176,7 @@ impl FunctionCompiler { } let env_label = if parameters.has_expressions() { - compiler.num_bindings = compiler.context.get_binding_number(); - compiler.context.push_compile_time_environment(true); + compiler.push_compile_environment(true); compiler.function_environment_push_location = compiler.next_opcode_location(); Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) } else { @@ -192,29 +193,24 @@ impl FunctionCompiler { compiler.compile_statement_list(body, false, false); if let Some(env_label) = env_label { - let (num_bindings, compile_environment) = - compiler.context.pop_compile_time_environment(); - let index_compile_environment = compiler.push_compile_environment(compile_environment); - compiler.patch_jump_with_target(env_label.0, num_bindings as u32); - compiler.patch_jump_with_target(env_label.1, index_compile_environment as u32); - - let (_, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); - } else { - let (num_bindings, compile_environment) = - compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); - compiler.num_bindings = num_bindings; + let env_info = compiler.pop_compile_environment(); + compiler.patch_jump_with_target(env_label.0, env_info.num_bindings as u32); + compiler.patch_jump_with_target(env_label.1, env_info.index as u32); } + if !self.strict && parameters.has_expressions() { + compiler.parameters_env_bindings = + Some(compiler.pop_compile_environment().num_bindings); + } + + compiler.num_bindings = compiler.pop_compile_environment().num_bindings; + if self.binding_identifier.is_some() { - let (_, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); + compiler.pop_compile_environment(); } if self.class_name.is_some() { - let (_, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler.push_compile_environment(compile_environment); + compiler.pop_compile_environment(); } compiler.params = parameters.clone(); diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 84868ca95f..8aa91c205e 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -2,6 +2,7 @@ mod class; mod declaration; +mod env; mod expression; mod function; mod jump_control; @@ -214,7 +215,7 @@ impl Access<'_> { /// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode. #[derive(Debug)] #[allow(clippy::struct_excessive_bools)] -pub struct ByteCompiler<'b, 'host> { +pub struct ByteCompiler<'ctx, 'host> { /// Name of this function. pub(crate) function_name: Sym, @@ -272,6 +273,12 @@ pub struct ByteCompiler<'b, 'host> { /// When the execution of the parameter expressions throws an error, we do not need to pop the function environment. pub(crate) function_environment_push_location: u32, + /// The environment that is currently active. + pub(crate) current_environment: Gc>, + + /// The number of bindings in the parameters environment. + pub(crate) parameters_env_bindings: Option, + literals_map: FxHashMap, names_map: FxHashMap, private_names_map: FxHashMap, @@ -279,21 +286,24 @@ pub struct ByteCompiler<'b, 'host> { jump_info: Vec, in_async_generator: bool, json_parse: bool, - context: &'b mut Context<'host>, + // TODO: remove when we separate scripts from the context + context: &'ctx mut Context<'host>, } -impl<'b, 'host> ByteCompiler<'b, 'host> { +impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { /// Represents a placeholder address that will be patched later. const DUMMY_ADDRESS: u32 = u32::MAX; /// Creates a new [`ByteCompiler`]. #[inline] - pub fn new( + pub(crate) fn new( name: Sym, strict: bool, json_parse: bool, - context: &'b mut Context<'host>, - ) -> ByteCompiler<'b, 'host> { + current_environment: Gc>, + // TODO: remove when we separate scripts from the context + context: &'ctx mut Context<'host>, + ) -> ByteCompiler<'ctx, 'host> { Self { function_name: name, strict, @@ -313,6 +323,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { is_class_constructor: false, class_field_initializer_name: None, function_environment_push_location: 0, + parameters_env_bindings: None, literals_map: FxHashMap::default(), names_map: FxHashMap::default(), @@ -321,6 +332,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { jump_info: Vec::new(), in_async_generator: false, json_parse, + current_environment, context, } } @@ -329,16 +341,6 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { self.context.interner() } - /// Push a compile time environment to the current `CodeBlock` and return it's index. - fn push_compile_environment( - &mut self, - environment: Gc>, - ) -> usize { - let index = self.compile_environments.len(); - self.compile_environments.push(environment); - index - } - fn get_or_insert_literal(&mut self, literal: Literal) -> u32 { if let Some(index) = self.literals_map.get(&literal) { return *index; @@ -393,41 +395,41 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) { match opcode { BindingOpcode::Var => { - let binding = self.context.initialize_mutable_binding(name, true); + let binding = self.initialize_mutable_binding(name, true); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefVar, &[index]); } BindingOpcode::Let => { - let binding = self.context.initialize_mutable_binding(name, false); + let binding = self.initialize_mutable_binding(name, false); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefLet, &[index]); } BindingOpcode::InitVar => { - let binding = if self.context.has_binding(name) { - self.context.set_mutable_binding(name) + let binding = if self.has_binding(name) { + self.set_mutable_binding(name) } else { - self.context.initialize_mutable_binding(name, true) + self.initialize_mutable_binding(name, true) }; let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } BindingOpcode::InitLet => { - let binding = self.context.initialize_mutable_binding(name, false); + let binding = self.initialize_mutable_binding(name, false); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitLet, &[index]); } BindingOpcode::InitArg => { - let binding = self.context.initialize_mutable_binding(name, false); + let binding = self.initialize_mutable_binding(name, true); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitArg, &[index]); } BindingOpcode::InitConst => { - let binding = self.context.initialize_immutable_binding(name); + let binding = self.initialize_immutable_binding(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitConst, &[index]); } BindingOpcode::SetName => { - let binding = self.context.set_mutable_binding(name); + let binding = self.set_mutable_binding(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::SetName, &[index]); } @@ -565,7 +567,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { fn access_get(&mut self, access: Access<'_>, use_expr: bool) { match access { Access::Variable { name } => { - let binding = self.context.get_binding_value(name); + let binding = self.get_binding_value(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetName, &[index]); } @@ -633,7 +635,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { if use_expr { self.emit(Opcode::Dup, &[]); } - let binding = self.context.set_mutable_binding(name); + let binding = self.set_mutable_binding(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::SetName, &[index]); } @@ -717,7 +719,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { } }, Access::Variable { name } => { - let binding = self.context.get_binding_value(name); + let binding = self.get_binding_value(name); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DeleteName, &[index]); } @@ -765,7 +767,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { use_expr: bool, strict: bool, ) { - self.context.push_compile_time_environment(strict); + self.push_compile_environment(strict); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.create_script_decls(list, true); @@ -793,10 +795,9 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { } } - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); } @@ -1122,7 +1123,12 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { .strict(self.strict) .arrow(arrow) .binding_identifier(binding_identifier) - .compile(parameters, body, self.context); + .compile( + parameters, + body, + self.current_environment.clone(), + self.context, + ); let index = self.functions.len() as u32; self.functions.push(code); @@ -1196,7 +1202,12 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { .arrow(arrow) .binding_identifier(binding_identifier) .class_name(class_name) - .compile(parameters, body, self.context); + .compile( + parameters, + body, + self.current_environment.clone(), + self.context, + ); let index = self.functions.len() as u32; self.functions.push(code); @@ -1327,6 +1338,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { is_class_constructor: self.is_class_constructor, class_field_initializer_name: self.class_field_initializer_name, function_environment_push_location: self.function_environment_push_location, + parameters_env_bindings: self.parameters_env_bindings, #[cfg(feature = "trace")] trace: std::cell::Cell::new(false), } @@ -1336,7 +1348,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { self.compile_declaration_pattern_impl(pattern, def); } - /// Creates the declarations for a sript. + /// Creates the declarations for a script. pub(crate) fn create_script_decls( &mut self, stmt_list: &StatementList, @@ -1360,16 +1372,14 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context - .create_mutable_binding(*ident, true, configurable_globals); + self.create_mutable_binding(*ident, true, configurable_globals); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { if ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context - .create_mutable_binding(ident, true, configurable_globals); + self.create_mutable_binding(ident, true, configurable_globals); } } } @@ -1388,14 +1398,14 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_mutable_binding(*ident, false, false); + self.create_mutable_binding(*ident, false, false); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { if ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_mutable_binding(ident, false, false); + self.create_mutable_binding(ident, false, false); } } } @@ -1409,14 +1419,14 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_immutable_binding(*ident, true); + self.create_immutable_binding(*ident, true); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { if ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_immutable_binding(ident, true); + self.create_immutable_binding(ident, true); } } } @@ -1435,37 +1445,32 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { Declaration::Lexical(decl) => self.create_decls_from_lexical_decl(decl), Declaration::Function(decl) => { let ident = decl.name().expect("function declaration must have a name"); - self.context - .create_mutable_binding(ident, true, configurable_globals); + self.create_mutable_binding(ident, true, configurable_globals); ident == Sym::ARGUMENTS } Declaration::Generator(decl) => { let ident = decl.name().expect("generator declaration must have a name"); - self.context - .create_mutable_binding(ident, true, configurable_globals); + self.create_mutable_binding(ident, true, configurable_globals); ident == Sym::ARGUMENTS } Declaration::AsyncFunction(decl) => { let ident = decl .name() .expect("async function declaration must have a name"); - self.context - .create_mutable_binding(ident, true, configurable_globals); + self.create_mutable_binding(ident, true, configurable_globals); ident == Sym::ARGUMENTS } Declaration::AsyncGenerator(decl) => { let ident = decl .name() .expect("async generator declaration must have a name"); - self.context - .create_mutable_binding(ident, true, configurable_globals); + self.create_mutable_binding(ident, true, configurable_globals); ident == Sym::ARGUMENTS } Declaration::Class(decl) => { let ident = decl.name().expect("class declaration must have a name"); - self.context - .create_mutable_binding(ident, false, configurable_globals); + self.create_mutable_binding(ident, false, configurable_globals); false } } diff --git a/boa_engine/src/bytecompiler/statement/block.rs b/boa_engine/src/bytecompiler/statement/block.rs index d6b535f857..cd74d73bca 100644 --- a/boa_engine/src/bytecompiler/statement/block.rs +++ b/boa_engine/src/bytecompiler/statement/block.rs @@ -10,16 +10,15 @@ impl ByteCompiler<'_, '_> { use_expr: bool, configurable_globals: bool, ) { - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.create_script_decls(block.statement_list(), configurable_globals); self.compile_statement_list(block.statement_list(), use_expr, configurable_globals); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); } diff --git a/boa_engine/src/bytecompiler/statement/loop.rs b/boa_engine/src/bytecompiler/statement/loop.rs index 5b2ae10128..bd9a15b50b 100644 --- a/boa_engine/src/bytecompiler/statement/loop.rs +++ b/boa_engine/src/bytecompiler/statement/loop.rs @@ -20,7 +20,7 @@ impl ByteCompiler<'_, '_> { label: Option, configurable_globals: bool, ) { - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.push_empty_loop_jump_control(); @@ -71,10 +71,9 @@ impl ByteCompiler<'_, '_> { self.emit(Opcode::Jump, &[start_address]); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.patch_jump(exit); self.patch_jump(loop_exit); @@ -94,18 +93,17 @@ impl ByteCompiler<'_, '_> { if init_bound_names.is_empty() { self.compile_expr(for_in_loop.target(), true); } else { - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); for name in init_bound_names { - self.context.create_mutable_binding(name, false, false); + self.create_mutable_binding(name, false, false); } self.compile_expr(for_in_loop.target(), true); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); } @@ -121,7 +119,7 @@ impl ByteCompiler<'_, '_> { self.patch_jump_with_target(continue_label, start_address); self.patch_jump_with_target(loop_start, start_address); - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.emit_opcode(Opcode::Pop); // pop the `done` value. self.emit_opcode(Opcode::IteratorNext); @@ -129,8 +127,8 @@ impl ByteCompiler<'_, '_> { match for_in_loop.initializer() { IterableLoopInitializer::Identifier(ident) => { - self.context.create_mutable_binding(*ident, true, true); - let binding = self.context.set_mutable_binding(*ident); + self.create_mutable_binding(*ident, true, true); + let binding = self.set_mutable_binding(*ident); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } @@ -143,44 +141,43 @@ impl ByteCompiler<'_, '_> { } IterableLoopInitializer::Var(declaration) => match declaration { Binding::Identifier(ident) => { - self.context - .create_mutable_binding(*ident, true, configurable_globals); + self.create_mutable_binding(*ident, true, configurable_globals); self.emit_binding(BindingOpcode::InitVar, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, true, false); + self.create_mutable_binding(ident, true, false); } self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); } }, IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(*ident, false, false); + self.create_mutable_binding(*ident, false, false); self.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, false, false); + self.create_mutable_binding(ident, false, false); } self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); } }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_immutable_binding(*ident, true); + self.create_immutable_binding(*ident, true); self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_immutable_binding(ident, true); + self.create_immutable_binding(ident, true); } self.compile_declaration_pattern(pattern, BindingOpcode::InitConst); } }, IterableLoopInitializer::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, true, true); + self.create_mutable_binding(ident, true, true); } self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); } @@ -188,10 +185,9 @@ impl ByteCompiler<'_, '_> { self.compile_stmt(for_in_loop.body(), false, configurable_globals); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); self.emit(Opcode::Jump, &[start_address]); @@ -216,18 +212,17 @@ impl ByteCompiler<'_, '_> { if init_bound_names.is_empty() { self.compile_expr(for_of_loop.iterable(), true); } else { - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); for name in init_bound_names { - self.context.create_mutable_binding(name, false, false); + self.create_mutable_binding(name, false, false); } self.compile_expr(for_of_loop.iterable(), true); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); } @@ -246,7 +241,7 @@ impl ByteCompiler<'_, '_> { self.patch_jump_with_target(loop_start, start_address); self.patch_jump_with_target(cont_label, start_address); - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.emit_opcode(Opcode::Pop); // pop the `done` value. @@ -259,8 +254,8 @@ impl ByteCompiler<'_, '_> { match for_of_loop.initializer() { IterableLoopInitializer::Identifier(ref ident) => { - self.context.create_mutable_binding(*ident, true, true); - let binding = self.context.set_mutable_binding(*ident); + self.create_mutable_binding(*ident, true, true); + let binding = self.set_mutable_binding(*ident); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } @@ -273,43 +268,43 @@ impl ByteCompiler<'_, '_> { } IterableLoopInitializer::Var(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(*ident, true, false); + self.create_mutable_binding(*ident, true, false); self.emit_binding(BindingOpcode::InitVar, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, true, false); + self.create_mutable_binding(ident, true, false); } self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); } }, IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(*ident, false, false); + self.create_mutable_binding(*ident, false, false); self.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, false, false); + self.create_mutable_binding(ident, false, false); } self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); } }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_immutable_binding(*ident, true); + self.create_immutable_binding(*ident, true); self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_immutable_binding(ident, true); + self.create_immutable_binding(ident, true); } self.compile_declaration_pattern(pattern, BindingOpcode::InitConst); } }, IterableLoopInitializer::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, true, true); + self.create_mutable_binding(ident, true, true); } self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); } @@ -317,10 +312,9 @@ impl ByteCompiler<'_, '_> { self.compile_stmt(for_of_loop.body(), false, configurable_globals); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); self.emit(Opcode::Jump, &[start_address]); diff --git a/boa_engine/src/bytecompiler/statement/switch.rs b/boa_engine/src/bytecompiler/statement/switch.rs index f9e96fd218..31babac24d 100644 --- a/boa_engine/src/bytecompiler/statement/switch.rs +++ b/boa_engine/src/bytecompiler/statement/switch.rs @@ -4,7 +4,7 @@ use boa_ast::statement::Switch; impl ByteCompiler<'_, '_> { /// Compile a [`Switch`] `boa_ast` node pub(crate) fn compile_switch(&mut self, switch: &Switch, configurable_globals: bool) { - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); for case in switch.cases() { self.create_script_decls(case.body(), configurable_globals); @@ -39,10 +39,9 @@ impl ByteCompiler<'_, '_> { self.patch_jump(end_label); self.emit_opcode(Opcode::LoopEnd); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); } } diff --git a/boa_engine/src/bytecompiler/statement/try.rs b/boa_engine/src/bytecompiler/statement/try.rs index 178392e6ea..10b3ac32ee 100644 --- a/boa_engine/src/bytecompiler/statement/try.rs +++ b/boa_engine/src/bytecompiler/statement/try.rs @@ -20,16 +20,15 @@ impl ByteCompiler<'_, '_> { } self.push_try_control_info(t.finally().is_some(), try_start); - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.create_script_decls(t.block().statement_list(), configurable_globals); self.compile_statement_list(t.block().statement_list(), use_expr, configurable_globals); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::TryEnd); @@ -70,18 +69,18 @@ impl ByteCompiler<'_, '_> { self.set_jump_control_in_catch(true); let catch_end = self.emit_opcode_with_operand(Opcode::CatchStart); - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); if let Some(binding) = catch.parameter() { match binding { Binding::Identifier(ident) => { - self.context.create_mutable_binding(*ident, false, false); + self.create_mutable_binding(*ident, false, false); self.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { - self.context.create_mutable_binding(ident, false, false); + self.create_mutable_binding(ident, false, false); } self.compile_declaration_pattern(pattern, BindingOpcode::InitLet); } @@ -97,10 +96,9 @@ impl ByteCompiler<'_, '_> { configurable_globals, ); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); if parent_try.finally().is_some() { self.emit_opcode(Opcode::CatchEnd); @@ -118,7 +116,7 @@ impl ByteCompiler<'_, '_> { finally_end_label: Label, configurable_globals: bool, ) { - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.create_script_decls(finally.block().statement_list(), configurable_globals); @@ -128,10 +126,9 @@ impl ByteCompiler<'_, '_> { configurable_globals, ); - let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); - let index_compile_environment = self.push_compile_environment(compile_environment); - self.patch_jump_with_target(push_env.0, num_bindings as u32); - self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + let env_info = self.pop_compile_environment(); + self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32); + self.patch_jump_with_target(push_env.1, env_info.index as u32); self.emit_opcode(Opcode::PopEnvironment); self.pop_finally_control_info(); diff --git a/boa_engine/src/bytecompiler/statement/with.rs b/boa_engine/src/bytecompiler/statement/with.rs index b8c1ee84cd..f6b9547ae1 100644 --- a/boa_engine/src/bytecompiler/statement/with.rs +++ b/boa_engine/src/bytecompiler/statement/with.rs @@ -5,10 +5,10 @@ impl ByteCompiler<'_, '_> { /// Compile a [`With`] `boa_ast` node pub(crate) fn compile_with(&mut self, with: &With, configurable_globals: bool) { self.compile_expr(with.expression(), true); - self.context.push_compile_time_environment(false); + self.push_compile_environment(false); self.emit_opcode(Opcode::PushObjectEnvironment); self.compile_stmt(with.statement(), false, configurable_globals); - self.context.pop_compile_time_environment(); + self.pop_compile_environment(); self.emit_opcode(Opcode::PopEnvironment); } } diff --git a/boa_engine/src/context/hooks.rs b/boa_engine/src/context/hooks.rs index ac7f912d97..417b0eafc9 100644 --- a/boa_engine/src/context/hooks.rs +++ b/boa_engine/src/context/hooks.rs @@ -2,6 +2,7 @@ use crate::{ builtins::promise::OperationType, job::JobCallback, object::{JsFunction, JsObject}, + realm::Realm, Context, JsResult, JsValue, }; @@ -18,13 +19,20 @@ use super::intrinsics::Intrinsics; /// need to be redefined: /// /// ``` -/// use boa_engine::{JsNativeError, JsResult, context::{Context, ContextBuilder, HostHooks}, Source}; +/// use boa_engine::{ +/// context::{Context, ContextBuilder, HostHooks}, +/// JsNativeError, +/// JsResult, +/// realm::Realm, +/// Source +/// }; /// /// struct Hooks; /// /// impl HostHooks for Hooks { /// fn ensure_can_compile_strings( /// &self, +/// _realm: Realm, /// context: &mut Context<'_>, /// ) -> JsResult<()> { /// Err(JsNativeError::typ().with_message("eval calls not available").into()) @@ -99,9 +107,11 @@ pub trait HostHooks { /// containing unused. This is already ensured by the return type. /// /// [spec]: https://tc39.es/ecma262/#sec-hostensurecancompilestrings + // TODO: Track https://github.com/tc39/ecma262/issues/938 fn ensure_can_compile_strings( &self, - /* Realm (WIP), */ _context: &mut Context<'_>, + _realm: Realm, + _context: &mut Context<'_>, ) -> JsResult<()> { // The default implementation of HostEnsureCanCompileStrings is to return NormalCompletion(unused). Ok(()) diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 218b4cfe4c..01b6ca182c 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -1,6 +1,6 @@ //! Data structures that contain intrinsic objects and constructors. -use boa_gc::{Finalize, Gc, Trace}; +use boa_gc::{Finalize, Trace}; use crate::{ builtins::{iterable::IteratorPrototypes, uri::UriFunctions}, @@ -11,28 +11,8 @@ use crate::{ /// /// `Intrinsics` is internally stored using a `Gc`, which makes it cheapily clonable /// for multiple references to the same set of intrinsic objects. -#[derive(Default, Clone, Trace, Finalize)] +#[derive(Debug, Default, Trace, Finalize)] pub struct Intrinsics { - inner: Gc, -} - -impl PartialEq for Intrinsics { - fn eq(&self, other: &Self) -> bool { - std::ptr::eq(&*self.inner, &*other.inner) - } -} - -impl std::fmt::Debug for Intrinsics { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Intrinsics") - .field("constructors", self.constructors()) - .field("objects", self.objects()) - .finish() - } -} - -#[derive(Default, Trace, Finalize)] -struct Inner { /// Cached standard constructors pub(super) constructors: StandardConstructors, /// Cached intrinsic objects @@ -42,19 +22,19 @@ struct Inner { impl Intrinsics { /// Return the cached intrinsic objects. #[inline] - pub fn objects(&self) -> &IntrinsicObjects { - &self.inner.objects + pub const fn objects(&self) -> &IntrinsicObjects { + &self.objects } /// Return the cached standard constructors. #[inline] - pub fn constructors(&self) -> &StandardConstructors { - &self.inner.constructors + pub const fn constructors(&self) -> &StandardConstructors { + &self.constructors } } /// Store a builtin constructor (such as `Object`) and its corresponding prototype. -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Trace, Finalize)] pub struct StandardConstructor { pub(crate) constructor: JsObject, pub(crate) prototype: JsObject, @@ -96,7 +76,7 @@ impl StandardConstructor { } /// Cached core standard constructors. -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Trace, Finalize)] pub struct StandardConstructors { object: StandardConstructor, proxy: StandardConstructor, diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 04b3ec1f1a..dfa7bcecf5 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -79,7 +79,7 @@ use boa_profiler::Profiler; /// ``` pub struct Context<'host> { /// realm holds both the global object and the environment - pub(crate) realm: Realm, + realm: Realm, /// String interner in the context. interner: Interner, @@ -135,7 +135,7 @@ impl Default for Context<'_> { } // ==== Public API ==== -impl Context<'_> { +impl<'host> Context<'host> { /// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or /// the icu data provider. #[must_use] @@ -245,7 +245,13 @@ impl Context<'_> { /// Compile the script AST into a `CodeBlock` ready to be executed by the VM. pub fn compile_script(&mut self, statement_list: &StatementList) -> JsResult> { let _timer = Profiler::global().start_event("Script compilation", "Main"); - let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), false, self); + let mut compiler = ByteCompiler::new( + Sym::MAIN, + statement_list.strict(), + false, + self.realm.environment().compile_env(), + self, + ); compiler.create_script_decls(statement_list, false); compiler.compile_statement_list(statement_list, true, false); Ok(Gc::new(compiler.finish())) @@ -255,7 +261,13 @@ impl Context<'_> { pub fn compile_module(&mut self, statement_list: &ModuleItemList) -> JsResult> { let _timer = Profiler::global().start_event("Module compilation", "Main"); - let mut compiler = ByteCompiler::new(Sym::MAIN, true, false, self); + let mut compiler = ByteCompiler::new( + Sym::MAIN, + true, + false, + self.realm.environment().compile_env(), + self, + ); compiler.create_module_decls(statement_list, false); compiler.compile_module_item_list(statement_list, false); Ok(Gc::new(compiler.finish())) @@ -276,7 +288,9 @@ impl Context<'_> { self.vm.push_frame(CallFrame::new(code_block)); - self.realm.set_global_binding_number(); + // TODO: Here should be https://tc39.es/ecma262/#sec-globaldeclarationinstantiation + + self.realm().resize_global_env(); let record = self.run(); self.vm.pop_frame(); self.clear_kept_objects(); @@ -446,10 +460,16 @@ impl Context<'_> { self.realm.global_object().clone() } - /// Returns the intrinsic constructors and objects. + /// Returns the currently active intrinsic constructors and objects. + #[inline] + pub fn intrinsics(&self) -> &Intrinsics { + self.realm.intrinsics() + } + + /// Returns the currently active realm. #[inline] - pub const fn intrinsics(&self) -> &Intrinsics { - &self.realm.intrinsics + pub const fn realm(&self) -> &Realm { + &self.realm } /// Set the value of trace on the context @@ -494,46 +514,31 @@ impl Context<'_> { pub fn clear_kept_objects(&mut self) { self.kept_alive.clear(); } -} - -// ==== Private API ==== -impl Context<'_> { - /// Compile the AST into a `CodeBlock` ready to be executed by the VM in a `JSON.parse` context. - pub(crate) fn compile_json_parse(&mut self, statement_list: &StatementList) -> Gc { - let _timer = Profiler::global().start_event("Compilation", "Main"); - let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), true, self); - compiler.create_script_decls(statement_list, false); - compiler.compile_statement_list(statement_list, true, false); - Gc::new(compiler.finish()) + /// Replaces the currently active realm with `realm`, and returns the old realm. + pub fn enter_realm(&mut self, realm: Realm) -> Realm { + self.vm + .environments + .replace_global(realm.environment().clone()); + std::mem::replace(&mut self.realm, realm) } - /// Compile the AST into a `CodeBlock` with an additional declarative environment. - pub(crate) fn compile_with_new_declarative( - &mut self, - statement_list: &StatementList, - strict: bool, - ) -> Gc { - let _timer = Profiler::global().start_event("Compilation", "Main"); - let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), false, self); - compiler.compile_statement_list_with_new_declarative(statement_list, true, strict); - Gc::new(compiler.finish()) - } -} - -impl<'host> Context<'host> { - /// Get the host hooks. + /// Gets the host hooks. pub fn host_hooks(&self) -> &'host dyn HostHooks { self.host_hooks } - /// Get the job queue. + /// Gets the job queue. pub fn job_queue(&mut self) -> &'host dyn JobQueue { self.job_queue } +} +// ==== Private API ==== + +#[cfg(feature = "intl")] +impl<'host> Context<'host> { /// Get the ICU related utilities - #[cfg(feature = "intl")] pub(crate) const fn icu(&self) -> &icu::Icu<'host> { &self.icu } @@ -662,11 +667,13 @@ impl<'icu, 'hooks, 'queue> ContextBuilder<'icu, 'hooks, 'queue> { 'queue: 'host, { let host_hooks = self.host_hooks.unwrap_or(&DefaultHooks); + let realm = Realm::create(host_hooks); + let vm = Vm::new(realm.environment().clone()); let mut context = Context { - realm: Realm::create(host_hooks), + realm, interner: self.interner.unwrap_or_default(), - vm: Vm::default(), + vm, strict: false, #[cfg(feature = "intl")] icu: self.icu.unwrap_or_else(|| { diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 49b2ec9fea..18771b54df 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -1,6 +1,4 @@ -use crate::{ - environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue, -}; +use crate::environments::runtime::BindingLocator; use boa_ast::expression::Identifier; use boa_gc::{Finalize, Gc, GcRefCell, Trace}; @@ -30,7 +28,7 @@ pub(crate) struct CompileTimeEnvironment { } impl CompileTimeEnvironment { - /// Crate a new global compile time environment. + /// Creates a new global compile time environment. pub(crate) fn new_global() -> Self { Self { outer: None, @@ -40,6 +38,17 @@ impl CompileTimeEnvironment { } } + /// Creates a new compile time environment. + pub(crate) fn new(parent: Gc>, function_scope: bool) -> Self { + let index = parent.borrow().environment_index + 1; + Self { + outer: Some(parent), + environment_index: index, + bindings: FxHashMap::default(), + function_scope, + } + } + /// Check if environment has a lexical binding with the given name. pub(crate) fn has_lex_binding(&self, name: Identifier) -> bool { self.bindings @@ -198,167 +207,14 @@ impl CompileTimeEnvironment { ), } } -} - -impl Context<'_> { - /// Push either a new declarative or function environment on the compile time environment stack. - /// - /// Note: This function only works at bytecode compile time! - pub(crate) fn push_compile_time_environment(&mut self, function_scope: bool) { - let environment_index = self.realm.compile_env.borrow().environment_index + 1; - let outer = self.realm.compile_env.clone(); - self.realm.compile_env = Gc::new(GcRefCell::new(CompileTimeEnvironment { - outer: Some(outer), - environment_index, - bindings: FxHashMap::default(), - function_scope, - })); + /// Gets the outer environment of this environment. + pub(crate) fn outer(&self) -> Option>> { + self.outer.clone() } - /// Pop the last compile time environment from the stack. - /// - /// Note: This function only works at bytecode compile time! - /// - /// # Panics - /// - /// Panics if there are no more environments that can be pop'ed. - pub(crate) fn pop_compile_time_environment( - &mut self, - ) -> (usize, Gc>) { - let current_env_borrow = self.realm.compile_env.borrow(); - if let Some(outer) = ¤t_env_borrow.outer { - let outer_clone = outer.clone(); - let num_bindings = current_env_borrow.num_bindings(); - drop(current_env_borrow); - let current = self.realm.compile_env.clone(); - self.realm.compile_env = outer_clone; - (num_bindings, current) - } else { - panic!("cannot pop global environment") - } - } - - /// Get the number of bindings for the current compile time environment. - /// - /// Note: This function only works at bytecode compile time! - /// - /// # Panics - /// - /// Panics if there are no environments on the compile time environment stack. - pub(crate) fn get_binding_number(&self) -> usize { - self.realm.compile_env.borrow().num_bindings() - } - - /// Get the binding locator of the binding at bytecode compile time. - /// - /// Note: This function only works at bytecode compile time! - pub(crate) fn get_binding_value(&self, name: Identifier) -> BindingLocator { - self.realm.compile_env.borrow().get_binding_recursive(name) - } - - /// Return if a declarative binding exists at bytecode compile time. - /// This does not include bindings on the global object. - /// - /// Note: This function only works at bytecode compile time! - pub(crate) fn has_binding(&self, name: Identifier) -> bool { - self.realm.compile_env.borrow().has_binding_recursive(name) - } - - /// Create a mutable binding at bytecode compile time. - /// This function returns a syntax error, if the binding is a redeclaration. - /// - /// Note: This function only works at bytecode compile time! - /// - /// # Panics - /// - /// Panics if the global environment is not function scoped. - pub(crate) fn create_mutable_binding( - &mut self, - name: Identifier, - function_scope: bool, - configurable: bool, - ) { - if !self - .realm - .compile_env - .borrow_mut() - .create_mutable_binding(name, function_scope) - { - let name_str = self - .interner() - .resolve_expect(name.sym()) - .into_common::(false); - - // TODO: defer global initialization to execution time. - if !self - .global_object() - .has_own_property(name_str.clone(), self) - .unwrap_or_default() - { - self.global_object().borrow_mut().insert( - name_str, - PropertyDescriptor::builder() - .value(JsValue::Undefined) - .writable(true) - .enumerable(true) - .configurable(configurable) - .build(), - ); - } - } - } - - /// Initialize a mutable binding at bytecode compile time and return it's binding locator. - /// - /// Note: This function only works at bytecode compile time! - pub(crate) fn initialize_mutable_binding( - &self, - name: Identifier, - function_scope: bool, - ) -> BindingLocator { - self.realm - .compile_env - .borrow() - .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. - /// - /// Note: This function only works at bytecode compile time! - /// - /// # Panics - /// - /// Panics if the global environment does not exist. - pub(crate) fn create_immutable_binding(&mut self, name: Identifier, strict: bool) { - self.realm - .compile_env - .borrow_mut() - .create_immutable_binding(name, strict); - } - - /// Initialize an immutable binding at bytecode compile time and return it's binding locator. - /// - /// Note: This function only works at bytecode compile time! - /// - /// # 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.realm - .compile_env - .borrow() - .initialize_immutable_binding(name) - } - - /// Return the binding locator for a set operation on an existing binding. - /// - /// Note: This function only works at bytecode compile time! - pub(crate) fn set_mutable_binding(&self, name: Identifier) -> BindingLocator { - self.realm - .compile_env - .borrow() - .set_mutable_binding_recursive(name) + /// Gets the environment index of this environment. + pub(crate) const fn environment_index(&self) -> usize { + self.environment_index } } diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 5647a1df97..a9a56ba53b 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -38,6 +38,29 @@ pub(crate) struct DeclarativeEnvironment { slots: Option, } +impl DeclarativeEnvironment { + /// Creates a new, global `DeclarativeEnvironment`. + pub(crate) fn new_global() -> Self { + DeclarativeEnvironment { + bindings: GcRefCell::new(Vec::new()), + compile: Gc::new(GcRefCell::new(CompileTimeEnvironment::new_global())), + poisoned: Cell::new(false), + with: Cell::new(false), + slots: Some(EnvironmentSlots::Global), + } + } + + /// Gets the compile time environment of this environment. + pub(crate) fn compile_env(&self) -> Gc> { + self.compile.clone() + } + + /// Gets the bindings of this environment. + pub(crate) const fn bindings(&self) -> &GcRefCell>> { + &self.bindings + } +} + /// Describes the different types of internal slot data that an environment can hold. #[derive(Clone, Debug, Trace, Finalize)] pub(crate) enum EnvironmentSlots { @@ -250,19 +273,18 @@ impl Environment { } impl DeclarativeEnvironmentStack { - /// Create a new environment stack with the most outer declarative environment. - pub(crate) fn new(global_compile_environment: Gc>) -> Self { + /// Create a new environment stack. + pub(crate) fn new(global: Gc) -> Self { Self { - stack: Vec::from([Environment::Declarative(Gc::new(DeclarativeEnvironment { - bindings: GcRefCell::new(Vec::new()), - compile: global_compile_environment, - poisoned: Cell::new(false), - with: Cell::new(false), - slots: Some(EnvironmentSlots::Global), - }))]), + stack: vec![Environment::Declarative(global)], } } + /// Replaces the current global with a new global environment. + pub(crate) fn replace_global(&mut self, global: Gc) { + self.stack[0] = Environment::Declarative(global); + } + /// 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). @@ -333,23 +355,6 @@ impl DeclarativeEnvironmentStack { self.stack.extend(other); } - /// Set the number of bindings on the global environment. - /// - /// # Panics - /// - /// Panics if no environment exists on the stack. - pub(crate) fn set_global_binding_number(&mut self, binding_number: usize) { - let environment = self - .stack - .get(0) - .and_then(Environment::as_declarative) - .expect("global environment must be declarative and exist"); - let mut bindings = environment.bindings.borrow_mut(); - if bindings.len() < binding_number { - bindings.resize(binding_number, None); - } - } - /// `GetThisEnvironment` /// /// Returns the environment that currently provides a `this` biding. @@ -396,6 +401,7 @@ impl DeclarativeEnvironmentStack { /// # Panics /// /// Panics if no environment exists on the stack. + #[track_caller] pub(crate) fn push_declarative( &mut self, num_bindings: usize, @@ -437,6 +443,7 @@ impl DeclarativeEnvironmentStack { /// # Panics /// /// Panics if no environment exists on the stack. + #[track_caller] pub(crate) fn push_function( &mut self, num_bindings: usize, @@ -488,16 +495,23 @@ impl DeclarativeEnvironmentStack { }))); } - /// Push a function environment that inherits it's internal slots from the outer environment. + /// Push a function environment that inherits it's internal slots from the outer function + /// environment. /// /// # Panics /// /// Panics if no environment exists on the stack. + #[track_caller] pub(crate) fn push_function_inherit( &mut self, num_bindings: usize, compile_environment: Gc>, ) { + debug_assert!( + self.stack.len() == compile_environment.borrow().environment_index(), + "tried to push an invalid compile environment" + ); + let (poisoned, with, slots) = { let with = self .stack @@ -510,6 +524,7 @@ impl DeclarativeEnvironmentStack { .stack .iter() .filter_map(Environment::as_declarative) + .filter(|e| e.slots().is_some()) .last() .expect("global environment must always exist"); ( @@ -585,29 +600,15 @@ impl DeclarativeEnvironmentStack { .clone() } - /// Mark that there may be added bindings in the current environment. - /// - /// # Panics - /// - /// Panics if no environment exists on the stack. - pub(crate) fn poison_current(&mut self) { - self.stack - .iter() - .filter_map(Environment::as_declarative) - .last() - .expect("global environment must always exist") - .poisoned - .set(true); - } - - /// Mark that there may be added binding in all environments. - pub(crate) fn poison_all(&mut self) { - for env in &mut self.stack { + /// Mark that there may be added bindings from the current environment to the next function + /// environment. + pub(crate) fn poison_last_function(&mut self) { + for env in self.stack.iter_mut().rev() { if let Some(env) = env.as_declarative() { - if env.poisoned.get() { + if env.compile_env().borrow().is_function() { + env.poisoned.set(true); return; } - env.poisoned.set(true); } } } @@ -661,6 +662,7 @@ impl DeclarativeEnvironmentStack { /// # Panics /// /// Panics if the environment or binding index are out of range. + #[track_caller] pub(crate) fn put_value( &mut self, environment_index: usize, @@ -685,6 +687,7 @@ impl DeclarativeEnvironmentStack { /// # Panics /// /// Panics if the environment or binding index are out of range. + #[track_caller] pub(crate) fn put_value_if_uninitialized( &mut self, environment_index: usize, @@ -858,7 +861,7 @@ impl Context<'_> { mut binding_index: usize, name: Identifier, ) -> JsResult> { - for env_index in (environment_index + 1..self.realm.environments.stack.len()).rev() { + for env_index in (environment_index + 1..self.vm.environments.stack.len()).rev() { match self.environment_expect(env_index) { Environment::Declarative(env) => { if env.poisoned.get() { @@ -911,7 +914,7 @@ impl Context<'_> { &mut self, name: Identifier, ) -> JsResult> { - for env_index in (0..self.realm.environments.stack.len()).rev() { + for env_index in (0..self.vm.environments.stack.len()).rev() { let env = self.environment_expect(env_index); match env { @@ -969,7 +972,7 @@ impl Context<'_> { name: Identifier, value: JsValue, ) -> JsResult { - for env_index in (environment_index + 1..self.realm.environments.stack.len()).rev() { + for env_index in (environment_index + 1..self.vm.environments.stack.len()).rev() { let env = self.environment_expect(env_index); match env { @@ -1035,7 +1038,7 @@ impl Context<'_> { name: Identifier, value: &JsValue, ) -> JsResult { - for env_index in (0..self.realm.environments.stack.len()).rev() { + for env_index in (0..self.vm.environments.stack.len()).rev() { let env = self.environment_expect(env_index); match env { @@ -1089,7 +1092,7 @@ impl Context<'_> { &mut self, name: Identifier, ) -> JsResult<(bool, bool)> { - for env_index in (0..self.realm.environments.stack.len()).rev() { + for env_index in (0..self.vm.environments.stack.len()).rev() { let env = self.environment_expect(env_index); match env { @@ -1122,7 +1125,7 @@ impl Context<'_> { /// Return the environment at the given index. Panics if the index is out of range. fn environment_expect(&self, index: usize) -> &Environment { - self.realm + self.vm .environments .stack .get(index) diff --git a/boa_engine/src/error.rs b/boa_engine/src/error.rs index c5f02ea446..e57aa4fc5c 100644 --- a/boa_engine/src/error.rs +++ b/boa_engine/src/error.rs @@ -5,6 +5,7 @@ use crate::{ object::JsObject, object::ObjectData, property::PropertyDescriptor, + realm::Realm, string::utf16, Context, JsString, JsValue, }; @@ -75,6 +76,10 @@ pub enum TryNativeError { #[error("property `message` cannot contain unpaired surrogates")] InvalidMessageEncoding, + /// The constructor property of the error object was invalid. + #[error("invalid `constructor` property of Error object")] + InvalidConstructor, + /// A property of the error object is not accessible. #[error("could not access property `{property}`")] InaccessibleProperty { @@ -98,6 +103,13 @@ pub enum TryNativeError { /// The error value is not an error object. #[error("opaque error of type `{:?}` is not an Error object", .0.get_type())] NotAnErrorObject(JsValue), + + /// The original realm of the error object was inaccessible. + #[error("could not access realm of Error object")] + InaccessibleRealm { + /// The source error. + source: JsError, + }, } impl std::error::Error for JsError { @@ -283,10 +295,18 @@ impl JsError { } }; + let realm = try_get_property("constructor", context)? + .as_ref() + .and_then(JsValue::as_constructor) + .ok_or(TryNativeError::InvalidConstructor)? + .get_function_realm(context) + .map_err(|err| TryNativeError::InaccessibleRealm { source: err })?; + Ok(JsNativeError { kind, message, cause: cause.map(|v| Box::new(Self::from_opaque(v))), + realm: Some(realm), }) } } @@ -332,11 +352,25 @@ impl JsError { /// assert!(error.as_native().is_none()); /// ``` pub const fn as_native(&self) -> Option<&JsNativeError> { - match self.inner { - Repr::Native(ref e) => Some(e), + match &self.inner { + Repr::Native(e) => Some(e), Repr::Opaque(_) => None, } } + + /// Injects a realm on the `realm` field of a native error. + /// + /// This is a no-op if the error is not native or if the `realm` field of the error is already + /// set. + pub(crate) fn inject_realm(mut self, realm: Realm) -> JsError { + match &mut self.inner { + Repr::Native(err) if err.realm.is_none() => { + err.realm = Some(realm); + } + _ => {} + } + self + } } impl From for JsError { @@ -391,6 +425,7 @@ pub struct JsNativeError { message: Box, #[source] cause: Option>, + realm: Option, } impl JsNativeError { @@ -400,6 +435,7 @@ impl JsNativeError { kind, message, cause, + realm: None, } } @@ -642,8 +678,12 @@ impl JsNativeError { kind, message, cause, + realm, } = self; - let constructors = context.intrinsics().constructors(); + let constructors = realm.as_ref().map_or_else( + || context.intrinsics().constructors(), + |realm| realm.intrinsics().constructors(), + ); let (prototype, tag) = match kind { JsNativeErrorKind::Aggregate(_) => ( constructors.aggregate_error().prototype(), @@ -700,6 +740,12 @@ impl JsNativeError { } o } + + /// Sets the realm of this error. + pub(crate) fn with_realm(mut self, realm: Realm) -> Self { + self.realm = Some(realm); + self + } } impl From for JsNativeError { diff --git a/boa_engine/src/job.rs b/boa_engine/src/job.rs index 6666e0d8bb..4d8294df05 100644 --- a/boa_engine/src/job.rs +++ b/boa_engine/src/job.rs @@ -21,6 +21,7 @@ use std::{any::Any, cell::RefCell, collections::VecDeque, fmt::Debug, future::Fu use crate::{ object::{JsFunction, NativeObject}, + realm::Realm, Context, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -66,6 +67,7 @@ pub type FutureJob = Pin + 'static>>; pub struct NativeJob { #[allow(clippy::type_complexity)] f: Box) -> JsResult>, + realm: Option, } impl Debug for NativeJob { @@ -80,12 +82,43 @@ impl NativeJob { where F: FnOnce(&mut Context<'_>) -> JsResult + 'static, { - Self { f: Box::new(f) } + Self { + f: Box::new(f), + realm: None, + } + } + + /// Creates a new `NativeJob` from a closure and an execution realm. + pub fn with_realm(f: F, realm: Realm) -> Self + where + F: FnOnce(&mut Context<'_>) -> JsResult + 'static, + { + Self { + f: Box::new(f), + realm: Some(realm), + } + } + + /// Gets a reference to the execution realm of the job. + pub const fn realm(&self) -> Option<&Realm> { + self.realm.as_ref() } /// Calls the native job with the specified [`Context`]. + /// + /// # Note + /// + /// 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(self, context: &mut Context<'_>) -> JsResult { - (self.f)(context) + if let Some(realm) = self.realm { + let old_realm = context.enter_realm(realm); + let result = (self.f)(context); + context.enter_realm(old_realm); + result + } else { + (self.f)(context) + } } } diff --git a/boa_engine/src/object/internal_methods/mod.rs b/boa_engine/src/object/internal_methods/mod.rs index 885816abc8..744415b5ab 100644 --- a/boa_engine/src/object/internal_methods/mod.rs +++ b/boa_engine/src/object/internal_methods/mod.rs @@ -928,16 +928,16 @@ where // The corresponding object must be an intrinsic that is intended to be used // as the [[Prototype]] value of an object. // 2. Let proto be ? Get(constructor, "prototype"). - let intrinsics = if let Some(constructor) = constructor.as_object() { + let realm = if let Some(constructor) = constructor.as_object() { if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() { return Ok(proto.clone()); } // 3. If Type(proto) is not Object, then // a. Let realm be ? GetFunctionRealm(constructor). - // b. Set proto to realm's intrinsic object named intrinsicDefaultProto. constructor.get_function_realm(context)? } else { - context.intrinsics().clone() + context.realm().clone() }; - Ok(default(intrinsics.constructors()).prototype()) + // b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + Ok(default(realm.intrinsics().constructors()).prototype()) } diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index f92a08b0fd..7b5f149fa8 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -2004,7 +2004,7 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> { function: self.function, constructor: self.constructor, }, - self.context.intrinsics().clone(), + self.context.realm().clone(), )), ); let property = PropertyDescriptor::builder() @@ -2400,7 +2400,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { function: self.function, constructor: self.constructor, }, - self.context.intrinsics().clone(), + self.context.realm().clone(), ); let length = PropertyDescriptor::builder() diff --git a/boa_engine/src/object/operations.rs b/boa_engine/src/object/operations.rs index 8ce4e3e5e6..5dfc97a236 100644 --- a/boa_engine/src/object/operations.rs +++ b/boa_engine/src/object/operations.rs @@ -1,9 +1,10 @@ use crate::{ builtins::{function::ClassFieldDefinition, Array}, - context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, + context::intrinsics::{StandardConstructor, StandardConstructors}, error::JsNativeError, object::{JsObject, PrivateElement, PROTOTYPE}, property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind}, + realm::Realm, string::utf16, value::Type, Context, JsResult, JsSymbol, JsValue, @@ -658,10 +659,10 @@ impl JsObject { /// Abstract operation [`GetFunctionRealm`][spec]. /// /// [spec]: https://tc39.es/ecma262/#sec-getfunctionrealm - pub(crate) fn get_function_realm(&self, context: &mut Context<'_>) -> JsResult { + pub(crate) fn get_function_realm(&self, context: &mut Context<'_>) -> JsResult { let constructor = self.borrow(); if let Some(fun) = constructor.as_function() { - return Ok(fun.realm_intrinsics().clone()); + return Ok(fun.realm().clone()); } if let Some(bound) = constructor.as_bound_function() { @@ -676,7 +677,7 @@ impl JsObject { return fun.get_function_realm(context); } - Ok(context.intrinsics().clone()) + Ok(context.realm().clone()) } // todo: CopyDataProperties diff --git a/boa_engine/src/realm.rs b/boa_engine/src/realm.rs index c5d5f6f4d3..e98ad23c58 100644 --- a/boa_engine/src/realm.rs +++ b/boa_engine/src/realm.rs @@ -8,20 +8,30 @@ use crate::{ context::{intrinsics::Intrinsics, HostHooks}, - environments::{CompileTimeEnvironment, DeclarativeEnvironmentStack}, + environments::DeclarativeEnvironment, object::JsObject, }; -use boa_gc::{Gc, GcRefCell}; +use boa_gc::{Finalize, Gc, Trace}; use boa_profiler::Profiler; /// Representation of a Realm. /// /// In the specification these are called Realm Records. -#[derive(Debug)] +#[derive(Clone, Debug, Trace, Finalize)] pub struct Realm { - pub(crate) intrinsics: Intrinsics, - pub(crate) environments: DeclarativeEnvironmentStack, - pub(crate) compile_env: Gc>, + inner: Gc, +} + +impl PartialEq for Realm { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(&*self.inner, &*other.inner) + } +} + +#[derive(Debug, Trace, Finalize)] +struct Inner { + intrinsics: Intrinsics, + environment: Gc, global_object: JsObject, global_this: JsObject, } @@ -32,36 +42,50 @@ impl Realm { pub fn create(hooks: &dyn HostHooks) -> Self { let _timer = Profiler::global().start_event("Realm::create", "realm"); - let intrinsics = Intrinsics::new(); - + let intrinsics = Intrinsics::default(); let global_object = hooks.create_global_object(&intrinsics); let global_this = hooks .create_global_this(&intrinsics) .unwrap_or_else(|| global_object.clone()); - let global_compile_environment = - Gc::new(GcRefCell::new(CompileTimeEnvironment::new_global())); + let realm = Self { + inner: Gc::new(Inner { + intrinsics, + environment: Gc::new(DeclarativeEnvironment::new_global()), + global_object, + global_this, + }), + }; - Self { - intrinsics, - global_object, - global_this, - environments: DeclarativeEnvironmentStack::new(global_compile_environment.clone()), - compile_env: global_compile_environment, - } + realm.initialize(); + + realm + } + + /// Gets the intrinsics of this `Realm`. + pub fn intrinsics(&self) -> &Intrinsics { + &self.inner.intrinsics } - pub(crate) const fn global_object(&self) -> &JsObject { - &self.global_object + pub(crate) fn environment(&self) -> &Gc { + &self.inner.environment } - pub(crate) const fn global_this(&self) -> &JsObject { - &self.global_this + pub(crate) fn global_object(&self) -> &JsObject { + &self.inner.global_object } - /// Set the number of bindings on the global environment. - pub(crate) fn set_global_binding_number(&mut self) { - let binding_number = self.compile_env.borrow().num_bindings(); - self.environments.set_global_binding_number(binding_number); + pub(crate) fn global_this(&self) -> &JsObject { + &self.inner.global_this + } + + /// Resizes the number of bindings on the global environment. + pub(crate) fn resize_global_env(&self) { + let binding_number = self.environment().compile_env().borrow().num_bindings(); + + let mut bindings = self.environment().bindings().borrow_mut(); + if bindings.len() < binding_number { + bindings.resize(binding_number, None); + } } } diff --git a/boa_engine/src/tests/env.rs b/boa_engine/src/tests/env.rs new file mode 100644 index 0000000000..3a6667c25e --- /dev/null +++ b/boa_engine/src/tests/env.rs @@ -0,0 +1,32 @@ +use indoc::indoc; + +use crate::{run_test_actions, TestAction}; + +#[test] +// https://github.com/boa-dev/boa/issues/2317 +fn fun_block_eval_2317() { + run_test_actions([ + TestAction::assert_eq( + indoc! {r#" + (function(y){ + { + eval("var x = 'inner';"); + } + return y + x; + })("arg"); + "#}, + "arginner", + ), + TestAction::assert_eq( + indoc! {r#" + (function(y = "default"){ + { + eval("var x = 'inner';"); + } + return y + x; + })(); + "#}, + "defaultinner", + ), + ]); +} diff --git a/boa_engine/src/tests/mod.rs b/boa_engine/src/tests/mod.rs index 56d5c71cb3..34930868bb 100644 --- a/boa_engine/src/tests/mod.rs +++ b/boa_engine/src/tests/mod.rs @@ -1,6 +1,7 @@ use indoc::indoc; mod control_flow; +mod env; mod function; mod operators; mod promise; diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 26c5b5f39d..fe44e4fea6 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -17,9 +17,10 @@ use crate::{ PROTOTYPE, }, property::PropertyDescriptor, + realm::Realm, string::utf16, vm::CallFrame, - Context, JsResult, JsString, JsValue, + Context, JsError, JsResult, JsString, JsValue, }; use boa_ast::{ expression::Identifier, @@ -129,6 +130,9 @@ pub struct CodeBlock { /// When the execution of the parameter expressions throws an error, we do not need to pop the function environment. pub(crate) function_environment_push_location: u32, + /// The number of bindings in the parameters environment. + pub(crate) parameters_env_bindings: Option, + #[cfg(feature = "trace")] /// Trace instruction execution to `stdout`. #[unsafe_ignore_trace] @@ -158,6 +162,7 @@ impl CodeBlock { is_class_constructor: false, class_field_initializer_name: None, function_environment_push_location: 0, + parameters_env_bindings: None, #[cfg(feature = "trace")] trace: std::cell::Cell::new(false), } @@ -598,25 +603,25 @@ pub(crate) fn create_function_object( Function::new( FunctionKind::Async { code, - environments: context.realm.environments.clone(), + environments: context.vm.environments.clone(), home_object: None, promise_capability, class_object: None, }, - context.intrinsics().clone(), + context.realm().clone(), ) } else { Function::new( FunctionKind::Ordinary { code, - environments: context.realm.environments.clone(), + environments: context.vm.environments.clone(), constructor_kind: ConstructorKind::Base, home_object: None, fields: ThinVec::new(), private_methods: ThinVec::new(), class_object: None, }, - context.intrinsics().clone(), + context.realm().clone(), ) }; @@ -713,11 +718,11 @@ pub(crate) fn create_generator_function_object( let function = Function::new( FunctionKind::AsyncGenerator { code, - environments: context.realm.environments.clone(), + environments: context.vm.environments.clone(), home_object: None, class_object: None, }, - context.intrinsics().clone(), + context.realm().clone(), ); JsObject::from_proto_and_data( function_prototype, @@ -727,11 +732,11 @@ pub(crate) fn create_generator_function_object( let function = Function::new( FunctionKind::Generator { code, - environments: context.realm.environments.clone(), + environments: context.vm.environments.clone(), home_object: None, class_object: None, }, - context.intrinsics().clone(), + context.realm().clone(), ); JsObject::from_proto_and_data(function_prototype, ObjectData::generator_function(function)) }; @@ -758,6 +763,49 @@ pub(crate) fn create_generator_function_object( constructor } +struct ContextCleanupGuard<'a, 'host> { + context: &'a mut Context<'host>, + old_realm: Realm, + old_active_function: Option, +} + +impl<'a, 'host> ContextCleanupGuard<'a, 'host> { + /// Creates a new guard that resets the realm of the context on exit. + fn new(context: &'a mut Context<'host>, realm: Realm, active_function: JsObject) -> Self { + let old_realm = context.enter_realm(realm); + let old_active_function = context.vm.active_function.replace(active_function); + Self { + context, + old_realm, + old_active_function, + } + } +} + +impl Drop for ContextCleanupGuard<'_, '_> { + fn drop(&mut self) { + self.context.enter_realm(self.old_realm.clone()); + std::mem::swap( + &mut self.context.vm.active_function, + &mut self.old_active_function, + ); + } +} + +impl<'host> std::ops::Deref for ContextCleanupGuard<'_, 'host> { + type Target = Context<'host>; + + fn deref(&self) -> &Self::Target { + self.context + } +} + +impl std::ops::DerefMut for ContextCleanupGuard<'_, '_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.context + } +} + impl JsObject { pub(crate) fn call_internal( &self, @@ -766,585 +814,279 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let this_function_object = self.clone(); - - if !self.is_callable() { - return Err(JsNativeError::typ() - .with_message("not a callable function") - .into()); - } - let old_active = context.vm.active_function.replace(self.clone()); - + let active_function = self.clone(); let object = self.borrow(); let function_object = object.as_function().expect("not a function"); - - let old_intrinsics = std::mem::replace( - &mut context.realm.intrinsics, - function_object.realm_intrinsics().clone(), - ); - - let result = match function_object.kind() { - FunctionKind::Native { - function, - constructor, - } => { - let function = function.clone(); - let constructor = *constructor; - drop(object); - - if constructor.is_some() { - function.call(&JsValue::undefined(), args, context) - } else { - function.call(this, args, context) - } - } - FunctionKind::Ordinary { - code, - environments, - class_object, - .. - } => { - let code = code.clone(); - let mut environments = environments.clone(); - let class_object = class_object.clone(); - drop(object); - - if code.is_class_constructor { - return Err(JsNativeError::typ() - .with_message("Class constructor cannot be invoked without 'new'") - .into()); - } - - let environments_len = environments.len(); - std::mem::swap(&mut environments, &mut context.realm.environments); - - let lexical_this_mode = code.this_mode == ThisMode::Lexical; - - let this = if lexical_this_mode { - None - } else if code.strict { - Some(this.clone()) - } else if this.is_null_or_undefined() { - Some(context.global_object().into()) - } else { - Some( - this.to_object(context) - .expect("conversion cannot fail") - .into(), - ) - }; - - let compile_time_environment_index = usize::from(code.params.has_expressions()); - - if let Some(class_object) = class_object { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index - + usize::from(code.has_binding_identifier) - + 1] - .clone(), - ); - context - .realm - .environments - .put_value(index, 0, class_object.into()); - } - - if code.has_binding_identifier { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index + 1].clone(), - ); - context - .realm - .environments - .put_value(index, 0, self.clone().into()); - } - - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[compile_time_environment_index].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); - - if let Some(binding) = code.arguments_binding { - let arguments_obj = if code.strict || !code.params.is_simple() { - Arguments::create_unmapped_arguments_object(args, context) + let realm = function_object.realm().clone(); + + let context = &mut ContextCleanupGuard::new(context, realm, active_function); + + let (code, mut environments, class_object, promise, async_, gen) = + 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 { - let env = context.realm.environments.current(); - Arguments::create_mapped_arguments_object( - &this_function_object, - &code.params, - args, - &env, - context, - ) - }; - context.realm.environments.put_value( - binding.environment_index(), - binding.binding_index(), - arguments_obj.into(), - ); - } - - let arg_count = args.len(); - - // Push function arguments to the stack. - let args = if code.params.as_ref().len() > args.len() { - let mut v = args.to_vec(); - v.extend(vec![ - JsValue::Undefined; - code.params.as_ref().len() - args.len() - ]); - v - } else { - args.to_vec() - }; - - for arg in args.iter().rev() { - context.vm.push(arg.clone()); + function.call(this, args, context) + } + .map_err(|err| err.inject_realm(context.realm().clone())); } - - let param_count = code.params.as_ref().len(); - - context.vm.push_frame( - CallFrame::new(code) - .with_param_count(param_count) - .with_arg_count(arg_count), - ); - - let record = context.run(); - context.vm.pop_frame().expect("must have frame"); - - std::mem::swap(&mut environments, &mut context.realm.environments); - environments.truncate(environments_len); - - record.consume() - } - FunctionKind::Async { - code, - environments, - promise_capability, - class_object, - .. - } => { - let code = code.clone(); - let mut environments = environments.clone(); - let promise = promise_capability.promise().clone(); - let class_object = class_object.clone(); - drop(object); - - let environments_len = environments.len(); - std::mem::swap(&mut environments, &mut context.realm.environments); - - let lexical_this_mode = code.this_mode == ThisMode::Lexical; - - let this = if lexical_this_mode { - None - } else if code.strict { - Some(this.clone()) - } else if this.is_null_or_undefined() { - Some(context.global_object().into()) - } else { - Some( - this.to_object(context) - .expect("conversion cannot fail") - .into(), + FunctionKind::Ordinary { + code, + environments, + class_object, + .. + } => { + 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(), + None, + false, + false, ) - }; - - let compile_time_environment_index = usize::from(code.params.has_expressions()); - - if let Some(class_object) = class_object { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index - + usize::from(code.has_binding_identifier) - + 1] - .clone(), - ); - context - .realm - .environments - .put_value(index, 0, class_object.into()); } - if code.has_binding_identifier { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index + 1].clone(), - ); - context - .realm - .environments - .put_value(index, 0, self.clone().into()); - } - - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[compile_time_environment_index].clone(), - this, - self.clone(), + FunctionKind::Async { + code, + environments, + promise_capability, + class_object, + .. + } => ( + code.clone(), + environments.clone(), + class_object.clone(), + Some(promise_capability.promise().clone()), + true, + false, + ), + FunctionKind::Generator { + code, + environments, + class_object, + .. + } => ( + code.clone(), + environments.clone(), + class_object.clone(), None, - lexical_this_mode, - ); - - if let Some(binding) = code.arguments_binding { - let arguments_obj = if code.strict || !code.params.is_simple() { - Arguments::create_unmapped_arguments_object(args, context) - } else { - let env = context.realm.environments.current(); - Arguments::create_mapped_arguments_object( - &this_function_object, - &code.params, - args, - &env, - context, - ) - }; - context.realm.environments.put_value( - binding.environment_index(), - binding.binding_index(), - arguments_obj.into(), - ); - } - - let arg_count = args.len(); - - // Push function arguments to the stack. - let args = if code.params.as_ref().len() > args.len() { - let mut v = args.to_vec(); - v.extend(vec![ - JsValue::Undefined; - code.params.as_ref().len() - args.len() - ]); - v - } else { - args.to_vec() - }; - - for arg in args.iter().rev() { - context.vm.push(arg.clone()); - } - - let param_count = code.params.as_ref().len(); - - context.vm.push_frame( - CallFrame::new(code) - .with_param_count(param_count) - .with_arg_count(arg_count), - ); + false, + true, + ), + FunctionKind::AsyncGenerator { + code, + environments, + class_object, + .. + } => ( + code.clone(), + environments.clone(), + class_object.clone(), + None, + true, + true, + ), + }; - let _result = context.run(); - context.vm.pop_frame().expect("must have frame"); + drop(object); - std::mem::swap(&mut environments, &mut context.realm.environments); - environments.truncate(environments_len); + std::mem::swap(&mut environments, &mut context.vm.environments); - Ok(promise.into()) - } - FunctionKind::Generator { - code, - environments, - class_object, - .. - } => { - let code = code.clone(); - let mut environments = environments.clone(); - let class_object = class_object.clone(); - drop(object); + let lexical_this_mode = code.this_mode == ThisMode::Lexical; - std::mem::swap(&mut environments, &mut context.realm.environments); + let this = if lexical_this_mode { + None + } else if code.strict { + Some(this.clone()) + } else if this.is_null_or_undefined() { + Some(context.global_object().into()) + } else { + Some( + this.to_object(context) + .expect("conversion cannot fail") + .into(), + ) + }; - let lexical_this_mode = code.this_mode == ThisMode::Lexical; + let mut last_env = code.compile_environments.len() - 1; - let this = if lexical_this_mode { - None - } else if code.strict { - Some(this.clone()) - } else if this.is_null_or_undefined() { - Some(context.global_object().into()) - } else { - Some( - this.to_object(context) - .expect("conversion cannot fail") - .into(), - ) - }; + if let Some(class_object) = class_object { + let index = context + .vm + .environments + .push_declarative(1, code.compile_environments[last_env].clone()); + context + .vm + .environments + .put_value(index, 0, class_object.into()); + last_env -= 1; + } - let compile_time_environment_index = usize::from(code.params.has_expressions()); + if code.has_binding_identifier { + let index = context + .vm + .environments + .push_declarative(1, code.compile_environments[last_env].clone()); + context + .vm + .environments + .put_value(index, 0, self.clone().into()); + last_env -= 1; + } - if let Some(class_object) = class_object { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index - + usize::from(code.has_binding_identifier) - + 1] - .clone(), - ); - context - .realm - .environments - .put_value(index, 0, class_object.into()); - } + context.vm.environments.push_function( + code.num_bindings, + code.compile_environments[last_env].clone(), + this, + self.clone(), + None, + lexical_this_mode, + ); - if code.has_binding_identifier { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index + 1].clone(), - ); - context - .realm - .environments - .put_value(index, 0, self.clone().into()); - } + if let Some(bindings) = code.parameters_env_bindings { + last_env -= 1; + context + .vm + .environments + .push_declarative(bindings, code.compile_environments[last_env].clone()); + } - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[compile_time_environment_index].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); + if let Some(binding) = code.arguments_binding { + let arguments_obj = if code.strict || !code.params.is_simple() { + Arguments::create_unmapped_arguments_object(args, context) + } else { + let env = context.vm.environments.current(); + Arguments::create_mapped_arguments_object( + &this_function_object, + &code.params, + args, + &env, + context, + ) + }; + context.vm.environments.put_value( + binding.environment_index(), + binding.binding_index(), + arguments_obj.into(), + ); + } - if let Some(binding) = code.arguments_binding { - let arguments_obj = if code.strict || !code.params.is_simple() { - Arguments::create_unmapped_arguments_object(args, context) - } else { - let env = context.realm.environments.current(); - Arguments::create_mapped_arguments_object( - &this_function_object, - &code.params, - args, - &env, - context, - ) - }; - context.realm.environments.put_value( - binding.environment_index(), - binding.binding_index(), - arguments_obj.into(), - ); - } + let arg_count = args.len(); - let arg_count = args.len(); + // Push function arguments to the stack. + let mut args = if code.params.as_ref().len() > args.len() { + let mut v = args.to_vec(); + v.extend(vec![ + JsValue::Undefined; + code.params.as_ref().len() - args.len() + ]); + v + } else { + args.to_vec() + }; + args.reverse(); + let mut stack = args; - // Push function arguments to the stack. - let mut args = if code.params.as_ref().len() > args.len() { - let mut v = args.to_vec(); - v.extend(vec![ - JsValue::Undefined; - code.params.as_ref().len() - args.len() - ]); - v - } else { - args.to_vec() - }; - args.reverse(); + std::mem::swap(&mut context.vm.stack, &mut stack); - let param_count = code.params.as_ref().len(); + let param_count = code.params.as_ref().len(); - let call_frame = CallFrame::new(code) - .with_param_count(param_count) - .with_arg_count(arg_count); - let mut stack = args; + context.vm.push_frame( + CallFrame::new(code) + .with_param_count(param_count) + .with_arg_count(arg_count), + ); - std::mem::swap(&mut context.vm.stack, &mut stack); - context.vm.push_frame(call_frame); + let result = context + .run() + .consume() + .map_err(|err| err.inject_realm(context.realm().clone())); - let init_result = context.run(); + let call_frame = context.vm.pop_frame().expect("frame must exist"); + std::mem::swap(&mut environments, &mut context.vm.environments); + std::mem::swap(&mut context.vm.stack, &mut stack); - let call_frame = context.vm.pop_frame().expect("frame must exist"); - std::mem::swap(&mut environments, &mut context.realm.environments); - std::mem::swap(&mut context.vm.stack, &mut stack); + if let Some(promise) = promise { + return Ok(promise.into()); + } - let prototype = this_function_object - .get(PROTOTYPE, context) - .expect("GeneratorFunction must have a prototype property") - .as_object() - .map_or_else(|| context.intrinsics().objects().generator(), Clone::clone); + let value = result?; + + let value = if gen { + let proto = this_function_object + .get(PROTOTYPE, context) + .expect("generator must have a prototype property") + .as_object() + .map_or_else( + || { + if async_ { + context.intrinsics().objects().async_generator() + } else { + context.intrinsics().objects().generator() + } + }, + Clone::clone, + ); - let generator = Self::from_proto_and_data( - prototype, - ObjectData::generator(Generator { - state: GeneratorState::SuspendedStart, + let generator = Self::from_proto_and_data( + proto, + if async_ { + ObjectData::async_generator(AsyncGenerator { + state: AsyncGeneratorState::SuspendedStart, context: Some(GeneratorContext { environments, call_frame, stack, active_function: context.vm.active_function.clone(), - realm_intrinsics: context.realm.intrinsics.clone(), + realm: context.realm().clone(), }), - }), - ); - - init_result.consume()?; - - Ok(generator.into()) - } - FunctionKind::AsyncGenerator { - code, - environments, - class_object, - .. - } => { - let code = code.clone(); - let mut environments = environments.clone(); - let class_object = class_object.clone(); - drop(object); - - std::mem::swap(&mut environments, &mut context.realm.environments); - - let lexical_this_mode = code.this_mode == ThisMode::Lexical; - - let this = if lexical_this_mode { - None - } else if code.strict { - Some(this.clone()) - } else if this.is_null_or_undefined() { - Some(context.global_object().into()) - } else { - Some( - this.to_object(context) - .expect("conversion cannot fail") - .into(), - ) - }; - - let compile_time_environment_index = usize::from(code.params.has_expressions()); - - if let Some(class_object) = class_object { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index - + usize::from(code.has_binding_identifier) - + 1] - .clone(), - ); - context - .realm - .environments - .put_value(index, 0, class_object.into()); - } - - if code.has_binding_identifier { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index + 1].clone(), - ); - context - .realm - .environments - .put_value(index, 0, self.clone().into()); - } - - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[compile_time_environment_index].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); - - if let Some(binding) = code.arguments_binding { - let arguments_obj = if code.strict || !code.params.is_simple() { - Arguments::create_unmapped_arguments_object(args, context) - } else { - let env = context.realm.environments.current(); - Arguments::create_mapped_arguments_object( - &this_function_object, - &code.params, - args, - &env, - context, - ) - }; - context.realm.environments.put_value( - binding.environment_index(), - binding.binding_index(), - arguments_obj.into(), - ); - } - - let arg_count = args.len(); - - // Push function arguments to the stack. - let mut args = if code.params.as_ref().len() > args.len() { - let mut v = args.to_vec(); - v.extend(vec![ - JsValue::Undefined; - code.params.as_ref().len() - args.len() - ]); - v + queue: VecDeque::new(), + }) } else { - args.to_vec() - }; - args.reverse(); - - let param_count = code.params.as_ref().len(); - - let call_frame = CallFrame::new(code) - .with_param_count(param_count) - .with_arg_count(arg_count); - let mut stack = args; - - std::mem::swap(&mut context.vm.stack, &mut stack); - context.vm.push_frame(call_frame); - - let init_result = context.run(); - - let call_frame = context.vm.pop_frame().expect("frame must exist"); - std::mem::swap(&mut environments, &mut context.realm.environments); - std::mem::swap(&mut context.vm.stack, &mut stack); - - let prototype = this_function_object - .get(PROTOTYPE, context) - .expect("AsyncGeneratorFunction must have a prototype property") - .as_object() - .map_or_else( - || context.intrinsics().objects().async_generator(), - Clone::clone, - ); - - let generator = Self::from_proto_and_data( - prototype, - ObjectData::async_generator(AsyncGenerator { - state: AsyncGeneratorState::SuspendedStart, + ObjectData::generator(Generator { + state: GeneratorState::SuspendedStart, context: Some(GeneratorContext { environments, call_frame, stack, active_function: context.vm.active_function.clone(), - realm_intrinsics: context.realm.intrinsics.clone(), + realm: context.realm().clone(), }), - queue: VecDeque::new(), - }), - ); - - { - let gen_clone = generator.clone(); - let mut generator_mut = generator.borrow_mut(); - let gen = generator_mut - .as_async_generator_mut() - .expect("must be object here"); - let gen_context = gen.context.as_mut().expect("must exist"); - gen_context.call_frame.async_generator = Some(gen_clone); - } - - init_result.consume()?; - - Ok(generator.into()) + }) + }, + ); + + if async_ { + let gen_clone = generator.clone(); + let mut generator_mut = generator.borrow_mut(); + let gen = generator_mut + .as_async_generator_mut() + .expect("must be object here"); + let gen_context = gen.context.as_mut().expect("must exist"); + gen_context.call_frame.async_generator = Some(gen_clone); } - }; - context.vm.active_function = old_active; - context.realm.intrinsics = old_intrinsics; + generator.into() + } else { + value + }; - result + Ok(value) } pub(crate) fn construct_internal( @@ -1354,30 +1096,13 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let this_function_object = self.clone(); - - let create_this = |context| { - let prototype = - get_prototype_from_constructor(this_target, StandardConstructors::object, context)?; - Ok(Self::from_proto_and_data(prototype, ObjectData::ordinary())) - }; - - if !self.is_constructor() { - return Err(JsNativeError::typ() - .with_message("not a constructor function") - .into()); - } - - let old_active = context.vm.active_function.replace(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(); - let old_intrinsics = std::mem::replace( - &mut context.realm.intrinsics, - function_object.realm_intrinsics().clone(), - ); - - let result = match function_object.kind() { + let context = &mut ContextCleanupGuard::new(context, realm, active_function); + match function_object.kind() { FunctionKind::Native { function, constructor, @@ -1387,20 +1112,30 @@ impl JsObject { let constructor = *constructor; drop(object); - match function.call(this_target, args, context)? { - JsValue::Object(ref o) => Ok(o.clone()), - val => { - if constructor.expect("hmm").is_base() || val.is_undefined() { - create_this(context) - } else { - Err(JsNativeError::typ() + function + .call(this_target, args, context) + .map_err(|err| err.inject_realm(context.realm().clone())) + .and_then(|v| match v { + JsValue::Object(ref o) => Ok(o.clone()), + val => { + if constructor.expect("must be a constructor").is_base() + || val.is_undefined() + { + let prototype = get_prototype_from_constructor( + this_target, + StandardConstructors::object, + context, + )?; + Ok(Self::from_proto_and_data(prototype, ObjectData::ordinary())) + } else { + Err(JsNativeError::typ() .with_message( - "Derived constructor can only return an Object or undefined", + "derived constructor can only return an Object or undefined", ) .into()) + } } - } - } + }) } FunctionKind::Ordinary { code, @@ -1433,37 +1168,45 @@ impl JsObject { }; let environments_len = environments.len(); - std::mem::swap(&mut environments, &mut context.realm.environments); + std::mem::swap(&mut environments, &mut context.vm.environments); let new_target = this_target.as_object().expect("must be object"); - let compile_time_environment_index = usize::from(code.params.has_expressions()); + let mut last_env = code.compile_environments.len() - 1; if code.has_binding_identifier { - let index = context.realm.environments.push_declarative( - 1, - code.compile_environments[compile_time_environment_index + 1].clone(), - ); + let index = context + .vm + .environments + .push_declarative(1, code.compile_environments[last_env].clone()); context - .realm + .vm .environments .put_value(index, 0, self.clone().into()); + last_env -= 1; } - context.realm.environments.push_function( + context.vm.environments.push_function( code.num_bindings, - code.compile_environments[compile_time_environment_index].clone(), + code.compile_environments[last_env].clone(), this.clone().map(Into::into), self.clone(), Some(new_target.clone()), false, ); + if let Some(bindings) = code.parameters_env_bindings { + context + .vm + .environments + .push_declarative(bindings, code.compile_environments[0].clone()); + } + if let Some(binding) = code.arguments_binding { let arguments_obj = if code.strict || !code.params.is_simple() { Arguments::create_unmapped_arguments_object(args, context) } else { - let env = context.realm.environments.current(); + let env = context.vm.environments.current(); Arguments::create_mapped_arguments_object( &this_function_object, &code.params, @@ -1472,7 +1215,7 @@ impl JsObject { context, ) }; - context.realm.environments.put_value( + context.vm.environments.put_value( binding.environment_index(), binding.binding_index(), arguments_obj.into(), @@ -1510,7 +1253,7 @@ impl JsObject { context.vm.pop_frame(); - std::mem::swap(&mut environments, &mut context.realm.environments); + std::mem::swap(&mut environments, &mut context.vm.environments); let environment = if has_binding_identifier { environments.truncate(environments_len + 2); @@ -1522,7 +1265,9 @@ impl JsObject { environments.pop() }; - let result = record.consume()?; + let result = record + .consume() + .map_err(|err| err.inject_realm(context.realm().clone()))?; if let Some(result) = result.as_object() { Ok(result.clone()) @@ -1530,7 +1275,7 @@ impl JsObject { Ok(this) } else if !result.is_undefined() { Err(JsNativeError::typ() - .with_message("Function constructor must not return non-object") + .with_message("derived constructor can only return an Object or undefined") .into()) } else { let function_env = environment @@ -1539,12 +1284,15 @@ impl JsObject { .expect("must be function environment") .as_function_slots() .expect("must be function environment"); - Ok(function_env + function_env .borrow() - .get_this_binding()? - .as_object() - .expect("this binding must be object") - .clone()) + .get_this_binding() + .map(|this| { + this.as_object() + .expect("this binding must be object") + .clone() + }) + .map_err(JsError::from) } } FunctionKind::Generator { .. } @@ -1552,10 +1300,6 @@ impl JsObject { | FunctionKind::AsyncGenerator { .. } => { unreachable!("not a constructor") } - }; - context.vm.active_function = old_active; - context.realm.intrinsics = old_intrinsics; - - result + } } } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index f147ccfd77..868eba5a9d 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -6,11 +6,13 @@ use crate::{ builtins::async_generator::{AsyncGenerator, AsyncGeneratorState}, + environments::{DeclarativeEnvironment, DeclarativeEnvironmentStack}, vm::{call_frame::EarlyReturnType, code_block::Readable}, Context, JsError, JsObject, JsResult, JsValue, }; #[cfg(feature = "fuzz")] use crate::{JsNativeError, JsNativeErrorKind}; +use boa_gc::Gc; use boa_profiler::Profiler; use std::{convert::TryInto, mem::size_of}; @@ -44,17 +46,20 @@ pub struct Vm { pub(crate) frames: Vec, pub(crate) stack: Vec, pub(crate) err: Option, + pub(crate) environments: DeclarativeEnvironmentStack, #[cfg(feature = "trace")] pub(crate) trace: bool, pub(crate) stack_size_limit: usize, pub(crate) active_function: Option, } -impl Default for Vm { - fn default() -> Self { +impl Vm { + /// Creates a new virtual machine. + pub(crate) fn new(global: Gc) -> Self { Self { frames: Vec::with_capacity(16), stack: Vec::with_capacity(1024), + environments: DeclarativeEnvironmentStack::new(global), err: None, #[cfg(feature = "trace")] trace: false, @@ -62,9 +67,7 @@ impl Default for Vm { active_function: None, } } -} -impl Vm { /// Push a value on the stack. pub(crate) fn push(&mut self, value: T) where @@ -187,7 +190,7 @@ impl Context<'_> { // If the current executing function is an async function we have to resolve/reject it's promise at the end. // The relevant spec section is 3. in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart). let promise_capability = self - .realm + .vm .environments .current_function_slots() .as_function_slots() @@ -403,10 +406,11 @@ impl Context<'_> { .take() .expect("err must exist on a Completion::Throw")), true, + None, self, ); } else { - AsyncGenerator::complete_step(&next, Ok(execution_result), true, self); + AsyncGenerator::complete_step(&next, Ok(execution_result), true, None, self); } AsyncGenerator::drain_queue(&generator_object, self); diff --git a/boa_engine/src/vm/opcode/await_stm/mod.rs b/boa_engine/src/vm/opcode/await_stm/mod.rs index c96b62b1d7..9e401e8a11 100644 --- a/boa_engine/src/vm/opcode/await_stm/mod.rs +++ b/boa_engine/src/vm/opcode/await_stm/mod.rs @@ -34,11 +34,11 @@ impl Operation for Await { )?; let generator_context = GeneratorContext { - environments: context.realm.environments.clone(), + environments: context.vm.environments.clone(), call_frame: context.vm.frame().clone(), stack: context.vm.stack.clone(), active_function: context.vm.active_function.clone(), - realm_intrinsics: context.realm.intrinsics.clone(), + realm: context.realm().clone(), }; // 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called: @@ -57,7 +57,7 @@ impl Operation for Await { // f. Return undefined. std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -65,10 +65,7 @@ impl Operation for Await { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + let old_realm = context.enter_realm(generator_context.realm.clone()); context.vm.push_frame(generator_context.call_frame.clone()); context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal; @@ -80,7 +77,7 @@ impl Operation for Await { .pop_frame() .expect("generator call frame must exist"); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -88,10 +85,7 @@ impl Operation for Await { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + context.enter_realm(old_realm); Ok(JsValue::undefined()) }, @@ -118,7 +112,7 @@ impl Operation for Await { // f. Return undefined. std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -126,10 +120,7 @@ impl Operation for Await { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + let old_realm = context.enter_realm(generator_context.realm.clone()); context.vm.push_frame(generator_context.call_frame.clone()); context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; @@ -141,7 +132,7 @@ impl Operation for Await { .pop_frame() .expect("generator call frame must exist"); std::mem::swap( - &mut context.realm.environments, + &mut context.vm.environments, &mut generator_context.environments, ); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); @@ -149,10 +140,7 @@ impl Operation for Await { &mut context.vm.active_function, &mut generator_context.active_function, ); - std::mem::swap( - &mut context.realm.intrinsics, - &mut generator_context.realm_intrinsics, - ); + context.enter_realm(old_realm); Ok(JsValue::undefined()) }, diff --git a/boa_engine/src/vm/opcode/control_flow/break.rs b/boa_engine/src/vm/opcode/control_flow/break.rs index 9f6a4d03d7..e897f5a1e5 100644 --- a/boa_engine/src/vm/opcode/control_flow/break.rs +++ b/boa_engine/src/vm/opcode/control_flow/break.rs @@ -34,8 +34,8 @@ impl Operation for Break { context.vm.frame_mut().env_stack.pop(); } - let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); // 2. Register target address in AbruptCompletionRecord. let new_record = AbruptCompletionRecord::new_break().with_initial_target(target_address); diff --git a/boa_engine/src/vm/opcode/control_flow/catch.rs b/boa_engine/src/vm/opcode/control_flow/catch.rs index 2e22ca0705..c526a52b2c 100644 --- a/boa_engine/src/vm/opcode/control_flow/catch.rs +++ b/boa_engine/src/vm/opcode/control_flow/catch.rs @@ -50,8 +50,8 @@ impl Operation for CatchEnd { } } - let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); Ok(CompletionType::Normal) } @@ -77,11 +77,11 @@ impl Operation for CatchEnd2 { .filter(|entry| entry.is_catch_env()) { let env_truncation_len = context - .realm + .vm .environments .len() .saturating_sub(catch_entry.env_num()); - context.realm.environments.truncate(env_truncation_len); + context.vm.environments.truncate(env_truncation_len); context.vm.frame_mut().env_stack.pop(); } diff --git a/boa_engine/src/vm/opcode/control_flow/continue.rs b/boa_engine/src/vm/opcode/control_flow/continue.rs index 763f2f0dcd..d20572445d 100644 --- a/boa_engine/src/vm/opcode/control_flow/continue.rs +++ b/boa_engine/src/vm/opcode/control_flow/continue.rs @@ -42,8 +42,8 @@ impl Operation for Continue { context.vm.frame_mut().env_stack.pop(); } - let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); // 2. Register target address in AbruptCompletionRecord. let new_record = AbruptCompletionRecord::new_continue().with_initial_target(target_address); diff --git a/boa_engine/src/vm/opcode/control_flow/finally.rs b/boa_engine/src/vm/opcode/control_flow/finally.rs index 8e3b9b40e2..a2c59b654a 100644 --- a/boa_engine/src/vm/opcode/control_flow/finally.rs +++ b/boa_engine/src/vm/opcode/control_flow/finally.rs @@ -64,9 +64,8 @@ impl Operation for FinallyEnd { context.vm.frame_mut().env_stack.pop(); } - let env_truncation_len = - context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); } Some(record) if record.is_break() && context.vm.frame().pc < record.target() as usize => @@ -84,9 +83,8 @@ impl Operation for FinallyEnd { context.vm.frame_mut().abrupt_completion = None; - let env_truncation_len = - context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); } Some(record) if record.is_continue() && context.vm.frame().pc > record.target() as usize => @@ -102,9 +100,8 @@ impl Operation for FinallyEnd { } context.vm.frame_mut().abrupt_completion = None; - let env_truncation_len = - context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); } Some(record) if record.is_return() => { return Ok(CompletionType::Return); @@ -121,9 +118,8 @@ impl Operation for FinallyEnd { } } context.vm.frame_mut().abrupt_completion = None; - let env_truncation_len = - context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); } Some(record) if !record.is_throw_with_target() => { let current_stack = context @@ -134,11 +130,11 @@ impl Operation for FinallyEnd { .expect("Popping current finally stack."); let env_truncation_len = context - .realm + .vm .environments .len() .saturating_sub(current_stack.env_num()); - context.realm.environments.truncate(env_truncation_len); + context.vm.environments.truncate(env_truncation_len); let err = JsError::from_opaque(context.vm.pop()); context.vm.err = Some(err); diff --git a/boa_engine/src/vm/opcode/control_flow/labelled.rs b/boa_engine/src/vm/opcode/control_flow/labelled.rs index 228352c4e6..a7a2020848 100644 --- a/boa_engine/src/vm/opcode/control_flow/labelled.rs +++ b/boa_engine/src/vm/opcode/control_flow/labelled.rs @@ -47,8 +47,8 @@ impl Operation for LabelledEnd { } } - let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); Ok(CompletionType::Normal) } diff --git a/boa_engine/src/vm/opcode/control_flow/return.rs b/boa_engine/src/vm/opcode/control_flow/return.rs index 09a42092e4..ade0c3d942 100644 --- a/boa_engine/src/vm/opcode/control_flow/return.rs +++ b/boa_engine/src/vm/opcode/control_flow/return.rs @@ -36,8 +36,8 @@ impl Operation for Return { context.vm.frame_mut().env_stack.pop(); } - let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop); + context.vm.environments.truncate(env_truncation_len); let record = AbruptCompletionRecord::new_return(); context.vm.frame_mut().abrupt_completion = Some(record); diff --git a/boa_engine/src/vm/opcode/control_flow/throw.rs b/boa_engine/src/vm/opcode/control_flow/throw.rs index 05ec410b7c..1d36bf70a9 100644 --- a/boa_engine/src/vm/opcode/control_flow/throw.rs +++ b/boa_engine/src/vm/opcode/control_flow/throw.rs @@ -59,8 +59,8 @@ impl Operation for Throw { context.vm.frame_mut().env_stack.pop(); } - let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop); + context.vm.environments.truncate(env_truncation_len); if target_address == catch_target { context.vm.frame_mut().pc = catch_target as usize; @@ -110,8 +110,8 @@ impl Operation for Throw { context.vm.frame_mut().env_stack.pop(); } - let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop); + context.vm.environments.truncate(env_truncation_len); let previous_stack_size = context .vm diff --git a/boa_engine/src/vm/opcode/control_flow/try.rs b/boa_engine/src/vm/opcode/control_flow/try.rs index 5211c956b9..8e74378f67 100644 --- a/boa_engine/src/vm/opcode/control_flow/try.rs +++ b/boa_engine/src/vm/opcode/control_flow/try.rs @@ -58,8 +58,8 @@ impl Operation for TryEnd { } } - let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); Ok(CompletionType::Normal) } diff --git a/boa_engine/src/vm/opcode/define/mod.rs b/boa_engine/src/vm/opcode/define/mod.rs index 5f0bd36719..42aaa3bc68 100644 --- a/boa_engine/src/vm/opcode/define/mod.rs +++ b/boa_engine/src/vm/opcode/define/mod.rs @@ -28,7 +28,7 @@ impl Operation for DefVar { if binding_locator.is_global() { // already initialized at compile time } else { - context.realm.environments.put_value_if_uninitialized( + context.vm.environments.put_value_if_uninitialized( binding_locator.environment_index(), binding_locator.binding_index(), JsValue::Undefined, @@ -72,7 +72,7 @@ impl Operation for DefInitVar { )?; } } else { - context.realm.environments.put_value( + context.vm.environments.put_value( binding_locator.environment_index(), binding_locator.binding_index(), value, @@ -96,7 +96,7 @@ impl Operation for DefLet { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); let binding_locator = context.vm.frame().code_block.bindings[index as usize]; - context.realm.environments.put_value( + context.vm.environments.put_value( binding_locator.environment_index(), binding_locator.binding_index(), JsValue::Undefined, @@ -105,7 +105,7 @@ impl Operation for DefLet { } } -macro_rules! implement_declaritives { +macro_rules! implement_declaratives { ($name:ident, $doc_string:literal) => { #[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] #[doc= "\n"] @@ -122,7 +122,7 @@ macro_rules! implement_declaritives { let index = context.vm.read::(); let value = context.vm.pop(); let binding_locator = context.vm.frame().code_block.bindings[index as usize]; - context.realm.environments.put_value( + context.vm.environments.put_value( binding_locator.environment_index(), binding_locator.binding_index(), value, @@ -133,6 +133,6 @@ macro_rules! implement_declaritives { }; } -implement_declaritives!(DefInitLet, "Declare and initialize `let` type variable"); -implement_declaritives!(DefInitConst, "Declare and initialize `const` type variable"); -implement_declaritives!(DefInitArg, "Declare and initialize function arguments"); +implement_declaratives!(DefInitLet, "Declare and initialize `let` type variable"); +implement_declaratives!(DefInitConst, "Declare and initialize `const` type variable"); +implement_declaratives!(DefInitArg, "Declare and initialize function arguments"); diff --git a/boa_engine/src/vm/opcode/delete/mod.rs b/boa_engine/src/vm/opcode/delete/mod.rs index 72820ae353..cb0cc539d5 100644 --- a/boa_engine/src/vm/opcode/delete/mod.rs +++ b/boa_engine/src/vm/opcode/delete/mod.rs @@ -81,7 +81,7 @@ impl Operation for DeleteName { let deleted = if binding_locator.is_global() && !context - .realm + .vm .environments .binding_in_poisoned_environment(binding_locator.name()) { @@ -111,7 +111,7 @@ impl Operation for DeleteName { deleted } else { context - .realm + .vm .environments .get_value_optional( binding_locator.environment_index(), diff --git a/boa_engine/src/vm/opcode/environment/mod.rs b/boa_engine/src/vm/opcode/environment/mod.rs index e01aed987b..8cfffe8a48 100644 --- a/boa_engine/src/vm/opcode/environment/mod.rs +++ b/boa_engine/src/vm/opcode/environment/mod.rs @@ -17,7 +17,7 @@ impl Operation for This { const INSTRUCTION: &'static str = "INST - This"; fn execute(context: &mut Context<'_>) -> JsResult { - let env = context.realm.environments.get_this_environment(); + let env = context.vm.environments.get_this_environment(); match env { EnvironmentSlots::Function(env) => { let binding_result = match env.borrow().get_this_binding() { @@ -28,7 +28,7 @@ impl Operation for This { context.vm.push(function_binding); } EnvironmentSlots::Global => { - let this = context.realm.global_this(); + let this = context.realm().global_this(); context.vm.push(this.clone()); } } @@ -50,7 +50,7 @@ impl Operation for Super { fn execute(context: &mut Context<'_>) -> JsResult { let home_result = { let env = context - .realm + .vm .environments .get_this_environment() .as_function_slots() @@ -97,7 +97,7 @@ impl Operation for SuperCallPrepare { fn execute(context: &mut Context<'_>) -> JsResult { let this_env = context - .realm + .vm .environments .get_this_environment() .as_function_slots() @@ -159,7 +159,7 @@ impl Operation for SuperCall { let result = super_constructor.__construct__(&arguments, new_target, context)?; let this_env = context - .realm + .vm .environments .get_this_environment() .as_function_slots() @@ -220,7 +220,7 @@ impl Operation for SuperCallSpread { let result = super_constructor.__construct__(&arguments, new_target, context)?; let this_env = context - .realm + .vm .environments .get_this_environment() .as_function_slots() @@ -262,7 +262,7 @@ impl Operation for SuperCallDerived { let (new_target, active_function) = { let this_env = context - .realm + .vm .environments .get_this_environment() .as_function_slots() @@ -289,7 +289,7 @@ impl Operation for SuperCallDerived { let result = super_constructor.__construct__(&arguments, &new_target, context)?; let this_env = context - .realm + .vm .environments .get_this_environment() .as_function_slots() diff --git a/boa_engine/src/vm/opcode/generator/mod.rs b/boa_engine/src/vm/opcode/generator/mod.rs index ec3d664b1f..a5fb7d41e7 100644 --- a/boa_engine/src/vm/opcode/generator/mod.rs +++ b/boa_engine/src/vm/opcode/generator/mod.rs @@ -92,7 +92,7 @@ impl Operation for AsyncGeneratorNext { .queue .pop_front() .expect("must have item in queue"); - AsyncGenerator::complete_step(&next, completion, false, context); + AsyncGenerator::complete_step(&next, completion, false, None, context); let mut generator_object_mut = generator_object.borrow_mut(); let gen = generator_object_mut diff --git a/boa_engine/src/vm/opcode/iteration/iterator.rs b/boa_engine/src/vm/opcode/iteration/iterator.rs index 9e6cf08e16..c3ce8a7f91 100644 --- a/boa_engine/src/vm/opcode/iteration/iterator.rs +++ b/boa_engine/src/vm/opcode/iteration/iterator.rs @@ -106,7 +106,7 @@ impl Operation for IteratorUnwrapNextOrJump { if next_result.complete(context)? { context.vm.frame_mut().pc = address as usize; context.vm.frame_mut().dec_frame_env_stack(); - context.realm.environments.pop(); + context.vm.environments.pop(); context.vm.push(true); } else { context.vm.push(false); diff --git a/boa_engine/src/vm/opcode/iteration/loop_ops.rs b/boa_engine/src/vm/opcode/iteration/loop_ops.rs index c159e7b861..3e14dca770 100644 --- a/boa_engine/src/vm/opcode/iteration/loop_ops.rs +++ b/boa_engine/src/vm/opcode/iteration/loop_ops.rs @@ -51,11 +51,11 @@ impl Operation for LoopContinue { .filter(|entry| entry.exit_address() == exit) { let env_truncation_len = context - .realm + .vm .environments .len() .saturating_sub(entry.env_num()); - context.realm.environments.truncate(env_truncation_len); + context.vm.environments.truncate(env_truncation_len); context.vm.frame_mut().env_stack.pop(); } @@ -92,8 +92,8 @@ impl Operation for LoopEnd { } } - let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); - context.realm.environments.truncate(env_truncation_len); + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); Ok(CompletionType::Normal) } diff --git a/boa_engine/src/vm/opcode/pop/mod.rs b/boa_engine/src/vm/opcode/pop/mod.rs index db1751ba55..e1fb43327c 100644 --- a/boa_engine/src/vm/opcode/pop/mod.rs +++ b/boa_engine/src/vm/opcode/pop/mod.rs @@ -55,7 +55,7 @@ impl Operation for PopEnvironment { const INSTRUCTION: &'static str = "INST - PopEnvironment"; fn execute(context: &mut Context<'_>) -> JsResult { - context.realm.environments.pop(); + context.vm.environments.pop(); context.vm.frame_mut().dec_frame_env_stack(); Ok(CompletionType::Normal) } diff --git a/boa_engine/src/vm/opcode/push/environment.rs b/boa_engine/src/vm/opcode/push/environment.rs index 47a4f4c53e..0313c01390 100644 --- a/boa_engine/src/vm/opcode/push/environment.rs +++ b/boa_engine/src/vm/opcode/push/environment.rs @@ -21,7 +21,7 @@ impl Operation for PushDeclarativeEnvironment { [compile_environments_index as usize] .clone(); context - .realm + .vm .environments .push_declarative(num_bindings as usize, compile_environment); context.vm.frame_mut().inc_frame_env_stack(); @@ -47,7 +47,7 @@ impl Operation for PushFunctionEnvironment { [compile_environments_index as usize] .clone(); context - .realm + .vm .environments .push_function_inherit(num_bindings as usize, compile_environment); Ok(CompletionType::Normal) @@ -69,7 +69,7 @@ impl Operation for PushObjectEnvironment { let object = context.vm.pop(); let object = object.to_object(context)?; - context.realm.environments.push_object(object); + context.vm.environments.push_object(object); context.vm.frame_mut().inc_frame_env_stack(); Ok(CompletionType::Normal) } diff --git a/boa_engine/src/vm/opcode/push/new_target.rs b/boa_engine/src/vm/opcode/push/new_target.rs index d36e001306..540b3e6554 100644 --- a/boa_engine/src/vm/opcode/push/new_target.rs +++ b/boa_engine/src/vm/opcode/push/new_target.rs @@ -15,20 +15,18 @@ impl Operation for PushNewTarget { const INSTRUCTION: &'static str = "INST - PushNewTarget"; fn execute(context: &mut Context<'_>) -> JsResult { - if let Some(env) = context - .realm + let new_target = if let Some(new_target) = context + .vm .environments .get_this_environment() .as_function_slots() + .and_then(|env| env.borrow().new_target().cloned()) { - if let Some(new_target) = env.borrow().new_target() { - context.vm.push(new_target.clone()); - } else { - context.vm.push(JsValue::undefined()); - } + new_target.into() } else { - context.vm.push(JsValue::undefined()); - } + JsValue::undefined() + }; + context.vm.push(new_target); Ok(CompletionType::Normal) } } diff --git a/boa_examples/src/bin/derive.rs b/boa_examples/src/bin/derive.rs index 298027dad9..fa4c101c0c 100644 --- a/boa_examples/src/bin/derive.rs +++ b/boa_examples/src/bin/derive.rs @@ -21,7 +21,6 @@ fn main() { hello: "World", my_float: 2.9, }; - x; "#; let js = Source::from_bytes(js_str);