Browse Source

Make `Realm` shareable between functions (#2801)

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`.
pull/2797/head
José Julián Espina 1 year ago
parent
commit
1e75fd0d21
  1. 13
      boa_engine/src/builtins/array/array_iterator.rs
  2. 27
      boa_engine/src/builtins/array/mod.rs
  3. 9
      boa_engine/src/builtins/array_buffer/mod.rs
  4. 11
      boa_engine/src/builtins/async_function/mod.rs
  5. 64
      boa_engine/src/builtins/async_generator/mod.rs
  6. 11
      boa_engine/src/builtins/async_generator_function/mod.rs
  7. 5
      boa_engine/src/builtins/bigint/mod.rs
  8. 5
      boa_engine/src/builtins/boolean/mod.rs
  9. 11
      boa_engine/src/builtins/dataview/mod.rs
  10. 5
      boa_engine/src/builtins/date/mod.rs
  11. 9
      boa_engine/src/builtins/error/aggregate.rs
  12. 9
      boa_engine/src/builtins/error/eval.rs
  13. 5
      boa_engine/src/builtins/error/mod.rs
  14. 9
      boa_engine/src/builtins/error/range.rs
  15. 9
      boa_engine/src/builtins/error/reference.rs
  16. 9
      boa_engine/src/builtins/error/syntax.rs
  17. 17
      boa_engine/src/builtins/error/type.rs
  18. 9
      boa_engine/src/builtins/error/uri.rs
  19. 11
      boa_engine/src/builtins/escape/mod.rs
  20. 60
      boa_engine/src/builtins/eval/mod.rs
  21. 62
      boa_engine/src/builtins/function/mod.rs
  22. 49
      boa_engine/src/builtins/generator/mod.rs
  23. 11
      boa_engine/src/builtins/generator_function/mod.rs
  24. 7
      boa_engine/src/builtins/intl/collator/mod.rs
  25. 5
      boa_engine/src/builtins/intl/date_time_format.rs
  26. 5
      boa_engine/src/builtins/intl/list_format/mod.rs
  27. 26
      boa_engine/src/builtins/intl/locale/mod.rs
  28. 23
      boa_engine/src/builtins/intl/mod.rs
  29. 5
      boa_engine/src/builtins/intl/segmenter/mod.rs
  30. 13
      boa_engine/src/builtins/iterable/async_from_sync_iterator.rs
  31. 9
      boa_engine/src/builtins/iterable/mod.rs
  32. 21
      boa_engine/src/builtins/json/mod.rs
  33. 13
      boa_engine/src/builtins/map/map_iterator.rs
  34. 11
      boa_engine/src/builtins/map/mod.rs
  35. 7
      boa_engine/src/builtins/math/mod.rs
  36. 221
      boa_engine/src/builtins/mod.rs
  37. 17
      boa_engine/src/builtins/number/globals.rs
  38. 9
      boa_engine/src/builtins/number/mod.rs
  39. 13
      boa_engine/src/builtins/object/for_in_iterator.rs
  40. 9
      boa_engine/src/builtins/object/mod.rs
  41. 66
      boa_engine/src/builtins/promise/mod.rs
  42. 5
      boa_engine/src/builtins/proxy/mod.rs
  43. 5
      boa_engine/src/builtins/reflect/mod.rs
  44. 25
      boa_engine/src/builtins/regexp/mod.rs
  45. 13
      boa_engine/src/builtins/regexp/regexp_string_iterator.rs
  46. 11
      boa_engine/src/builtins/set/mod.rs
  47. 13
      boa_engine/src/builtins/set/set_iterator.rs
  48. 5
      boa_engine/src/builtins/string/mod.rs
  49. 13
      boa_engine/src/builtins/string/string_iterator.rs
  50. 9
      boa_engine/src/builtins/symbol/mod.rs
  51. 37
      boa_engine/src/builtins/typed_array/mod.rs
  52. 17
      boa_engine/src/builtins/uri/mod.rs
  53. 5
      boa_engine/src/builtins/weak/weak_ref.rs
  54. 5
      boa_engine/src/builtins/weak_map/mod.rs
  55. 5
      boa_engine/src/builtins/weak_set/mod.rs
  56. 170
      boa_engine/src/bytecompiler/class.rs
  57. 150
      boa_engine/src/bytecompiler/env.rs
  58. 4
      boa_engine/src/bytecompiler/expression/assign.rs
  59. 2
      boa_engine/src/bytecompiler/expression/unary.rs
  60. 4
      boa_engine/src/bytecompiler/expression/update.rs
  61. 84
      boa_engine/src/bytecompiler/function.rs
  62. 113
      boa_engine/src/bytecompiler/mod.rs
  63. 9
      boa_engine/src/bytecompiler/statement/block.rs
  64. 86
      boa_engine/src/bytecompiler/statement/loop.rs
  65. 9
      boa_engine/src/bytecompiler/statement/switch.rs
  66. 31
      boa_engine/src/bytecompiler/statement/try.rs
  67. 4
      boa_engine/src/bytecompiler/statement/with.rs
  68. 14
      boa_engine/src/context/hooks.rs
  69. 36
      boa_engine/src/context/intrinsics.rs
  70. 83
      boa_engine/src/context/mod.rs
  71. 182
      boa_engine/src/environments/compile.rs
  72. 109
      boa_engine/src/environments/runtime.rs
  73. 52
      boa_engine/src/error.rs
  74. 37
      boa_engine/src/job.rs
  75. 8
      boa_engine/src/object/internal_methods/mod.rs
  76. 4
      boa_engine/src/object/mod.rs
  77. 9
      boa_engine/src/object/operations.rs
  78. 74
      boa_engine/src/realm.rs
  79. 32
      boa_engine/src/tests/env.rs
  80. 1
      boa_engine/src/tests/mod.rs
  81. 942
      boa_engine/src/vm/code_block.rs
  82. 16
      boa_engine/src/vm/mod.rs
  83. 32
      boa_engine/src/vm/opcode/await_stm/mod.rs
  84. 4
      boa_engine/src/vm/opcode/control_flow/break.rs
  85. 8
      boa_engine/src/vm/opcode/control_flow/catch.rs
  86. 4
      boa_engine/src/vm/opcode/control_flow/continue.rs
  87. 24
      boa_engine/src/vm/opcode/control_flow/finally.rs
  88. 4
      boa_engine/src/vm/opcode/control_flow/labelled.rs
  89. 4
      boa_engine/src/vm/opcode/control_flow/return.rs
  90. 8
      boa_engine/src/vm/opcode/control_flow/throw.rs
  91. 4
      boa_engine/src/vm/opcode/control_flow/try.rs
  92. 16
      boa_engine/src/vm/opcode/define/mod.rs
  93. 4
      boa_engine/src/vm/opcode/delete/mod.rs
  94. 16
      boa_engine/src/vm/opcode/environment/mod.rs
  95. 2
      boa_engine/src/vm/opcode/generator/mod.rs
  96. 2
      boa_engine/src/vm/opcode/iteration/iterator.rs
  97. 8
      boa_engine/src/vm/opcode/iteration/loop_ops.rs
  98. 2
      boa_engine/src/vm/opcode/pop/mod.rs
  99. 6
      boa_engine/src/vm/opcode/push/environment.rs
  100. 16
      boa_engine/src/vm/opcode/push/new_target.rs
  101. Some files were not shown because too many files have changed in this diff Show More

13
boa_engine/src/builtins/array/array_iterator.rs

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, Context, JsResult,
}; };
@ -35,11 +36,17 @@ pub struct ArrayIterator {
} }
impl IntrinsicObject for ArrayIterator { impl IntrinsicObject for ArrayIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("ArrayIterator", "init"); let _timer = Profiler::global().start_event("ArrayIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0) .static_method(Self::next, "next", 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

27
boa_engine/src/builtins/array/mod.rs

@ -22,6 +22,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR},
property::{Attribute, PropertyDescriptor, PropertyNameKind}, property::{Attribute, PropertyDescriptor, PropertyNameKind},
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
@ -40,26 +41,28 @@ mod tests;
pub(crate) struct Array; pub(crate) struct Array;
impl IntrinsicObject for Array { impl IntrinsicObject for Array {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let symbol_iterator = JsSymbol::iterator(); let symbol_iterator = JsSymbol::iterator();
let symbol_unscopables = JsSymbol::unscopables(); let symbol_unscopables = JsSymbol::unscopables();
let get_species = BuiltInBuilder::new(intrinsics) let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
let values_function = let values_function = BuiltInBuilder::with_object(
BuiltInBuilder::with_object(intrinsics, intrinsics.objects().array_prototype_values()) realm,
.callable(Self::values) realm.intrinsics().objects().array_prototype_values(),
.name("values") )
.build(); .callable(Self::values)
.name("values")
.build();
let unscopables_object = Self::unscopables_object(); let unscopables_object = Self::unscopables_object();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),
@ -369,12 +372,14 @@ impl Array {
// 4. If IsConstructor(C) is true, then // 4. If IsConstructor(C) is true, then
if let Some(c) = c.as_constructor() { if let Some(c) = c.as_constructor() {
// a. Let thisRealm be the current Realm Record. // 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). // 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 // 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. // i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
// Note: fast path to step 6. // Note: fast path to step 6.
return Self::array_create(length, None, context); return Self::array_create(length, None, context);

9
boa_engine/src/builtins/array_buffer/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, Numeric}, value::{IntegerOrInfinity, Numeric},
@ -47,22 +48,22 @@ impl ArrayBuffer {
} }
impl IntrinsicObject for ArrayBuffer { impl IntrinsicObject for ArrayBuffer {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;
let get_species = BuiltInBuilder::new(intrinsics) let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
let get_byte_length = BuiltInBuilder::new(intrinsics) let get_byte_length = BuiltInBuilder::new(realm)
.callable(Self::get_byte_length) .callable(Self::get_byte_length)
.name("get byteLength") .name("get byteLength")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.accessor( .accessor(
utf16!("byteLength"), utf16!("byteLength"),
Some(get_byte_length), Some(get_byte_length),

11
boa_engine/src/builtins/async_function/mod.rs

@ -11,6 +11,7 @@ use crate::{
builtins::{function::BuiltInFunctionObject, BuiltInObject}, builtins::{function::BuiltInFunctionObject, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -23,12 +24,14 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
pub struct AsyncFunction; pub struct AsyncFunction;
impl IntrinsicObject for AsyncFunction { impl IntrinsicObject for AsyncFunction {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().function().constructor()) .prototype(realm.intrinsics().constructors().function().constructor())
.inherits(Some(intrinsics.constructors().function().prototype())) .inherits(Some(
realm.intrinsics().constructors().function().prototype(),
))
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,

64
boa_engine/src/builtins/async_generator/mod.rs

@ -15,6 +15,7 @@ use crate::{
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR}, object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR},
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
vm::GeneratorResumeKind, vm::GeneratorResumeKind,
@ -66,11 +67,17 @@ pub struct AsyncGenerator {
} }
impl IntrinsicObject for AsyncGenerator { impl IntrinsicObject for AsyncGenerator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().async_iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.async_iterator(),
)
.static_method(Self::next, "next", 1) .static_method(Self::next, "next", 1)
.static_method(Self::r#return, "return", 1) .static_method(Self::r#return, "return", 1)
.static_method(Self::throw, "throw", 1) .static_method(Self::throw, "throw", 1)
@ -81,7 +88,8 @@ impl IntrinsicObject for AsyncGenerator {
) )
.static_property( .static_property(
CONSTRUCTOR, CONSTRUCTOR,
intrinsics realm
.intrinsics()
.constructors() .constructors()
.async_generator_function() .async_generator_function()
.prototype(), .prototype(),
@ -399,6 +407,7 @@ impl AsyncGenerator {
next: &AsyncGeneratorRequest, next: &AsyncGeneratorRequest,
completion: JsResult<JsValue>, completion: JsResult<JsValue>,
done: bool, done: bool,
realm: Option<Realm>,
context: &mut Context<'_>, context: &mut Context<'_>,
) { ) {
// 1. Let queue be generator.[[AsyncGeneratorQueue]]. // 1. Let queue be generator.[[AsyncGeneratorQueue]].
@ -422,15 +431,24 @@ impl AsyncGenerator {
Ok(value) => { Ok(value) => {
// a. Assert: completion.[[Type]] is normal. // a. Assert: completion.[[Type]] is normal.
// TODO: Realm handling not implemented yet.
// b. If realm is present, then // b. If realm is present, then
// i. Let oldRealm be the running execution context's Realm. let iterator_result = if let Some(realm) = realm {
// ii. Set the running execution context's Realm to realm. // i. Let oldRealm be the running execution context's Realm.
// iii. Let iteratorResult be CreateIterResultObject(value, done). // ii. Set the running execution context's Realm to realm.
// iv. Set the running execution context's Realm to oldRealm. let old_realm = context.enter_realm(realm);
// c. Else,
// i. Let iteratorResult be CreateIterResultObject(value, done). // iii. Let iteratorResult be CreateIterResultObject(value, done).
let iterator_result = create_iter_result_object(value, done, context); 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 »). // d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
promise_capability promise_capability
@ -474,7 +492,7 @@ impl AsyncGenerator {
// 6. Push genContext onto the execution context stack; genContext is now the running execution context. // 6. Push genContext onto the execution context stack; genContext is now the running execution context.
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -482,10 +500,7 @@ impl AsyncGenerator {
&mut context.vm.active_function, &mut context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( let old_realm = context.enter_realm(generator_context.realm.clone());
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.vm.push_frame(generator_context.call_frame.clone()); context.vm.push_frame(generator_context.call_frame.clone());
@ -508,7 +523,7 @@ impl AsyncGenerator {
let result = context.run(); let result = context.run();
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -517,10 +532,7 @@ impl AsyncGenerator {
&mut context.vm.active_function, &mut context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( context.enter_realm(old_realm);
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
generator generator
.borrow_mut() .borrow_mut()
@ -575,7 +587,7 @@ impl AsyncGenerator {
gen.context = None; gen.context = None;
let next = gen.queue.pop_front().expect("queue must not be empty"); let next = gen.queue.pop_front().expect("queue must not be empty");
drop(generator_borrow_mut); 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); Self::drain_queue(&generator, context);
return; return;
} }
@ -604,7 +616,7 @@ impl AsyncGenerator {
let result = Ok(args.get_or_undefined(0).clone()); let result = Ok(args.get_or_undefined(0).clone());
// c. Perform AsyncGeneratorCompleteStep(generator, result, true). // 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). // d. Perform AsyncGeneratorDrainQueue(generator).
Self::drain_queue(generator, context); Self::drain_queue(generator, context);
@ -640,7 +652,7 @@ impl AsyncGenerator {
// c. Perform AsyncGeneratorCompleteStep(generator, result, true). // c. Perform AsyncGeneratorCompleteStep(generator, result, true).
let next = gen.queue.pop_front().expect("must have one entry"); let next = gen.queue.pop_front().expect("must have one entry");
drop(generator_borrow_mut); drop(generator_borrow_mut);
Self::complete_step(&next, result, true, context); Self::complete_step(&next, result, true, None, context);
// d. Perform AsyncGeneratorDrainQueue(generator). // d. Perform AsyncGeneratorDrainQueue(generator).
Self::drain_queue(generator, context); Self::drain_queue(generator, context);
@ -721,7 +733,7 @@ impl AsyncGenerator {
// ii. Perform AsyncGeneratorCompleteStep(generator, completion, true). // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true).
let next = queue.pop_front().expect("must have entry"); 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. // iii. If queue is empty, set done to true.
if queue.is_empty() { if queue.is_empty() {

11
boa_engine/src/builtins/async_generator_function/mod.rs

@ -10,6 +10,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{JsObject, PROTOTYPE}, object::{JsObject, PROTOTYPE},
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsResult, Context, JsResult,
@ -23,15 +24,17 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
pub struct AsyncGeneratorFunction; pub struct AsyncGeneratorFunction;
impl IntrinsicObject for AsyncGeneratorFunction { impl IntrinsicObject for AsyncGeneratorFunction {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.inherits(Some(intrinsics.constructors().function().prototype())) .inherits(Some(
realm.intrinsics().constructors().function().prototype(),
))
.constructor_attributes(Attribute::CONFIGURABLE) .constructor_attributes(Attribute::CONFIGURABLE)
.property( .property(
PROTOTYPE, PROTOTYPE,
intrinsics.objects().async_generator(), realm.intrinsics().objects().async_generator(),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(

5
boa_engine/src/builtins/bigint/mod.rs

@ -18,6 +18,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, PreferredType}, value::{IntegerOrInfinity, PreferredType},
Context, JsArgs, JsBigInt, JsResult, JsValue, Context, JsArgs, JsBigInt, JsResult, JsValue,
@ -35,10 +36,10 @@ mod tests;
pub struct BigInt; pub struct BigInt;
impl IntrinsicObject for BigInt { impl IntrinsicObject for BigInt {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, "valueOf", 0)
.static_method(Self::as_int_n, "asIntN", 2) .static_method(Self::as_int_n, "asIntN", 2)

5
boa_engine/src/builtins/boolean/mod.rs

@ -17,6 +17,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -28,10 +29,10 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
pub(crate) struct Boolean; pub(crate) struct Boolean;
impl IntrinsicObject for Boolean { impl IntrinsicObject for Boolean {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, "valueOf", 0)
.build(); .build();

11
boa_engine/src/builtins/dataview/mod.rs

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
@ -31,25 +32,25 @@ pub struct DataView {
} }
impl IntrinsicObject for DataView { impl IntrinsicObject for DataView {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;
let get_buffer = BuiltInBuilder::new(intrinsics) let get_buffer = BuiltInBuilder::new(realm)
.callable(Self::get_buffer) .callable(Self::get_buffer)
.name("get buffer") .name("get buffer")
.build(); .build();
let get_byte_length = BuiltInBuilder::new(intrinsics) let get_byte_length = BuiltInBuilder::new(realm)
.callable(Self::get_byte_length) .callable(Self::get_byte_length)
.name("get byteLength") .name("get byteLength")
.build(); .build();
let get_byte_offset = BuiltInBuilder::new(intrinsics) let get_byte_offset = BuiltInBuilder::new(realm)
.callable(Self::get_byte_offset) .callable(Self::get_byte_offset)
.name("get byteOffset") .name("get byteOffset")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes) .accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes)
.accessor( .accessor(
utf16!("byteLength"), utf16!("byteLength"),

5
boa_engine/src/builtins/date/mod.rs

@ -19,6 +19,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrNan, JsValue, PreferredType}, value::{IntegerOrNan, JsValue, PreferredType},
@ -100,10 +101,10 @@ impl Default for Date {
} }
impl IntrinsicObject for Date { impl IntrinsicObject for Date {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::get_date::<true>, "getDate", 0) .method(Self::get_date::<true>, "getDate", 0)
.method(Self::get_day::<true>, "getDay", 0) .method(Self::get_day::<true>, "getDay", 0)
.method(Self::get_full_year::<true>, "getFullYear", 0) .method(Self::get_full_year::<true>, "getFullYear", 0)

9
boa_engine/src/builtins/error/aggregate.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptorBuilder}, property::{Attribute, PropertyDescriptorBuilder},
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -26,13 +27,13 @@ use super::{Error, ErrorKind};
pub(crate) struct AggregateError; pub(crate) struct AggregateError;
impl IntrinsicObject for AggregateError { impl IntrinsicObject for AggregateError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();

9
boa_engine/src/builtins/error/eval.rs

@ -16,6 +16,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -28,13 +29,13 @@ use super::{Error, ErrorKind};
pub(crate) struct EvalError; pub(crate) struct EvalError;
impl IntrinsicObject for EvalError { impl IntrinsicObject for EvalError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();

5
boa_engine/src/builtins/error/mod.rs

@ -17,6 +17,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -127,11 +128,11 @@ pub enum ErrorKind {
pub(crate) struct Error; pub(crate) struct Error;
impl IntrinsicObject for Error { impl IntrinsicObject for Error {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)

9
boa_engine/src/builtins/error/range.rs

@ -14,6 +14,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -26,13 +27,13 @@ use super::{Error, ErrorKind};
pub(crate) struct RangeError; pub(crate) struct RangeError;
impl IntrinsicObject for RangeError { impl IntrinsicObject for RangeError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();

9
boa_engine/src/builtins/error/reference.rs

@ -14,6 +14,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -25,13 +26,13 @@ use super::{Error, ErrorKind};
pub(crate) struct ReferenceError; pub(crate) struct ReferenceError;
impl IntrinsicObject for ReferenceError { impl IntrinsicObject for ReferenceError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();

9
boa_engine/src/builtins/error/syntax.rs

@ -16,6 +16,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -28,13 +29,13 @@ use super::{Error, ErrorKind};
pub(crate) struct SyntaxError; pub(crate) struct SyntaxError;
impl IntrinsicObject for SyntaxError { impl IntrinsicObject for SyntaxError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();

17
boa_engine/src/builtins/error/type.rs

@ -25,6 +25,7 @@ use crate::{
native_function::NativeFunction, native_function::NativeFunction,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -37,13 +38,13 @@ use super::{Error, ErrorKind};
pub(crate) struct TypeError; pub(crate) struct TypeError;
impl IntrinsicObject for TypeError { impl IntrinsicObject for TypeError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
@ -114,7 +115,7 @@ impl BuiltInConstructor for TypeError {
pub(crate) struct ThrowTypeError; pub(crate) struct ThrowTypeError;
impl IntrinsicObject for ThrowTypeError { impl IntrinsicObject for ThrowTypeError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
fn throw_type_error(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { fn throw_type_error(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
Err(JsNativeError::typ() Err(JsNativeError::typ()
.with_message( .with_message(
@ -124,8 +125,8 @@ impl IntrinsicObject for ThrowTypeError {
.into()) .into())
} }
let obj = BuiltInBuilder::with_intrinsic::<Self>(intrinsics) let obj = BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.constructors().function().prototype()) .prototype(realm.intrinsics().constructors().function().prototype())
.static_property(utf16!("name"), "ThrowTypeError", Attribute::empty()) .static_property(utf16!("name"), "ThrowTypeError", Attribute::empty())
.static_property(utf16!("length"), 0, Attribute::empty()) .static_property(utf16!("length"), 0, Attribute::empty())
.build(); .build();
@ -137,7 +138,7 @@ impl IntrinsicObject for ThrowTypeError {
function: NativeFunction::from_fn_ptr(throw_type_error), function: NativeFunction::from_fn_ptr(throw_type_error),
constructor: None, constructor: None,
}, },
intrinsics.clone(), realm.clone(),
)); ));
} }

9
boa_engine/src/builtins/error/uri.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -27,13 +28,13 @@ use super::{Error, ErrorKind};
pub(crate) struct UriError; pub(crate) struct UriError;
impl IntrinsicObject for UriError { impl IntrinsicObject for UriError {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();

11
boa_engine/src/builtins/escape/mod.rs

@ -11,7 +11,8 @@
//! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object //! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object
use crate::{ 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}; use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};
@ -21,8 +22,8 @@ use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};
pub(crate) struct Escape; pub(crate) struct Escape;
impl IntrinsicObject for Escape { impl IntrinsicObject for Escape {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(escape) .callable(escape)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -94,8 +95,8 @@ fn escape(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<
pub(crate) struct Unescape; pub(crate) struct Unescape;
impl IntrinsicObject for Unescape { impl IntrinsicObject for Unescape {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(unescape) .callable(unescape)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)

60
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 //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
use crate::{ use crate::{
builtins::BuiltInObject, context::intrinsics::Intrinsics, environments::Environment, builtins::BuiltInObject, bytecompiler::ByteCompiler, context::intrinsics::Intrinsics,
error::JsNativeError, object::JsObject, Context, JsArgs, JsResult, JsString, JsValue, environments::Environment, error::JsNativeError, object::JsObject, realm::Realm, Context,
JsArgs, JsResult, JsString, JsValue,
}; };
use boa_ast::operations::{ use boa_ast::operations::{
contains, contains_arguments, top_level_var_declared_names, ContainsSymbol, contains, contains_arguments, top_level_var_declared_names, ContainsSymbol,
}; };
use boa_gc::Gc;
use boa_interner::Sym;
use boa_parser::{Parser, Source}; use boa_parser::{Parser, Source};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -25,10 +28,10 @@ use super::{BuiltInBuilder, IntrinsicObject};
pub(crate) struct Eval; pub(crate) struct Eval;
impl IntrinsicObject for Eval { impl IntrinsicObject for Eval {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(Self::eval) .callable(Self::eval)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -91,12 +94,12 @@ impl Eval {
fn restore_environment(context: &mut Context<'_>, action: EnvStackAction) { fn restore_environment(context: &mut Context<'_>, action: EnvStackAction) {
match action { match action {
EnvStackAction::Truncate(size) => { EnvStackAction::Truncate(size) => {
context.realm.environments.truncate(size); context.vm.environments.truncate(size);
} }
EnvStackAction::Restore(envs) => { EnvStackAction::Restore(envs) => {
// Pop all environments created during the eval execution and restore the original stack. // Pop all environments created during the eval execution and restore the original stack.
context.realm.environments.truncate(1); context.vm.environments.truncate(1);
context.realm.environments.extend(envs); context.vm.environments.extend(envs);
} }
} }
} }
@ -112,8 +115,13 @@ impl Eval {
// Because of implementation details the following code differs from the spec. // 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). // 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: // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
// a. Let script be ParseText(StringToCodePoints(x), Script). // a. Let script be ParseText(StringToCodePoints(x), Script).
@ -132,7 +140,7 @@ impl Eval {
// 9. Let inClassFieldInitializer be false. // 9. Let inClassFieldInitializer be false.
// a. Let thisEnvRec be GetThisEnvironment(). // a. Let thisEnvRec be GetThisEnvironment().
let flags = match context let flags = match context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
@ -203,28 +211,21 @@ impl Eval {
let action = if direct { let action = if direct {
// If the call to eval is direct, the code is executed in the current environment. // 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 { 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. // 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.vm.environments.len();
let environments_len = context.realm.environments.len();
// Pop any added runtime environments that were not removed during the eval execution. // Pop any added runtime environments that were not removed during the eval execution.
EnvStackAction::Truncate(environments_len) EnvStackAction::Truncate(environments_len)
} else { } else {
// If the call to eval is indirect, the code is executed in the global environment. // 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. // Pop all environments before the eval execution.
let environments = context.realm.environments.pop_to_global(); let environments = context.vm.environments.pop_to_global();
context.realm.compile_env = context.realm.environments.current_compile_environment();
// Restore all environments to the state from before the eval execution. // Restore all environments to the state from before the eval execution.
EnvStackAction::Restore(environments) EnvStackAction::Restore(environments)
@ -235,7 +236,7 @@ impl Eval {
if !strict { if !strict {
// Error if any var declaration in the eval code already exists as a let/const declaration in the current running environment. // 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 if let Some(name) = context
.realm .vm
.environments .environments
.has_lex_binding_until_function_environment(&top_level_var_declared_names(&body)) .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. // TODO: check if private identifiers inside `eval` are valid.
// Compile and execute the eval statement list. // 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 // Indirect calls don't need extensions, because a non-strict indirect call modifies only
// the global object. // the global object.
// Strict direct calls also don't need extensions, since all strict eval calls push a new // Strict direct calls also don't need extensions, since all strict eval calls push a new
// function environment before evaluating. // function environment before evaluating.
if direct && !strict { if direct && !strict {
context context.vm.environments.extend_outer_function_environment();
.realm
.environments
.extend_outer_function_environment();
} }
let result = context.execute(code_block); let result = context.execute(code_block);

62
boa_engine/src/builtins/function/mod.rs

@ -22,6 +22,7 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData},
object::{JsFunction, PrivateElement}, object::{JsFunction, PrivateElement},
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
@ -296,7 +297,7 @@ unsafe impl Trace for FunctionKind {
#[derive(Debug, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
pub struct Function { pub struct Function {
kind: FunctionKind, kind: FunctionKind,
realm_intrinsics: Intrinsics, realm: Realm,
} }
impl Function { impl Function {
@ -323,11 +324,8 @@ impl Function {
} }
/// Creates a new `Function`. /// Creates a new `Function`.
pub(crate) fn new(kind: FunctionKind, intrinsics: Intrinsics) -> Self { pub(crate) fn new(kind: FunctionKind, realm: Realm) -> Self {
Self { Self { kind, realm }
kind,
realm_intrinsics: intrinsics,
}
} }
/// Returns true if the function object is a derived constructor. /// 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. /// Gets the `Realm` from where this function originates.
pub const fn realm_intrinsics(&self) -> &Intrinsics { pub const fn realm(&self) -> &Realm {
&self.realm_intrinsics &self.realm
} }
/// Gets a reference to the [`FunctionKind`] of the `Function`. /// Gets a reference to the [`FunctionKind`] of the `Function`.
@ -462,24 +460,27 @@ impl Function {
pub struct BuiltInFunctionObject; pub struct BuiltInFunctionObject;
impl IntrinsicObject for BuiltInFunctionObject { impl IntrinsicObject for BuiltInFunctionObject {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("function", "init"); let _timer = Profiler::global().start_event("function", "init");
BuiltInBuilder::with_object(intrinsics, intrinsics.constructors().function().prototype()) BuiltInBuilder::with_object(
.callable(Self::prototype) realm,
.name("") realm.intrinsics().constructors().function().prototype(),
.length(0) )
.build(); .callable(Self::prototype)
.name("")
.length(0)
.build();
let has_instance = BuiltInBuilder::new(intrinsics) let has_instance = BuiltInBuilder::new(realm)
.callable(Self::has_instance) .callable(Self::has_instance)
.name("[Symbol.iterator]") .name("[Symbol.iterator]")
.length(1) .length(1)
.build(); .build();
let throw_type_error = intrinsics.objects().throw_type_error(); let throw_type_error = realm.intrinsics().objects().throw_type_error();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::apply, "apply", 2) .method(Self::apply, "apply", 2)
.method(Self::bind, "bind", 1) .method(Self::bind, "bind", 1)
.method(Self::call, "call", 1) .method(Self::call, "call", 1)
@ -558,7 +559,9 @@ impl BuiltInFunctionObject {
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
// 1. Let currentRealm be the current Realm Record. // 1. Let currentRealm be the current Realm Record.
// 2. Perform ? HostEnsureCanCompileStrings(currentRealm). // 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. // 3. If newTarget is undefined, set newTarget to constructor.
let new_target = if new_target.is_undefined() { let new_target = if new_target.is_undefined() {
@ -731,9 +734,14 @@ impl BuiltInFunctionObject {
.name(Sym::ANONYMOUS) .name(Sym::ANONYMOUS)
.generator(generator) .generator(generator)
.r#async(r#async) .r#async(r#async)
.compile(&parameters, &body, context); .compile(
&parameters,
&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 { let function_object = if generator {
crate::vm::create_generator_function_object( 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) Ok(function_object)
} else if generator { } else if generator {
@ -764,10 +772,11 @@ impl BuiltInFunctionObject {
.compile( .compile(
&FormalParameterList::default(), &FormalParameterList::default(),
&StatementList::default(), &StatementList::default(),
context.realm().environment().compile_env(),
context, 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( let function_object = crate::vm::create_generator_function_object(
code, code,
r#async, r#async,
@ -775,17 +784,18 @@ impl BuiltInFunctionObject {
Some(prototype), Some(prototype),
context, context,
); );
context.realm.environments.extend(environments); context.vm.environments.extend(environments);
Ok(function_object) Ok(function_object)
} else { } else {
let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile( let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile(
&FormalParameterList::default(), &FormalParameterList::default(),
&StatementList::default(), &StatementList::default(),
context.realm().environment().compile_env(),
context, 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( let function_object = crate::vm::create_function_object(
code, code,
r#async, r#async,
@ -794,7 +804,7 @@ impl BuiltInFunctionObject {
false, false,
context, context,
); );
context.realm.environments.extend(environments); context.vm.environments.extend(environments);
Ok(function_object) Ok(function_object)
} }

49
boa_engine/src/builtins/generator/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, CONSTRUCTOR}, object::{JsObject, CONSTRUCTOR},
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
vm::{CallFrame, CompletionRecord, GeneratorResumeKind}, vm::{CallFrame, CompletionRecord, GeneratorResumeKind},
@ -46,7 +47,7 @@ pub(crate) struct GeneratorContext {
pub(crate) call_frame: CallFrame, pub(crate) call_frame: CallFrame,
pub(crate) stack: Vec<JsValue>, pub(crate) stack: Vec<JsValue>,
pub(crate) active_function: Option<JsObject>, pub(crate) active_function: Option<JsObject>,
pub(crate) realm_intrinsics: Intrinsics, pub(crate) realm: Realm,
} }
/// The internal representation of a `Generator` object. /// The internal representation of a `Generator` object.
@ -61,11 +62,17 @@ pub struct Generator {
} }
impl IntrinsicObject for Generator { impl IntrinsicObject for Generator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 1) .static_method(Self::next, "next", 1)
.static_method(Self::r#return, "return", 1) .static_method(Self::r#return, "return", 1)
.static_method(Self::throw, "throw", 1) .static_method(Self::throw, "throw", 1)
@ -76,7 +83,11 @@ impl IntrinsicObject for Generator {
) )
.static_property( .static_property(
CONSTRUCTOR, CONSTRUCTOR,
intrinsics.constructors().generator_function().prototype(), realm
.intrinsics()
.constructors()
.generator_function()
.prototype(),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.build(); .build();
@ -217,7 +228,7 @@ impl Generator {
drop(generator_obj_mut); drop(generator_obj_mut);
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -225,10 +236,7 @@ impl Generator {
&mut context.vm.active_function, &mut context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( let old_realm = context.enter_realm(generator_context.realm.clone());
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.vm.push_frame(generator_context.call_frame.clone()); context.vm.push_frame(generator_context.call_frame.clone());
if !first_execution { if !first_execution {
context.vm.push(value.clone()); context.vm.push(value.clone());
@ -243,7 +251,7 @@ impl Generator {
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -251,10 +259,7 @@ impl Generator {
&mut context.vm.active_function, &mut context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( context.enter_realm(old_realm);
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
let mut generator_obj_mut = generator_obj.borrow_mut(); let mut generator_obj_mut = generator_obj.borrow_mut();
let generator = generator_obj_mut let generator = generator_obj_mut
@ -348,7 +353,7 @@ impl Generator {
drop(generator_obj_mut); drop(generator_obj_mut);
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -356,10 +361,7 @@ impl Generator {
&mut context.vm.active_function, &mut context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( let old_realm = context.enter_realm(generator_context.realm.clone());
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.vm.push_frame(generator_context.call_frame.clone()); context.vm.push_frame(generator_context.call_frame.clone());
let completion_record = match abrupt_completion { let completion_record = match abrupt_completion {
@ -380,7 +382,7 @@ impl Generator {
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -388,10 +390,7 @@ impl Generator {
&mut context.vm.active_function, &mut context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( context.enter_realm(old_realm);
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
let mut generator_obj_mut = generator_obj.borrow_mut(); let mut generator_obj_mut = generator_obj.borrow_mut();
let generator = generator_obj_mut let generator = generator_obj_mut

11
boa_engine/src/builtins/generator_function/mod.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::PROTOTYPE, object::PROTOTYPE,
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsResult, Context, JsResult,
@ -28,15 +29,17 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
pub struct GeneratorFunction; pub struct GeneratorFunction;
impl IntrinsicObject for GeneratorFunction { impl IntrinsicObject for GeneratorFunction {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.inherits(Some(intrinsics.constructors().function().prototype())) .inherits(Some(
realm.intrinsics().constructors().function().prototype(),
))
.constructor_attributes(Attribute::CONFIGURABLE) .constructor_attributes(Attribute::CONFIGURABLE)
.property( .property(
PROTOTYPE, PROTOTYPE,
intrinsics.objects().generator(), realm.intrinsics().objects().generator(),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(

7
boa_engine/src/builtins/intl/collator/mod.rs

@ -22,6 +22,7 @@ use crate::{
JsObject, ObjectData, JsObject, ObjectData,
}, },
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
@ -159,15 +160,15 @@ impl Service for Collator {
} }
impl IntrinsicObject for Collator { impl IntrinsicObject for Collator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let compare = BuiltInBuilder::new(intrinsics) let compare = BuiltInBuilder::new(realm)
.callable(Self::compare) .callable(Self::compare)
.name("get compare") .name("get compare")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .static_method(Self::supported_locales_of, "supportedLocalesOf", 1)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

5
boa_engine/src/builtins/intl/date_time_format.rs

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm,
string::utf16, string::utf16,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -62,10 +63,10 @@ pub struct DateTimeFormat {
} }
impl IntrinsicObject for DateTimeFormat { impl IntrinsicObject for DateTimeFormat {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics).build(); BuiltInBuilder::from_standard_constructor::<Self>(realm).build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

5
boa_engine/src/builtins/intl/list_format/mod.rs

@ -10,6 +10,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
@ -48,10 +49,10 @@ impl Service for ListFormat {
} }
impl IntrinsicObject for ListFormat { impl IntrinsicObject for ListFormat {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .static_method(Self::supported_locales_of, "supportedLocalesOf", 1)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

26
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 boa_profiler::Profiler;
use icu_collator::CaseFirst; use icu_collator::CaseFirst;
use icu_datetime::options::preferences::HourCycle; use icu_datetime::options::preferences::HourCycle;
@ -32,60 +32,60 @@ use super::options::{coerce_options_to_object, get_option};
pub(crate) struct Locale; pub(crate) struct Locale;
impl IntrinsicObject for Locale { impl IntrinsicObject for Locale {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(Self::base_name)
.name("get baseName") .name("get baseName")
.build(); .build();
let calendar = BuiltInBuilder::new(intrinsics) let calendar = BuiltInBuilder::new(realm)
.callable(Self::calendar) .callable(Self::calendar)
.name("get calendar") .name("get calendar")
.build(); .build();
let case_first = BuiltInBuilder::new(intrinsics) let case_first = BuiltInBuilder::new(realm)
.callable(Self::case_first) .callable(Self::case_first)
.name("get caseFirst") .name("get caseFirst")
.build(); .build();
let collation = BuiltInBuilder::new(intrinsics) let collation = BuiltInBuilder::new(realm)
.callable(Self::collation) .callable(Self::collation)
.name("get collation") .name("get collation")
.build(); .build();
let hour_cycle = BuiltInBuilder::new(intrinsics) let hour_cycle = BuiltInBuilder::new(realm)
.callable(Self::hour_cycle) .callable(Self::hour_cycle)
.name("get hourCycle") .name("get hourCycle")
.build(); .build();
let numeric = BuiltInBuilder::new(intrinsics) let numeric = BuiltInBuilder::new(realm)
.callable(Self::numeric) .callable(Self::numeric)
.name("get numeric") .name("get numeric")
.build(); .build();
let numbering_system = BuiltInBuilder::new(intrinsics) let numbering_system = BuiltInBuilder::new(realm)
.callable(Self::numbering_system) .callable(Self::numbering_system)
.name("get numberingSystem") .name("get numberingSystem")
.build(); .build();
let language = BuiltInBuilder::new(intrinsics) let language = BuiltInBuilder::new(realm)
.callable(Self::language) .callable(Self::language)
.name("get language") .name("get language")
.build(); .build();
let script = BuiltInBuilder::new(intrinsics) let script = BuiltInBuilder::new(realm)
.callable(Self::script) .callable(Self::script)
.name("get script") .name("get script")
.build(); .build();
let region = BuiltInBuilder::new(intrinsics) let region = BuiltInBuilder::new(realm)
.callable(Self::region) .callable(Self::region)
.name("get region") .name("get region")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Intl.Locale", "Intl.Locale",

23
boa_engine/src/builtins/intl/mod.rs

@ -14,6 +14,7 @@ use crate::{
context::{intrinsics::Intrinsics, BoaProvider}, context::{intrinsics::Intrinsics, BoaProvider},
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -39,10 +40,10 @@ mod options;
pub(crate) struct Intl; pub(crate) struct Intl;
impl IntrinsicObject for Intl { impl IntrinsicObject for Intl {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -50,27 +51,35 @@ impl IntrinsicObject for Intl {
) )
.static_property( .static_property(
Collator::NAME, Collator::NAME,
intrinsics.constructors().collator().constructor(), realm.intrinsics().constructors().collator().constructor(),
Collator::ATTRIBUTE, Collator::ATTRIBUTE,
) )
.static_property( .static_property(
ListFormat::NAME, ListFormat::NAME,
intrinsics.constructors().list_format().constructor(), realm
.intrinsics()
.constructors()
.list_format()
.constructor(),
ListFormat::ATTRIBUTE, ListFormat::ATTRIBUTE,
) )
.static_property( .static_property(
Locale::NAME, Locale::NAME,
intrinsics.constructors().locale().constructor(), realm.intrinsics().constructors().locale().constructor(),
Locale::ATTRIBUTE, Locale::ATTRIBUTE,
) )
.static_property( .static_property(
Segmenter::NAME, Segmenter::NAME,
intrinsics.constructors().segmenter().constructor(), realm.intrinsics().constructors().segmenter().constructor(),
Segmenter::ATTRIBUTE, Segmenter::ATTRIBUTE,
) )
.static_property( .static_property(
DateTimeFormat::NAME, DateTimeFormat::NAME,
intrinsics.constructors().date_time_format().constructor(), realm
.intrinsics()
.constructors()
.date_time_format()
.constructor(),
DateTimeFormat::ATTRIBUTE, DateTimeFormat::ATTRIBUTE,
) )
.static_method(Self::get_canonical_locales, "getCanonicalLocales", 1) .static_method(Self::get_canonical_locales, "getCanonicalLocales", 1)

5
boa_engine/src/builtins/intl/segmenter/mod.rs

@ -6,6 +6,7 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::JsObject, object::JsObject,
realm::Realm,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -17,10 +18,10 @@ pub(crate) use options::*;
pub(crate) struct Segmenter; pub(crate) struct Segmenter;
impl IntrinsicObject for Segmenter { impl IntrinsicObject for Segmenter {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics).build(); BuiltInBuilder::from_standard_constructor::<Self>(realm).build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

13
boa_engine/src/builtins/iterable/async_from_sync_iterator.rs

@ -7,6 +7,7 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, ObjectData}, object::{FunctionObjectBuilder, JsObject, ObjectData},
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
@ -26,11 +27,17 @@ pub struct AsyncFromSyncIterator {
} }
impl IntrinsicObject for AsyncFromSyncIterator { impl IntrinsicObject for AsyncFromSyncIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("AsyncFromSyncIteratorPrototype", "init"); let _timer = Profiler::global().start_event("AsyncFromSyncIteratorPrototype", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().async_iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.async_iterator(),
)
.static_method(Self::next, "next", 1) .static_method(Self::next, "next", 1)
.static_method(Self::r#return, "return", 1) .static_method(Self::r#return, "return", 1)
.static_method(Self::throw, "throw", 1) .static_method(Self::throw, "throw", 1)

9
boa_engine/src/builtins/iterable/mod.rs

@ -5,6 +5,7 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsValue, Context, JsResult, JsValue,
@ -135,10 +136,10 @@ impl IteratorPrototypes {
pub(crate) struct Iterator; pub(crate) struct Iterator;
impl IntrinsicObject for Iterator { impl IntrinsicObject for Iterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("Iterator Prototype", "init"); let _timer = Profiler::global().start_event("Iterator Prototype", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method( .static_method(
|v, _, _| Ok(v.clone()), |v, _, _| Ok(v.clone()),
(JsSymbol::iterator(), "[Symbol.iterator]"), (JsSymbol::iterator(), "[Symbol.iterator]"),
@ -161,10 +162,10 @@ impl IntrinsicObject for Iterator {
pub(crate) struct AsyncIterator; pub(crate) struct AsyncIterator;
impl IntrinsicObject for AsyncIterator { impl IntrinsicObject for AsyncIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("AsyncIteratorPrototype", "init"); let _timer = Profiler::global().start_event("AsyncIteratorPrototype", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method( .static_method(
|v, _, _| Ok(v.clone()), |v, _, _| Ok(v.clone()),
(JsSymbol::async_iterator(), "[Symbol.asyncIterator]"), (JsSymbol::async_iterator(), "[Symbol.asyncIterator]"),

21
boa_engine/src/builtins/json/mod.rs

@ -20,16 +20,20 @@ use std::{
use crate::{ use crate::{
builtins::BuiltInObject, builtins::BuiltInObject,
bytecompiler::ByteCompiler,
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{JsObject, RecursionLimiter}, object::{JsObject, RecursionLimiter},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
string::{utf16, CodePoint}, string::{utf16, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
Context, JsArgs, JsResult, JsString, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_gc::Gc;
use boa_interner::Sym;
use boa_parser::{Parser, Source}; use boa_parser::{Parser, Source};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -137,13 +141,13 @@ where
pub(crate) struct Json; pub(crate) struct Json;
impl IntrinsicObject for Json { impl IntrinsicObject for Json {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let to_string_tag = JsSymbol::to_string_tag(); let to_string_tag = JsSymbol::to_string_tag();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method(Self::parse, "parse", 2) .static_method(Self::parse, "parse", 2)
.static_method(Self::stringify, "stringify", 3) .static_method(Self::stringify, "stringify", 3)
.static_property(to_string_tag, Self::NAME, attribute) .static_property(to_string_tag, Self::NAME, attribute)
@ -206,7 +210,18 @@ impl Json {
let mut parser = Parser::new(Source::from_bytes(&script_string)); let mut parser = Parser::new(Source::from_bytes(&script_string));
parser.set_json_parse(); parser.set_json_parse();
let statement_list = parser.parse_script(context.interner_mut())?; 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)?; let unfiltered = context.execute(code_block)?;
// 11. If IsCallable(reviver) is true, then // 11. If IsCallable(reviver) is true, then

13
boa_engine/src/builtins/map/map_iterator.rs

@ -14,6 +14,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, Context, JsResult,
}; };
@ -36,11 +37,17 @@ pub struct MapIterator {
} }
impl IntrinsicObject for MapIterator { impl IntrinsicObject for MapIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("MapIterator", "init"); let _timer = Profiler::global().start_event("MapIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0) .static_method(Self::next, "next", 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

11
boa_engine/src/builtins/map/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
@ -37,25 +38,25 @@ mod tests;
pub(crate) struct Map; pub(crate) struct Map;
impl IntrinsicObject for Map { impl IntrinsicObject for Map {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
let get_size = BuiltInBuilder::new(intrinsics) let get_size = BuiltInBuilder::new(realm)
.callable(Self::get_size) .callable(Self::get_size)
.name("get size") .name("get size")
.build(); .build();
let entries_function = BuiltInBuilder::new(intrinsics) let entries_function = BuiltInBuilder::new(realm)
.callable(Self::entries) .callable(Self::entries)
.name("entries") .name("entries")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),

7
boa_engine/src/builtins/math/mod.rs

@ -13,7 +13,8 @@
use crate::{ use crate::{
builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject, 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; use boa_profiler::Profiler;
@ -27,11 +28,11 @@ mod tests;
pub(crate) struct Math; pub(crate) struct Math;
impl IntrinsicObject for Math { impl IntrinsicObject for Math {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_property(utf16!("E"), std::f64::consts::E, attribute) .static_property(utf16!("E"), std::f64::consts::E, attribute)
.static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute) .static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute)
.static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute) .static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute)

221
boa_engine/src/builtins/mod.rs

@ -100,6 +100,7 @@ use crate::{
FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE, FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE,
}, },
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
string::utf16, string::utf16,
Context, JsResult, JsString, JsValue, 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 /// This is where the methods, properties, static methods and the constructor of a built-in must
/// be initialized to be accessible from ECMAScript. /// be initialized to be accessible from ECMAScript.
fn init(intrinsics: &Intrinsics); fn init(realm: &Realm);
/// Gets the intrinsic object. /// Gets the intrinsic object.
fn get(intrinsics: &Intrinsics) -> JsObject; fn get(intrinsics: &Intrinsics) -> JsObject;
@ -183,97 +184,93 @@ fn global_binding<B: BuiltInObject>(context: &mut Context<'_>) -> JsResult<()> {
Ok(()) Ok(())
} }
impl Intrinsics { impl Realm {
/// Abstract operation [`CreateIntrinsics ( realmRec )`][spec] /// Abstract operation [`CreateIntrinsics ( realmRec )`][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-createintrinsics /// [spec]: https://tc39.es/ecma262/#sec-createintrinsics
pub(crate) fn new() -> Self { pub(crate) fn initialize(&self) {
let intrinsics = Self::default(); BuiltInFunctionObject::init(self);
BuiltInObjectObject::init(self);
BuiltInFunctionObject::init(&intrinsics); Iterator::init(self);
BuiltInObjectObject::init(&intrinsics); AsyncIterator::init(self);
Iterator::init(&intrinsics); AsyncFromSyncIterator::init(self);
AsyncIterator::init(&intrinsics); ForInIterator::init(self);
AsyncFromSyncIterator::init(&intrinsics); Math::init(self);
ForInIterator::init(&intrinsics); Json::init(self);
Math::init(&intrinsics); Array::init(self);
Json::init(&intrinsics); ArrayIterator::init(self);
Array::init(&intrinsics); Proxy::init(self);
ArrayIterator::init(&intrinsics); ArrayBuffer::init(self);
Proxy::init(&intrinsics); BigInt::init(self);
ArrayBuffer::init(&intrinsics); Boolean::init(self);
BigInt::init(&intrinsics); Date::init(self);
Boolean::init(&intrinsics); DataView::init(self);
Date::init(&intrinsics); Map::init(self);
DataView::init(&intrinsics); MapIterator::init(self);
Map::init(&intrinsics); IsFinite::init(self);
MapIterator::init(&intrinsics); IsNaN::init(self);
IsFinite::init(&intrinsics); ParseInt::init(self);
IsNaN::init(&intrinsics); ParseFloat::init(self);
ParseInt::init(&intrinsics); Number::init(self);
ParseFloat::init(&intrinsics); Eval::init(self);
Number::init(&intrinsics); Set::init(self);
Eval::init(&intrinsics); SetIterator::init(self);
Set::init(&intrinsics); String::init(self);
SetIterator::init(&intrinsics); StringIterator::init(self);
String::init(&intrinsics); RegExp::init(self);
StringIterator::init(&intrinsics); RegExpStringIterator::init(self);
RegExp::init(&intrinsics); TypedArray::init(self);
RegExpStringIterator::init(&intrinsics); Int8Array::init(self);
TypedArray::init(&intrinsics); Uint8Array::init(self);
Int8Array::init(&intrinsics); Uint8ClampedArray::init(self);
Uint8Array::init(&intrinsics); Int16Array::init(self);
Uint8ClampedArray::init(&intrinsics); Uint16Array::init(self);
Int16Array::init(&intrinsics); Int32Array::init(self);
Uint16Array::init(&intrinsics); Uint32Array::init(self);
Int32Array::init(&intrinsics); BigInt64Array::init(self);
Uint32Array::init(&intrinsics); BigUint64Array::init(self);
BigInt64Array::init(&intrinsics); Float32Array::init(self);
BigUint64Array::init(&intrinsics); Float64Array::init(self);
Float32Array::init(&intrinsics); Symbol::init(self);
Float64Array::init(&intrinsics); Error::init(self);
Symbol::init(&intrinsics); RangeError::init(self);
Error::init(&intrinsics); ReferenceError::init(self);
RangeError::init(&intrinsics); TypeError::init(self);
ReferenceError::init(&intrinsics); ThrowTypeError::init(self);
TypeError::init(&intrinsics); SyntaxError::init(self);
ThrowTypeError::init(&intrinsics); EvalError::init(self);
SyntaxError::init(&intrinsics); UriError::init(self);
EvalError::init(&intrinsics); AggregateError::init(self);
UriError::init(&intrinsics); Reflect::init(self);
AggregateError::init(&intrinsics); Generator::init(self);
Reflect::init(&intrinsics); GeneratorFunction::init(self);
Generator::init(&intrinsics); Promise::init(self);
GeneratorFunction::init(&intrinsics); AsyncFunction::init(self);
Promise::init(&intrinsics); AsyncGenerator::init(self);
AsyncFunction::init(&intrinsics); AsyncGeneratorFunction::init(self);
AsyncGenerator::init(&intrinsics); EncodeUri::init(self);
AsyncGeneratorFunction::init(&intrinsics); EncodeUriComponent::init(self);
EncodeUri::init(&intrinsics); DecodeUri::init(self);
EncodeUriComponent::init(&intrinsics); DecodeUriComponent::init(self);
DecodeUri::init(&intrinsics); WeakRef::init(self);
DecodeUriComponent::init(&intrinsics); WeakMap::init(self);
WeakRef::init(&intrinsics); WeakSet::init(self);
WeakMap::init(&intrinsics);
WeakSet::init(&intrinsics);
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
{ {
escape::Escape::init(&intrinsics); escape::Escape::init(self);
escape::Unescape::init(&intrinsics); escape::Unescape::init(self);
} }
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
{ {
intl::Intl::init(&intrinsics); intl::Intl::init(self);
intl::Collator::init(&intrinsics); intl::Collator::init(self);
intl::ListFormat::init(&intrinsics); intl::ListFormat::init(self);
intl::Locale::init(&intrinsics); intl::Locale::init(self);
intl::DateTimeFormat::init(&intrinsics); intl::DateTimeFormat::init(self);
intl::Segmenter::init(&intrinsics); 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( global_object.define_property_or_throw(
utf16!("globalThis"), utf16!("globalThis"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(context.realm.global_this().clone()) .value(context.realm().global_this().clone())
.writable(true) .writable(true)
.enumerable(false) .enumerable(false)
.configurable(true), .configurable(true),
@ -429,7 +426,7 @@ struct Callable<Kind> {
name: JsString, name: JsString,
length: usize, length: usize,
kind: Kind, kind: Kind,
intrinsics: Intrinsics, realm: Realm,
} }
/// Marker for an ordinary object. /// Marker for an ordinary object.
@ -483,7 +480,7 @@ impl<S: ApplyToObject + IsConstructor> ApplyToObject for Callable<S> {
function: NativeFunction::from_fn_ptr(self.function), function: NativeFunction::from_fn_ptr(self.function),
constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base), constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base),
}, },
self.intrinsics, self.realm,
); );
let length = PropertyDescriptor::builder() let length = PropertyDescriptor::builder()
@ -517,42 +514,39 @@ impl ApplyToObject for OrdinaryObject {
#[derive(Debug)] #[derive(Debug)]
#[must_use = "You need to call the `build` method in order for this to correctly assign the inner data"] #[must_use = "You need to call the `build` method in order for this to correctly assign the inner data"]
struct BuiltInBuilder<'ctx, Kind> { struct BuiltInBuilder<'ctx, Kind> {
intrinsics: &'ctx Intrinsics, realm: &'ctx Realm,
object: JsObject, object: JsObject,
kind: Kind, kind: Kind,
prototype: JsObject, prototype: JsObject,
} }
impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
fn new(intrinsics: &'ctx Intrinsics) -> BuiltInBuilder<'ctx, OrdinaryObject> { fn new(realm: &'ctx Realm) -> BuiltInBuilder<'ctx, OrdinaryObject> {
BuiltInBuilder { BuiltInBuilder {
intrinsics, realm,
object: JsObject::with_null_proto(), object: JsObject::with_null_proto(),
kind: OrdinaryObject, kind: OrdinaryObject,
prototype: intrinsics.constructors().object().prototype(), prototype: realm.intrinsics().constructors().object().prototype(),
} }
} }
fn with_intrinsic<I: IntrinsicObject>( fn with_intrinsic<I: IntrinsicObject>(
intrinsics: &'ctx Intrinsics, realm: &'ctx Realm,
) -> BuiltInBuilder<'ctx, OrdinaryObject> { ) -> BuiltInBuilder<'ctx, OrdinaryObject> {
BuiltInBuilder { BuiltInBuilder {
intrinsics, realm,
object: I::get(intrinsics), object: I::get(realm.intrinsics()),
kind: OrdinaryObject, kind: OrdinaryObject,
prototype: intrinsics.constructors().object().prototype(), prototype: realm.intrinsics().constructors().object().prototype(),
} }
} }
fn with_object( fn with_object(realm: &'ctx Realm, object: JsObject) -> BuiltInBuilder<'ctx, OrdinaryObject> {
intrinsics: &'ctx Intrinsics,
object: JsObject,
) -> BuiltInBuilder<'ctx, OrdinaryObject> {
BuiltInBuilder { BuiltInBuilder {
intrinsics, realm,
object, object,
kind: OrdinaryObject, kind: OrdinaryObject,
prototype: intrinsics.constructors().object().prototype(), prototype: realm.intrinsics().constructors().object().prototype(),
} }
} }
} }
@ -563,27 +557,32 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
function: NativeFunctionPointer, function: NativeFunctionPointer,
) -> BuiltInBuilder<'ctx, Callable<OrdinaryFunction>> { ) -> BuiltInBuilder<'ctx, Callable<OrdinaryFunction>> {
BuiltInBuilder { BuiltInBuilder {
intrinsics: self.intrinsics, realm: self.realm,
object: self.object, object: self.object,
kind: Callable { kind: Callable {
function, function,
name: js_string!(""), name: js_string!(""),
length: 0, length: 0,
kind: OrdinaryFunction, 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<Constructor>> { impl<'ctx> BuiltInBuilder<'ctx, Callable<Constructor>> {
fn from_standard_constructor<SC: BuiltInConstructor>( fn from_standard_constructor<SC: BuiltInConstructor>(
intrinsics: &'ctx Intrinsics, realm: &'ctx Realm,
) -> BuiltInBuilder<'ctx, Callable<Constructor>> { ) -> BuiltInBuilder<'ctx, Callable<Constructor>> {
let constructor = SC::STANDARD_CONSTRUCTOR(intrinsics.constructors()); let constructor = SC::STANDARD_CONSTRUCTOR(realm.intrinsics().constructors());
BuiltInBuilder { BuiltInBuilder {
intrinsics, realm,
object: constructor.constructor(), object: constructor.constructor(),
kind: Callable { kind: Callable {
function: SC::constructor, function: SC::constructor,
@ -591,25 +590,25 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable<Constructor>> {
length: SC::LENGTH, length: SC::LENGTH,
kind: Constructor { kind: Constructor {
prototype: constructor.prototype(), prototype: constructor.prototype(),
inherits: Some(intrinsics.constructors().object().prototype()), inherits: Some(realm.intrinsics().constructors().object().prototype()),
attributes: Attribute::WRITABLE | Attribute::CONFIGURABLE, 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<ConstructorNoProto>> { fn no_proto(self) -> BuiltInBuilder<'ctx, Callable<ConstructorNoProto>> {
BuiltInBuilder { BuiltInBuilder {
intrinsics: self.intrinsics, realm: self.realm,
object: self.object, object: self.object,
kind: Callable { kind: Callable {
function: self.kind.function, function: self.kind.function,
name: self.kind.name, name: self.kind.name,
length: self.kind.length, length: self.kind.length,
kind: ConstructorNoProto, kind: ConstructorNoProto,
intrinsics: self.intrinsics.clone(), realm: self.realm.clone(),
}, },
prototype: self.prototype, prototype: self.prototype,
} }
@ -623,7 +622,7 @@ impl<T> BuiltInBuilder<'_, T> {
B: Into<FunctionBinding>, B: Into<FunctionBinding>,
{ {
let binding = binding.into(); let binding = binding.into();
let function = BuiltInBuilder::new(self.intrinsics) let function = BuiltInBuilder::new(self.realm)
.callable(function) .callable(function)
.name(binding.name) .name(binding.name)
.length(length) .length(length)
@ -691,7 +690,7 @@ impl BuiltInBuilder<'_, Callable<Constructor>> {
B: Into<FunctionBinding>, B: Into<FunctionBinding>,
{ {
let binding = binding.into(); let binding = binding.into();
let function = BuiltInBuilder::new(self.intrinsics) let function = BuiltInBuilder::new(self.realm)
.callable(function) .callable(function)
.name(binding.name) .name(binding.name)
.length(length) .length(length)

17
boa_engine/src/builtins/number/globals.rs

@ -4,6 +4,7 @@ use crate::{
builtins::{string::is_trimmable_whitespace, BuiltInBuilder, BuiltInObject, IntrinsicObject}, builtins::{string::is_trimmable_whitespace, BuiltInBuilder, BuiltInObject, IntrinsicObject},
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
object::JsObject, object::JsObject,
realm::Realm,
string::Utf16Trim, string::Utf16Trim,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -36,8 +37,8 @@ fn is_finite(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResu
pub(crate) struct IsFinite; pub(crate) struct IsFinite;
impl IntrinsicObject for IsFinite { impl IntrinsicObject for IsFinite {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(is_finite) .callable(is_finite)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -83,8 +84,8 @@ pub(crate) fn is_nan(
pub(crate) struct IsNaN; pub(crate) struct IsNaN;
impl IntrinsicObject for IsNaN { impl IntrinsicObject for IsNaN {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(is_nan) .callable(is_nan)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -225,8 +226,8 @@ pub(crate) fn parse_int(
pub(crate) struct ParseInt; pub(crate) struct ParseInt;
impl IntrinsicObject for ParseInt { impl IntrinsicObject for ParseInt {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(parse_int) .callable(parse_int)
.name(Self::NAME) .name(Self::NAME)
.length(2) .length(2)
@ -299,8 +300,8 @@ pub(crate) fn parse_float(
pub(crate) struct ParseFloat; pub(crate) struct ParseFloat;
impl IntrinsicObject for ParseFloat { impl IntrinsicObject for ParseFloat {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(parse_float) .callable(parse_float)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)

9
boa_engine/src/builtins/number/mod.rs

@ -19,6 +19,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
value::{AbstractRelation, IntegerOrInfinity, JsValue}, value::{AbstractRelation, IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
@ -45,12 +46,12 @@ const BUF_SIZE: usize = 2200;
pub(crate) struct Number; pub(crate) struct Number;
impl IntrinsicObject for Number { impl IntrinsicObject for Number {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_property(utf16!("EPSILON"), f64::EPSILON, attribute) .static_property(utf16!("EPSILON"), f64::EPSILON, attribute)
.static_property( .static_property(
utf16!("MAX_SAFE_INTEGER"), utf16!("MAX_SAFE_INTEGER"),
@ -69,12 +70,12 @@ impl IntrinsicObject for Number {
.static_property(utf16!("NaN"), f64::NAN, attribute) .static_property(utf16!("NaN"), f64::NAN, attribute)
.static_property( .static_property(
utf16!("parseInt"), utf16!("parseInt"),
intrinsics.objects().parse_int(), realm.intrinsics().objects().parse_int(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.static_property( .static_property(
utf16!("parseFloat"), utf16!("parseFloat"),
intrinsics.objects().parse_float(), realm.intrinsics().objects().parse_float(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.static_method(Self::number_is_finite, "isFinite", 1) .static_method(Self::number_is_finite, "isFinite", 1)

13
boa_engine/src/builtins/object/for_in_iterator.rs

@ -14,6 +14,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::PropertyKey, property::PropertyKey,
realm::Realm,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -37,11 +38,17 @@ pub struct ForInIterator {
} }
impl IntrinsicObject for ForInIterator { impl IntrinsicObject for ForInIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("ForInIterator", "init"); let _timer = Profiler::global().start_event("ForInIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0) .static_method(Self::next, "next", 0)
.build(); .build();
} }

9
boa_engine/src/builtins/object/mod.rs

@ -27,6 +27,7 @@ use crate::{
JsObject, ObjectData, ObjectKind, JsObject, ObjectData, ObjectKind,
}, },
property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind},
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
@ -44,20 +45,20 @@ mod tests;
pub struct Object; pub struct Object;
impl IntrinsicObject for Object { impl IntrinsicObject for Object {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(Self::legacy_proto_getter)
.name("get __proto__") .name("get __proto__")
.build(); .build();
let legacy_setter_proto = BuiltInBuilder::new(intrinsics) let legacy_setter_proto = BuiltInBuilder::new(realm)
.callable(Self::legacy_proto_setter) .callable(Self::legacy_proto_setter)
.name("set __proto__") .name("set __proto__")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.inherits(None) .inherits(None)
.accessor( .accessor(
utf16!("__proto__"), utf16!("__proto__"),

66
boa_engine/src/builtins/promise/mod.rs

@ -15,6 +15,7 @@ use crate::{
JsObject, ObjectData, CONSTRUCTOR, JsObject, ObjectData, CONSTRUCTOR,
}, },
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
@ -307,15 +308,15 @@ impl PromiseCapability {
} }
impl IntrinsicObject for Promise { impl IntrinsicObject for Promise {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::all, "all", 1) .static_method(Self::all, "all", 1)
.static_method(Self::all_settled, "allSettled", 1) .static_method(Self::all_settled, "allSettled", 1)
.static_method(Self::any, "any", 1) .static_method(Self::any, "any", 1)
@ -1856,7 +1857,8 @@ impl Promise {
// a. Let value be promise.[[PromiseResult]]. // a. Let value be promise.[[PromiseResult]].
PromiseState::Fulfilled(ref value) => { PromiseState::Fulfilled(ref value) => {
// b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, 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]]). // c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]).
context context
@ -1878,7 +1880,7 @@ impl Promise {
} }
// d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason). // 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]]). // e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]).
context.job_queue().enqueue_promise_job(reject_job, context); context.job_queue().enqueue_promise_job(reject_job, context);
@ -1955,7 +1957,7 @@ impl Promise {
// 1. For each element reaction of reactions, do // 1. For each element reaction of reactions, do
for reaction in reactions { for reaction in reactions {
// a. Let job be NewPromiseReactionJob(reaction, argument). // 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]]). // b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
context.job_queue().enqueue_promise_job(job, context); context.job_queue().enqueue_promise_job(job, context);
@ -2163,6 +2165,7 @@ impl Promise {
promise.clone(), promise.clone(),
resolution.clone(), resolution.clone(),
then_job_callback, then_job_callback,
context
); );
// 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). // 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
@ -2235,7 +2238,26 @@ impl Promise {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-newpromisereactionjob /// [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: // 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<'_>| { let job = move |context: &mut Context<'_>| {
// a. Let promiseCapability be reaction.[[Capability]]. // 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 }. // 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }.
NativeJob::new(job) NativeJob::with_realm(job, realm)
} }
/// More information: /// More information:
@ -2321,7 +2335,19 @@ fn new_promise_resolve_thenable_job(
promise_to_resolve: JsObject, promise_to_resolve: JsObject,
thenable: JsValue, thenable: JsValue,
then: JobCallback, then: JobCallback,
context: &mut Context<'_>,
) -> NativeJob { ) -> 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: // 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<'_>| { let job = move |context: &mut Context<'_>| {
// a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve). // a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
@ -2351,12 +2377,6 @@ fn new_promise_resolve_thenable_job(
then_call_result 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 }. // 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }.
NativeJob::new(job) NativeJob::with_realm(job, realm)
} }

5
boa_engine/src/builtins/proxy/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData}, object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData},
realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -31,10 +32,10 @@ pub struct Proxy {
} }
impl IntrinsicObject for Proxy { impl IntrinsicObject for Proxy {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.no_proto() .no_proto()
.static_method(Self::revocable, "revocable", 2) .static_method(Self::revocable, "revocable", 2)
.build(); .build();

5
boa_engine/src/builtins/reflect/mod.rs

@ -17,6 +17,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -30,12 +31,12 @@ mod tests;
pub(crate) struct Reflect; pub(crate) struct Reflect;
impl IntrinsicObject for Reflect { impl IntrinsicObject for Reflect {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let to_string_tag = JsSymbol::to_string_tag(); let to_string_tag = JsSymbol::to_string_tag();
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method(Self::apply, "apply", 3) .static_method(Self::apply, "apply", 3)
.static_method(Self::construct, "construct", 2) .static_method(Self::construct, "construct", 2)
.static_method(Self::define_property, "defineProperty", 3) .static_method(Self::define_property, "defineProperty", 3)

25
boa_engine/src/builtins/regexp/mod.rs

@ -16,6 +16,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR},
property::{Attribute, PropertyDescriptorBuilder}, property::{Attribute, PropertyDescriptorBuilder},
realm::Realm,
string::{utf16, CodePoint}, string::{utf16, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
@ -44,53 +45,53 @@ pub struct RegExp {
} }
impl IntrinsicObject for RegExp { impl IntrinsicObject for RegExp {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; 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) .callable(Self::get_has_indices)
.name("get hasIndices") .name("get hasIndices")
.build(); .build();
let get_global = BuiltInBuilder::new(intrinsics) let get_global = BuiltInBuilder::new(realm)
.callable(Self::get_global) .callable(Self::get_global)
.name("get global") .name("get global")
.build(); .build();
let get_ignore_case = BuiltInBuilder::new(intrinsics) let get_ignore_case = BuiltInBuilder::new(realm)
.callable(Self::get_ignore_case) .callable(Self::get_ignore_case)
.name("get ignoreCase") .name("get ignoreCase")
.build(); .build();
let get_multiline = BuiltInBuilder::new(intrinsics) let get_multiline = BuiltInBuilder::new(realm)
.callable(Self::get_multiline) .callable(Self::get_multiline)
.name("get multiline") .name("get multiline")
.build(); .build();
let get_dot_all = BuiltInBuilder::new(intrinsics) let get_dot_all = BuiltInBuilder::new(realm)
.callable(Self::get_dot_all) .callable(Self::get_dot_all)
.name("get dotAll") .name("get dotAll")
.build(); .build();
let get_unicode = BuiltInBuilder::new(intrinsics) let get_unicode = BuiltInBuilder::new(realm)
.callable(Self::get_unicode) .callable(Self::get_unicode)
.name("get unicode") .name("get unicode")
.build(); .build();
let get_sticky = BuiltInBuilder::new(intrinsics) let get_sticky = BuiltInBuilder::new(realm)
.callable(Self::get_sticky) .callable(Self::get_sticky)
.name("get sticky") .name("get sticky")
.build(); .build();
let get_flags = BuiltInBuilder::new(intrinsics) let get_flags = BuiltInBuilder::new(realm)
.callable(Self::get_flags) .callable(Self::get_flags)
.name("get flags") .name("get flags")
.build(); .build();
let get_source = BuiltInBuilder::new(intrinsics) let get_source = BuiltInBuilder::new(realm)
.callable(Self::get_source) .callable(Self::get_source)
.name("get source") .name("get source")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),

13
boa_engine/src/builtins/regexp/regexp_string_iterator.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
@ -40,11 +41,17 @@ pub struct RegExpStringIterator {
} }
impl IntrinsicObject for RegExpStringIterator { impl IntrinsicObject for RegExpStringIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("RegExpStringIterator", "init"); let _timer = Profiler::global().start_event("RegExpStringIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0) .static_method(Self::next, "next", 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

11
boa_engine/src/builtins/set/mod.rs

@ -24,6 +24,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
@ -40,25 +41,25 @@ impl IntrinsicObject for Set {
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
} }
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
let size_getter = BuiltInBuilder::new(intrinsics) let size_getter = BuiltInBuilder::new(realm)
.callable(Self::size_getter) .callable(Self::size_getter)
.name("get size") .name("get size")
.build(); .build();
let values_function = BuiltInBuilder::new(intrinsics) let values_function = BuiltInBuilder::new(realm)
.callable(Self::values) .callable(Self::values)
.name("values") .name("values")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),

13
boa_engine/src/builtins/set/set_iterator.rs

@ -14,6 +14,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, Context, JsResult,
}; };
@ -36,11 +37,17 @@ pub struct SetIterator {
} }
impl IntrinsicObject for SetIterator { impl IntrinsicObject for SetIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("SetIterator", "init"); let _timer = Profiler::global().start_event("SetIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0) .static_method(Self::next, "next", 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

5
boa_engine/src/builtins/string/mod.rs

@ -16,6 +16,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor}, property::{Attribute, PropertyDescriptor},
realm::Realm,
string::utf16, string::utf16,
string::{CodePoint, Utf16Trim}, string::{CodePoint, Utf16Trim},
symbol::JsSymbol, symbol::JsSymbol,
@ -64,13 +65,13 @@ pub(crate) const fn is_trimmable_whitespace(c: char) -> bool {
pub(crate) struct String; pub(crate) struct String;
impl IntrinsicObject for String { impl IntrinsicObject for String {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let symbol_iterator = JsSymbol::iterator(); let symbol_iterator = JsSymbol::iterator();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(utf16!("length"), 0, attribute) .property(utf16!("length"), 0, attribute)
.static_method(Self::raw, "raw", 1) .static_method(Self::raw, "raw", 1)
.static_method(Self::from_char_code, "fromCharCode", 1) .static_method(Self::from_char_code, "fromCharCode", 1)

13
boa_engine/src/builtins/string/string_iterator.rs

@ -12,6 +12,7 @@ use crate::{
js_string, js_string,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -31,11 +32,17 @@ pub struct StringIterator {
} }
impl IntrinsicObject for StringIterator { impl IntrinsicObject for StringIterator {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("StringIterator", "init"); let _timer = Profiler::global().start_event("StringIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(intrinsics.objects().iterator_prototypes().iterator()) .prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0) .static_method(Self::next, "next", 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),

9
boa_engine/src/builtins/symbol/mod.rs

@ -27,6 +27,7 @@ use crate::{
js_string, js_string,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
@ -91,7 +92,7 @@ impl GlobalSymbolRegistry {
pub struct Symbol; pub struct Symbol;
impl IntrinsicObject for Symbol { impl IntrinsicObject for Symbol {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
let symbol_async_iterator = JsSymbol::async_iterator(); 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 attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let to_primitive = BuiltInBuilder::new(intrinsics) let to_primitive = BuiltInBuilder::new(realm)
.callable(Self::to_primitive) .callable(Self::to_primitive)
.name("[Symbol.toPrimitive]") .name("[Symbol.toPrimitive]")
.length(1) .length(1)
.build(); .build();
let get_description = BuiltInBuilder::new(intrinsics) let get_description = BuiltInBuilder::new(realm)
.callable(Self::get_description) .callable(Self::get_description)
.name("get description") .name("get description")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::for_, "for", 1) .static_method(Self::for_, "for", 1)
.static_method(Self::key_for, "keyFor", 1) .static_method(Self::key_for, "keyFor", 1)
.static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute) .static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute)

37
boa_engine/src/builtins/typed_array/mod.rs

@ -25,6 +25,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
@ -47,17 +48,25 @@ macro_rules! typed_array {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
} }
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); 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) .callable(TypedArray::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(intrinsics.constructors().typed_array().constructor()) .prototype(
.inherits(Some(intrinsics.constructors().typed_array().prototype())) realm
.intrinsics()
.constructors()
.typed_array()
.constructor(),
)
.inherits(Some(
realm.intrinsics().constructors().typed_array().prototype(),
))
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),
@ -231,44 +240,44 @@ macro_rules! typed_array {
pub(crate) struct TypedArray; pub(crate) struct TypedArray;
impl IntrinsicObject for TypedArray { impl IntrinsicObject for TypedArray {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let get_species = BuiltInBuilder::new(intrinsics) let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species) .callable(Self::get_species)
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
let get_buffer = BuiltInBuilder::new(intrinsics) let get_buffer = BuiltInBuilder::new(realm)
.callable(Self::buffer) .callable(Self::buffer)
.name("get buffer") .name("get buffer")
.build(); .build();
let get_byte_length = BuiltInBuilder::new(intrinsics) let get_byte_length = BuiltInBuilder::new(realm)
.callable(Self::byte_length) .callable(Self::byte_length)
.name("get byteLength") .name("get byteLength")
.build(); .build();
let get_byte_offset = BuiltInBuilder::new(intrinsics) let get_byte_offset = BuiltInBuilder::new(realm)
.callable(Self::byte_offset) .callable(Self::byte_offset)
.name("get byteOffset") .name("get byteOffset")
.build(); .build();
let get_length = BuiltInBuilder::new(intrinsics) let get_length = BuiltInBuilder::new(realm)
.callable(Self::length) .callable(Self::length)
.name("get length") .name("get length")
.build(); .build();
let get_to_string_tag = BuiltInBuilder::new(intrinsics) let get_to_string_tag = BuiltInBuilder::new(realm)
.callable(Self::to_string_tag) .callable(Self::to_string_tag)
.name("get [Symbol.toStringTag]") .name("get [Symbol.toStringTag]")
.build(); .build();
let values_function = BuiltInBuilder::new(intrinsics) let values_function = BuiltInBuilder::new(realm)
.callable(Self::values) .callable(Self::values)
.name("values") .name("values")
.length(0) .length(0)
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),

17
boa_engine/src/builtins/uri/mod.rs

@ -24,6 +24,7 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
js_string, js_string,
object::{JsFunction, JsObject}, object::{JsFunction, JsObject},
realm::Realm,
string::CodePoint, string::CodePoint,
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
@ -80,8 +81,8 @@ impl UriFunctions {
pub(crate) struct DecodeUri; pub(crate) struct DecodeUri;
impl IntrinsicObject for DecodeUri { impl IntrinsicObject for DecodeUri {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(decode_uri) .callable(decode_uri)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -99,8 +100,8 @@ impl BuiltInObject for DecodeUri {
pub(crate) struct DecodeUriComponent; pub(crate) struct DecodeUriComponent;
impl IntrinsicObject for DecodeUriComponent { impl IntrinsicObject for DecodeUriComponent {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(decode_uri_component) .callable(decode_uri_component)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -122,8 +123,8 @@ impl BuiltInObject for DecodeUriComponent {
pub(crate) struct EncodeUri; pub(crate) struct EncodeUri;
impl IntrinsicObject for EncodeUri { impl IntrinsicObject for EncodeUri {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(encode_uri) .callable(encode_uri)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
@ -140,8 +141,8 @@ impl BuiltInObject for EncodeUri {
pub(crate) struct EncodeUriComponent; pub(crate) struct EncodeUriComponent;
impl IntrinsicObject for EncodeUriComponent { impl IntrinsicObject for EncodeUriComponent {
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(encode_uri_component) .callable(encode_uri_component)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)

5
boa_engine/src/builtins/weak/weak_ref.rs

@ -6,6 +6,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
@ -28,9 +29,9 @@ impl IntrinsicObject for WeakRef {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
} }
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"WeakRef", "WeakRef",

5
boa_engine/src/builtins/weak_map/mod.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
@ -30,9 +31,9 @@ impl IntrinsicObject for WeakMap {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
} }
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,

5
boa_engine/src/builtins/weak_set/mod.rs

@ -12,6 +12,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
@ -27,9 +28,9 @@ impl IntrinsicObject for WeakSet {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
} }
fn init(intrinsics: &Intrinsics) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,

170
boa_engine/src/bytecompiler/class.rs

@ -19,29 +19,30 @@ impl ByteCompiler<'_, '_> {
pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) { pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) {
let class_name = class.name().map_or(Sym::EMPTY_STRING, Identifier::sym); 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 let Some(class_name) = class.name() {
if class.has_binding_identifier() { if class.has_binding_identifier() {
compiler.has_binding_identifier = true; compiler.has_binding_identifier = true;
compiler.context.push_compile_time_environment(false); compiler.push_compile_environment(false);
compiler.context.create_immutable_binding(class_name, true); 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() { if let Some(expr) = class.constructor() {
compiler.length = expr.parameters().length(); compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone(); compiler.params = expr.parameters().clone();
compiler compiler.create_mutable_binding(Sym::ARGUMENTS.into(), false, false);
.context compiler.arguments_binding =
.create_mutable_binding(Sym::ARGUMENTS.into(), false, false); Some(compiler.initialize_mutable_binding(Sym::ARGUMENTS.into(), false));
compiler.arguments_binding = Some(
compiler
.context
.initialize_mutable_binding(Sym::ARGUMENTS.into(), false),
);
for parameter in expr.parameters().as_ref() { for parameter in expr.parameters().as_ref() {
if parameter.is_rest_param() { if parameter.is_rest_param() {
compiler.emit_opcode(Opcode::RestParameterInit); compiler.emit_opcode(Opcode::RestParameterInit);
@ -49,9 +50,7 @@ impl ByteCompiler<'_, '_> {
match parameter.variable().binding() { match parameter.variable().binding() {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
compiler compiler.create_mutable_binding(*ident, false, false);
.context
.create_mutable_binding(*ident, false, false);
if let Some(init) = parameter.variable().init() { if let Some(init) = parameter.variable().init() {
let skip = let skip =
compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
@ -62,7 +61,7 @@ impl ByteCompiler<'_, '_> {
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); compiler.compile_declaration_pattern(pattern, BindingOpcode::InitArg);
} }
@ -72,8 +71,8 @@ impl ByteCompiler<'_, '_> {
compiler.emit_opcode(Opcode::RestParameterPop); compiler.emit_opcode(Opcode::RestParameterPop);
} }
let env_label = if expr.parameters().has_expressions() { let env_label = if expr.parameters().has_expressions() {
compiler.num_bindings = compiler.context.get_binding_number(); compiler.num_bindings = compiler.current_environment.borrow().num_bindings();
compiler.context.push_compile_time_environment(true); compiler.push_compile_environment(true);
compiler.function_environment_push_location = compiler.next_opcode_location(); compiler.function_environment_push_location = compiler.next_opcode_location();
Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment))
} else { } else {
@ -81,36 +80,28 @@ impl ByteCompiler<'_, '_> {
}; };
compiler.create_script_decls(expr.body(), false); compiler.create_script_decls(expr.body(), false);
compiler.compile_statement_list(expr.body(), false, false); compiler.compile_statement_list(expr.body(), false, false);
let env_info = compiler.pop_compile_environment();
if let Some(env_label) = env_label { if let Some(env_label) = env_label {
let (num_bindings, compile_environment) = compiler.patch_jump_with_target(env_label.0, env_info.num_bindings as u32);
compiler.context.pop_compile_time_environment(); compiler.patch_jump_with_target(env_label.1, env_info.index as u32);
let index_compile_environment = compiler.pop_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 { } else {
let (num_bindings, compile_environment) = compiler.num_bindings = env_info.num_bindings;
compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.num_bindings = num_bindings;
compiler.is_class_constructor = true; compiler.is_class_constructor = true;
} }
} else { } else {
if class.super_ref().is_some() { if class.super_ref().is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived); compiler.emit_opcode(Opcode::SuperCallDerived);
} }
let (num_bindings, compile_environment) = let env_info = compiler.pop_compile_environment();
compiler.context.pop_compile_time_environment(); compiler.num_bindings = env_info.num_bindings;
compiler.push_compile_environment(compile_environment);
compiler.num_bindings = num_bindings;
compiler.is_class_constructor = true; compiler.is_class_constructor = true;
} }
if class.name().is_some() && class.has_binding_identifier() { if class.name().is_some() && class.has_binding_identifier() {
let (_, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.pop_compile_environment();
compiler.push_compile_environment(compile_environment);
} }
compiler.emit_opcode(Opcode::PushUndefined); compiler.emit_opcode(Opcode::PushUndefined);
@ -266,25 +257,24 @@ impl ByteCompiler<'_, '_> {
self.compile_expr(name, true); self.compile_expr(name, true);
} }
} }
let mut field_compiler = let mut field_compiler = ByteCompiler::new(
ByteCompiler::new(Sym::EMPTY_STRING, true, self.json_parse, self.context); Sym::EMPTY_STRING,
field_compiler.context.push_compile_time_environment(false); true,
field_compiler self.json_parse,
.context self.current_environment.clone(),
.create_immutable_binding(class_name.into(), true); self.context,
field_compiler.context.push_compile_time_environment(true); );
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 { if let Some(node) = field {
field_compiler.compile_expr(node, true); field_compiler.compile_expr(node, true);
} else { } else {
field_compiler.emit_opcode(Opcode::PushUndefined); field_compiler.emit_opcode(Opcode::PushUndefined);
} }
let (num_bindings, compile_environment) = let env_info = field_compiler.pop_compile_environment();
field_compiler.context.pop_compile_time_environment(); field_compiler.pop_compile_environment();
field_compiler.push_compile_environment(compile_environment); field_compiler.num_bindings = env_info.num_bindings;
let (_, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
field_compiler.num_bindings = num_bindings;
field_compiler.emit_opcode(Opcode::Return); field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish(); let mut code = field_compiler.finish();
@ -299,25 +289,24 @@ impl ByteCompiler<'_, '_> {
ClassElement::PrivateFieldDefinition(name, field) => { ClassElement::PrivateFieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
let name_index = self.get_or_insert_private_name(*name); let name_index = self.get_or_insert_private_name(*name);
let mut field_compiler = let mut field_compiler = ByteCompiler::new(
ByteCompiler::new(class_name, true, self.json_parse, self.context); class_name,
field_compiler.context.push_compile_time_environment(false); true,
field_compiler self.json_parse,
.context self.current_environment.clone(),
.create_immutable_binding(class_name.into(), true); self.context,
field_compiler.context.push_compile_time_environment(true); );
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 { if let Some(node) = field {
field_compiler.compile_expr(node, true); field_compiler.compile_expr(node, true);
} else { } else {
field_compiler.emit_opcode(Opcode::PushUndefined); field_compiler.emit_opcode(Opcode::PushUndefined);
} }
let (num_bindings, compile_environment) = let env_info = field_compiler.pop_compile_environment();
field_compiler.context.pop_compile_time_environment(); field_compiler.pop_compile_environment();
field_compiler.push_compile_environment(compile_environment); field_compiler.num_bindings = env_info.num_bindings;
let (_, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
field_compiler.num_bindings = num_bindings;
field_compiler.emit_opcode(Opcode::Return); field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish(); let mut code = field_compiler.finish();
@ -342,25 +331,24 @@ impl ByteCompiler<'_, '_> {
None None
} }
}; };
let mut field_compiler = let mut field_compiler = ByteCompiler::new(
ByteCompiler::new(class_name, true, self.json_parse, self.context); class_name,
field_compiler.context.push_compile_time_environment(false); true,
field_compiler self.json_parse,
.context self.current_environment.clone(),
.create_immutable_binding(class_name.into(), true); self.context,
field_compiler.context.push_compile_time_environment(true); );
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 { if let Some(node) = field {
field_compiler.compile_expr(node, true); field_compiler.compile_expr(node, true);
} else { } else {
field_compiler.emit_opcode(Opcode::PushUndefined); field_compiler.emit_opcode(Opcode::PushUndefined);
} }
let (num_bindings, compile_environment) = let env_info = field_compiler.pop_compile_environment();
field_compiler.context.pop_compile_time_environment(); field_compiler.pop_compile_environment();
field_compiler.push_compile_environment(compile_environment); field_compiler.num_bindings = env_info.num_bindings;
let (_, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
field_compiler.num_bindings = num_bindings;
field_compiler.emit_opcode(Opcode::Return); field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish(); let mut code = field_compiler.finish();
@ -390,21 +378,21 @@ impl ByteCompiler<'_, '_> {
} }
ClassElement::StaticBlock(statement_list) => { ClassElement::StaticBlock(statement_list) => {
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
let mut compiler = let mut compiler = ByteCompiler::new(
ByteCompiler::new(Sym::EMPTY_STRING, true, false, self.context); Sym::EMPTY_STRING,
compiler.context.push_compile_time_environment(false); true,
compiler false,
.context self.current_environment.clone(),
.create_immutable_binding(class_name.into(), true); self.context,
compiler.context.push_compile_time_environment(true); );
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.create_script_decls(statement_list, false);
compiler.compile_statement_list(statement_list, false, false); compiler.compile_statement_list(statement_list, false, false);
let (num_bindings, compile_environment) = let env_info = compiler.pop_compile_environment();
compiler.context.pop_compile_time_environment(); compiler.pop_compile_environment();
compiler.push_compile_environment(compile_environment); compiler.num_bindings = env_info.num_bindings;
let (_, compile_environment) = compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.num_bindings = num_bindings;
let code = Gc::new(compiler.finish()); let code = Gc::new(compiler.finish());
let index = self.functions.len() as u32; let index = self.functions.len() as u32;

150
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::<JsString>(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)
}
}

4
boa_engine/src/bytecompiler/expression/assign.rs

@ -54,7 +54,7 @@ impl ByteCompiler<'_, '_> {
match access { match access {
Access::Variable { name } => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::GetName, &[index]); self.emit(Opcode::GetName, &[index]);
@ -69,7 +69,7 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Dup); 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::SetName, &[index]); self.emit(Opcode::SetName, &[index]);
} }

2
boa_engine/src/bytecompiler/expression/unary.rs

@ -27,7 +27,7 @@ impl ByteCompiler<'_, '_> {
UnaryOp::TypeOf => { UnaryOp::TypeOf => {
match unary.target().flatten() { match unary.target().flatten() {
Expression::Identifier(identifier) => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::GetNameOrUndefined, &[index]); self.emit(Opcode::GetNameOrUndefined, &[index]);
} }

4
boa_engine/src/bytecompiler/expression/update.rs

@ -22,7 +22,7 @@ impl ByteCompiler<'_, '_> {
match Access::from_update_target(update.target()) { match Access::from_update_target(update.target()) {
Access::Variable { name } => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::GetName, &[index]); self.emit(Opcode::GetName, &[index]);
@ -33,7 +33,7 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Dup); 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::SetName, &[index]); self.emit(Opcode::SetName, &[index]);
} }

84
boa_engine/src/bytecompiler/function.rs

@ -1,13 +1,14 @@
use crate::{ use crate::{
builtins::function::ThisMode, builtins::function::ThisMode,
bytecompiler::ByteCompiler, bytecompiler::ByteCompiler,
environments::CompileTimeEnvironment,
vm::{BindingOpcode, CodeBlock, Opcode}, vm::{BindingOpcode, CodeBlock, Opcode},
Context, Context,
}; };
use boa_ast::{ use boa_ast::{
declaration::Binding, function::FormalParameterList, operations::bound_names, StatementList, declaration::Binding, function::FormalParameterList, operations::bound_names, StatementList,
}; };
use boa_gc::Gc; use boa_gc::{Gc, GcRefCell};
use boa_interner::Sym; use boa_interner::Sym;
/// `FunctionCompiler` is used to compile AST functions to bytecode. /// `FunctionCompiler` is used to compile AST functions to bytecode.
@ -89,13 +90,14 @@ impl FunctionCompiler {
mut self, mut self,
parameters: &FormalParameterList, parameters: &FormalParameterList,
body: &StatementList, body: &StatementList,
outer_env: Gc<GcRefCell<CompileTimeEnvironment>>,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> Gc<CodeBlock> { ) -> Gc<CodeBlock> {
self.strict = self.strict || body.strict(); self.strict = self.strict || body.strict();
let length = parameters.length(); 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.length = length;
compiler.in_async_generator = self.generator && self.r#async; compiler.in_async_generator = self.generator && self.r#async;
@ -104,21 +106,23 @@ impl FunctionCompiler {
} }
if let Some(class_name) = self.class_name { if let Some(class_name) = self.class_name {
compiler.context.push_compile_time_environment(false); compiler.push_compile_environment(false);
compiler compiler.create_immutable_binding(class_name.into(), true);
.context
.create_immutable_binding(class_name.into(), true);
} }
if let Some(binding_identifier) = self.binding_identifier { if let Some(binding_identifier) = self.binding_identifier {
compiler.has_binding_identifier = true; compiler.has_binding_identifier = true;
compiler.context.push_compile_time_environment(false); compiler.push_compile_environment(false);
compiler compiler.create_immutable_binding(binding_identifier.into(), self.strict);
.context
.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 // An arguments object is added when all of the following conditions are met
// - If not in an arrow function (10.2.11.16) // - 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. // 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 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() { if !(self.arrow) && !parameters.has_arguments() {
compiler let arguments = Sym::ARGUMENTS.into();
.context compiler.arguments_binding = Some(if self.strict {
.create_mutable_binding(Sym::ARGUMENTS.into(), false, false); compiler.create_immutable_binding(arguments, true);
compiler.arguments_binding = Some( compiler.initialize_immutable_binding(arguments)
compiler } else {
.context compiler.create_mutable_binding(arguments, false, false);
.initialize_mutable_binding(Sym::ARGUMENTS.into(), false), compiler.initialize_mutable_binding(arguments, false)
); });
} }
for parameter in parameters.as_ref() { for parameter in parameters.as_ref() {
@ -143,20 +147,18 @@ impl FunctionCompiler {
match parameter.variable().binding() { match parameter.variable().binding() {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
compiler compiler.create_mutable_binding(*ident, false, false);
.context
.create_mutable_binding(*ident, false, false);
// TODO: throw custom error if ident is in init // TODO: throw custom error if ident is in init
if let Some(init) = parameter.variable().init() { if let Some(init) = parameter.variable().init() {
let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
compiler.compile_expr(init, true); compiler.compile_expr(init, true);
compiler.patch_jump(skip); compiler.patch_jump(skip);
} }
compiler.emit_binding(BindingOpcode::InitArg, *ident); compiler.emit_binding(BindingOpcode::InitLet, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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 // TODO: throw custom error if ident is in init
if let Some(init) = parameter.variable().init() { if let Some(init) = parameter.variable().init() {
@ -164,7 +166,7 @@ impl FunctionCompiler {
compiler.compile_expr(init, true); compiler.compile_expr(init, true);
compiler.patch_jump(skip); 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() { let env_label = if parameters.has_expressions() {
compiler.num_bindings = compiler.context.get_binding_number(); compiler.push_compile_environment(true);
compiler.context.push_compile_time_environment(true);
compiler.function_environment_push_location = compiler.next_opcode_location(); compiler.function_environment_push_location = compiler.next_opcode_location();
Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment))
} else { } else {
@ -192,29 +193,24 @@ impl FunctionCompiler {
compiler.compile_statement_list(body, false, false); compiler.compile_statement_list(body, false, false);
if let Some(env_label) = env_label { if let Some(env_label) = env_label {
let (num_bindings, compile_environment) = let env_info = compiler.pop_compile_environment();
compiler.context.pop_compile_time_environment(); compiler.patch_jump_with_target(env_label.0, env_info.num_bindings as u32);
let index_compile_environment = compiler.push_compile_environment(compile_environment); compiler.patch_jump_with_target(env_label.1, env_info.index as u32);
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;
} }
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() { if self.binding_identifier.is_some() {
let (_, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.pop_compile_environment();
compiler.push_compile_environment(compile_environment);
} }
if self.class_name.is_some() { if self.class_name.is_some() {
let (_, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.pop_compile_environment();
compiler.push_compile_environment(compile_environment);
} }
compiler.params = parameters.clone(); compiler.params = parameters.clone();

113
boa_engine/src/bytecompiler/mod.rs

@ -2,6 +2,7 @@
mod class; mod class;
mod declaration; mod declaration;
mod env;
mod expression; mod expression;
mod function; mod function;
mod jump_control; mod jump_control;
@ -214,7 +215,7 @@ impl Access<'_> {
/// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode. /// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode.
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct ByteCompiler<'b, 'host> { pub struct ByteCompiler<'ctx, 'host> {
/// Name of this function. /// Name of this function.
pub(crate) function_name: Sym, 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. /// 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, pub(crate) function_environment_push_location: u32,
/// The environment that is currently active.
pub(crate) current_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
/// The number of bindings in the parameters environment.
pub(crate) parameters_env_bindings: Option<usize>,
literals_map: FxHashMap<Literal, u32>, literals_map: FxHashMap<Literal, u32>,
names_map: FxHashMap<Identifier, u32>, names_map: FxHashMap<Identifier, u32>,
private_names_map: FxHashMap<PrivateName, u32>, private_names_map: FxHashMap<PrivateName, u32>,
@ -279,21 +286,24 @@ pub struct ByteCompiler<'b, 'host> {
jump_info: Vec<JumpControlInfo>, jump_info: Vec<JumpControlInfo>,
in_async_generator: bool, in_async_generator: bool,
json_parse: 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. /// Represents a placeholder address that will be patched later.
const DUMMY_ADDRESS: u32 = u32::MAX; const DUMMY_ADDRESS: u32 = u32::MAX;
/// Creates a new [`ByteCompiler`]. /// Creates a new [`ByteCompiler`].
#[inline] #[inline]
pub fn new( pub(crate) fn new(
name: Sym, name: Sym,
strict: bool, strict: bool,
json_parse: bool, json_parse: bool,
context: &'b mut Context<'host>, current_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) -> ByteCompiler<'b, 'host> { // TODO: remove when we separate scripts from the context
context: &'ctx mut Context<'host>,
) -> ByteCompiler<'ctx, 'host> {
Self { Self {
function_name: name, function_name: name,
strict, strict,
@ -313,6 +323,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
is_class_constructor: false, is_class_constructor: false,
class_field_initializer_name: None, class_field_initializer_name: None,
function_environment_push_location: 0, function_environment_push_location: 0,
parameters_env_bindings: None,
literals_map: FxHashMap::default(), literals_map: FxHashMap::default(),
names_map: FxHashMap::default(), names_map: FxHashMap::default(),
@ -321,6 +332,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
jump_info: Vec::new(), jump_info: Vec::new(),
in_async_generator: false, in_async_generator: false,
json_parse, json_parse,
current_environment,
context, context,
} }
} }
@ -329,16 +341,6 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
self.context.interner() 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<GcRefCell<CompileTimeEnvironment>>,
) -> usize {
let index = self.compile_environments.len();
self.compile_environments.push(environment);
index
}
fn get_or_insert_literal(&mut self, literal: Literal) -> u32 { fn get_or_insert_literal(&mut self, literal: Literal) -> u32 {
if let Some(index) = self.literals_map.get(&literal) { if let Some(index) = self.literals_map.get(&literal) {
return *index; return *index;
@ -393,41 +395,41 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) { fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) {
match opcode { match opcode {
BindingOpcode::Var => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefVar, &[index]); self.emit(Opcode::DefVar, &[index]);
} }
BindingOpcode::Let => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefLet, &[index]); self.emit(Opcode::DefLet, &[index]);
} }
BindingOpcode::InitVar => { BindingOpcode::InitVar => {
let binding = if self.context.has_binding(name) { let binding = if self.has_binding(name) {
self.context.set_mutable_binding(name) self.set_mutable_binding(name)
} else { } else {
self.context.initialize_mutable_binding(name, true) self.initialize_mutable_binding(name, true)
}; };
let index = self.get_or_insert_binding(binding); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitVar, &[index]); self.emit(Opcode::DefInitVar, &[index]);
} }
BindingOpcode::InitLet => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitLet, &[index]); self.emit(Opcode::DefInitLet, &[index]);
} }
BindingOpcode::InitArg => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitArg, &[index]); self.emit(Opcode::DefInitArg, &[index]);
} }
BindingOpcode::InitConst => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitConst, &[index]); self.emit(Opcode::DefInitConst, &[index]);
} }
BindingOpcode::SetName => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::SetName, &[index]); 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) { fn access_get(&mut self, access: Access<'_>, use_expr: bool) {
match access { match access {
Access::Variable { name } => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::GetName, &[index]); self.emit(Opcode::GetName, &[index]);
} }
@ -633,7 +635,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
if use_expr { if use_expr {
self.emit(Opcode::Dup, &[]); 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::SetName, &[index]); self.emit(Opcode::SetName, &[index]);
} }
@ -717,7 +719,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
} }
}, },
Access::Variable { name } => { 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); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DeleteName, &[index]); self.emit(Opcode::DeleteName, &[index]);
} }
@ -765,7 +767,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
use_expr: bool, use_expr: bool,
strict: 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.create_script_decls(list, true); 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 env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
} }
@ -1122,7 +1123,12 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
.strict(self.strict) .strict(self.strict)
.arrow(arrow) .arrow(arrow)
.binding_identifier(binding_identifier) .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; let index = self.functions.len() as u32;
self.functions.push(code); self.functions.push(code);
@ -1196,7 +1202,12 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
.arrow(arrow) .arrow(arrow)
.binding_identifier(binding_identifier) .binding_identifier(binding_identifier)
.class_name(class_name) .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; let index = self.functions.len() as u32;
self.functions.push(code); self.functions.push(code);
@ -1327,6 +1338,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
is_class_constructor: self.is_class_constructor, is_class_constructor: self.is_class_constructor,
class_field_initializer_name: self.class_field_initializer_name, class_field_initializer_name: self.class_field_initializer_name,
function_environment_push_location: self.function_environment_push_location, function_environment_push_location: self.function_environment_push_location,
parameters_env_bindings: self.parameters_env_bindings,
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
trace: std::cell::Cell::new(false), trace: std::cell::Cell::new(false),
} }
@ -1336,7 +1348,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
self.compile_declaration_pattern_impl(pattern, def); 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( pub(crate) fn create_script_decls(
&mut self, &mut self,
stmt_list: &StatementList, stmt_list: &StatementList,
@ -1360,16 +1372,14 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
if *ident == Sym::ARGUMENTS { if *ident == Sym::ARGUMENTS {
has_identifier_argument = true; has_identifier_argument = true;
} }
self.context self.create_mutable_binding(*ident, true, configurable_globals);
.create_mutable_binding(*ident, true, configurable_globals);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(pattern) { for ident in bound_names(pattern) {
if ident == Sym::ARGUMENTS { if ident == Sym::ARGUMENTS {
has_identifier_argument = true; has_identifier_argument = true;
} }
self.context self.create_mutable_binding(ident, true, configurable_globals);
.create_mutable_binding(ident, true, configurable_globals);
} }
} }
} }
@ -1388,14 +1398,14 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
if *ident == Sym::ARGUMENTS { if *ident == Sym::ARGUMENTS {
has_identifier_argument = true; has_identifier_argument = true;
} }
self.context.create_mutable_binding(*ident, false, false); self.create_mutable_binding(*ident, false, false);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(pattern) { for ident in bound_names(pattern) {
if ident == Sym::ARGUMENTS { if ident == Sym::ARGUMENTS {
has_identifier_argument = true; 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 { if *ident == Sym::ARGUMENTS {
has_identifier_argument = true; has_identifier_argument = true;
} }
self.context.create_immutable_binding(*ident, true); self.create_immutable_binding(*ident, true);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(pattern) { for ident in bound_names(pattern) {
if ident == Sym::ARGUMENTS { if ident == Sym::ARGUMENTS {
has_identifier_argument = true; 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::Lexical(decl) => self.create_decls_from_lexical_decl(decl),
Declaration::Function(decl) => { Declaration::Function(decl) => {
let ident = decl.name().expect("function declaration must have a name"); let ident = decl.name().expect("function declaration must have a name");
self.context self.create_mutable_binding(ident, true, configurable_globals);
.create_mutable_binding(ident, true, configurable_globals);
ident == Sym::ARGUMENTS ident == Sym::ARGUMENTS
} }
Declaration::Generator(decl) => { Declaration::Generator(decl) => {
let ident = decl.name().expect("generator declaration must have a name"); let ident = decl.name().expect("generator declaration must have a name");
self.context self.create_mutable_binding(ident, true, configurable_globals);
.create_mutable_binding(ident, true, configurable_globals);
ident == Sym::ARGUMENTS ident == Sym::ARGUMENTS
} }
Declaration::AsyncFunction(decl) => { Declaration::AsyncFunction(decl) => {
let ident = decl let ident = decl
.name() .name()
.expect("async function declaration must have a name"); .expect("async function declaration must have a name");
self.context self.create_mutable_binding(ident, true, configurable_globals);
.create_mutable_binding(ident, true, configurable_globals);
ident == Sym::ARGUMENTS ident == Sym::ARGUMENTS
} }
Declaration::AsyncGenerator(decl) => { Declaration::AsyncGenerator(decl) => {
let ident = decl let ident = decl
.name() .name()
.expect("async generator declaration must have a name"); .expect("async generator declaration must have a name");
self.context self.create_mutable_binding(ident, true, configurable_globals);
.create_mutable_binding(ident, true, configurable_globals);
ident == Sym::ARGUMENTS ident == Sym::ARGUMENTS
} }
Declaration::Class(decl) => { Declaration::Class(decl) => {
let ident = decl.name().expect("class declaration must have a name"); let ident = decl.name().expect("class declaration must have a name");
self.context self.create_mutable_binding(ident, false, configurable_globals);
.create_mutable_binding(ident, false, configurable_globals);
false false
} }
} }

9
boa_engine/src/bytecompiler/statement/block.rs

@ -10,16 +10,15 @@ impl ByteCompiler<'_, '_> {
use_expr: bool, use_expr: bool,
configurable_globals: 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.create_script_decls(block.statement_list(), configurable_globals); self.create_script_decls(block.statement_list(), configurable_globals);
self.compile_statement_list(block.statement_list(), use_expr, 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 env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
} }

86
boa_engine/src/bytecompiler/statement/loop.rs

@ -20,7 +20,7 @@ impl ByteCompiler<'_, '_> {
label: Option<Sym>, label: Option<Sym>,
configurable_globals: 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.push_empty_loop_jump_control(); self.push_empty_loop_jump_control();
@ -71,10 +71,9 @@ impl ByteCompiler<'_, '_> {
self.emit(Opcode::Jump, &[start_address]); self.emit(Opcode::Jump, &[start_address]);
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.patch_jump(exit); self.patch_jump(exit);
self.patch_jump(loop_exit); self.patch_jump(loop_exit);
@ -94,18 +93,17 @@ impl ByteCompiler<'_, '_> {
if init_bound_names.is_empty() { if init_bound_names.is_empty() {
self.compile_expr(for_in_loop.target(), true); self.compile_expr(for_in_loop.target(), true);
} else { } else {
self.context.push_compile_time_environment(false); self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for name in init_bound_names { 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); self.compile_expr(for_in_loop.target(), true);
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); 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(continue_label, start_address);
self.patch_jump_with_target(loop_start, 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.emit_opcode(Opcode::Pop); // pop the `done` value. self.emit_opcode(Opcode::Pop); // pop the `done` value.
self.emit_opcode(Opcode::IteratorNext); self.emit_opcode(Opcode::IteratorNext);
@ -129,8 +127,8 @@ impl ByteCompiler<'_, '_> {
match for_in_loop.initializer() { match for_in_loop.initializer() {
IterableLoopInitializer::Identifier(ident) => { IterableLoopInitializer::Identifier(ident) => {
self.context.create_mutable_binding(*ident, true, true); self.create_mutable_binding(*ident, true, true);
let binding = self.context.set_mutable_binding(*ident); let binding = self.set_mutable_binding(*ident);
let index = self.get_or_insert_binding(binding); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitVar, &[index]); self.emit(Opcode::DefInitVar, &[index]);
} }
@ -143,44 +141,43 @@ impl ByteCompiler<'_, '_> {
} }
IterableLoopInitializer::Var(declaration) => match declaration { IterableLoopInitializer::Var(declaration) => match declaration {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context self.create_mutable_binding(*ident, true, configurable_globals);
.create_mutable_binding(*ident, true, configurable_globals);
self.emit_binding(BindingOpcode::InitVar, *ident); self.emit_binding(BindingOpcode::InitVar, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitVar);
} }
}, },
IterableLoopInitializer::Let(declaration) => match declaration { IterableLoopInitializer::Let(declaration) => match declaration {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context.create_mutable_binding(*ident, false, false); self.create_mutable_binding(*ident, false, false);
self.emit_binding(BindingOpcode::InitLet, *ident); self.emit_binding(BindingOpcode::InitLet, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
} }
}, },
IterableLoopInitializer::Const(declaration) => match declaration { IterableLoopInitializer::Const(declaration) => match declaration {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context.create_immutable_binding(*ident, true); self.create_immutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitConst, *ident); self.emit_binding(BindingOpcode::InitConst, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitConst);
} }
}, },
IterableLoopInitializer::Pattern(pattern) => { IterableLoopInitializer::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitVar);
} }
@ -188,10 +185,9 @@ impl ByteCompiler<'_, '_> {
self.compile_stmt(for_in_loop.body(), false, configurable_globals); self.compile_stmt(for_in_loop.body(), false, configurable_globals);
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
self.emit(Opcode::Jump, &[start_address]); self.emit(Opcode::Jump, &[start_address]);
@ -216,18 +212,17 @@ impl ByteCompiler<'_, '_> {
if init_bound_names.is_empty() { if init_bound_names.is_empty() {
self.compile_expr(for_of_loop.iterable(), true); self.compile_expr(for_of_loop.iterable(), true);
} else { } else {
self.context.push_compile_time_environment(false); self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for name in init_bound_names { 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); self.compile_expr(for_of_loop.iterable(), true);
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); 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(loop_start, start_address);
self.patch_jump_with_target(cont_label, 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.emit_opcode(Opcode::Pop); // pop the `done` value. self.emit_opcode(Opcode::Pop); // pop the `done` value.
@ -259,8 +254,8 @@ impl ByteCompiler<'_, '_> {
match for_of_loop.initializer() { match for_of_loop.initializer() {
IterableLoopInitializer::Identifier(ref ident) => { IterableLoopInitializer::Identifier(ref ident) => {
self.context.create_mutable_binding(*ident, true, true); self.create_mutable_binding(*ident, true, true);
let binding = self.context.set_mutable_binding(*ident); let binding = self.set_mutable_binding(*ident);
let index = self.get_or_insert_binding(binding); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitVar, &[index]); self.emit(Opcode::DefInitVar, &[index]);
} }
@ -273,43 +268,43 @@ impl ByteCompiler<'_, '_> {
} }
IterableLoopInitializer::Var(declaration) => match declaration { IterableLoopInitializer::Var(declaration) => match declaration {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context.create_mutable_binding(*ident, true, false); self.create_mutable_binding(*ident, true, false);
self.emit_binding(BindingOpcode::InitVar, *ident); self.emit_binding(BindingOpcode::InitVar, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitVar);
} }
}, },
IterableLoopInitializer::Let(declaration) => match declaration { IterableLoopInitializer::Let(declaration) => match declaration {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context.create_mutable_binding(*ident, false, false); self.create_mutable_binding(*ident, false, false);
self.emit_binding(BindingOpcode::InitLet, *ident); self.emit_binding(BindingOpcode::InitLet, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
} }
}, },
IterableLoopInitializer::Const(declaration) => match declaration { IterableLoopInitializer::Const(declaration) => match declaration {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context.create_immutable_binding(*ident, true); self.create_immutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitConst, *ident); self.emit_binding(BindingOpcode::InitConst, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitConst);
} }
}, },
IterableLoopInitializer::Pattern(pattern) => { IterableLoopInitializer::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitVar);
} }
@ -317,10 +312,9 @@ impl ByteCompiler<'_, '_> {
self.compile_stmt(for_of_loop.body(), false, configurable_globals); self.compile_stmt(for_of_loop.body(), false, configurable_globals);
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
self.emit(Opcode::Jump, &[start_address]); self.emit(Opcode::Jump, &[start_address]);

9
boa_engine/src/bytecompiler/statement/switch.rs

@ -4,7 +4,7 @@ use boa_ast::statement::Switch;
impl ByteCompiler<'_, '_> { impl ByteCompiler<'_, '_> {
/// Compile a [`Switch`] `boa_ast` node /// Compile a [`Switch`] `boa_ast` node
pub(crate) fn compile_switch(&mut self, switch: &Switch, configurable_globals: bool) { 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for case in switch.cases() { for case in switch.cases() {
self.create_script_decls(case.body(), configurable_globals); self.create_script_decls(case.body(), configurable_globals);
@ -39,10 +39,9 @@ impl ByteCompiler<'_, '_> {
self.patch_jump(end_label); self.patch_jump(end_label);
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
} }
} }

31
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.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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.create_script_decls(t.block().statement_list(), configurable_globals); self.create_script_decls(t.block().statement_list(), configurable_globals);
self.compile_statement_list(t.block().statement_list(), use_expr, 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 env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
self.emit_opcode(Opcode::TryEnd); self.emit_opcode(Opcode::TryEnd);
@ -70,18 +69,18 @@ impl ByteCompiler<'_, '_> {
self.set_jump_control_in_catch(true); self.set_jump_control_in_catch(true);
let catch_end = self.emit_opcode_with_operand(Opcode::CatchStart); 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
if let Some(binding) = catch.parameter() { if let Some(binding) = catch.parameter() {
match binding { match binding {
Binding::Identifier(ident) => { Binding::Identifier(ident) => {
self.context.create_mutable_binding(*ident, false, false); self.create_mutable_binding(*ident, false, false);
self.emit_binding(BindingOpcode::InitLet, *ident); self.emit_binding(BindingOpcode::InitLet, *ident);
} }
Binding::Pattern(pattern) => { Binding::Pattern(pattern) => {
for ident in bound_names(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); self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
} }
@ -97,10 +96,9 @@ impl ByteCompiler<'_, '_> {
configurable_globals, configurable_globals,
); );
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
if parent_try.finally().is_some() { if parent_try.finally().is_some() {
self.emit_opcode(Opcode::CatchEnd); self.emit_opcode(Opcode::CatchEnd);
@ -118,7 +116,7 @@ impl ByteCompiler<'_, '_> {
finally_end_label: Label, finally_end_label: Label,
configurable_globals: 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); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.create_script_decls(finally.block().statement_list(), configurable_globals); self.create_script_decls(finally.block().statement_list(), configurable_globals);
@ -128,10 +126,9 @@ impl ByteCompiler<'_, '_> {
configurable_globals, configurable_globals,
); );
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let env_info = self.pop_compile_environment();
let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, env_info.num_bindings as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, env_info.index as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
self.pop_finally_control_info(); self.pop_finally_control_info();

4
boa_engine/src/bytecompiler/statement/with.rs

@ -5,10 +5,10 @@ impl ByteCompiler<'_, '_> {
/// Compile a [`With`] `boa_ast` node /// Compile a [`With`] `boa_ast` node
pub(crate) fn compile_with(&mut self, with: &With, configurable_globals: bool) { pub(crate) fn compile_with(&mut self, with: &With, configurable_globals: bool) {
self.compile_expr(with.expression(), true); self.compile_expr(with.expression(), true);
self.context.push_compile_time_environment(false); self.push_compile_environment(false);
self.emit_opcode(Opcode::PushObjectEnvironment); self.emit_opcode(Opcode::PushObjectEnvironment);
self.compile_stmt(with.statement(), false, configurable_globals); self.compile_stmt(with.statement(), false, configurable_globals);
self.context.pop_compile_time_environment(); self.pop_compile_environment();
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
} }
} }

14
boa_engine/src/context/hooks.rs

@ -2,6 +2,7 @@ use crate::{
builtins::promise::OperationType, builtins::promise::OperationType,
job::JobCallback, job::JobCallback,
object::{JsFunction, JsObject}, object::{JsFunction, JsObject},
realm::Realm,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -18,13 +19,20 @@ use super::intrinsics::Intrinsics;
/// need to be redefined: /// 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; /// struct Hooks;
/// ///
/// impl HostHooks for Hooks { /// impl HostHooks for Hooks {
/// fn ensure_can_compile_strings( /// fn ensure_can_compile_strings(
/// &self, /// &self,
/// _realm: Realm,
/// context: &mut Context<'_>, /// context: &mut Context<'_>,
/// ) -> JsResult<()> { /// ) -> JsResult<()> {
/// Err(JsNativeError::typ().with_message("eval calls not available").into()) /// 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. /// containing unused. This is already ensured by the return type.
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-hostensurecancompilestrings /// [spec]: https://tc39.es/ecma262/#sec-hostensurecancompilestrings
// TODO: Track https://github.com/tc39/ecma262/issues/938
fn ensure_can_compile_strings( fn ensure_can_compile_strings(
&self, &self,
/* Realm (WIP), */ _context: &mut Context<'_>, _realm: Realm,
_context: &mut Context<'_>,
) -> JsResult<()> { ) -> JsResult<()> {
// The default implementation of HostEnsureCanCompileStrings is to return NormalCompletion(unused). // The default implementation of HostEnsureCanCompileStrings is to return NormalCompletion(unused).
Ok(()) Ok(())

36
boa_engine/src/context/intrinsics.rs

@ -1,6 +1,6 @@
//! Data structures that contain intrinsic objects and constructors. //! Data structures that contain intrinsic objects and constructors.
use boa_gc::{Finalize, Gc, Trace}; use boa_gc::{Finalize, Trace};
use crate::{ use crate::{
builtins::{iterable::IteratorPrototypes, uri::UriFunctions}, builtins::{iterable::IteratorPrototypes, uri::UriFunctions},
@ -11,28 +11,8 @@ use crate::{
/// ///
/// `Intrinsics` is internally stored using a `Gc`, which makes it cheapily clonable /// `Intrinsics` is internally stored using a `Gc`, which makes it cheapily clonable
/// for multiple references to the same set of intrinsic objects. /// for multiple references to the same set of intrinsic objects.
#[derive(Default, Clone, Trace, Finalize)] #[derive(Debug, Default, Trace, Finalize)]
pub struct Intrinsics { pub struct Intrinsics {
inner: Gc<Inner>,
}
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 /// Cached standard constructors
pub(super) constructors: StandardConstructors, pub(super) constructors: StandardConstructors,
/// Cached intrinsic objects /// Cached intrinsic objects
@ -42,19 +22,19 @@ struct Inner {
impl Intrinsics { impl Intrinsics {
/// Return the cached intrinsic objects. /// Return the cached intrinsic objects.
#[inline] #[inline]
pub fn objects(&self) -> &IntrinsicObjects { pub const fn objects(&self) -> &IntrinsicObjects {
&self.inner.objects &self.objects
} }
/// Return the cached standard constructors. /// Return the cached standard constructors.
#[inline] #[inline]
pub fn constructors(&self) -> &StandardConstructors { pub const fn constructors(&self) -> &StandardConstructors {
&self.inner.constructors &self.constructors
} }
} }
/// Store a builtin constructor (such as `Object`) and its corresponding prototype. /// Store a builtin constructor (such as `Object`) and its corresponding prototype.
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
pub struct StandardConstructor { pub struct StandardConstructor {
pub(crate) constructor: JsObject, pub(crate) constructor: JsObject,
pub(crate) prototype: JsObject, pub(crate) prototype: JsObject,
@ -96,7 +76,7 @@ impl StandardConstructor {
} }
/// Cached core standard constructors. /// Cached core standard constructors.
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
pub struct StandardConstructors { pub struct StandardConstructors {
object: StandardConstructor, object: StandardConstructor,
proxy: StandardConstructor, proxy: StandardConstructor,

83
boa_engine/src/context/mod.rs

@ -79,7 +79,7 @@ use boa_profiler::Profiler;
/// ``` /// ```
pub struct Context<'host> { pub struct Context<'host> {
/// realm holds both the global object and the environment /// realm holds both the global object and the environment
pub(crate) realm: Realm, realm: Realm,
/// String interner in the context. /// String interner in the context.
interner: Interner, interner: Interner,
@ -135,7 +135,7 @@ impl Default for Context<'_> {
} }
// ==== Public API ==== // ==== Public API ====
impl Context<'_> { impl<'host> Context<'host> {
/// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or /// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or
/// the icu data provider. /// the icu data provider.
#[must_use] #[must_use]
@ -245,7 +245,13 @@ impl Context<'_> {
/// Compile the script AST into a `CodeBlock` ready to be executed by the VM. /// Compile the script AST into a `CodeBlock` ready to be executed by the VM.
pub fn compile_script(&mut self, statement_list: &StatementList) -> JsResult<Gc<CodeBlock>> { pub fn compile_script(&mut self, statement_list: &StatementList) -> JsResult<Gc<CodeBlock>> {
let _timer = Profiler::global().start_event("Script compilation", "Main"); 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.create_script_decls(statement_list, false);
compiler.compile_statement_list(statement_list, true, false); compiler.compile_statement_list(statement_list, true, false);
Ok(Gc::new(compiler.finish())) Ok(Gc::new(compiler.finish()))
@ -255,7 +261,13 @@ impl Context<'_> {
pub fn compile_module(&mut self, statement_list: &ModuleItemList) -> JsResult<Gc<CodeBlock>> { pub fn compile_module(&mut self, statement_list: &ModuleItemList) -> JsResult<Gc<CodeBlock>> {
let _timer = Profiler::global().start_event("Module compilation", "Main"); 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.create_module_decls(statement_list, false);
compiler.compile_module_item_list(statement_list, false); compiler.compile_module_item_list(statement_list, false);
Ok(Gc::new(compiler.finish())) Ok(Gc::new(compiler.finish()))
@ -276,7 +288,9 @@ impl Context<'_> {
self.vm.push_frame(CallFrame::new(code_block)); 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(); let record = self.run();
self.vm.pop_frame(); self.vm.pop_frame();
self.clear_kept_objects(); self.clear_kept_objects();
@ -446,10 +460,16 @@ impl Context<'_> {
self.realm.global_object().clone() 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] #[inline]
pub const fn intrinsics(&self) -> &Intrinsics { pub const fn realm(&self) -> &Realm {
&self.realm.intrinsics &self.realm
} }
/// Set the value of trace on the context /// Set the value of trace on the context
@ -494,46 +514,31 @@ impl Context<'_> {
pub fn clear_kept_objects(&mut self) { pub fn clear_kept_objects(&mut self) {
self.kept_alive.clear(); self.kept_alive.clear();
} }
}
// ==== Private API ====
impl Context<'_> { /// Replaces the currently active realm with `realm`, and returns the old realm.
/// Compile the AST into a `CodeBlock` ready to be executed by the VM in a `JSON.parse` context. pub fn enter_realm(&mut self, realm: Realm) -> Realm {
pub(crate) fn compile_json_parse(&mut self, statement_list: &StatementList) -> Gc<CodeBlock> { self.vm
let _timer = Profiler::global().start_event("Compilation", "Main"); .environments
let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), true, self); .replace_global(realm.environment().clone());
compiler.create_script_decls(statement_list, false); std::mem::replace(&mut self.realm, realm)
compiler.compile_statement_list(statement_list, true, false);
Gc::new(compiler.finish())
} }
/// Compile the AST into a `CodeBlock` with an additional declarative environment. /// Gets the host hooks.
pub(crate) fn compile_with_new_declarative(
&mut self,
statement_list: &StatementList,
strict: bool,
) -> Gc<CodeBlock> {
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.
pub fn host_hooks(&self) -> &'host dyn HostHooks { pub fn host_hooks(&self) -> &'host dyn HostHooks {
self.host_hooks self.host_hooks
} }
/// Get the job queue. /// Gets the job queue.
pub fn job_queue(&mut self) -> &'host dyn JobQueue { pub fn job_queue(&mut self) -> &'host dyn JobQueue {
self.job_queue self.job_queue
} }
}
// ==== Private API ====
#[cfg(feature = "intl")]
impl<'host> Context<'host> {
/// Get the ICU related utilities /// Get the ICU related utilities
#[cfg(feature = "intl")]
pub(crate) const fn icu(&self) -> &icu::Icu<'host> { pub(crate) const fn icu(&self) -> &icu::Icu<'host> {
&self.icu &self.icu
} }
@ -662,11 +667,13 @@ impl<'icu, 'hooks, 'queue> ContextBuilder<'icu, 'hooks, 'queue> {
'queue: 'host, 'queue: 'host,
{ {
let host_hooks = self.host_hooks.unwrap_or(&DefaultHooks); 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 { let mut context = Context {
realm: Realm::create(host_hooks), realm,
interner: self.interner.unwrap_or_default(), interner: self.interner.unwrap_or_default(),
vm: Vm::default(), vm,
strict: false, strict: false,
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
icu: self.icu.unwrap_or_else(|| { icu: self.icu.unwrap_or_else(|| {

182
boa_engine/src/environments/compile.rs

@ -1,6 +1,4 @@
use crate::{ use crate::environments::runtime::BindingLocator;
environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue,
};
use boa_ast::expression::Identifier; use boa_ast::expression::Identifier;
use boa_gc::{Finalize, Gc, GcRefCell, Trace}; use boa_gc::{Finalize, Gc, GcRefCell, Trace};
@ -30,7 +28,7 @@ pub(crate) struct CompileTimeEnvironment {
} }
impl CompileTimeEnvironment { impl CompileTimeEnvironment {
/// Crate a new global compile time environment. /// Creates a new global compile time environment.
pub(crate) fn new_global() -> Self { pub(crate) fn new_global() -> Self {
Self { Self {
outer: None, outer: None,
@ -40,6 +38,17 @@ impl CompileTimeEnvironment {
} }
} }
/// Creates a new compile time environment.
pub(crate) fn new(parent: Gc<GcRefCell<Self>>, 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. /// Check if environment has a lexical binding with the given name.
pub(crate) fn has_lex_binding(&self, name: Identifier) -> bool { pub(crate) fn has_lex_binding(&self, name: Identifier) -> bool {
self.bindings 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 { /// Gets the outer environment of this environment.
outer: Some(outer), pub(crate) fn outer(&self) -> Option<Gc<GcRefCell<Self>>> {
environment_index, self.outer.clone()
bindings: FxHashMap::default(),
function_scope,
}));
} }
/// Pop the last compile time environment from the stack. /// Gets the environment index of this environment.
/// pub(crate) const fn environment_index(&self) -> usize {
/// Note: This function only works at bytecode compile time! self.environment_index
///
/// # Panics
///
/// Panics if there are no more environments that can be pop'ed.
pub(crate) fn pop_compile_time_environment(
&mut self,
) -> (usize, Gc<GcRefCell<CompileTimeEnvironment>>) {
let current_env_borrow = self.realm.compile_env.borrow();
if let Some(outer) = &current_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::<JsString>(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)
} }
} }

109
boa_engine/src/environments/runtime.rs

@ -38,6 +38,29 @@ pub(crate) struct DeclarativeEnvironment {
slots: Option<EnvironmentSlots>, slots: Option<EnvironmentSlots>,
} }
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<GcRefCell<CompileTimeEnvironment>> {
self.compile.clone()
}
/// Gets the bindings of this environment.
pub(crate) const fn bindings(&self) -> &GcRefCell<Vec<Option<JsValue>>> {
&self.bindings
}
}
/// Describes the different types of internal slot data that an environment can hold. /// Describes the different types of internal slot data that an environment can hold.
#[derive(Clone, Debug, Trace, Finalize)] #[derive(Clone, Debug, Trace, Finalize)]
pub(crate) enum EnvironmentSlots { pub(crate) enum EnvironmentSlots {
@ -250,19 +273,18 @@ impl Environment {
} }
impl DeclarativeEnvironmentStack { impl DeclarativeEnvironmentStack {
/// Create a new environment stack with the most outer declarative environment. /// Create a new environment stack.
pub(crate) fn new(global_compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>) -> Self { pub(crate) fn new(global: Gc<DeclarativeEnvironment>) -> Self {
Self { Self {
stack: Vec::from([Environment::Declarative(Gc::new(DeclarativeEnvironment { stack: vec![Environment::Declarative(global)],
bindings: GcRefCell::new(Vec::new()),
compile: global_compile_environment,
poisoned: Cell::new(false),
with: Cell::new(false),
slots: Some(EnvironmentSlots::Global),
}))]),
} }
} }
/// Replaces the current global with a new global environment.
pub(crate) fn replace_global(&mut self, global: Gc<DeclarativeEnvironment>) {
self.stack[0] = Environment::Declarative(global);
}
/// Extends the length of the next outer function environment to the number of compiled bindings. /// 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). /// This is only useful when compiled bindings are added after the initial compilation (eval).
@ -333,23 +355,6 @@ impl DeclarativeEnvironmentStack {
self.stack.extend(other); 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` /// `GetThisEnvironment`
/// ///
/// Returns the environment that currently provides a `this` biding. /// Returns the environment that currently provides a `this` biding.
@ -396,6 +401,7 @@ impl DeclarativeEnvironmentStack {
/// # Panics /// # Panics
/// ///
/// Panics if no environment exists on the stack. /// Panics if no environment exists on the stack.
#[track_caller]
pub(crate) fn push_declarative( pub(crate) fn push_declarative(
&mut self, &mut self,
num_bindings: usize, num_bindings: usize,
@ -437,6 +443,7 @@ impl DeclarativeEnvironmentStack {
/// # Panics /// # Panics
/// ///
/// Panics if no environment exists on the stack. /// Panics if no environment exists on the stack.
#[track_caller]
pub(crate) fn push_function( pub(crate) fn push_function(
&mut self, &mut self,
num_bindings: usize, 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
/// ///
/// Panics if no environment exists on the stack. /// Panics if no environment exists on the stack.
#[track_caller]
pub(crate) fn push_function_inherit( pub(crate) fn push_function_inherit(
&mut self, &mut self,
num_bindings: usize, num_bindings: usize,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>, compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) { ) {
debug_assert!(
self.stack.len() == compile_environment.borrow().environment_index(),
"tried to push an invalid compile environment"
);
let (poisoned, with, slots) = { let (poisoned, with, slots) = {
let with = self let with = self
.stack .stack
@ -510,6 +524,7 @@ impl DeclarativeEnvironmentStack {
.stack .stack
.iter() .iter()
.filter_map(Environment::as_declarative) .filter_map(Environment::as_declarative)
.filter(|e| e.slots().is_some())
.last() .last()
.expect("global environment must always exist"); .expect("global environment must always exist");
( (
@ -585,29 +600,15 @@ impl DeclarativeEnvironmentStack {
.clone() .clone()
} }
/// Mark that there may be added bindings in the current environment. /// Mark that there may be added bindings from the current environment to the next function
/// /// environment.
/// # Panics pub(crate) fn poison_last_function(&mut self) {
/// for env in self.stack.iter_mut().rev() {
/// 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 {
if let Some(env) = env.as_declarative() { if let Some(env) = env.as_declarative() {
if env.poisoned.get() { if env.compile_env().borrow().is_function() {
env.poisoned.set(true);
return; return;
} }
env.poisoned.set(true);
} }
} }
} }
@ -661,6 +662,7 @@ impl DeclarativeEnvironmentStack {
/// # Panics /// # Panics
/// ///
/// Panics if the environment or binding index are out of range. /// Panics if the environment or binding index are out of range.
#[track_caller]
pub(crate) fn put_value( pub(crate) fn put_value(
&mut self, &mut self,
environment_index: usize, environment_index: usize,
@ -685,6 +687,7 @@ impl DeclarativeEnvironmentStack {
/// # Panics /// # Panics
/// ///
/// Panics if the environment or binding index are out of range. /// Panics if the environment or binding index are out of range.
#[track_caller]
pub(crate) fn put_value_if_uninitialized( pub(crate) fn put_value_if_uninitialized(
&mut self, &mut self,
environment_index: usize, environment_index: usize,
@ -858,7 +861,7 @@ impl Context<'_> {
mut binding_index: usize, mut binding_index: usize,
name: Identifier, name: Identifier,
) -> JsResult<Option<JsValue>> { ) -> JsResult<Option<JsValue>> {
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) { match self.environment_expect(env_index) {
Environment::Declarative(env) => { Environment::Declarative(env) => {
if env.poisoned.get() { if env.poisoned.get() {
@ -911,7 +914,7 @@ impl Context<'_> {
&mut self, &mut self,
name: Identifier, name: Identifier,
) -> JsResult<Option<JsValue>> { ) -> JsResult<Option<JsValue>> {
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); let env = self.environment_expect(env_index);
match env { match env {
@ -969,7 +972,7 @@ impl Context<'_> {
name: Identifier, name: Identifier,
value: JsValue, value: JsValue,
) -> JsResult<bool> { ) -> JsResult<bool> {
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); let env = self.environment_expect(env_index);
match env { match env {
@ -1035,7 +1038,7 @@ impl Context<'_> {
name: Identifier, name: Identifier,
value: &JsValue, value: &JsValue,
) -> JsResult<bool> { ) -> JsResult<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); let env = self.environment_expect(env_index);
match env { match env {
@ -1089,7 +1092,7 @@ impl Context<'_> {
&mut self, &mut self,
name: Identifier, name: Identifier,
) -> JsResult<(bool, bool)> { ) -> 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); let env = self.environment_expect(env_index);
match env { match env {
@ -1122,7 +1125,7 @@ impl Context<'_> {
/// Return the environment at the given index. Panics if the index is out of range. /// Return the environment at the given index. Panics if the index is out of range.
fn environment_expect(&self, index: usize) -> &Environment { fn environment_expect(&self, index: usize) -> &Environment {
self.realm self.vm
.environments .environments
.stack .stack
.get(index) .get(index)

52
boa_engine/src/error.rs

@ -5,6 +5,7 @@ use crate::{
object::JsObject, object::JsObject,
object::ObjectData, object::ObjectData,
property::PropertyDescriptor, property::PropertyDescriptor,
realm::Realm,
string::utf16, string::utf16,
Context, JsString, JsValue, Context, JsString, JsValue,
}; };
@ -75,6 +76,10 @@ pub enum TryNativeError {
#[error("property `message` cannot contain unpaired surrogates")] #[error("property `message` cannot contain unpaired surrogates")]
InvalidMessageEncoding, 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. /// A property of the error object is not accessible.
#[error("could not access property `{property}`")] #[error("could not access property `{property}`")]
InaccessibleProperty { InaccessibleProperty {
@ -98,6 +103,13 @@ pub enum TryNativeError {
/// The error value is not an error object. /// The error value is not an error object.
#[error("opaque error of type `{:?}` is not an Error object", .0.get_type())] #[error("opaque error of type `{:?}` is not an Error object", .0.get_type())]
NotAnErrorObject(JsValue), 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 { 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 { Ok(JsNativeError {
kind, kind,
message, message,
cause: cause.map(|v| Box::new(Self::from_opaque(v))), 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()); /// assert!(error.as_native().is_none());
/// ``` /// ```
pub const fn as_native(&self) -> Option<&JsNativeError> { pub const fn as_native(&self) -> Option<&JsNativeError> {
match self.inner { match &self.inner {
Repr::Native(ref e) => Some(e), Repr::Native(e) => Some(e),
Repr::Opaque(_) => None, 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<boa_parser::Error> for JsError { impl From<boa_parser::Error> for JsError {
@ -391,6 +425,7 @@ pub struct JsNativeError {
message: Box<str>, message: Box<str>,
#[source] #[source]
cause: Option<Box<JsError>>, cause: Option<Box<JsError>>,
realm: Option<Realm>,
} }
impl JsNativeError { impl JsNativeError {
@ -400,6 +435,7 @@ impl JsNativeError {
kind, kind,
message, message,
cause, cause,
realm: None,
} }
} }
@ -642,8 +678,12 @@ impl JsNativeError {
kind, kind,
message, message,
cause, cause,
realm,
} = self; } = 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 { let (prototype, tag) = match kind {
JsNativeErrorKind::Aggregate(_) => ( JsNativeErrorKind::Aggregate(_) => (
constructors.aggregate_error().prototype(), constructors.aggregate_error().prototype(),
@ -700,6 +740,12 @@ impl JsNativeError {
} }
o o
} }
/// Sets the realm of this error.
pub(crate) fn with_realm(mut self, realm: Realm) -> Self {
self.realm = Some(realm);
self
}
} }
impl From<boa_parser::Error> for JsNativeError { impl From<boa_parser::Error> for JsNativeError {

37
boa_engine/src/job.rs

@ -21,6 +21,7 @@ use std::{any::Any, cell::RefCell, collections::VecDeque, fmt::Debug, future::Fu
use crate::{ use crate::{
object::{JsFunction, NativeObject}, object::{JsFunction, NativeObject},
realm::Realm,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -66,6 +67,7 @@ pub type FutureJob = Pin<Box<dyn Future<Output = NativeJob> + 'static>>;
pub struct NativeJob { pub struct NativeJob {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
f: Box<dyn FnOnce(&mut Context<'_>) -> JsResult<JsValue>>, f: Box<dyn FnOnce(&mut Context<'_>) -> JsResult<JsValue>>,
realm: Option<Realm>,
} }
impl Debug for NativeJob { impl Debug for NativeJob {
@ -80,12 +82,43 @@ impl NativeJob {
where where
F: FnOnce(&mut Context<'_>) -> JsResult<JsValue> + 'static, F: FnOnce(&mut Context<'_>) -> JsResult<JsValue> + '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: F, realm: Realm) -> Self
where
F: FnOnce(&mut Context<'_>) -> JsResult<JsValue> + '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`]. /// 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<JsValue> { pub fn call(self, context: &mut Context<'_>) -> JsResult<JsValue> {
(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)
}
} }
} }

8
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 // The corresponding object must be an intrinsic that is intended to be used
// as the [[Prototype]] value of an object. // as the [[Prototype]] value of an object.
// 2. Let proto be ? Get(constructor, "prototype"). // 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() { if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() {
return Ok(proto.clone()); return Ok(proto.clone());
} }
// 3. If Type(proto) is not Object, then // 3. If Type(proto) is not Object, then
// a. Let realm be ? GetFunctionRealm(constructor). // a. Let realm be ? GetFunctionRealm(constructor).
// b. Set proto to realm's intrinsic object named intrinsicDefaultProto.
constructor.get_function_realm(context)? constructor.get_function_realm(context)?
} else { } 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())
} }

4
boa_engine/src/object/mod.rs

@ -2004,7 +2004,7 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> {
function: self.function, function: self.function,
constructor: self.constructor, constructor: self.constructor,
}, },
self.context.intrinsics().clone(), self.context.realm().clone(),
)), )),
); );
let property = PropertyDescriptor::builder() let property = PropertyDescriptor::builder()
@ -2400,7 +2400,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> {
function: self.function, function: self.function,
constructor: self.constructor, constructor: self.constructor,
}, },
self.context.intrinsics().clone(), self.context.realm().clone(),
); );
let length = PropertyDescriptor::builder() let length = PropertyDescriptor::builder()

9
boa_engine/src/object/operations.rs

@ -1,9 +1,10 @@
use crate::{ use crate::{
builtins::{function::ClassFieldDefinition, Array}, builtins::{function::ClassFieldDefinition, Array},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{JsObject, PrivateElement, PROTOTYPE}, object::{JsObject, PrivateElement, PROTOTYPE},
property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind}, property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind},
realm::Realm,
string::utf16, string::utf16,
value::Type, value::Type,
Context, JsResult, JsSymbol, JsValue, Context, JsResult, JsSymbol, JsValue,
@ -658,10 +659,10 @@ impl JsObject {
/// Abstract operation [`GetFunctionRealm`][spec]. /// Abstract operation [`GetFunctionRealm`][spec].
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-getfunctionrealm /// [spec]: https://tc39.es/ecma262/#sec-getfunctionrealm
pub(crate) fn get_function_realm(&self, context: &mut Context<'_>) -> JsResult<Intrinsics> { pub(crate) fn get_function_realm(&self, context: &mut Context<'_>) -> JsResult<Realm> {
let constructor = self.borrow(); let constructor = self.borrow();
if let Some(fun) = constructor.as_function() { 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() { if let Some(bound) = constructor.as_bound_function() {
@ -676,7 +677,7 @@ impl JsObject {
return fun.get_function_realm(context); return fun.get_function_realm(context);
} }
Ok(context.intrinsics().clone()) Ok(context.realm().clone())
} }
// todo: CopyDataProperties // todo: CopyDataProperties

74
boa_engine/src/realm.rs

@ -8,20 +8,30 @@
use crate::{ use crate::{
context::{intrinsics::Intrinsics, HostHooks}, context::{intrinsics::Intrinsics, HostHooks},
environments::{CompileTimeEnvironment, DeclarativeEnvironmentStack}, environments::DeclarativeEnvironment,
object::JsObject, object::JsObject,
}; };
use boa_gc::{Gc, GcRefCell}; use boa_gc::{Finalize, Gc, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
/// Representation of a Realm. /// Representation of a Realm.
/// ///
/// In the specification these are called Realm Records. /// In the specification these are called Realm Records.
#[derive(Debug)] #[derive(Clone, Debug, Trace, Finalize)]
pub struct Realm { pub struct Realm {
pub(crate) intrinsics: Intrinsics, inner: Gc<Inner>,
pub(crate) environments: DeclarativeEnvironmentStack, }
pub(crate) compile_env: Gc<GcRefCell<CompileTimeEnvironment>>,
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<DeclarativeEnvironment>,
global_object: JsObject, global_object: JsObject,
global_this: JsObject, global_this: JsObject,
} }
@ -32,36 +42,50 @@ impl Realm {
pub fn create(hooks: &dyn HostHooks) -> Self { pub fn create(hooks: &dyn HostHooks) -> Self {
let _timer = Profiler::global().start_event("Realm::create", "realm"); 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_object = hooks.create_global_object(&intrinsics);
let global_this = hooks let global_this = hooks
.create_global_this(&intrinsics) .create_global_this(&intrinsics)
.unwrap_or_else(|| global_object.clone()); .unwrap_or_else(|| global_object.clone());
let global_compile_environment = let realm = Self {
Gc::new(GcRefCell::new(CompileTimeEnvironment::new_global())); inner: Gc::new(Inner {
intrinsics,
environment: Gc::new(DeclarativeEnvironment::new_global()),
global_object,
global_this,
}),
};
Self { realm.initialize();
intrinsics,
global_object, realm
global_this, }
environments: DeclarativeEnvironmentStack::new(global_compile_environment.clone()),
compile_env: global_compile_environment, /// Gets the intrinsics of this `Realm`.
} pub fn intrinsics(&self) -> &Intrinsics {
&self.inner.intrinsics
} }
pub(crate) const fn global_object(&self) -> &JsObject { pub(crate) fn environment(&self) -> &Gc<DeclarativeEnvironment> {
&self.global_object &self.inner.environment
} }
pub(crate) const fn global_this(&self) -> &JsObject { pub(crate) fn global_object(&self) -> &JsObject {
&self.global_this &self.inner.global_object
} }
/// Set the number of bindings on the global environment. pub(crate) fn global_this(&self) -> &JsObject {
pub(crate) fn set_global_binding_number(&mut self) { &self.inner.global_this
let binding_number = self.compile_env.borrow().num_bindings(); }
self.environments.set_global_binding_number(binding_number);
/// 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);
}
} }
} }

32
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",
),
]);
}

1
boa_engine/src/tests/mod.rs

@ -1,6 +1,7 @@
use indoc::indoc; use indoc::indoc;
mod control_flow; mod control_flow;
mod env;
mod function; mod function;
mod operators; mod operators;
mod promise; mod promise;

942
boa_engine/src/vm/code_block.rs

File diff suppressed because it is too large Load Diff

16
boa_engine/src/vm/mod.rs

@ -6,11 +6,13 @@
use crate::{ use crate::{
builtins::async_generator::{AsyncGenerator, AsyncGeneratorState}, builtins::async_generator::{AsyncGenerator, AsyncGeneratorState},
environments::{DeclarativeEnvironment, DeclarativeEnvironmentStack},
vm::{call_frame::EarlyReturnType, code_block::Readable}, vm::{call_frame::EarlyReturnType, code_block::Readable},
Context, JsError, JsObject, JsResult, JsValue, Context, JsError, JsObject, JsResult, JsValue,
}; };
#[cfg(feature = "fuzz")] #[cfg(feature = "fuzz")]
use crate::{JsNativeError, JsNativeErrorKind}; use crate::{JsNativeError, JsNativeErrorKind};
use boa_gc::Gc;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::{convert::TryInto, mem::size_of}; use std::{convert::TryInto, mem::size_of};
@ -44,17 +46,20 @@ pub struct Vm {
pub(crate) frames: Vec<CallFrame>, pub(crate) frames: Vec<CallFrame>,
pub(crate) stack: Vec<JsValue>, pub(crate) stack: Vec<JsValue>,
pub(crate) err: Option<JsError>, pub(crate) err: Option<JsError>,
pub(crate) environments: DeclarativeEnvironmentStack,
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
pub(crate) trace: bool, pub(crate) trace: bool,
pub(crate) stack_size_limit: usize, pub(crate) stack_size_limit: usize,
pub(crate) active_function: Option<JsObject>, pub(crate) active_function: Option<JsObject>,
} }
impl Default for Vm { impl Vm {
fn default() -> Self { /// Creates a new virtual machine.
pub(crate) fn new(global: Gc<DeclarativeEnvironment>) -> Self {
Self { Self {
frames: Vec::with_capacity(16), frames: Vec::with_capacity(16),
stack: Vec::with_capacity(1024), stack: Vec::with_capacity(1024),
environments: DeclarativeEnvironmentStack::new(global),
err: None, err: None,
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
trace: false, trace: false,
@ -62,9 +67,7 @@ impl Default for Vm {
active_function: None, active_function: None,
} }
} }
}
impl Vm {
/// Push a value on the stack. /// Push a value on the stack.
pub(crate) fn push<T>(&mut self, value: T) pub(crate) fn push<T>(&mut self, value: T)
where 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. // 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). // The relevant spec section is 3. in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart).
let promise_capability = self let promise_capability = self
.realm .vm
.environments .environments
.current_function_slots() .current_function_slots()
.as_function_slots() .as_function_slots()
@ -403,10 +406,11 @@ impl Context<'_> {
.take() .take()
.expect("err must exist on a Completion::Throw")), .expect("err must exist on a Completion::Throw")),
true, true,
None,
self, self,
); );
} else { } 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); AsyncGenerator::drain_queue(&generator_object, self);

32
boa_engine/src/vm/opcode/await_stm/mod.rs

@ -34,11 +34,11 @@ impl Operation for Await {
)?; )?;
let generator_context = GeneratorContext { let generator_context = GeneratorContext {
environments: context.realm.environments.clone(), environments: context.vm.environments.clone(),
call_frame: context.vm.frame().clone(), call_frame: context.vm.frame().clone(),
stack: context.vm.stack.clone(), stack: context.vm.stack.clone(),
active_function: context.vm.active_function.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: // 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. // f. Return undefined.
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); 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 context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( let old_realm = context.enter_realm(generator_context.realm.clone());
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.vm.push_frame(generator_context.call_frame.clone()); context.vm.push_frame(generator_context.call_frame.clone());
context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal; context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal;
@ -80,7 +77,7 @@ impl Operation for Await {
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); 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 context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( context.enter_realm(old_realm);
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
Ok(JsValue::undefined()) Ok(JsValue::undefined())
}, },
@ -118,7 +112,7 @@ impl Operation for Await {
// f. Return undefined. // f. Return undefined.
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); 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 context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( let old_realm = context.enter_realm(generator_context.realm.clone());
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.vm.push_frame(generator_context.call_frame.clone()); context.vm.push_frame(generator_context.call_frame.clone());
context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw;
@ -141,7 +132,7 @@ impl Operation for Await {
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
std::mem::swap( std::mem::swap(
&mut context.realm.environments, &mut context.vm.environments,
&mut generator_context.environments, &mut generator_context.environments,
); );
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); 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 context.vm.active_function,
&mut generator_context.active_function, &mut generator_context.active_function,
); );
std::mem::swap( context.enter_realm(old_realm);
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
Ok(JsValue::undefined()) Ok(JsValue::undefined())
}, },

4
boa_engine/src/vm/opcode/control_flow/break.rs

@ -34,8 +34,8 @@ impl Operation for Break {
context.vm.frame_mut().env_stack.pop(); context.vm.frame_mut().env_stack.pop();
} }
let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
// 2. Register target address in AbruptCompletionRecord. // 2. Register target address in AbruptCompletionRecord.
let new_record = AbruptCompletionRecord::new_break().with_initial_target(target_address); let new_record = AbruptCompletionRecord::new_break().with_initial_target(target_address);

8
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); let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
@ -77,11 +77,11 @@ impl Operation for CatchEnd2 {
.filter(|entry| entry.is_catch_env()) .filter(|entry| entry.is_catch_env())
{ {
let env_truncation_len = context let env_truncation_len = context
.realm .vm
.environments .environments
.len() .len()
.saturating_sub(catch_entry.env_num()); .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(); context.vm.frame_mut().env_stack.pop();
} }

4
boa_engine/src/vm/opcode/control_flow/continue.rs

@ -42,8 +42,8 @@ impl Operation for Continue {
context.vm.frame_mut().env_stack.pop(); context.vm.frame_mut().env_stack.pop();
} }
let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
// 2. Register target address in AbruptCompletionRecord. // 2. Register target address in AbruptCompletionRecord.
let new_record = AbruptCompletionRecord::new_continue().with_initial_target(target_address); let new_record = AbruptCompletionRecord::new_continue().with_initial_target(target_address);

24
boa_engine/src/vm/opcode/control_flow/finally.rs

@ -64,9 +64,8 @@ impl Operation for FinallyEnd {
context.vm.frame_mut().env_stack.pop(); context.vm.frame_mut().env_stack.pop();
} }
let env_truncation_len = let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.len().saturating_sub(envs_to_pop); context.vm.environments.truncate(env_truncation_len);
context.realm.environments.truncate(env_truncation_len);
} }
Some(record) Some(record)
if record.is_break() && context.vm.frame().pc < record.target() as usize => 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; context.vm.frame_mut().abrupt_completion = None;
let env_truncation_len = let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.len().saturating_sub(envs_to_pop); context.vm.environments.truncate(env_truncation_len);
context.realm.environments.truncate(env_truncation_len);
} }
Some(record) Some(record)
if record.is_continue() && context.vm.frame().pc > record.target() as usize => 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; context.vm.frame_mut().abrupt_completion = None;
let env_truncation_len = let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.len().saturating_sub(envs_to_pop); context.vm.environments.truncate(env_truncation_len);
context.realm.environments.truncate(env_truncation_len);
} }
Some(record) if record.is_return() => { Some(record) if record.is_return() => {
return Ok(CompletionType::Return); return Ok(CompletionType::Return);
@ -121,9 +118,8 @@ impl Operation for FinallyEnd {
} }
} }
context.vm.frame_mut().abrupt_completion = None; context.vm.frame_mut().abrupt_completion = None;
let env_truncation_len = let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.len().saturating_sub(envs_to_pop); context.vm.environments.truncate(env_truncation_len);
context.realm.environments.truncate(env_truncation_len);
} }
Some(record) if !record.is_throw_with_target() => { Some(record) if !record.is_throw_with_target() => {
let current_stack = context let current_stack = context
@ -134,11 +130,11 @@ impl Operation for FinallyEnd {
.expect("Popping current finally stack."); .expect("Popping current finally stack.");
let env_truncation_len = context let env_truncation_len = context
.realm .vm
.environments .environments
.len() .len()
.saturating_sub(current_stack.env_num()); .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()); let err = JsError::from_opaque(context.vm.pop());
context.vm.err = Some(err); context.vm.err = Some(err);

4
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); let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }

4
boa_engine/src/vm/opcode/control_flow/return.rs

@ -36,8 +36,8 @@ impl Operation for Return {
context.vm.frame_mut().env_stack.pop(); context.vm.frame_mut().env_stack.pop();
} }
let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
let record = AbruptCompletionRecord::new_return(); let record = AbruptCompletionRecord::new_return();
context.vm.frame_mut().abrupt_completion = Some(record); context.vm.frame_mut().abrupt_completion = Some(record);

8
boa_engine/src/vm/opcode/control_flow/throw.rs

@ -59,8 +59,8 @@ impl Operation for Throw {
context.vm.frame_mut().env_stack.pop(); context.vm.frame_mut().env_stack.pop();
} }
let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
if target_address == catch_target { if target_address == catch_target {
context.vm.frame_mut().pc = catch_target as usize; context.vm.frame_mut().pc = catch_target as usize;
@ -110,8 +110,8 @@ impl Operation for Throw {
context.vm.frame_mut().env_stack.pop(); context.vm.frame_mut().env_stack.pop();
} }
let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
let previous_stack_size = context let previous_stack_size = context
.vm .vm

4
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); let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }

16
boa_engine/src/vm/opcode/define/mod.rs

@ -28,7 +28,7 @@ impl Operation for DefVar {
if binding_locator.is_global() { if binding_locator.is_global() {
// already initialized at compile time // already initialized at compile time
} else { } else {
context.realm.environments.put_value_if_uninitialized( context.vm.environments.put_value_if_uninitialized(
binding_locator.environment_index(), binding_locator.environment_index(),
binding_locator.binding_index(), binding_locator.binding_index(),
JsValue::Undefined, JsValue::Undefined,
@ -72,7 +72,7 @@ impl Operation for DefInitVar {
)?; )?;
} }
} else { } else {
context.realm.environments.put_value( context.vm.environments.put_value(
binding_locator.environment_index(), binding_locator.environment_index(),
binding_locator.binding_index(), binding_locator.binding_index(),
value, value,
@ -96,7 +96,7 @@ impl Operation for DefLet {
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> { fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>(); let index = context.vm.read::<u32>();
let binding_locator = context.vm.frame().code_block.bindings[index as usize]; 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.environment_index(),
binding_locator.binding_index(), binding_locator.binding_index(),
JsValue::Undefined, JsValue::Undefined,
@ -105,7 +105,7 @@ impl Operation for DefLet {
} }
} }
macro_rules! implement_declaritives { macro_rules! implement_declaratives {
($name:ident, $doc_string:literal) => { ($name:ident, $doc_string:literal) => {
#[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] #[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")]
#[doc= "\n"] #[doc= "\n"]
@ -122,7 +122,7 @@ macro_rules! implement_declaritives {
let index = context.vm.read::<u32>(); let index = context.vm.read::<u32>();
let value = context.vm.pop(); let value = context.vm.pop();
let binding_locator = context.vm.frame().code_block.bindings[index as usize]; 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.environment_index(),
binding_locator.binding_index(), binding_locator.binding_index(),
value, value,
@ -133,6 +133,6 @@ macro_rules! implement_declaritives {
}; };
} }
implement_declaritives!(DefInitLet, "Declare and initialize `let` type variable"); implement_declaratives!(DefInitLet, "Declare and initialize `let` type variable");
implement_declaritives!(DefInitConst, "Declare and initialize `const` type variable"); implement_declaratives!(DefInitConst, "Declare and initialize `const` type variable");
implement_declaritives!(DefInitArg, "Declare and initialize function arguments"); implement_declaratives!(DefInitArg, "Declare and initialize function arguments");

4
boa_engine/src/vm/opcode/delete/mod.rs

@ -81,7 +81,7 @@ impl Operation for DeleteName {
let deleted = if binding_locator.is_global() let deleted = if binding_locator.is_global()
&& !context && !context
.realm .vm
.environments .environments
.binding_in_poisoned_environment(binding_locator.name()) .binding_in_poisoned_environment(binding_locator.name())
{ {
@ -111,7 +111,7 @@ impl Operation for DeleteName {
deleted deleted
} else { } else {
context context
.realm .vm
.environments .environments
.get_value_optional( .get_value_optional(
binding_locator.environment_index(), binding_locator.environment_index(),

16
boa_engine/src/vm/opcode/environment/mod.rs

@ -17,7 +17,7 @@ impl Operation for This {
const INSTRUCTION: &'static str = "INST - This"; const INSTRUCTION: &'static str = "INST - This";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> { fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let env = context.realm.environments.get_this_environment(); let env = context.vm.environments.get_this_environment();
match env { match env {
EnvironmentSlots::Function(env) => { EnvironmentSlots::Function(env) => {
let binding_result = match env.borrow().get_this_binding() { let binding_result = match env.borrow().get_this_binding() {
@ -28,7 +28,7 @@ impl Operation for This {
context.vm.push(function_binding); context.vm.push(function_binding);
} }
EnvironmentSlots::Global => { EnvironmentSlots::Global => {
let this = context.realm.global_this(); let this = context.realm().global_this();
context.vm.push(this.clone()); context.vm.push(this.clone());
} }
} }
@ -50,7 +50,7 @@ impl Operation for Super {
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> { fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let home_result = { let home_result = {
let env = context let env = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
@ -97,7 +97,7 @@ impl Operation for SuperCallPrepare {
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> { fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let this_env = context let this_env = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
@ -159,7 +159,7 @@ impl Operation for SuperCall {
let result = super_constructor.__construct__(&arguments, new_target, context)?; let result = super_constructor.__construct__(&arguments, new_target, context)?;
let this_env = context let this_env = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
@ -220,7 +220,7 @@ impl Operation for SuperCallSpread {
let result = super_constructor.__construct__(&arguments, new_target, context)?; let result = super_constructor.__construct__(&arguments, new_target, context)?;
let this_env = context let this_env = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
@ -262,7 +262,7 @@ impl Operation for SuperCallDerived {
let (new_target, active_function) = { let (new_target, active_function) = {
let this_env = context let this_env = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
@ -289,7 +289,7 @@ impl Operation for SuperCallDerived {
let result = super_constructor.__construct__(&arguments, &new_target, context)?; let result = super_constructor.__construct__(&arguments, &new_target, context)?;
let this_env = context let this_env = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()

2
boa_engine/src/vm/opcode/generator/mod.rs

@ -92,7 +92,7 @@ impl Operation for AsyncGeneratorNext {
.queue .queue
.pop_front() .pop_front()
.expect("must have item in queue"); .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 mut generator_object_mut = generator_object.borrow_mut();
let gen = generator_object_mut let gen = generator_object_mut

2
boa_engine/src/vm/opcode/iteration/iterator.rs

@ -106,7 +106,7 @@ impl Operation for IteratorUnwrapNextOrJump {
if next_result.complete(context)? { if next_result.complete(context)? {
context.vm.frame_mut().pc = address as usize; context.vm.frame_mut().pc = address as usize;
context.vm.frame_mut().dec_frame_env_stack(); context.vm.frame_mut().dec_frame_env_stack();
context.realm.environments.pop(); context.vm.environments.pop();
context.vm.push(true); context.vm.push(true);
} else { } else {
context.vm.push(false); context.vm.push(false);

8
boa_engine/src/vm/opcode/iteration/loop_ops.rs

@ -51,11 +51,11 @@ impl Operation for LoopContinue {
.filter(|entry| entry.exit_address() == exit) .filter(|entry| entry.exit_address() == exit)
{ {
let env_truncation_len = context let env_truncation_len = context
.realm .vm
.environments .environments
.len() .len()
.saturating_sub(entry.env_num()); .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(); 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); let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len); context.vm.environments.truncate(env_truncation_len);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }

2
boa_engine/src/vm/opcode/pop/mod.rs

@ -55,7 +55,7 @@ impl Operation for PopEnvironment {
const INSTRUCTION: &'static str = "INST - PopEnvironment"; const INSTRUCTION: &'static str = "INST - PopEnvironment";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> { fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
context.realm.environments.pop(); context.vm.environments.pop();
context.vm.frame_mut().dec_frame_env_stack(); context.vm.frame_mut().dec_frame_env_stack();
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }

6
boa_engine/src/vm/opcode/push/environment.rs

@ -21,7 +21,7 @@ impl Operation for PushDeclarativeEnvironment {
[compile_environments_index as usize] [compile_environments_index as usize]
.clone(); .clone();
context context
.realm .vm
.environments .environments
.push_declarative(num_bindings as usize, compile_environment); .push_declarative(num_bindings as usize, compile_environment);
context.vm.frame_mut().inc_frame_env_stack(); context.vm.frame_mut().inc_frame_env_stack();
@ -47,7 +47,7 @@ impl Operation for PushFunctionEnvironment {
[compile_environments_index as usize] [compile_environments_index as usize]
.clone(); .clone();
context context
.realm .vm
.environments .environments
.push_function_inherit(num_bindings as usize, compile_environment); .push_function_inherit(num_bindings as usize, compile_environment);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
@ -69,7 +69,7 @@ impl Operation for PushObjectEnvironment {
let object = context.vm.pop(); let object = context.vm.pop();
let object = object.to_object(context)?; 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(); context.vm.frame_mut().inc_frame_env_stack();
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }

16
boa_engine/src/vm/opcode/push/new_target.rs

@ -15,20 +15,18 @@ impl Operation for PushNewTarget {
const INSTRUCTION: &'static str = "INST - PushNewTarget"; const INSTRUCTION: &'static str = "INST - PushNewTarget";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> { fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
if let Some(env) = context let new_target = if let Some(new_target) = context
.realm .vm
.environments .environments
.get_this_environment() .get_this_environment()
.as_function_slots() .as_function_slots()
.and_then(|env| env.borrow().new_target().cloned())
{ {
if let Some(new_target) = env.borrow().new_target() { new_target.into()
context.vm.push(new_target.clone());
} else {
context.vm.push(JsValue::undefined());
}
} else { } else {
context.vm.push(JsValue::undefined()); JsValue::undefined()
} };
context.vm.push(new_target);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save