Skip to content

Commit

Permalink
Make Realm shareable between functions (#2801)
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
jedel1043 committed Apr 10, 2023
1 parent 7a4d652 commit 1e75fd0
Show file tree
Hide file tree
Showing 101 changed files with 1,768 additions and 1,740 deletions.
13 changes: 10 additions & 3 deletions boa_engine/src/builtins/array/array_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
error::JsNativeError,
object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind},
realm::Realm,
symbol::JsSymbol,
Context, JsResult,
};
Expand All @@ -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(),
Expand Down
27 changes: 16 additions & 11 deletions boa_engine/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -40,26 +41,28 @@ mod tests;
pub(crate) struct Array;

impl IntrinsicObject for Array {
fn init(intrinsics: &Intrinsics) {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");

let symbol_iterator = JsSymbol::iterator();
let symbol_unscopables = JsSymbol::unscopables();

let get_species = BuiltInBuilder::new(intrinsics)
let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species)
.name("get [Symbol.species]")
.build();

let values_function =
BuiltInBuilder::with_object(intrinsics, intrinsics.objects().array_prototype_values())
.callable(Self::values)
.name("values")
.build();
let values_function = BuiltInBuilder::with_object(
realm,
realm.intrinsics().objects().array_prototype_values(),
)
.callable(Self::values)
.name("values")
.build();

let unscopables_object = Self::unscopables_object();

BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor(
JsSymbol::species(),
Some(get_species),
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 5 additions & 4 deletions boa_engine/src/builtins/array_buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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),
Expand Down
11 changes: 7 additions & 4 deletions boa_engine/src/builtins/async_function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
builtins::{function::BuiltInFunctionObject, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
property::Attribute,
realm::Realm,
symbol::JsSymbol,
Context, JsResult, JsValue,
};
Expand All @@ -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,
Expand Down
64 changes: 38 additions & 26 deletions boa_engine/src/builtins/async_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR},
property::Attribute,
realm::Realm,
symbol::JsSymbol,
value::JsValue,
vm::GeneratorResumeKind,
Expand Down Expand Up @@ -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)
Expand All @@ -81,7 +88,8 @@ impl IntrinsicObject for AsyncGenerator {
)
.static_property(
CONSTRUCTOR,
intrinsics
realm
.intrinsics()
.constructors()
.async_generator_function()
.prototype(),
Expand Down Expand Up @@ -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]].
Expand All @@ -422,15 +431,24 @@ impl AsyncGenerator {
Ok(value) => {
// a. Assert: completion.[[Type]] is normal.

// TODO: Realm handling not implemented yet.
// b. If realm is present, then
// i. Let oldRealm be the running execution context's Realm.
// ii. Set the running execution context's Realm to realm.
// iii. Let iteratorResult be CreateIterResultObject(value, done).
// iv. Set the running execution context's Realm to oldRealm.
// c. Else,
// i. Let iteratorResult be CreateIterResultObject(value, done).
let iterator_result = create_iter_result_object(value, done, context);
let iterator_result = if let Some(realm) = realm {
// i. Let oldRealm be the running execution context's Realm.
// ii. Set the running execution context's Realm to realm.
let old_realm = context.enter_realm(realm);

// iii. Let iteratorResult be CreateIterResultObject(value, done).
let iterator_result = create_iter_result_object(value, done, context);

// iv. Set the running execution context's Realm to oldRealm.
context.enter_realm(old_realm);

iterator_result
} else {
// c. Else,
// i. Let iteratorResult be CreateIterResultObject(value, done).
create_iter_result_object(value, done, context)
};

// d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
promise_capability
Expand Down Expand Up @@ -474,18 +492,15 @@ 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);
std::mem::swap(
&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());

Expand All @@ -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);
Expand All @@ -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()
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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() {
Expand Down
11 changes: 7 additions & 4 deletions boa_engine/src/builtins/async_generator_function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{JsObject, PROTOTYPE},
property::Attribute,
realm::Realm,
symbol::JsSymbol,
value::JsValue,
Context, JsResult,
Expand All @@ -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(
Expand Down
5 changes: 3 additions & 2 deletions boa_engine/src/builtins/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
error::JsNativeError,
object::JsObject,
property::Attribute,
realm::Realm,
symbol::JsSymbol,
value::{IntegerOrInfinity, PreferredType},
Context, JsArgs, JsBigInt, JsResult, JsValue,
Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions boa_engine/src/builtins/boolean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down
Loading

0 comments on commit 1e75fd0

Please sign in to comment.