From 1e75fd0d215cc61b196b3b0c96867004739ddf89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Mon, 10 Apr 2023 14:31:54 +0000 Subject: [PATCH] Make `Realm` shareable between functions (#2801) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This Pull Request fixes #2317 and #1835, finally giving our engine proper realms 🥳. It changes the following: - Extracts the compile environment stack from `Realm` and into `Vm`. - Adjusts the bytecompiler to accommodate this change. - Adjusts `call/construct_internal` to accommodate this change. This also coincidentally fixed #2317, which I'm pretty happy about. - Adjusts several APIs (`NativeJob`, `Realm`) and builtins (`eval`, initializers) to accommodate this change. - Adjusts `JsNativeError`s to hold a reference to the Realm from which they were created. This only affects errors created within calls to function objects. Native calls don't need to set the realm because it's inherited by the next outer active function object. TLDR: `JsError` API stays the same, we just set the origin Realm of errors in `JsObject::call/construct_internal`. --- .../src/builtins/array/array_iterator.rs | 13 +- boa_engine/src/builtins/array/mod.rs | 27 +- boa_engine/src/builtins/array_buffer/mod.rs | 9 +- boa_engine/src/builtins/async_function/mod.rs | 11 +- .../src/builtins/async_generator/mod.rs | 64 +- .../builtins/async_generator_function/mod.rs | 11 +- boa_engine/src/builtins/bigint/mod.rs | 5 +- boa_engine/src/builtins/boolean/mod.rs | 5 +- boa_engine/src/builtins/dataview/mod.rs | 11 +- boa_engine/src/builtins/date/mod.rs | 5 +- boa_engine/src/builtins/error/aggregate.rs | 9 +- boa_engine/src/builtins/error/eval.rs | 9 +- boa_engine/src/builtins/error/mod.rs | 5 +- boa_engine/src/builtins/error/range.rs | 9 +- boa_engine/src/builtins/error/reference.rs | 9 +- boa_engine/src/builtins/error/syntax.rs | 9 +- boa_engine/src/builtins/error/type.rs | 17 +- boa_engine/src/builtins/error/uri.rs | 9 +- boa_engine/src/builtins/escape/mod.rs | 11 +- boa_engine/src/builtins/eval/mod.rs | 60 +- boa_engine/src/builtins/function/mod.rs | 62 +- boa_engine/src/builtins/generator/mod.rs | 49 +- .../src/builtins/generator_function/mod.rs | 11 +- boa_engine/src/builtins/intl/collator/mod.rs | 7 +- .../src/builtins/intl/date_time_format.rs | 5 +- .../src/builtins/intl/list_format/mod.rs | 5 +- boa_engine/src/builtins/intl/locale/mod.rs | 26 +- boa_engine/src/builtins/intl/mod.rs | 23 +- boa_engine/src/builtins/intl/segmenter/mod.rs | 5 +- .../iterable/async_from_sync_iterator.rs | 13 +- boa_engine/src/builtins/iterable/mod.rs | 9 +- boa_engine/src/builtins/json/mod.rs | 21 +- boa_engine/src/builtins/map/map_iterator.rs | 13 +- boa_engine/src/builtins/map/mod.rs | 11 +- boa_engine/src/builtins/math/mod.rs | 7 +- boa_engine/src/builtins/mod.rs | 221 ++-- boa_engine/src/builtins/number/globals.rs | 17 +- boa_engine/src/builtins/number/mod.rs | 9 +- .../src/builtins/object/for_in_iterator.rs | 13 +- boa_engine/src/builtins/object/mod.rs | 9 +- boa_engine/src/builtins/promise/mod.rs | 66 +- boa_engine/src/builtins/proxy/mod.rs | 5 +- boa_engine/src/builtins/reflect/mod.rs | 5 +- boa_engine/src/builtins/regexp/mod.rs | 25 +- .../builtins/regexp/regexp_string_iterator.rs | 13 +- boa_engine/src/builtins/set/mod.rs | 11 +- boa_engine/src/builtins/set/set_iterator.rs | 13 +- boa_engine/src/builtins/string/mod.rs | 5 +- .../src/builtins/string/string_iterator.rs | 13 +- boa_engine/src/builtins/symbol/mod.rs | 9 +- boa_engine/src/builtins/typed_array/mod.rs | 37 +- boa_engine/src/builtins/uri/mod.rs | 17 +- boa_engine/src/builtins/weak/weak_ref.rs | 5 +- boa_engine/src/builtins/weak_map/mod.rs | 5 +- boa_engine/src/builtins/weak_set/mod.rs | 5 +- boa_engine/src/bytecompiler/class.rs | 170 ++-- boa_engine/src/bytecompiler/env.rs | 150 +++ .../src/bytecompiler/expression/assign.rs | 4 +- .../src/bytecompiler/expression/unary.rs | 2 +- .../src/bytecompiler/expression/update.rs | 4 +- boa_engine/src/bytecompiler/function.rs | 84 +- boa_engine/src/bytecompiler/mod.rs | 113 ++- .../src/bytecompiler/statement/block.rs | 9 +- boa_engine/src/bytecompiler/statement/loop.rs | 86 +- .../src/bytecompiler/statement/switch.rs | 9 +- boa_engine/src/bytecompiler/statement/try.rs | 31 +- boa_engine/src/bytecompiler/statement/with.rs | 4 +- boa_engine/src/context/hooks.rs | 14 +- boa_engine/src/context/intrinsics.rs | 36 +- boa_engine/src/context/mod.rs | 83 +- boa_engine/src/environments/compile.rs | 182 +--- boa_engine/src/environments/runtime.rs | 109 +- boa_engine/src/error.rs | 52 +- boa_engine/src/job.rs | 37 +- boa_engine/src/object/internal_methods/mod.rs | 8 +- boa_engine/src/object/mod.rs | 4 +- boa_engine/src/object/operations.rs | 9 +- boa_engine/src/realm.rs | 74 +- boa_engine/src/tests/env.rs | 32 + boa_engine/src/tests/mod.rs | 1 + boa_engine/src/vm/code_block.rs | 942 +++++++----------- boa_engine/src/vm/mod.rs | 16 +- boa_engine/src/vm/opcode/await_stm/mod.rs | 32 +- .../src/vm/opcode/control_flow/break.rs | 4 +- .../src/vm/opcode/control_flow/catch.rs | 8 +- .../src/vm/opcode/control_flow/continue.rs | 4 +- .../src/vm/opcode/control_flow/finally.rs | 24 +- .../src/vm/opcode/control_flow/labelled.rs | 4 +- .../src/vm/opcode/control_flow/return.rs | 4 +- .../src/vm/opcode/control_flow/throw.rs | 8 +- boa_engine/src/vm/opcode/control_flow/try.rs | 4 +- boa_engine/src/vm/opcode/define/mod.rs | 16 +- boa_engine/src/vm/opcode/delete/mod.rs | 4 +- boa_engine/src/vm/opcode/environment/mod.rs | 16 +- boa_engine/src/vm/opcode/generator/mod.rs | 2 +- .../src/vm/opcode/iteration/iterator.rs | 2 +- .../src/vm/opcode/iteration/loop_ops.rs | 8 +- boa_engine/src/vm/opcode/pop/mod.rs | 2 +- boa_engine/src/vm/opcode/push/environment.rs | 6 +- boa_engine/src/vm/opcode/push/new_target.rs | 16 +- boa_examples/src/bin/derive.rs | 1 - 101 files changed, 1768 insertions(+), 1740 deletions(-) create mode 100644 boa_engine/src/bytecompiler/env.rs create mode 100644 boa_engine/src/tests/env.rs 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);