Browse Source

Implement getter and setter of Object.prototype.__proto__ (#2110)

This Pull Request fixes part of #2067.

It changes the following:

- implemented getter of `Object.prototype.__proto__`
- implemented setter of `Object.prototype.__proto__`

I've tried to run tests with `test262/test/built-ins/Object/prototype/__proto__/` and all tests under that directory are pass now 🙏 

<details>

<summary>Toggle to see __proto__ test status</summary>

```
   Compiling boa_engine v0.15.0 (/codespace/rust/boa/boa_engine)
   Compiling boa_tester v0.15.0 (/codespace/rust/boa/boa_tester)
    Finished release [optimized] target(s) in 2m 57s
     Running `target/release/boa_tester run -vv -d -s /codespace/rust/boa/test262/test/built-ins/Object/prototype/__proto__/`
Loading the test suite...
Test suite loaded, starting tests...
Suite __proto__:
`get-fn-name` (strict mode): starting
`get-fn-name` (strict mode): Passed
`get-fn-name`: starting
`get-fn-name`: Passed
`set-cycle-shadowed` (strict mode): starting
`set-cycle-shadowed` (strict mode): Passed
`set-cycle-shadowed`: starting
`set-cycle-shadowed`: Passed
`set-abrupt` (strict mode): starting
`set-abrupt` (strict mode): Passed
`set-abrupt`: starting
`set-abrupt`: Passed
`get-to-obj-abrupt` (strict mode): starting
`get-to-obj-abrupt` (strict mode): Passed
`get-to-obj-abrupt`: starting
`get-to-obj-abrupt`: Passed
`set-fn-name` (strict mode): starting
`set-fn-name` (strict mode): Passed
`set-fn-name`: starting
`set-fn-name`: Passed
`get-ordinary-obj` (strict mode): starting
`get-ordinary-obj` (strict mode): Passed
`get-ordinary-obj`: starting
`get-ordinary-obj`: Passed
`set-non-object` (strict mode): starting
`set-non-object` (strict mode): Passed
`set-non-object`: starting
`set-non-object`: Passed
`set-invalid-value` (strict mode): starting
`set-invalid-value` (strict mode): Passed
`set-invalid-value`: starting
`set-invalid-value`: Passed
`set-immutable` (strict mode): starting
`set-immutable` (strict mode): Passed
`set-immutable`: starting
`set-immutable`: Passed
`set-non-obj-coercible` (strict mode): starting
`set-non-obj-coercible` (strict mode): Passed
`set-non-obj-coercible`: starting
`set-non-obj-coercible`: Passed
`set-cycle` (strict mode): starting
`set-cycle` (strict mode): Passed
`set-cycle`: starting
`set-cycle`: Passed
`prop-desc` (strict mode): starting
`prop-desc` (strict mode): Passed
`prop-desc`: starting
`prop-desc`: Passed
`get-abrupt` (strict mode): starting
`get-abrupt` (strict mode): Passed
`get-abrupt`: starting
`get-abrupt`: Passed
`set-ordinary-obj` (strict mode): starting
`set-ordinary-obj` (strict mode): Passed
`set-ordinary-obj`: starting
`set-ordinary-obj`: Passed
`set-non-extensible` (strict mode): starting
`set-non-extensible` (strict mode): Passed
`set-non-extensible`: starting
`set-non-extensible`: Passed

Suite __proto__ results: total: 30, passed: 30, ignored: 0, failed: 0 (panics: 0), conformance: 100.00%

Results:
Total tests: 30
Passed tests: 30
Ignored tests: 0
Failed tests: 0 (panics: 0)
Conformance: 100.00%
```

</details>
pull/2113/head
cybai (Haku) 2 years ago
parent
commit
220af1c375
  1. 85
      boa_engine/src/builtins/object/mod.rs

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

@ -21,7 +21,7 @@ use crate::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder,
IntegrityLevel, JsObject, ObjectData, ObjectKind,
},
property::{PropertyDescriptor, PropertyKey, PropertyNameKind},
property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind},
symbol::WellKnownSymbols,
value::JsValue,
Context, JsResult, JsString,
@ -43,6 +43,14 @@ impl BuiltIn for Object {
fn init(context: &mut Context) -> Option<JsValue> {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let legacy_proto_getter = FunctionBuilder::native(context, Self::legacy_proto_getter)
.name("get __proto__")
.build();
let legacy_setter_proto = FunctionBuilder::native(context, Self::legacy_proto_setter)
.name("set __proto__")
.build();
ConstructorBuilder::with_standard_constructor(
context,
Self::constructor,
@ -51,6 +59,12 @@ impl BuiltIn for Object {
.name(Self::NAME)
.length(Self::LENGTH)
.inherit(None)
.accessor(
"__proto__",
Some(legacy_proto_getter),
Some(legacy_setter_proto),
Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.method(Self::has_own_property, "hasOwnProperty", 1)
.method(Self::property_is_enumerable, "propertyIsEnumerable", 1)
.method(Self::to_string, "toString", 0)
@ -117,6 +131,75 @@ impl Object {
Ok(context.construct_object().into())
}
/// `get Object.prototype.__proto__`
///
/// The `__proto__` getter function exposes the value of the
/// internal `[[Prototype]]` of an object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
pub fn legacy_proto_getter(
this: &JsValue,
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let O be ? ToObject(this value).
let obj = this.to_object(context)?;
// 2. Return ? O.[[GetPrototypeOf]]().
let proto = obj.__get_prototype_of__(context)?;
Ok(proto.map_or(JsValue::Null, JsValue::new))
}
/// `set Object.prototype.__proto__`
///
/// The `__proto__` setter allows the `[[Prototype]]` of
/// an object to be mutated.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-set-object.prototype.__proto__
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
pub fn legacy_proto_setter(
this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let O be ? RequireObjectCoercible(this value).
let this = this.require_object_coercible(context)?;
// 2. If Type(proto) is neither Object nor Null, return undefined.
let proto = match args.get_or_undefined(0) {
JsValue::Object(proto) => Some(proto.clone()),
JsValue::Null => None,
_ => return Ok(JsValue::undefined()),
};
// 3. If Type(O) is not Object, return undefined.
let object = match this {
JsValue::Object(object) => object,
_ => return Ok(JsValue::undefined()),
};
// 4. Let status be ? O.[[SetPrototypeOf]](proto).
let status = object.__set_prototype_of__(proto, context)?;
// 5. If status is false, throw a TypeError exception.
if !status {
return context.throw_type_error("__proto__ called on null or undefined");
}
// 6. Return undefined.
Ok(JsValue::undefined())
}
/// `Object.prototype.__defineGetter__(prop, func)`
///
/// Binds an object's property to a function to be called when that property is looked up.

Loading…
Cancel
Save