mirror of https://github.com/boa-dev/boa.git
Browse Source
This Pull Request fixes test [`assert-throws-same-realm.js`](eb44f67274/test/harness/assert-throws-same-realm.js
).
It changes the following:
- Handles global variables through the global object, instead of the `context`.
- Adds an `active_function` field to the vm, which is used as the `NewTarget` when certain builtins aren't called with `new`.
- Adds a `realm_intrinsics` field to `Function`.
pull/2799/head
José Julián Espina
2 years ago
68 changed files with 1069 additions and 1006 deletions
@ -0,0 +1,14 @@ |
|||||||
|
use boa_engine::{object::ObjectInitializer, Context, JsObject, JsResult, JsValue, NativeFunction}; |
||||||
|
|
||||||
|
/// Creates a new ECMAScript Realm and returns the global object of the realm.
|
||||||
|
fn create(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let context = &mut Context::default(); |
||||||
|
|
||||||
|
Ok(context.global_object().into()) |
||||||
|
} |
||||||
|
|
||||||
|
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
ObjectInitializer::new(context) |
||||||
|
.function(NativeFunction::from_fn_ptr(create), "create", 0) |
||||||
|
.build() |
||||||
|
} |
@ -1,431 +0,0 @@ |
|||||||
use crate::{ |
|
||||||
object::{InternalObjectMethods, JsObject, ORDINARY_INTERNAL_METHODS}, |
|
||||||
property::{PropertyDescriptor, PropertyKey}, |
|
||||||
value::JsValue, |
|
||||||
Context, JsResult, |
|
||||||
}; |
|
||||||
use boa_profiler::Profiler; |
|
||||||
|
|
||||||
/// Definitions of the internal object methods for global object.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-global-object
|
|
||||||
pub(crate) static GLOBAL_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods { |
|
||||||
__get_own_property__: global_get_own_property, |
|
||||||
__define_own_property__: global_define_own_property, |
|
||||||
__set__: global_set, |
|
||||||
__delete__: global_delete, |
|
||||||
__own_property_keys__: global_own_property_keys, |
|
||||||
..ORDINARY_INTERNAL_METHODS |
|
||||||
}; |
|
||||||
|
|
||||||
/// Abstract operation `OrdinaryGetOwnProperty`.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-ordinarygetownproperty
|
|
||||||
#[allow(clippy::unnecessary_wraps)] |
|
||||||
pub(crate) fn global_get_own_property( |
|
||||||
_obj: &JsObject, |
|
||||||
key: &PropertyKey, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<Option<PropertyDescriptor>> { |
|
||||||
let _timer = Profiler::global().start_event("Object::global_get_own_property", "object"); |
|
||||||
// 1. Assert: IsPropertyKey(P) is true.
|
|
||||||
// 2. If O does not have an own property with key P, return undefined.
|
|
||||||
// 3. Let D be a newly created Property Descriptor with no fields.
|
|
||||||
// 4. Let X be O's own property whose key is P.
|
|
||||||
// 5. If X is a data property, then
|
|
||||||
// a. Set D.[[Value]] to the value of X's [[Value]] attribute.
|
|
||||||
// b. Set D.[[Writable]] to the value of X's [[Writable]] attribute.
|
|
||||||
// 6. Else,
|
|
||||||
// a. Assert: X is an accessor property.
|
|
||||||
// b. Set D.[[Get]] to the value of X's [[Get]] attribute.
|
|
||||||
// c. Set D.[[Set]] to the value of X's [[Set]] attribute.
|
|
||||||
// 7. Set D.[[Enumerable]] to the value of X's [[Enumerable]] attribute.
|
|
||||||
// 8. Set D.[[Configurable]] to the value of X's [[Configurable]] attribute.
|
|
||||||
// 9. Return D.
|
|
||||||
Ok(context.realm.global_property_map.get(key)) |
|
||||||
} |
|
||||||
|
|
||||||
/// Abstract operation `OrdinaryDefineOwnProperty`.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty
|
|
||||||
#[allow(clippy::needless_pass_by_value)] |
|
||||||
pub(crate) fn global_define_own_property( |
|
||||||
obj: &JsObject, |
|
||||||
key: &PropertyKey, |
|
||||||
desc: PropertyDescriptor, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<bool> { |
|
||||||
let _timer = Profiler::global().start_event("Object::global_define_own_property", "object"); |
|
||||||
// 1. Let current be ? O.[[GetOwnProperty]](P).
|
|
||||||
let current = global_get_own_property(obj, key, context)?; |
|
||||||
|
|
||||||
// 2. Let extensible be ? IsExtensible(O).
|
|
||||||
let extensible = obj.__is_extensible__(context)?; |
|
||||||
|
|
||||||
// 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
|
|
||||||
Ok(validate_and_apply_property_descriptor( |
|
||||||
key, extensible, desc, current, context, |
|
||||||
)) |
|
||||||
} |
|
||||||
|
|
||||||
/// Abstract operation `OrdinarySet`.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-ordinaryset
|
|
||||||
#[allow(clippy::needless_pass_by_value)] |
|
||||||
pub(crate) fn global_set( |
|
||||||
_obj: &JsObject, |
|
||||||
key: PropertyKey, |
|
||||||
value: JsValue, |
|
||||||
_receiver: JsValue, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<bool> { |
|
||||||
global_set_no_receiver(&key, value, context) |
|
||||||
} |
|
||||||
|
|
||||||
pub(crate) fn global_set_no_receiver( |
|
||||||
key: &PropertyKey, |
|
||||||
value: JsValue, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<bool> { |
|
||||||
let _timer = Profiler::global().start_event("Object::global_set", "object"); |
|
||||||
|
|
||||||
// 1. Assert: IsPropertyKey(P) is true.
|
|
||||||
// 2. Let ownDesc be ? O.[[GetOwnProperty]](P).
|
|
||||||
// 3. Return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).
|
|
||||||
|
|
||||||
// OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc )
|
|
||||||
// https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ordinarysetwithowndescriptor
|
|
||||||
|
|
||||||
// 1. Assert: IsPropertyKey(P) is true.
|
|
||||||
let own_desc = if let Some(desc) = context.realm.global_property_map.get(key) { |
|
||||||
desc |
|
||||||
} |
|
||||||
// c. Else,
|
|
||||||
else { |
|
||||||
PropertyDescriptor::builder() |
|
||||||
.value(value.clone()) |
|
||||||
.writable(true) |
|
||||||
.enumerable(true) |
|
||||||
.configurable(true) |
|
||||||
.build() |
|
||||||
}; |
|
||||||
|
|
||||||
// 3. If IsDataDescriptor(ownDesc) is true, then
|
|
||||||
if own_desc.is_data_descriptor() { |
|
||||||
// a. If ownDesc.[[Writable]] is false, return false.
|
|
||||||
if !own_desc.expect_writable() { |
|
||||||
return Ok(false); |
|
||||||
} |
|
||||||
|
|
||||||
// c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
|
|
||||||
// d. If existingDescriptor is not undefined, then
|
|
||||||
let desc = if let Some(existing_desc) = context.realm.global_property_map.get(key) { |
|
||||||
// i. If IsAccessorDescriptor(existingDescriptor) is true, return false.
|
|
||||||
if existing_desc.is_accessor_descriptor() { |
|
||||||
return Ok(false); |
|
||||||
} |
|
||||||
|
|
||||||
// ii. If existingDescriptor.[[Writable]] is false, return false.
|
|
||||||
if !existing_desc.expect_writable() { |
|
||||||
return Ok(false); |
|
||||||
} |
|
||||||
|
|
||||||
// iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
|
|
||||||
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
|
|
||||||
PropertyDescriptor::builder().value(value).build() |
|
||||||
} else { |
|
||||||
// i. Assert: Receiver does not currently have a property P.
|
|
||||||
// ii. Return ? CreateDataProperty(Receiver, P, V).
|
|
||||||
PropertyDescriptor::builder() |
|
||||||
.value(value) |
|
||||||
.writable(true) |
|
||||||
.enumerable(true) |
|
||||||
.configurable(true) |
|
||||||
.build() |
|
||||||
}; |
|
||||||
|
|
||||||
// 1. Let current be ? O.[[GetOwnProperty]](P).
|
|
||||||
let current = context.realm.global_property_map.get(key); |
|
||||||
|
|
||||||
// 2. Let extensible be ? IsExtensible(O).
|
|
||||||
let extensible = context.global_object().clone().is_extensible(context)?; |
|
||||||
|
|
||||||
// 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
|
|
||||||
return Ok(validate_and_apply_property_descriptor( |
|
||||||
key, extensible, desc, current, context, |
|
||||||
)); |
|
||||||
} |
|
||||||
|
|
||||||
// 4. Assert: IsAccessorDescriptor(ownDesc) is true.
|
|
||||||
debug_assert!(own_desc.is_accessor_descriptor()); |
|
||||||
|
|
||||||
// 5. Let setter be ownDesc.[[Set]].
|
|
||||||
match own_desc.set() { |
|
||||||
Some(set) if !set.is_undefined() => { |
|
||||||
// 7. Perform ? Call(setter, Receiver, « V »).
|
|
||||||
set.call(&context.global_object().clone().into(), &[value], context)?; |
|
||||||
|
|
||||||
// 8. Return true.
|
|
||||||
Ok(true) |
|
||||||
} |
|
||||||
// 6. If setter is undefined, return false.
|
|
||||||
_ => Ok(false), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Abstract operation `OrdinaryDelete`.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydelete
|
|
||||||
#[allow(clippy::unnecessary_wraps, clippy::needless_pass_by_value)] |
|
||||||
pub(crate) fn global_delete( |
|
||||||
_obj: &JsObject, |
|
||||||
key: &PropertyKey, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<bool> { |
|
||||||
global_delete_no_receiver(key, context) |
|
||||||
} |
|
||||||
|
|
||||||
/// Abstract operation `OrdinaryDelete`.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydelete
|
|
||||||
#[allow(clippy::unnecessary_wraps)] |
|
||||||
pub(crate) fn global_delete_no_receiver( |
|
||||||
key: &PropertyKey, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<bool> { |
|
||||||
let _timer = Profiler::global().start_event("Object::global_delete", "object"); |
|
||||||
// 1. Assert: IsPropertyKey(P) is true.
|
|
||||||
// 2. Let desc be ? O.[[GetOwnProperty]](P).
|
|
||||||
match context.realm.global_property_map.get(key) { |
|
||||||
// 4. If desc.[[Configurable]] is true, then
|
|
||||||
Some(desc) if desc.expect_configurable() => { |
|
||||||
// a. Remove the own property with name P from O.
|
|
||||||
context.realm.global_property_map.remove(key); |
|
||||||
// b. Return true.
|
|
||||||
Ok(true) |
|
||||||
} |
|
||||||
// 5. Return false.
|
|
||||||
Some(_) => Ok(false), |
|
||||||
// 3. If desc is undefined, return true.
|
|
||||||
None => Ok(true), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Abstract operation `OrdinaryOwnPropertyKeys`.
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
|
|
||||||
#[allow(clippy::unnecessary_wraps)] |
|
||||||
pub(crate) fn global_own_property_keys( |
|
||||||
_: &JsObject, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> JsResult<Vec<PropertyKey>> { |
|
||||||
// 1. Let keys be a new empty List.
|
|
||||||
let mut keys = Vec::new(); |
|
||||||
|
|
||||||
let ordered_indexes = { |
|
||||||
let mut indexes: Vec<_> = context |
|
||||||
.realm |
|
||||||
.global_property_map |
|
||||||
.index_property_keys() |
|
||||||
.collect(); |
|
||||||
indexes.sort_unstable(); |
|
||||||
indexes |
|
||||||
}; |
|
||||||
|
|
||||||
// 2. For each own property key P of O such that P is an array index, in ascending numeric index order, do
|
|
||||||
// a. Add P as the last element of keys.
|
|
||||||
keys.extend(ordered_indexes.into_iter().map(Into::into)); |
|
||||||
|
|
||||||
// 3. For each own property key P of O such that Type(P) is String and P is not an array index, in ascending chronological order of property creation, do
|
|
||||||
// a. Add P as the last element of keys.
|
|
||||||
keys.extend( |
|
||||||
context |
|
||||||
.realm |
|
||||||
.global_property_map |
|
||||||
.string_property_keys() |
|
||||||
.cloned() |
|
||||||
.map(Into::into), |
|
||||||
); |
|
||||||
|
|
||||||
// 4. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do
|
|
||||||
// a. Add P as the last element of keys.
|
|
||||||
keys.extend( |
|
||||||
context |
|
||||||
.realm |
|
||||||
.global_property_map |
|
||||||
.symbol_property_keys() |
|
||||||
.cloned() |
|
||||||
.map(Into::into), |
|
||||||
); |
|
||||||
|
|
||||||
// 5. Return keys.
|
|
||||||
Ok(keys) |
|
||||||
} |
|
||||||
|
|
||||||
/// Abstract operation `ValidateAndApplyPropertyDescriptor`
|
|
||||||
///
|
|
||||||
/// More information:
|
|
||||||
/// - [ECMAScript reference][spec]
|
|
||||||
///
|
|
||||||
/// [spec]: https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
|
|
||||||
pub(crate) fn validate_and_apply_property_descriptor( |
|
||||||
key: &PropertyKey, |
|
||||||
extensible: bool, |
|
||||||
desc: PropertyDescriptor, |
|
||||||
current: Option<PropertyDescriptor>, |
|
||||||
context: &mut Context<'_>, |
|
||||||
) -> bool { |
|
||||||
let _timer = Profiler::global().start_event( |
|
||||||
"Object::global_validate_and_apply_property_descriptor", |
|
||||||
"object", |
|
||||||
); |
|
||||||
// 1. Assert: If O is not undefined, then IsPropertyKey(P) is true.
|
|
||||||
|
|
||||||
let Some(mut current) = current else { |
|
||||||
// 2. If current is undefined, then
|
|
||||||
// a. If extensible is false, return false.
|
|
||||||
if !extensible { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// b. Assert: extensible is true.
|
|
||||||
context.realm.global_property_map.insert( |
|
||||||
key, |
|
||||||
// c. If IsGenericDescriptor(Desc) is true or IsDataDescriptor(Desc) is true, then
|
|
||||||
if desc.is_generic_descriptor() || desc.is_data_descriptor() { |
|
||||||
// i. If O is not undefined, create an own data property named P of
|
|
||||||
// object O whose [[Value]], [[Writable]], [[Enumerable]], and
|
|
||||||
// [[Configurable]] attribute values are described by Desc.
|
|
||||||
// If the value of an attribute field of Desc is absent, the attribute
|
|
||||||
// of the newly created property is set to its default value.
|
|
||||||
desc.into_data_defaulted() |
|
||||||
} |
|
||||||
// d. Else,
|
|
||||||
else { |
|
||||||
// i. Assert: ! IsAccessorDescriptor(Desc) is true.
|
|
||||||
|
|
||||||
// ii. If O is not undefined, create an own accessor property named P
|
|
||||||
// of object O whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]]
|
|
||||||
// attribute values are described by Desc. If the value of an attribute field
|
|
||||||
// of Desc is absent, the attribute of the newly created property is set to
|
|
||||||
// its default value.
|
|
||||||
desc.into_accessor_defaulted() |
|
||||||
}, |
|
||||||
); |
|
||||||
|
|
||||||
// e. Return true.
|
|
||||||
return true; |
|
||||||
}; |
|
||||||
|
|
||||||
// 3. If every field in Desc is absent, return true.
|
|
||||||
if desc.is_empty() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// 4. If current.[[Configurable]] is false, then
|
|
||||||
if !current.expect_configurable() { |
|
||||||
// a. If Desc.[[Configurable]] is present and its value is true, return false.
|
|
||||||
if matches!(desc.configurable(), Some(true)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// b. If Desc.[[Enumerable]] is present and ! SameValue(Desc.[[Enumerable]], current.[[Enumerable]])
|
|
||||||
// is false, return false.
|
|
||||||
if matches!(desc.enumerable(), Some(desc_enum) if desc_enum != current.expect_enumerable()) |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 5. If ! IsGenericDescriptor(Desc) is true, then
|
|
||||||
if desc.is_generic_descriptor() { |
|
||||||
// a. NOTE: No further validation is required.
|
|
||||||
} |
|
||||||
// 6. Else if ! SameValue(! IsDataDescriptor(current), ! IsDataDescriptor(Desc)) is false, then
|
|
||||||
else if current.is_data_descriptor() != desc.is_data_descriptor() { |
|
||||||
// a. If current.[[Configurable]] is false, return false.
|
|
||||||
if !current.expect_configurable() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// b. If IsDataDescriptor(current) is true, then
|
|
||||||
if current.is_data_descriptor() { |
|
||||||
// i. If O is not undefined, convert the property named P of object O from a data
|
|
||||||
// property to an accessor property. Preserve the existing values of the converted
|
|
||||||
// property's [[Configurable]] and [[Enumerable]] attributes and set the rest of
|
|
||||||
// the property's attributes to their default values.
|
|
||||||
current = current.into_accessor_defaulted(); |
|
||||||
} |
|
||||||
// c. Else,
|
|
||||||
else { |
|
||||||
// i. If O is not undefined, convert the property named P of object O from an
|
|
||||||
// accessor property to a data property. Preserve the existing values of the
|
|
||||||
// converted property's [[Configurable]] and [[Enumerable]] attributes and set
|
|
||||||
// the rest of the property's attributes to their default values.
|
|
||||||
current = current.into_data_defaulted(); |
|
||||||
} |
|
||||||
} |
|
||||||
// 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
|
|
||||||
else if current.is_data_descriptor() && desc.is_data_descriptor() { |
|
||||||
// a. If current.[[Configurable]] is false and current.[[Writable]] is false, then
|
|
||||||
if !current.expect_configurable() && !current.expect_writable() { |
|
||||||
// i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true, return false.
|
|
||||||
if matches!(desc.writable(), Some(true)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
// ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false.
|
|
||||||
if matches!(desc.value(), Some(value) if !JsValue::same_value(value, current.expect_value())) |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
// iii. Return true.
|
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
// 8. Else,
|
|
||||||
// a. Assert: ! IsAccessorDescriptor(current) and ! IsAccessorDescriptor(Desc) are both true.
|
|
||||||
// b. If current.[[Configurable]] is false, then
|
|
||||||
else if !current.expect_configurable() { |
|
||||||
// i. If Desc.[[Set]] is present and SameValue(Desc.[[Set]], current.[[Set]]) is false, return false.
|
|
||||||
if matches!(desc.set(), Some(set) if !JsValue::same_value(set, current.expect_set())) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// ii. If Desc.[[Get]] is present and SameValue(Desc.[[Get]], current.[[Get]]) is false, return false.
|
|
||||||
if matches!(desc.get(), Some(get) if !JsValue::same_value(get, current.expect_get())) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
// iii. Return true.
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// 9. If O is not undefined, then
|
|
||||||
// a. For each field of Desc that is present, set the corresponding attribute of the
|
|
||||||
// property named P of object O to the value of the field.
|
|
||||||
current.fill_with(&desc); |
|
||||||
context.realm.global_property_map.insert(key, current); |
|
||||||
|
|
||||||
// 10. Return true.
|
|
||||||
true |
|
||||||
} |
|
Loading…
Reference in new issue