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 2 years ago
parent
commit
1e75fd0d21
  1. 13
      boa_engine/src/builtins/array/array_iterator.rs
  2. 21
      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. 52
      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. 54
      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. 35
      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. 68
      boa_engine/src/realm.rs
  79. 32
      boa_engine/src/tests/env.rs
  80. 1
      boa_engine/src/tests/mod.rs
  81. 700
      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,
object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind},
realm::Realm,
symbol::JsSymbol,
Context, JsResult,
};
@ -35,11 +36,17 @@ pub struct ArrayIterator {
}
impl IntrinsicObject for ArrayIterator {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("ArrayIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
.prototype(intrinsics.objects().iterator_prototypes().iterator())
BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0)
.static_property(
JsSymbol::to_string_tag(),

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

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

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

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

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

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

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

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

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

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

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

@ -18,6 +18,7 @@ use crate::{
error::JsNativeError,
object::JsObject,
property::Attribute,
realm::Realm,
symbol::JsSymbol,
value::{IntegerOrInfinity, PreferredType},
Context, JsArgs, JsBigInt, JsResult, JsValue,
@ -35,10 +36,10 @@ mod tests;
pub struct BigInt;
impl IntrinsicObject for BigInt {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0)
.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},
error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm,
Context, JsResult, JsValue,
};
use boa_profiler::Profiler;
@ -28,10 +29,10 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
pub(crate) struct Boolean;
impl IntrinsicObject for Boolean {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0)
.build();

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

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

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

@ -19,6 +19,7 @@ use crate::{
error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm,
string::utf16,
symbol::JsSymbol,
value::{IntegerOrNan, JsValue, PreferredType},
@ -100,10 +101,10 @@ impl Default for Date {
}
impl IntrinsicObject for Date {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::get_date::<true>, "getDate", 0)
.method(Self::get_day::<true>, "getDay", 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},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptorBuilder},
realm::Realm,
string::utf16,
Context, JsArgs, JsResult, JsValue,
};
@ -26,13 +27,13 @@ use super::{Error, ErrorKind};
pub(crate) struct AggregateError;
impl IntrinsicObject for AggregateError {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype()))
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute)
.build();

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -11,7 +11,8 @@
//! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object
use crate::{
context::intrinsics::Intrinsics, js_string, Context, JsArgs, JsObject, JsResult, JsValue,
context::intrinsics::Intrinsics, js_string, realm::Realm, Context, JsArgs, JsObject, JsResult,
JsValue,
};
use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};
@ -21,8 +22,8 @@ use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};
pub(crate) struct Escape;
impl IntrinsicObject for Escape {
fn init(intrinsics: &Intrinsics) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(escape)
.name(Self::NAME)
.length(1)
@ -94,8 +95,8 @@ fn escape(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<
pub(crate) struct Unescape;
impl IntrinsicObject for Unescape {
fn init(intrinsics: &Intrinsics) {
BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(unescape)
.name(Self::NAME)
.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
use crate::{
builtins::BuiltInObject, context::intrinsics::Intrinsics, environments::Environment,
error::JsNativeError, object::JsObject, Context, JsArgs, JsResult, JsString, JsValue,
builtins::BuiltInObject, bytecompiler::ByteCompiler, context::intrinsics::Intrinsics,
environments::Environment, error::JsNativeError, object::JsObject, realm::Realm, Context,
JsArgs, JsResult, JsString, JsValue,
};
use boa_ast::operations::{
contains, contains_arguments, top_level_var_declared_names, ContainsSymbol,
};
use boa_gc::Gc;
use boa_interner::Sym;
use boa_parser::{Parser, Source};
use boa_profiler::Profiler;
@ -25,10 +28,10 @@ use super::{BuiltInBuilder, IntrinsicObject};
pub(crate) struct Eval;
impl IntrinsicObject for Eval {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
BuiltInBuilder::with_intrinsic::<Self>(realm)
.callable(Self::eval)
.name(Self::NAME)
.length(1)
@ -91,12 +94,12 @@ impl Eval {
fn restore_environment(context: &mut Context<'_>, action: EnvStackAction) {
match action {
EnvStackAction::Truncate(size) => {
context.realm.environments.truncate(size);
context.vm.environments.truncate(size);
}
EnvStackAction::Restore(envs) => {
// Pop all environments created during the eval execution and restore the original stack.
context.realm.environments.truncate(1);
context.realm.environments.extend(envs);
context.vm.environments.truncate(1);
context.vm.environments.extend(envs);
}
}
}
@ -112,8 +115,13 @@ impl Eval {
// Because of implementation details the following code differs from the spec.
// 3. Let evalRealm be the current Realm Record.
// 4. NOTE: In the case of a direct eval, evalRealm is the realm of both the caller of eval
// and of the eval function itself.
// 5. Perform ? HostEnsureCanCompileStrings(evalRealm).
context.host_hooks().ensure_can_compile_strings(context)?;
context
.host_hooks()
.ensure_can_compile_strings(context.realm().clone(), context)?;
// 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
// a. Let script be ParseText(StringToCodePoints(x), Script).
@ -132,7 +140,7 @@ impl Eval {
// 9. Let inClassFieldInitializer be false.
// a. Let thisEnvRec be GetThisEnvironment().
let flags = match context
.realm
.vm
.environments
.get_this_environment()
.as_function_slots()
@ -203,28 +211,21 @@ impl Eval {
let action = if direct {
// If the call to eval is direct, the code is executed in the current environment.
// Poison the current environment, because it may contain new declarations after/during eval.
// Poison the last parent function environment, because it may contain new declarations after/during eval.
if !strict {
context.realm.environments.poison_current();
context.vm.environments.poison_last_function();
}
// Set the compile time environment to the current running environment and save the number of current environments.
context.realm.compile_env = context.realm.environments.current_compile_environment();
let environments_len = context.realm.environments.len();
let environments_len = context.vm.environments.len();
// Pop any added runtime environments that were not removed during the eval execution.
EnvStackAction::Truncate(environments_len)
} else {
// If the call to eval is indirect, the code is executed in the global environment.
// Poison all environments, because the global environment may contain new declarations after/during eval.
if !strict {
context.realm.environments.poison_all();
}
// Pop all environments before the eval execution.
let environments = context.realm.environments.pop_to_global();
context.realm.compile_env = context.realm.environments.current_compile_environment();
let environments = context.vm.environments.pop_to_global();
// Restore all environments to the state from before the eval execution.
EnvStackAction::Restore(environments)
@ -235,7 +236,7 @@ impl Eval {
if !strict {
// Error if any var declaration in the eval code already exists as a let/const declaration in the current running environment.
if let Some(name) = context
.realm
.vm
.environments
.has_lex_binding_until_function_environment(&top_level_var_declared_names(&body))
{
@ -249,16 +250,23 @@ impl Eval {
// TODO: check if private identifiers inside `eval` are valid.
// Compile and execute the eval statement list.
let code_block = context.compile_with_new_declarative(&body, strict);
let code_block = {
let mut compiler = ByteCompiler::new(
Sym::MAIN,
body.strict(),
false,
context.vm.environments.current_compile_environment(),
context,
);
compiler.compile_statement_list_with_new_declarative(&body, true, strict);
Gc::new(compiler.finish())
};
// Indirect calls don't need extensions, because a non-strict indirect call modifies only
// the global object.
// Strict direct calls also don't need extensions, since all strict eval calls push a new
// function environment before evaluating.
if direct && !strict {
context
.realm
.environments
.extend_outer_function_environment();
context.vm.environments.extend_outer_function_environment();
}
let result = context.execute(code_block);

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

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

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

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

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

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

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

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

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

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm,
string::utf16,
Context, JsResult, JsString, JsValue,
};
@ -62,10 +63,10 @@ pub struct DateTimeFormat {
}
impl IntrinsicObject for DateTimeFormat {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics).build();
BuiltInBuilder::from_standard_constructor::<Self>(realm).build();
}
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},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm,
string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
@ -48,10 +49,10 @@ impl Service for ListFormat {
}
impl IntrinsicObject for ListFormat {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1)
.property(
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 icu_collator::CaseFirst;
use icu_datetime::options::preferences::HourCycle;
@ -32,60 +32,60 @@ use super::options::{coerce_options_to_object, get_option};
pub(crate) struct Locale;
impl IntrinsicObject for Locale {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let base_name = BuiltInBuilder::new(intrinsics)
let base_name = BuiltInBuilder::new(realm)
.callable(Self::base_name)
.name("get baseName")
.build();
let calendar = BuiltInBuilder::new(intrinsics)
let calendar = BuiltInBuilder::new(realm)
.callable(Self::calendar)
.name("get calendar")
.build();
let case_first = BuiltInBuilder::new(intrinsics)
let case_first = BuiltInBuilder::new(realm)
.callable(Self::case_first)
.name("get caseFirst")
.build();
let collation = BuiltInBuilder::new(intrinsics)
let collation = BuiltInBuilder::new(realm)
.callable(Self::collation)
.name("get collation")
.build();
let hour_cycle = BuiltInBuilder::new(intrinsics)
let hour_cycle = BuiltInBuilder::new(realm)
.callable(Self::hour_cycle)
.name("get hourCycle")
.build();
let numeric = BuiltInBuilder::new(intrinsics)
let numeric = BuiltInBuilder::new(realm)
.callable(Self::numeric)
.name("get numeric")
.build();
let numbering_system = BuiltInBuilder::new(intrinsics)
let numbering_system = BuiltInBuilder::new(realm)
.callable(Self::numbering_system)
.name("get numberingSystem")
.build();
let language = BuiltInBuilder::new(intrinsics)
let language = BuiltInBuilder::new(realm)
.callable(Self::language)
.name("get language")
.build();
let script = BuiltInBuilder::new(intrinsics)
let script = BuiltInBuilder::new(realm)
.callable(Self::script)
.name("get script")
.build();
let region = BuiltInBuilder::new(intrinsics)
let region = BuiltInBuilder::new(realm)
.callable(Self::region)
.name("get region")
.build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(
JsSymbol::to_string_tag(),
"Intl.Locale",

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

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

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

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

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

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

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

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

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

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

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

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

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

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

221
boa_engine/src/builtins/mod.rs

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

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

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

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

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

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

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

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

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

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

@ -15,6 +15,7 @@ use crate::{
JsObject, ObjectData, CONSTRUCTOR,
},
property::Attribute,
realm::Realm,
string::utf16,
symbol::JsSymbol,
value::JsValue,
@ -307,15 +308,15 @@ impl PromiseCapability {
}
impl IntrinsicObject for Promise {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let get_species = BuiltInBuilder::new(intrinsics)
let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species)
.name("get [Symbol.species]")
.build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::all, "all", 1)
.static_method(Self::all_settled, "allSettled", 1)
.static_method(Self::any, "any", 1)
@ -1856,7 +1857,8 @@ impl Promise {
// a. Let value be promise.[[PromiseResult]].
PromiseState::Fulfilled(ref value) => {
// b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, value).
let fulfill_job = new_promise_reaction_job(fulfill_reaction, value.clone());
let fulfill_job =
new_promise_reaction_job(fulfill_reaction, value.clone(), context);
// c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]).
context
@ -1878,7 +1880,7 @@ impl Promise {
}
// d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason).
let reject_job = new_promise_reaction_job(reject_reaction, reason.clone());
let reject_job = new_promise_reaction_job(reject_reaction, reason.clone(), context);
// e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]).
context.job_queue().enqueue_promise_job(reject_job, context);
@ -1955,7 +1957,7 @@ impl Promise {
// 1. For each element reaction of reactions, do
for reaction in reactions {
// a. Let job be NewPromiseReactionJob(reaction, argument).
let job = new_promise_reaction_job(reaction, argument.clone());
let job = new_promise_reaction_job(reaction, argument.clone(), context);
// b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
context.job_queue().enqueue_promise_job(job, context);
@ -2163,6 +2165,7 @@ impl Promise {
promise.clone(),
resolution.clone(),
then_job_callback,
context
);
// 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
@ -2235,7 +2238,26 @@ impl Promise {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-newpromisereactionjob
fn new_promise_reaction_job(mut reaction: ReactionRecord, argument: JsValue) -> NativeJob {
fn new_promise_reaction_job(
mut reaction: ReactionRecord,
argument: JsValue,
context: &mut Context<'_>,
) -> NativeJob {
// Inverting order since `job` captures `reaction` by value.
// 2. Let handlerRealm be null.
// 3. If reaction.[[Handler]] is not empty, then
// a. Let getHandlerRealmResult be Completion(GetFunctionRealm(reaction.[[Handler]].[[Callback]])).
// b. If getHandlerRealmResult is a normal completion, set handlerRealm to getHandlerRealmResult.[[Value]].
// c. Else, set handlerRealm to the current Realm Record.
// d. NOTE: handlerRealm is never null unless the handler is undefined. When the handler is a
// revoked Proxy and no ECMAScript code runs, handlerRealm is used to create error objects.
let realm = reaction
.handler
.as_ref()
.and_then(|handler| handler.callback().get_function_realm(context).ok())
.unwrap_or_else(|| context.realm().clone());
// 1. Let job be a new Job Abstract Closure with no parameters that captures reaction and argument and performs the following steps when called:
let job = move |context: &mut Context<'_>| {
// a. Let promiseCapability be reaction.[[Capability]].
@ -2301,16 +2323,8 @@ fn new_promise_reaction_job(mut reaction: ReactionRecord, argument: JsValue) ->
}
};
// TODO: handle realms
// 2. Let handlerRealm be null.
// 3. If reaction.[[Handler]] is not empty, then
// a. Let getHandlerRealmResult be Completion(GetFunctionRealm(reaction.[[Handler]].[[Callback]])).
// b. If getHandlerRealmResult is a normal completion, set handlerRealm to getHandlerRealmResult.[[Value]].
// c. Else, set handlerRealm to the current Realm Record.
// d. NOTE: handlerRealm is never null unless the handler is undefined. When the handler is a revoked Proxy and no ECMAScript code runs, handlerRealm is used to create error objects.
// 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }.
NativeJob::new(job)
NativeJob::with_realm(job, realm)
}
/// More information:
@ -2321,7 +2335,19 @@ fn new_promise_resolve_thenable_job(
promise_to_resolve: JsObject,
thenable: JsValue,
then: JobCallback,
context: &mut Context<'_>,
) -> NativeJob {
// Inverting order since `job` captures variables by value.
// 2. Let getThenRealmResult be Completion(GetFunctionRealm(then.[[Callback]])).
// 3. If getThenRealmResult is a normal completion, let thenRealm be getThenRealmResult.[[Value]].
// 4. Else, let thenRealm be the current Realm Record.
// 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked Proxy and no code runs, thenRealm is used to create error objects.
let realm = then
.callback()
.get_function_realm(context)
.unwrap_or_else(|_| context.realm().clone());
// 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve, thenable, and then and performs the following steps when called:
let job = move |context: &mut Context<'_>| {
// a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
@ -2351,12 +2377,6 @@ fn new_promise_resolve_thenable_job(
then_call_result
};
// TODO: handle realms
// 2. Let getThenRealmResult be Completion(GetFunctionRealm(then.[[Callback]])).
// 3. If getThenRealmResult is a normal completion, let thenRealm be getThenRealmResult.[[Value]].
// 4. Else, let thenRealm be the current Realm Record.
// 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked Proxy and no code runs, thenRealm is used to create error objects.
// 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }.
NativeJob::new(job)
NativeJob::with_realm(job, realm)
}

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -16,6 +16,7 @@ use crate::{
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor},
realm::Realm,
string::utf16,
string::{CodePoint, Utf16Trim},
symbol::JsSymbol,
@ -64,13 +65,13 @@ pub(crate) const fn is_trimmable_whitespace(c: char) -> bool {
pub(crate) struct String;
impl IntrinsicObject for String {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let symbol_iterator = JsSymbol::iterator();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(utf16!("length"), 0, attribute)
.static_method(Self::raw, "raw", 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,
object::{JsObject, ObjectData},
property::Attribute,
realm::Realm,
symbol::JsSymbol,
Context, JsResult, JsString, JsValue,
};
@ -31,11 +32,17 @@ pub struct StringIterator {
}
impl IntrinsicObject for StringIterator {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("StringIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
.prototype(intrinsics.objects().iterator_prototypes().iterator())
BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0)
.static_property(
JsSymbol::to_string_tag(),

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

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

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

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

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

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

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

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

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

@ -12,6 +12,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm,
string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue,
@ -27,9 +28,9 @@ impl IntrinsicObject for WeakSet {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
}
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(
JsSymbol::to_string_tag(),
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) {
let class_name = class.name().map_or(Sym::EMPTY_STRING, Identifier::sym);
let mut compiler = ByteCompiler::new(class_name, true, self.json_parse, self.context);
let mut compiler = ByteCompiler::new(
class_name,
true,
self.json_parse,
self.current_environment.clone(),
self.context,
);
if let Some(class_name) = class.name() {
if class.has_binding_identifier() {
compiler.has_binding_identifier = true;
compiler.context.push_compile_time_environment(false);
compiler.context.create_immutable_binding(class_name, true);
compiler.push_compile_environment(false);
compiler.create_immutable_binding(class_name, true);
}
}
compiler.context.push_compile_time_environment(true);
compiler.push_compile_environment(true);
if let Some(expr) = class.constructor() {
compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone();
compiler
.context
.create_mutable_binding(Sym::ARGUMENTS.into(), false, false);
compiler.arguments_binding = Some(
compiler
.context
.initialize_mutable_binding(Sym::ARGUMENTS.into(), false),
);
compiler.create_mutable_binding(Sym::ARGUMENTS.into(), false, false);
compiler.arguments_binding =
Some(compiler.initialize_mutable_binding(Sym::ARGUMENTS.into(), false));
for parameter in expr.parameters().as_ref() {
if parameter.is_rest_param() {
compiler.emit_opcode(Opcode::RestParameterInit);
@ -49,9 +50,7 @@ impl ByteCompiler<'_, '_> {
match parameter.variable().binding() {
Binding::Identifier(ident) => {
compiler
.context
.create_mutable_binding(*ident, false, false);
compiler.create_mutable_binding(*ident, false, false);
if let Some(init) = parameter.variable().init() {
let skip =
compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
@ -62,7 +61,7 @@ impl ByteCompiler<'_, '_> {
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
compiler.context.create_mutable_binding(ident, false, false);
compiler.create_mutable_binding(ident, false, false);
}
compiler.compile_declaration_pattern(pattern, BindingOpcode::InitArg);
}
@ -72,8 +71,8 @@ impl ByteCompiler<'_, '_> {
compiler.emit_opcode(Opcode::RestParameterPop);
}
let env_label = if expr.parameters().has_expressions() {
compiler.num_bindings = compiler.context.get_binding_number();
compiler.context.push_compile_time_environment(true);
compiler.num_bindings = compiler.current_environment.borrow().num_bindings();
compiler.push_compile_environment(true);
compiler.function_environment_push_location = compiler.next_opcode_location();
Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment))
} else {
@ -81,36 +80,28 @@ impl ByteCompiler<'_, '_> {
};
compiler.create_script_decls(expr.body(), false);
compiler.compile_statement_list(expr.body(), false, false);
let env_info = compiler.pop_compile_environment();
if let Some(env_label) = env_label {
let (num_bindings, compile_environment) =
compiler.context.pop_compile_time_environment();
let index_compile_environment =
compiler.push_compile_environment(compile_environment);
compiler.patch_jump_with_target(env_label.0, num_bindings as u32);
compiler.patch_jump_with_target(env_label.1, index_compile_environment as u32);
let (_, compile_environment) = compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.patch_jump_with_target(env_label.0, env_info.num_bindings as u32);
compiler.patch_jump_with_target(env_label.1, env_info.index as u32);
compiler.pop_compile_environment();
} else {
let (num_bindings, compile_environment) =
compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.num_bindings = num_bindings;
compiler.num_bindings = env_info.num_bindings;
compiler.is_class_constructor = true;
}
} else {
if class.super_ref().is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived);
}
let (num_bindings, compile_environment) =
compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.num_bindings = num_bindings;
let env_info = compiler.pop_compile_environment();
compiler.num_bindings = env_info.num_bindings;
compiler.is_class_constructor = true;
}
if class.name().is_some() && class.has_binding_identifier() {
let (_, compile_environment) = compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.pop_compile_environment();
}
compiler.emit_opcode(Opcode::PushUndefined);
@ -266,25 +257,24 @@ impl ByteCompiler<'_, '_> {
self.compile_expr(name, true);
}
}
let mut field_compiler =
ByteCompiler::new(Sym::EMPTY_STRING, true, self.json_parse, self.context);
field_compiler.context.push_compile_time_environment(false);
field_compiler
.context
.create_immutable_binding(class_name.into(), true);
field_compiler.context.push_compile_time_environment(true);
let mut field_compiler = ByteCompiler::new(
Sym::EMPTY_STRING,
true,
self.json_parse,
self.current_environment.clone(),
self.context,
);
field_compiler.push_compile_environment(false);
field_compiler.create_immutable_binding(class_name.into(), true);
field_compiler.push_compile_environment(true);
if let Some(node) = field {
field_compiler.compile_expr(node, true);
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
let (num_bindings, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
let (_, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
field_compiler.num_bindings = num_bindings;
let env_info = field_compiler.pop_compile_environment();
field_compiler.pop_compile_environment();
field_compiler.num_bindings = env_info.num_bindings;
field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish();
@ -299,25 +289,24 @@ impl ByteCompiler<'_, '_> {
ClassElement::PrivateFieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
let name_index = self.get_or_insert_private_name(*name);
let mut field_compiler =
ByteCompiler::new(class_name, true, self.json_parse, self.context);
field_compiler.context.push_compile_time_environment(false);
field_compiler
.context
.create_immutable_binding(class_name.into(), true);
field_compiler.context.push_compile_time_environment(true);
let mut field_compiler = ByteCompiler::new(
class_name,
true,
self.json_parse,
self.current_environment.clone(),
self.context,
);
field_compiler.push_compile_environment(false);
field_compiler.create_immutable_binding(class_name.into(), true);
field_compiler.push_compile_environment(true);
if let Some(node) = field {
field_compiler.compile_expr(node, true);
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
let (num_bindings, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
let (_, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
field_compiler.num_bindings = num_bindings;
let env_info = field_compiler.pop_compile_environment();
field_compiler.pop_compile_environment();
field_compiler.num_bindings = env_info.num_bindings;
field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish();
@ -342,25 +331,24 @@ impl ByteCompiler<'_, '_> {
None
}
};
let mut field_compiler =
ByteCompiler::new(class_name, true, self.json_parse, self.context);
field_compiler.context.push_compile_time_environment(false);
field_compiler
.context
.create_immutable_binding(class_name.into(), true);
field_compiler.context.push_compile_time_environment(true);
let mut field_compiler = ByteCompiler::new(
class_name,
true,
self.json_parse,
self.current_environment.clone(),
self.context,
);
field_compiler.push_compile_environment(false);
field_compiler.create_immutable_binding(class_name.into(), true);
field_compiler.push_compile_environment(true);
if let Some(node) = field {
field_compiler.compile_expr(node, true);
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
let (num_bindings, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
let (_, compile_environment) =
field_compiler.context.pop_compile_time_environment();
field_compiler.push_compile_environment(compile_environment);
field_compiler.num_bindings = num_bindings;
let env_info = field_compiler.pop_compile_environment();
field_compiler.pop_compile_environment();
field_compiler.num_bindings = env_info.num_bindings;
field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish();
@ -390,21 +378,21 @@ impl ByteCompiler<'_, '_> {
}
ClassElement::StaticBlock(statement_list) => {
self.emit_opcode(Opcode::Dup);
let mut compiler =
ByteCompiler::new(Sym::EMPTY_STRING, true, false, self.context);
compiler.context.push_compile_time_environment(false);
compiler
.context
.create_immutable_binding(class_name.into(), true);
compiler.context.push_compile_time_environment(true);
let mut compiler = ByteCompiler::new(
Sym::EMPTY_STRING,
true,
false,
self.current_environment.clone(),
self.context,
);
compiler.push_compile_environment(false);
compiler.create_immutable_binding(class_name.into(), true);
compiler.push_compile_environment(true);
compiler.create_script_decls(statement_list, false);
compiler.compile_statement_list(statement_list, false, false);
let (num_bindings, compile_environment) =
compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
let (_, compile_environment) = compiler.context.pop_compile_time_environment();
compiler.push_compile_environment(compile_environment);
compiler.num_bindings = num_bindings;
let env_info = compiler.pop_compile_environment();
compiler.pop_compile_environment();
compiler.num_bindings = env_info.num_bindings;
let code = Gc::new(compiler.finish());
let index = self.functions.len() as u32;

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

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

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

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

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

84
boa_engine/src/bytecompiler/function.rs

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

113
boa_engine/src/bytecompiler/mod.rs

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

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

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

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

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

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

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

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

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

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

14
boa_engine/src/context/hooks.rs

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

36
boa_engine/src/context/intrinsics.rs

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

83
boa_engine/src/context/mod.rs

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

182
boa_engine/src/environments/compile.rs

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

109
boa_engine/src/environments/runtime.rs

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

52
boa_engine/src/error.rs

@ -5,6 +5,7 @@ use crate::{
object::JsObject,
object::ObjectData,
property::PropertyDescriptor,
realm::Realm,
string::utf16,
Context, JsString, JsValue,
};
@ -75,6 +76,10 @@ pub enum TryNativeError {
#[error("property `message` cannot contain unpaired surrogates")]
InvalidMessageEncoding,
/// The constructor property of the error object was invalid.
#[error("invalid `constructor` property of Error object")]
InvalidConstructor,
/// A property of the error object is not accessible.
#[error("could not access property `{property}`")]
InaccessibleProperty {
@ -98,6 +103,13 @@ pub enum TryNativeError {
/// The error value is not an error object.
#[error("opaque error of type `{:?}` is not an Error object", .0.get_type())]
NotAnErrorObject(JsValue),
/// The original realm of the error object was inaccessible.
#[error("could not access realm of Error object")]
InaccessibleRealm {
/// The source error.
source: JsError,
},
}
impl std::error::Error for JsError {
@ -283,10 +295,18 @@ impl JsError {
}
};
let realm = try_get_property("constructor", context)?
.as_ref()
.and_then(JsValue::as_constructor)
.ok_or(TryNativeError::InvalidConstructor)?
.get_function_realm(context)
.map_err(|err| TryNativeError::InaccessibleRealm { source: err })?;
Ok(JsNativeError {
kind,
message,
cause: cause.map(|v| Box::new(Self::from_opaque(v))),
realm: Some(realm),
})
}
}
@ -332,11 +352,25 @@ impl JsError {
/// assert!(error.as_native().is_none());
/// ```
pub const fn as_native(&self) -> Option<&JsNativeError> {
match self.inner {
Repr::Native(ref e) => Some(e),
match &self.inner {
Repr::Native(e) => Some(e),
Repr::Opaque(_) => None,
}
}
/// Injects a realm on the `realm` field of a native error.
///
/// This is a no-op if the error is not native or if the `realm` field of the error is already
/// set.
pub(crate) fn inject_realm(mut self, realm: Realm) -> JsError {
match &mut self.inner {
Repr::Native(err) if err.realm.is_none() => {
err.realm = Some(realm);
}
_ => {}
}
self
}
}
impl From<boa_parser::Error> for JsError {
@ -391,6 +425,7 @@ pub struct JsNativeError {
message: Box<str>,
#[source]
cause: Option<Box<JsError>>,
realm: Option<Realm>,
}
impl JsNativeError {
@ -400,6 +435,7 @@ impl JsNativeError {
kind,
message,
cause,
realm: None,
}
}
@ -642,8 +678,12 @@ impl JsNativeError {
kind,
message,
cause,
realm,
} = self;
let constructors = context.intrinsics().constructors();
let constructors = realm.as_ref().map_or_else(
|| context.intrinsics().constructors(),
|realm| realm.intrinsics().constructors(),
);
let (prototype, tag) = match kind {
JsNativeErrorKind::Aggregate(_) => (
constructors.aggregate_error().prototype(),
@ -700,6 +740,12 @@ impl JsNativeError {
}
o
}
/// Sets the realm of this error.
pub(crate) fn with_realm(mut self, realm: Realm) -> Self {
self.realm = Some(realm);
self
}
}
impl From<boa_parser::Error> for JsNativeError {

35
boa_engine/src/job.rs

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

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

4
boa_engine/src/object/mod.rs

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

9
boa_engine/src/object/operations.rs

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

68
boa_engine/src/realm.rs

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

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;
mod control_flow;
mod env;
mod function;
mod operators;
mod promise;

700
boa_engine/src/vm/code_block.rs

@ -17,9 +17,10 @@ use crate::{
PROTOTYPE,
},
property::PropertyDescriptor,
realm::Realm,
string::utf16,
vm::CallFrame,
Context, JsResult, JsString, JsValue,
Context, JsError, JsResult, JsString, JsValue,
};
use boa_ast::{
expression::Identifier,
@ -129,6 +130,9 @@ pub struct CodeBlock {
/// When the execution of the parameter expressions throws an error, we do not need to pop the function environment.
pub(crate) function_environment_push_location: u32,
/// The number of bindings in the parameters environment.
pub(crate) parameters_env_bindings: Option<usize>,
#[cfg(feature = "trace")]
/// Trace instruction execution to `stdout`.
#[unsafe_ignore_trace]
@ -158,6 +162,7 @@ impl CodeBlock {
is_class_constructor: false,
class_field_initializer_name: None,
function_environment_push_location: 0,
parameters_env_bindings: None,
#[cfg(feature = "trace")]
trace: std::cell::Cell::new(false),
}
@ -598,25 +603,25 @@ pub(crate) fn create_function_object(
Function::new(
FunctionKind::Async {
code,
environments: context.realm.environments.clone(),
environments: context.vm.environments.clone(),
home_object: None,
promise_capability,
class_object: None,
},
context.intrinsics().clone(),
context.realm().clone(),
)
} else {
Function::new(
FunctionKind::Ordinary {
code,
environments: context.realm.environments.clone(),
environments: context.vm.environments.clone(),
constructor_kind: ConstructorKind::Base,
home_object: None,
fields: ThinVec::new(),
private_methods: ThinVec::new(),
class_object: None,
},
context.intrinsics().clone(),
context.realm().clone(),
)
};
@ -713,11 +718,11 @@ pub(crate) fn create_generator_function_object(
let function = Function::new(
FunctionKind::AsyncGenerator {
code,
environments: context.realm.environments.clone(),
environments: context.vm.environments.clone(),
home_object: None,
class_object: None,
},
context.intrinsics().clone(),
context.realm().clone(),
);
JsObject::from_proto_and_data(
function_prototype,
@ -727,11 +732,11 @@ pub(crate) fn create_generator_function_object(
let function = Function::new(
FunctionKind::Generator {
code,
environments: context.realm.environments.clone(),
environments: context.vm.environments.clone(),
home_object: None,
class_object: None,
},
context.intrinsics().clone(),
context.realm().clone(),
);
JsObject::from_proto_and_data(function_prototype, ObjectData::generator_function(function))
};
@ -758,6 +763,49 @@ pub(crate) fn create_generator_function_object(
constructor
}
struct ContextCleanupGuard<'a, 'host> {
context: &'a mut Context<'host>,
old_realm: Realm,
old_active_function: Option<JsObject>,
}
impl<'a, 'host> ContextCleanupGuard<'a, 'host> {
/// Creates a new guard that resets the realm of the context on exit.
fn new(context: &'a mut Context<'host>, realm: Realm, active_function: JsObject) -> Self {
let old_realm = context.enter_realm(realm);
let old_active_function = context.vm.active_function.replace(active_function);
Self {
context,
old_realm,
old_active_function,
}
}
}
impl Drop for ContextCleanupGuard<'_, '_> {
fn drop(&mut self) {
self.context.enter_realm(self.old_realm.clone());
std::mem::swap(
&mut self.context.vm.active_function,
&mut self.old_active_function,
);
}
}
impl<'host> std::ops::Deref for ContextCleanupGuard<'_, 'host> {
type Target = Context<'host>;
fn deref(&self) -> &Self::Target {
self.context
}
}
impl std::ops::DerefMut for ContextCleanupGuard<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.context
}
}
impl JsObject {
pub(crate) fn call_internal(
&self,
@ -766,23 +814,15 @@ impl JsObject {
context: &mut Context<'_>,
) -> JsResult<JsValue> {
let this_function_object = self.clone();
if !self.is_callable() {
return Err(JsNativeError::typ()
.with_message("not a callable function")
.into());
}
let old_active = context.vm.active_function.replace(self.clone());
let active_function = self.clone();
let object = self.borrow();
let function_object = object.as_function().expect("not a function");
let realm = function_object.realm().clone();
let old_intrinsics = std::mem::replace(
&mut context.realm.intrinsics,
function_object.realm_intrinsics().clone(),
);
let context = &mut ContextCleanupGuard::new(context, realm, active_function);
let result = match function_object.kind() {
let (code, mut environments, class_object, promise, async_, gen) =
match function_object.kind() {
FunctionKind::Native {
function,
constructor,
@ -791,11 +831,12 @@ impl JsObject {
let constructor = *constructor;
drop(object);
if constructor.is_some() {
return if constructor.is_some() {
function.call(&JsValue::undefined(), args, context)
} else {
function.call(this, args, context)
}
.map_err(|err| err.inject_realm(context.realm().clone()));
}
FunctionKind::Ordinary {
code,
@ -804,399 +845,67 @@ impl JsObject {
..
} => {
let code = code.clone();
let mut environments = environments.clone();
let class_object = class_object.clone();
drop(object);
if code.is_class_constructor {
return Err(JsNativeError::typ()
.with_message("Class constructor cannot be invoked without 'new'")
.with_message("class constructor cannot be invoked without 'new'")
.with_realm(context.realm().clone())
.into());
}
let environments_len = environments.len();
std::mem::swap(&mut environments, &mut context.realm.environments);
let lexical_this_mode = code.this_mode == ThisMode::Lexical;
let this = if lexical_this_mode {
None
} else if code.strict {
Some(this.clone())
} else if this.is_null_or_undefined() {
Some(context.global_object().into())
} else {
Some(
this.to_object(context)
.expect("conversion cannot fail")
.into(),
)
};
let compile_time_environment_index = usize::from(code.params.has_expressions());
if let Some(class_object) = class_object {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index
+ usize::from(code.has_binding_identifier)
+ 1]
.clone(),
);
context
.realm
.environments
.put_value(index, 0, class_object.into());
}
if code.has_binding_identifier {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index + 1].clone(),
);
context
.realm
.environments
.put_value(index, 0, self.clone().into());
}
context.realm.environments.push_function(
code.num_bindings,
code.compile_environments[compile_time_environment_index].clone(),
this,
self.clone(),
(
code,
environments.clone(),
class_object.clone(),
None,
lexical_this_mode,
);
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
false,
false,
)
};
context.realm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
arguments_obj.into(),
);
}
let arg_count = args.len();
// Push function arguments to the stack.
let args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
code.params.as_ref().len() - args.len()
]);
v
} else {
args.to_vec()
};
for arg in args.iter().rev() {
context.vm.push(arg.clone());
}
let param_count = code.params.as_ref().len();
context.vm.push_frame(
CallFrame::new(code)
.with_param_count(param_count)
.with_arg_count(arg_count),
);
let record = context.run();
context.vm.pop_frame().expect("must have frame");
std::mem::swap(&mut environments, &mut context.realm.environments);
environments.truncate(environments_len);
record.consume()
}
FunctionKind::Async {
code,
environments,
promise_capability,
class_object,
..
} => {
let code = code.clone();
let mut environments = environments.clone();
let promise = promise_capability.promise().clone();
let class_object = class_object.clone();
drop(object);
let environments_len = environments.len();
std::mem::swap(&mut environments, &mut context.realm.environments);
let lexical_this_mode = code.this_mode == ThisMode::Lexical;
let this = if lexical_this_mode {
None
} else if code.strict {
Some(this.clone())
} else if this.is_null_or_undefined() {
Some(context.global_object().into())
} else {
Some(
this.to_object(context)
.expect("conversion cannot fail")
.into(),
)
};
let compile_time_environment_index = usize::from(code.params.has_expressions());
if let Some(class_object) = class_object {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index
+ usize::from(code.has_binding_identifier)
+ 1]
.clone(),
);
context
.realm
.environments
.put_value(index, 0, class_object.into());
}
if code.has_binding_identifier {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index + 1].clone(),
);
context
.realm
.environments
.put_value(index, 0, self.clone().into());
}
context.realm.environments.push_function(
code.num_bindings,
code.compile_environments[compile_time_environment_index].clone(),
this,
self.clone(),
None,
lexical_this_mode,
);
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
context.realm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
arguments_obj.into(),
);
}
let arg_count = args.len();
// Push function arguments to the stack.
let args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
code.params.as_ref().len() - args.len()
]);
v
} else {
args.to_vec()
};
for arg in args.iter().rev() {
context.vm.push(arg.clone());
}
let param_count = code.params.as_ref().len();
context.vm.push_frame(
CallFrame::new(code)
.with_param_count(param_count)
.with_arg_count(arg_count),
);
let _result = context.run();
context.vm.pop_frame().expect("must have frame");
std::mem::swap(&mut environments, &mut context.realm.environments);
environments.truncate(environments_len);
Ok(promise.into())
}
} => (
code.clone(),
environments.clone(),
class_object.clone(),
Some(promise_capability.promise().clone()),
true,
false,
),
FunctionKind::Generator {
code,
environments,
class_object,
..
} => {
let code = code.clone();
let mut environments = environments.clone();
let class_object = class_object.clone();
drop(object);
std::mem::swap(&mut environments, &mut context.realm.environments);
let lexical_this_mode = code.this_mode == ThisMode::Lexical;
let this = if lexical_this_mode {
None
} else if code.strict {
Some(this.clone())
} else if this.is_null_or_undefined() {
Some(context.global_object().into())
} else {
Some(
this.to_object(context)
.expect("conversion cannot fail")
.into(),
)
};
let compile_time_environment_index = usize::from(code.params.has_expressions());
if let Some(class_object) = class_object {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index
+ usize::from(code.has_binding_identifier)
+ 1]
.clone(),
);
context
.realm
.environments
.put_value(index, 0, class_object.into());
}
if code.has_binding_identifier {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index + 1].clone(),
);
context
.realm
.environments
.put_value(index, 0, self.clone().into());
}
context.realm.environments.push_function(
code.num_bindings,
code.compile_environments[compile_time_environment_index].clone(),
this,
self.clone(),
} => (
code.clone(),
environments.clone(),
class_object.clone(),
None,
lexical_this_mode,
);
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
context.realm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
arguments_obj.into(),
);
}
let arg_count = args.len();
// Push function arguments to the stack.
let mut args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
code.params.as_ref().len() - args.len()
]);
v
} else {
args.to_vec()
};
args.reverse();
let param_count = code.params.as_ref().len();
let call_frame = CallFrame::new(code)
.with_param_count(param_count)
.with_arg_count(arg_count);
let mut stack = args;
std::mem::swap(&mut context.vm.stack, &mut stack);
context.vm.push_frame(call_frame);
let init_result = context.run();
let call_frame = context.vm.pop_frame().expect("frame must exist");
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = this_function_object
.get(PROTOTYPE, context)
.expect("GeneratorFunction must have a prototype property")
.as_object()
.map_or_else(|| context.intrinsics().objects().generator(), Clone::clone);
let generator = Self::from_proto_and_data(
prototype,
ObjectData::generator(Generator {
state: GeneratorState::SuspendedStart,
context: Some(GeneratorContext {
environments,
call_frame,
stack,
active_function: context.vm.active_function.clone(),
realm_intrinsics: context.realm.intrinsics.clone(),
}),
}),
);
init_result.consume()?;
Ok(generator.into())
}
false,
true,
),
FunctionKind::AsyncGenerator {
code,
environments,
class_object,
..
} => {
let code = code.clone();
let mut environments = environments.clone();
let class_object = class_object.clone();
} => (
code.clone(),
environments.clone(),
class_object.clone(),
None,
true,
true,
),
};
drop(object);
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut environments, &mut context.vm.environments);
let lexical_this_mode = code.this_mode == ThisMode::Lexical;
@ -1214,47 +923,54 @@ impl JsObject {
)
};
let compile_time_environment_index = usize::from(code.params.has_expressions());
let mut last_env = code.compile_environments.len() - 1;
if let Some(class_object) = class_object {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index
+ usize::from(code.has_binding_identifier)
+ 1]
.clone(),
);
let index = context
.vm
.environments
.push_declarative(1, code.compile_environments[last_env].clone());
context
.realm
.vm
.environments
.put_value(index, 0, class_object.into());
last_env -= 1;
}
if code.has_binding_identifier {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index + 1].clone(),
);
let index = context
.vm
.environments
.push_declarative(1, code.compile_environments[last_env].clone());
context
.realm
.vm
.environments
.put_value(index, 0, self.clone().into());
last_env -= 1;
}
context.realm.environments.push_function(
context.vm.environments.push_function(
code.num_bindings,
code.compile_environments[compile_time_environment_index].clone(),
code.compile_environments[last_env].clone(),
this,
self.clone(),
None,
lexical_this_mode,
);
if let Some(bindings) = code.parameters_env_bindings {
last_env -= 1;
context
.vm
.environments
.push_declarative(bindings, code.compile_environments[last_env].clone());
}
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
let env = context.vm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
@ -1263,7 +979,7 @@ impl JsObject {
context,
)
};
context.realm.environments.put_value(
context.vm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
arguments_obj.into(),
@ -1284,34 +1000,52 @@ impl JsObject {
args.to_vec()
};
args.reverse();
let mut stack = args;
std::mem::swap(&mut context.vm.stack, &mut stack);
let param_count = code.params.as_ref().len();
let call_frame = CallFrame::new(code)
context.vm.push_frame(
CallFrame::new(code)
.with_param_count(param_count)
.with_arg_count(arg_count);
let mut stack = args;
std::mem::swap(&mut context.vm.stack, &mut stack);
context.vm.push_frame(call_frame);
.with_arg_count(arg_count),
);
let init_result = context.run();
let result = context
.run()
.consume()
.map_err(|err| err.inject_realm(context.realm().clone()));
let call_frame = context.vm.pop_frame().expect("frame must exist");
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut environments, &mut context.vm.environments);
std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = this_function_object
if let Some(promise) = promise {
return Ok(promise.into());
}
let value = result?;
let value = if gen {
let proto = this_function_object
.get(PROTOTYPE, context)
.expect("AsyncGeneratorFunction must have a prototype property")
.expect("generator must have a prototype property")
.as_object()
.map_or_else(
|| context.intrinsics().objects().async_generator(),
|| {
if async_ {
context.intrinsics().objects().async_generator()
} else {
context.intrinsics().objects().generator()
}
},
Clone::clone,
);
let generator = Self::from_proto_and_data(
prototype,
proto,
if async_ {
ObjectData::async_generator(AsyncGenerator {
state: AsyncGeneratorState::SuspendedStart,
context: Some(GeneratorContext {
@ -1319,13 +1053,25 @@ impl JsObject {
call_frame,
stack,
active_function: context.vm.active_function.clone(),
realm_intrinsics: context.realm.intrinsics.clone(),
realm: context.realm().clone(),
}),
queue: VecDeque::new(),
})
} else {
ObjectData::generator(Generator {
state: GeneratorState::SuspendedStart,
context: Some(GeneratorContext {
environments,
call_frame,
stack,
active_function: context.vm.active_function.clone(),
realm: context.realm().clone(),
}),
})
},
);
{
if async_ {
let gen_clone = generator.clone();
let mut generator_mut = generator.borrow_mut();
let gen = generator_mut
@ -1335,16 +1081,12 @@ impl JsObject {
gen_context.call_frame.async_generator = Some(gen_clone);
}
init_result.consume()?;
Ok(generator.into())
}
generator.into()
} else {
value
};
context.vm.active_function = old_active;
context.realm.intrinsics = old_intrinsics;
result
Ok(value)
}
pub(crate) fn construct_internal(
@ -1354,30 +1096,13 @@ impl JsObject {
context: &mut Context<'_>,
) -> JsResult<Self> {
let this_function_object = self.clone();
let create_this = |context| {
let prototype =
get_prototype_from_constructor(this_target, StandardConstructors::object, context)?;
Ok(Self::from_proto_and_data(prototype, ObjectData::ordinary()))
};
if !self.is_constructor() {
return Err(JsNativeError::typ()
.with_message("not a constructor function")
.into());
}
let old_active = context.vm.active_function.replace(self.clone());
let active_function = self.clone();
let object = self.borrow();
let function_object = object.as_function().expect("not a function");
let realm = function_object.realm().clone();
let old_intrinsics = std::mem::replace(
&mut context.realm.intrinsics,
function_object.realm_intrinsics().clone(),
);
let result = match function_object.kind() {
let context = &mut ContextCleanupGuard::new(context, realm, active_function);
match function_object.kind() {
FunctionKind::Native {
function,
constructor,
@ -1387,20 +1112,30 @@ impl JsObject {
let constructor = *constructor;
drop(object);
match function.call(this_target, args, context)? {
function
.call(this_target, args, context)
.map_err(|err| err.inject_realm(context.realm().clone()))
.and_then(|v| match v {
JsValue::Object(ref o) => Ok(o.clone()),
val => {
if constructor.expect("hmm").is_base() || val.is_undefined() {
create_this(context)
if constructor.expect("must be a constructor").is_base()
|| val.is_undefined()
{
let prototype = get_prototype_from_constructor(
this_target,
StandardConstructors::object,
context,
)?;
Ok(Self::from_proto_and_data(prototype, ObjectData::ordinary()))
} else {
Err(JsNativeError::typ()
.with_message(
"Derived constructor can only return an Object or undefined",
"derived constructor can only return an Object or undefined",
)
.into())
}
}
}
})
}
FunctionKind::Ordinary {
code,
@ -1433,37 +1168,45 @@ impl JsObject {
};
let environments_len = environments.len();
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut environments, &mut context.vm.environments);
let new_target = this_target.as_object().expect("must be object");
let compile_time_environment_index = usize::from(code.params.has_expressions());
let mut last_env = code.compile_environments.len() - 1;
if code.has_binding_identifier {
let index = context.realm.environments.push_declarative(
1,
code.compile_environments[compile_time_environment_index + 1].clone(),
);
let index = context
.vm
.environments
.push_declarative(1, code.compile_environments[last_env].clone());
context
.realm
.vm
.environments
.put_value(index, 0, self.clone().into());
last_env -= 1;
}
context.realm.environments.push_function(
context.vm.environments.push_function(
code.num_bindings,
code.compile_environments[compile_time_environment_index].clone(),
code.compile_environments[last_env].clone(),
this.clone().map(Into::into),
self.clone(),
Some(new_target.clone()),
false,
);
if let Some(bindings) = code.parameters_env_bindings {
context
.vm
.environments
.push_declarative(bindings, code.compile_environments[0].clone());
}
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
let env = context.vm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
@ -1472,7 +1215,7 @@ impl JsObject {
context,
)
};
context.realm.environments.put_value(
context.vm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
arguments_obj.into(),
@ -1510,7 +1253,7 @@ impl JsObject {
context.vm.pop_frame();
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut environments, &mut context.vm.environments);
let environment = if has_binding_identifier {
environments.truncate(environments_len + 2);
@ -1522,7 +1265,9 @@ impl JsObject {
environments.pop()
};
let result = record.consume()?;
let result = record
.consume()
.map_err(|err| err.inject_realm(context.realm().clone()))?;
if let Some(result) = result.as_object() {
Ok(result.clone())
@ -1530,7 +1275,7 @@ impl JsObject {
Ok(this)
} else if !result.is_undefined() {
Err(JsNativeError::typ()
.with_message("Function constructor must not return non-object")
.with_message("derived constructor can only return an Object or undefined")
.into())
} else {
let function_env = environment
@ -1539,12 +1284,15 @@ impl JsObject {
.expect("must be function environment")
.as_function_slots()
.expect("must be function environment");
Ok(function_env
function_env
.borrow()
.get_this_binding()?
.as_object()
.get_this_binding()
.map(|this| {
this.as_object()
.expect("this binding must be object")
.clone())
.clone()
})
.map_err(JsError::from)
}
}
FunctionKind::Generator { .. }
@ -1552,10 +1300,6 @@ impl JsObject {
| FunctionKind::AsyncGenerator { .. } => {
unreachable!("not a constructor")
}
};
context.vm.active_function = old_active;
context.realm.intrinsics = old_intrinsics;
result
}
}
}

16
boa_engine/src/vm/mod.rs

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

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

@ -34,11 +34,11 @@ impl Operation for Await {
)?;
let generator_context = GeneratorContext {
environments: context.realm.environments.clone(),
environments: context.vm.environments.clone(),
call_frame: context.vm.frame().clone(),
stack: context.vm.stack.clone(),
active_function: context.vm.active_function.clone(),
realm_intrinsics: context.realm.intrinsics.clone(),
realm: context.realm().clone(),
};
// 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called:
@ -57,7 +57,7 @@ impl Operation for Await {
// f. Return undefined.
std::mem::swap(
&mut context.realm.environments,
&mut context.vm.environments,
&mut generator_context.environments,
);
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -65,10 +65,7 @@ impl Operation for Await {
&mut context.vm.active_function,
&mut generator_context.active_function,
);
std::mem::swap(
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
let old_realm = context.enter_realm(generator_context.realm.clone());
context.vm.push_frame(generator_context.call_frame.clone());
context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal;
@ -80,7 +77,7 @@ impl Operation for Await {
.pop_frame()
.expect("generator call frame must exist");
std::mem::swap(
&mut context.realm.environments,
&mut context.vm.environments,
&mut generator_context.environments,
);
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -88,10 +85,7 @@ impl Operation for Await {
&mut context.vm.active_function,
&mut generator_context.active_function,
);
std::mem::swap(
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.enter_realm(old_realm);
Ok(JsValue::undefined())
},
@ -118,7 +112,7 @@ impl Operation for Await {
// f. Return undefined.
std::mem::swap(
&mut context.realm.environments,
&mut context.vm.environments,
&mut generator_context.environments,
);
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -126,10 +120,7 @@ impl Operation for Await {
&mut context.vm.active_function,
&mut generator_context.active_function,
);
std::mem::swap(
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
let old_realm = context.enter_realm(generator_context.realm.clone());
context.vm.push_frame(generator_context.call_frame.clone());
context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw;
@ -141,7 +132,7 @@ impl Operation for Await {
.pop_frame()
.expect("generator call frame must exist");
std::mem::swap(
&mut context.realm.environments,
&mut context.vm.environments,
&mut generator_context.environments,
);
std::mem::swap(&mut context.vm.stack, &mut generator_context.stack);
@ -149,10 +140,7 @@ impl Operation for Await {
&mut context.vm.active_function,
&mut generator_context.active_function,
);
std::mem::swap(
&mut context.realm.intrinsics,
&mut generator_context.realm_intrinsics,
);
context.enter_realm(old_realm);
Ok(JsValue::undefined())
},

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();
}
let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len);
let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.vm.environments.truncate(env_truncation_len);
// 2. Register target address in AbruptCompletionRecord.
let new_record = AbruptCompletionRecord::new_break().with_initial_target(target_address);

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

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();
}
let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len);
let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.vm.environments.truncate(env_truncation_len);
// 2. Register target address in AbruptCompletionRecord.
let new_record = AbruptCompletionRecord::new_continue().with_initial_target(target_address);

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

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

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();
}
let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop);
context.realm.environments.truncate(env_truncation_len);
let env_truncation_len = context.vm.environments.len().saturating_sub(env_to_pop);
context.vm.environments.truncate(env_truncation_len);
let record = AbruptCompletionRecord::new_return();
context.vm.frame_mut().abrupt_completion = Some(record);

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

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

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

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

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

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

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

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

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

@ -92,7 +92,7 @@ impl Operation for AsyncGeneratorNext {
.queue
.pop_front()
.expect("must have item in queue");
AsyncGenerator::complete_step(&next, completion, false, context);
AsyncGenerator::complete_step(&next, completion, false, None, context);
let mut generator_object_mut = generator_object.borrow_mut();
let gen = generator_object_mut

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

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

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

@ -51,11 +51,11 @@ impl Operation for LoopContinue {
.filter(|entry| entry.exit_address() == exit)
{
let env_truncation_len = context
.realm
.vm
.environments
.len()
.saturating_sub(entry.env_num());
context.realm.environments.truncate(env_truncation_len);
context.vm.environments.truncate(env_truncation_len);
context.vm.frame_mut().env_stack.pop();
}
@ -92,8 +92,8 @@ impl Operation for LoopEnd {
}
}
let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop);
context.realm.environments.truncate(env_truncation_len);
let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.vm.environments.truncate(env_truncation_len);
Ok(CompletionType::Normal)
}

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

@ -55,7 +55,7 @@ impl Operation for PopEnvironment {
const INSTRUCTION: &'static str = "INST - PopEnvironment";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
context.realm.environments.pop();
context.vm.environments.pop();
context.vm.frame_mut().dec_frame_env_stack();
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]
.clone();
context
.realm
.vm
.environments
.push_declarative(num_bindings as usize, compile_environment);
context.vm.frame_mut().inc_frame_env_stack();
@ -47,7 +47,7 @@ impl Operation for PushFunctionEnvironment {
[compile_environments_index as usize]
.clone();
context
.realm
.vm
.environments
.push_function_inherit(num_bindings as usize, compile_environment);
Ok(CompletionType::Normal)
@ -69,7 +69,7 @@ impl Operation for PushObjectEnvironment {
let object = context.vm.pop();
let object = object.to_object(context)?;
context.realm.environments.push_object(object);
context.vm.environments.push_object(object);
context.vm.frame_mut().inc_frame_env_stack();
Ok(CompletionType::Normal)
}

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

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

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

Loading…
Cancel
Save