Browse Source

Implement missing global object internal methods (#2287)

This Pull Request fixes/closes #1987.

It changes the following:

- Add a prototype to the global object.
- Implement `__get_prototype_of__`, `__set_prototype_of__`, `__get_own_property__`, and `__own_property_keys__` for the global object
pull/2292/head
raskad 2 years ago
parent
commit
db8a299386
  1. 2
      boa_engine/benches/full.rs
  2. 5
      boa_engine/src/context/mod.rs
  3. 165
      boa_engine/src/object/internal_methods/global.rs
  4. 6
      boa_engine/src/realm.rs

2
boa_engine/benches/full.rs

@ -11,7 +11,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
fn create_realm(c: &mut Criterion) { fn create_realm(c: &mut Criterion) {
c.bench_function("Create Realm", move |b| b.iter(Realm::create)); c.bench_function("Create Realm", move |b| b.iter(|| Realm::create(None)));
} }
macro_rules! full_benchmarks { macro_rules! full_benchmarks {

5
boa_engine/src/context/mod.rs

@ -826,12 +826,13 @@ impl ContextBuilder {
/// Builds a new [`Context`] with the provided parameters, and defaults /// Builds a new [`Context`] with the provided parameters, and defaults
/// all missing parameters to their default values. /// all missing parameters to their default values.
pub fn build(self) -> Context { pub fn build(self) -> Context {
let intrinsics = Intrinsics::default();
let mut context = Context { let mut context = Context {
realm: Realm::create(), realm: Realm::create(intrinsics.constructors().object().prototype().into()),
interner: self.interner.unwrap_or_default(), interner: self.interner.unwrap_or_default(),
#[cfg(feature = "console")] #[cfg(feature = "console")]
console: Console::default(), console: Console::default(),
intrinsics: Intrinsics::default(), intrinsics,
vm: Vm { vm: Vm {
frames: Vec::with_capacity(16), frames: Vec::with_capacity(16),
stack: Vec::with_capacity(1024), stack: Vec::with_capacity(1024),

165
boa_engine/src/object/internal_methods/global.rs

@ -1,5 +1,8 @@
use crate::{ use crate::{
object::{InternalObjectMethods, JsObject, ORDINARY_INTERNAL_METHODS}, object::{
internal_methods::ordinary_get_prototype_of, InternalObjectMethods, JsObject, JsPrototype,
ORDINARY_INTERNAL_METHODS,
},
property::{DescriptorKind, PropertyDescriptor, PropertyKey}, property::{DescriptorKind, PropertyDescriptor, PropertyKey},
value::JsValue, value::JsValue,
Context, JsResult, Context, JsResult,
@ -13,17 +16,96 @@ use boa_profiler::Profiler;
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-global-object /// [spec]: https://tc39.es/ecma262/#sec-global-object
pub(crate) static GLOBAL_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods { pub(crate) static GLOBAL_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods {
__get_own_property__: global_get_own_property, __get_prototype_of__: global_get_prototype_of,
__set_prototype_of__: global_set_prototype_of,
__is_extensible__: global_is_extensible, __is_extensible__: global_is_extensible,
__prevent_extensions__: global_prevent_extensions, __prevent_extensions__: global_prevent_extensions,
__get_own_property__: global_get_own_property,
__define_own_property__: global_define_own_property, __define_own_property__: global_define_own_property,
__has_property__: global_has_property, __has_property__: global_has_property,
__get__: global_get, __get__: global_get,
__set__: global_set, __set__: global_set,
__delete__: global_delete, __delete__: global_delete,
__own_property_keys__: global_own_property_keys,
..ORDINARY_INTERNAL_METHODS ..ORDINARY_INTERNAL_METHODS
}; };
/// Abstract operation `OrdinaryGetPrototypeOf`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarygetprototypeof
#[inline]
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn global_get_prototype_of(
_: &JsObject,
context: &mut Context,
) -> JsResult<JsPrototype> {
// 1. Return O.[[Prototype]].
Ok(context.realm.global_prototype.clone())
}
/// Abstract operation `OrdinarySetPrototypeOf`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarysetprototypeof
#[inline]
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn global_set_prototype_of(
_: &JsObject,
val: JsPrototype,
context: &mut Context,
) -> JsResult<bool> {
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
{
// 2. Let current be O.[[Prototype]].
let current = &context.realm.global_prototype;
// 3. If SameValue(V, current) is true, return true.
if val == *current {
return Ok(true);
}
}
// 4. Let extensible be O.[[Extensible]].
// 5. If extensible is false, return false.
if !context.realm.global_extensible {
return Ok(false);
}
// 6. Let p be V.
let mut p = val.clone();
// 7. Let done be false.
// 8. Repeat, while done is false,
// a. If p is null, set done to true.
while let Some(proto) = p {
// b. Else if SameValue(p, O) is true, return false.
if &proto == context.realm.global_object() {
return Ok(false);
}
// c. Else,
// i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined
// in 10.1.1, set done to true.
else if proto.borrow().data.internal_methods.__get_prototype_of__ as usize
!= ordinary_get_prototype_of as usize
{
break;
}
// ii. Else, set p to p.[[Prototype]].
p = proto.prototype().clone();
}
// 9. Set O.[[Prototype]] to V.
context.realm.global_object().set_prototype(val);
// 10. Return true.
Ok(true)
}
/// Abstract operation `OrdinaryGetOwnProperty`. /// Abstract operation `OrdinaryGetOwnProperty`.
/// ///
/// More information: /// More information:
@ -125,7 +207,20 @@ pub(crate) fn global_has_property(
context: &mut Context, context: &mut Context,
) -> JsResult<bool> { ) -> JsResult<bool> {
let _timer = Profiler::global().start_event("Object::global_has_property", "object"); let _timer = Profiler::global().start_event("Object::global_has_property", "object");
Ok(context.realm.global_property_map.contains_key(key)) // 1. Assert: IsPropertyKey(P) is true.
// 2. Let hasOwn be ? O.[[GetOwnProperty]](P).
// 3. If hasOwn is not undefined, return true.
if context.realm.global_property_map.contains_key(key) {
Ok(true)
} else {
// 4. Let parent be ? O.[[GetPrototypeOf]]().
let parent = context.realm.global_prototype.clone();
// 5. If parent is not null, then
// a. Return ? parent.[[HasProperty]](P).
// 6. Return false.
parent.map_or(Ok(false), |obj| obj.__has_property__(key, context))
}
} }
/// Abstract operation `OrdinaryGet`. /// Abstract operation `OrdinaryGet`.
@ -148,8 +243,15 @@ pub(crate) fn global_get(
match global_get_own_property(obj, key, context)? { match global_get_own_property(obj, key, context)? {
// If desc is undefined, then // If desc is undefined, then
None => { None => {
// a. Let parent be ? O.[[GetPrototypeOf]]().
if let Some(parent) = context.realm.global_prototype.clone() {
// c. Return ? parent.[[Get]](P, Receiver).
parent.__get__(key, receiver, context)
}
// b. If parent is null, return undefined. // b. If parent is null, return undefined.
Ok(JsValue::undefined()) else {
Ok(JsValue::undefined())
}
} }
Some(ref desc) => match desc.kind() { Some(ref desc) => match desc.kind() {
// 4. If IsDataDescriptor(desc) is true, return desc.[[Value]]. // 4. If IsDataDescriptor(desc) is true, return desc.[[Value]].
@ -309,6 +411,61 @@ pub(crate) fn global_delete(
} }
} }
/// Abstract operation `OrdinaryOwnPropertyKeys`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
#[inline]
#[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` /// Abstract operation `ValidateAndApplyPropertyDescriptor`
/// ///
/// More information: /// More information:

6
boa_engine/src/realm.rs

@ -6,7 +6,7 @@
use crate::{ use crate::{
environments::{CompileTimeEnvironment, DeclarativeEnvironmentStack}, environments::{CompileTimeEnvironment, DeclarativeEnvironmentStack},
object::{GlobalPropertyMap, JsObject, ObjectData, PropertyMap}, object::{GlobalPropertyMap, JsObject, JsPrototype, ObjectData, PropertyMap},
}; };
use boa_gc::{Cell, Gc}; use boa_gc::{Cell, Gc};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -19,13 +19,14 @@ pub struct Realm {
global_object: JsObject, global_object: JsObject,
pub(crate) global_extensible: bool, pub(crate) global_extensible: bool,
pub(crate) global_property_map: PropertyMap, pub(crate) global_property_map: PropertyMap,
pub(crate) global_prototype: JsPrototype,
pub(crate) environments: DeclarativeEnvironmentStack, pub(crate) environments: DeclarativeEnvironmentStack,
pub(crate) compile_env: Gc<Cell<CompileTimeEnvironment>>, pub(crate) compile_env: Gc<Cell<CompileTimeEnvironment>>,
} }
impl Realm { impl Realm {
#[inline] #[inline]
pub fn create() -> Self { pub fn create(global_prototype: JsPrototype) -> Self {
let _timer = Profiler::global().start_event("Realm::create", "realm"); let _timer = Profiler::global().start_event("Realm::create", "realm");
// Create brand new global object // Create brand new global object
// Global has no prototype to pass None to new_obj // Global has no prototype to pass None to new_obj
@ -38,6 +39,7 @@ impl Realm {
global_object, global_object,
global_extensible: true, global_extensible: true,
global_property_map: PropertyMap::default(), global_property_map: PropertyMap::default(),
global_prototype,
environments: DeclarativeEnvironmentStack::new(global_compile_environment.clone()), environments: DeclarativeEnvironmentStack::new(global_compile_environment.clone()),
compile_env: global_compile_environment, compile_env: global_compile_environment,
} }

Loading…
Cancel
Save