Browse Source

Change type of object prototypes to `Option<JsObject>` (#1640)

pull/1646/head
jedel1043 3 years ago committed by GitHub
parent
commit
510623b0e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa/src/builtins/array_buffer/mod.rs
  2. 10
      boa/src/builtins/boolean/tests.rs
  3. 1
      boa/src/builtins/console/mod.rs
  4. 2
      boa/src/builtins/error/eval.rs
  5. 2
      boa/src/builtins/error/range.rs
  6. 2
      boa/src/builtins/error/reference.rs
  7. 2
      boa/src/builtins/error/syntax.rs
  8. 2
      boa/src/builtins/error/type.rs
  9. 2
      boa/src/builtins/error/uri.rs
  10. 23
      boa/src/builtins/json/tests.rs
  11. 5
      boa/src/builtins/object/for_in_iterator.rs
  12. 44
      boa/src/builtins/object/mod.rs
  13. 15
      boa/src/builtins/reflect/mod.rs
  14. 2
      boa/src/builtins/typed_array/integer_indexed_object.rs
  15. 4
      boa/src/builtins/typed_array/mod.rs
  16. 14
      boa/src/environment/function_environment_record.rs
  17. 9
      boa/src/exec/tests.rs
  18. 84
      boa/src/object/internal_methods/mod.rs
  19. 63
      boa/src/object/jsobject.rs
  20. 58
      boa/src/object/mod.rs
  21. 6
      boa/src/value/display.rs
  22. 6
      boa/src/value/mod.rs

2
boa/src/builtins/array_buffer/mod.rs

@ -307,7 +307,7 @@ impl ArrayBuffer {
context, context,
)?; )?;
let obj = context.construct_object(); let obj = context.construct_object();
obj.set_prototype_instance(prototype.into()); obj.set_prototype(prototype.into());
// 2. Let block be ? CreateByteDataBlock(byteLength). // 2. Let block be ? CreateByteDataBlock(byteLength).
// TODO: for now just a arbitrary limit to not OOM. // TODO: for now just a arbitrary limit to not OOM.

10
boa/src/builtins/boolean/tests.rs

@ -1,4 +1,4 @@
use crate::{forward, forward_val, Context, JsValue}; use crate::{forward, forward_val, Context};
/// Test the correct type is returned from call and construct /// Test the correct type is returned from call and construct
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
@ -58,8 +58,8 @@ fn instances_have_correct_proto_set() {
let bool_instance = forward_val(&mut context, "boolInstance").expect("value expected"); let bool_instance = forward_val(&mut context, "boolInstance").expect("value expected");
let bool_prototype = forward_val(&mut context, "boolProto").expect("value expected"); let bool_prototype = forward_val(&mut context, "boolProto").expect("value expected");
assert!(JsValue::same_value( assert_eq!(
&bool_instance.as_object().unwrap().prototype_instance(), &*bool_instance.as_object().unwrap().prototype(),
&bool_prototype &bool_prototype.as_object()
)); );
} }

1
boa/src/builtins/console/mod.rs

@ -568,7 +568,6 @@ impl Console {
LogMessage::Info(display_obj(args.get_or_undefined(0), true)), LogMessage::Info(display_obj(args.get_or_undefined(0), true)),
context.console(), context.console(),
); );
Ok(JsValue::undefined()) Ok(JsValue::undefined())
} }
} }

2
boa/src/builtins/error/eval.rs

@ -46,7 +46,7 @@ impl BuiltIn for EvalError {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(error_prototype.into()) .inherit(error_prototype)
.property("name", Self::NAME, attribute) .property("name", Self::NAME, attribute)
.property("message", "", attribute) .property("message", "", attribute)
.build(); .build();

2
boa/src/builtins/error/range.rs

@ -43,7 +43,7 @@ impl BuiltIn for RangeError {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(error_prototype.into()) .inherit(error_prototype)
.property("name", Self::NAME, attribute) .property("name", Self::NAME, attribute)
.property("message", "", attribute) .property("message", "", attribute)
.build(); .build();

2
boa/src/builtins/error/reference.rs

@ -42,7 +42,7 @@ impl BuiltIn for ReferenceError {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(error_prototype.into()) .inherit(error_prototype)
.property("name", Self::NAME, attribute) .property("name", Self::NAME, attribute)
.property("message", "", attribute) .property("message", "", attribute)
.build(); .build();

2
boa/src/builtins/error/syntax.rs

@ -45,7 +45,7 @@ impl BuiltIn for SyntaxError {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(error_prototype.into()) .inherit(error_prototype)
.property("name", Self::NAME, attribute) .property("name", Self::NAME, attribute)
.property("message", "", attribute) .property("message", "", attribute)
.build(); .build();

2
boa/src/builtins/error/type.rs

@ -48,7 +48,7 @@ impl BuiltIn for TypeError {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(error_prototype.into()) .inherit(error_prototype)
.property("name", Self::NAME, attribute) .property("name", Self::NAME, attribute)
.property("message", "", attribute) .property("message", "", attribute)
.build(); .build();

2
boa/src/builtins/error/uri.rs

@ -44,7 +44,7 @@ impl BuiltIn for UriError {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(error_prototype.into()) .inherit(error_prototype)
.property("name", Self::NAME, attribute) .property("name", Self::NAME, attribute)
.property("message", "", attribute) .property("message", "", attribute)
.build(); .build();

23
boa/src/builtins/json/tests.rs

@ -1,4 +1,4 @@
use crate::{forward, forward_val, Context, JsValue}; use crate::{forward, forward_val, Context};
#[test] #[test]
fn json_sanity() { fn json_sanity() {
@ -413,27 +413,22 @@ fn json_parse_sets_prototypes() {
.unwrap() .unwrap()
.as_object() .as_object()
.unwrap() .unwrap()
.prototype_instance(); .prototype()
.clone();
let array_prototype = forward_val(&mut context, r#"jsonObj.arr"#) let array_prototype = forward_val(&mut context, r#"jsonObj.arr"#)
.unwrap() .unwrap()
.as_object() .as_object()
.unwrap() .unwrap()
.prototype_instance(); .prototype()
let global_object_prototype: JsValue = context .clone();
let global_object_prototype = context
.standard_objects() .standard_objects()
.object_object() .object_object()
.prototype() .prototype()
.into(); .into();
let global_array_prototype: JsValue = let global_array_prototype = context.standard_objects().array_object().prototype().into();
context.standard_objects().array_object().prototype().into(); assert_eq!(object_prototype, global_object_prototype);
assert!(JsValue::same_value( assert_eq!(array_prototype, global_array_prototype);
&object_prototype,
&global_object_prototype
));
assert!(JsValue::same_value(
&array_prototype,
&global_array_prototype
));
} }
#[test] #[test]

5
boa/src/builtins/object/for_in_iterator.rs

@ -98,8 +98,9 @@ impl ForInIterator {
} }
} }
} }
match object.prototype_instance().to_object(context) { let proto = object.prototype().clone();
Ok(o) => { match proto {
Some(o) => {
object = o; object = o;
} }
_ => { _ => {

44
boa/src/builtins/object/mod.rs

@ -22,7 +22,7 @@ use crate::{
}, },
property::{Attribute, DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind}, property::{Attribute, DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind},
symbol::WellKnownSymbols, symbol::WellKnownSymbols,
value::{JsValue, Type}, value::JsValue,
BoaProfiler, Context, JsResult, BoaProfiler, Context, JsResult,
}; };
@ -53,7 +53,7 @@ impl BuiltIn for Object {
) )
.name(Self::NAME) .name(Self::NAME)
.length(Self::LENGTH) .length(Self::LENGTH)
.inherit(JsValue::null()) .inherit(None)
.method(Self::has_own_property, "hasOwnProperty", 0) .method(Self::has_own_property, "hasOwnProperty", 0)
.method(Self::property_is_enumerable, "propertyIsEnumerable", 0) .method(Self::property_is_enumerable, "propertyIsEnumerable", 0)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
@ -284,7 +284,9 @@ impl Object {
let obj = args[0].clone().to_object(ctx)?; let obj = args[0].clone().to_object(ctx)?;
// 2. Return ? obj.[[GetPrototypeOf]](). // 2. Return ? obj.[[GetPrototypeOf]]().
Ok(obj.prototype_instance()) Ok(obj
.__get_prototype_of__(ctx)?
.map_or(JsValue::Null, JsValue::new))
} }
/// Set the `prototype` of an object. /// Set the `prototype` of an object.
@ -301,32 +303,32 @@ impl Object {
} }
// 1. Set O to ? RequireObjectCoercible(O). // 1. Set O to ? RequireObjectCoercible(O).
let obj = args let o = args
.get(0) .get(0)
.cloned() .cloned()
.unwrap_or_default() .unwrap_or_default()
.require_object_coercible(ctx)? .require_object_coercible(ctx)?
.clone(); .clone();
// 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. let proto = match args.get_or_undefined(1) {
let proto = args.get_or_undefined(1); JsValue::Object(obj) => Some(obj.clone()),
if !matches!(proto.get_type(), Type::Object | Type::Null) { JsValue::Null => None,
return ctx.throw_type_error(format!( // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception.
"expected an object or null, got {}", val => {
proto.type_of() return ctx
)); .throw_type_error(format!("expected an object or null, got {}", val.type_of()))
} }
};
// 3. If Type(O) is not Object, return O. let mut obj = if let Some(obj) = o.as_object() {
if !obj.is_object() { obj
return Ok(obj); } else {
} // 3. If Type(O) is not Object, return O.
return Ok(o);
};
// 4. Let status be ? O.[[SetPrototypeOf]](proto). // 4. Let status be ? O.[[SetPrototypeOf]](proto).
let status = obj let status = obj.__set_prototype_of__(proto, ctx)?;
.as_object()
.expect("obj was not an object")
.__set_prototype_of__(proto.clone(), ctx)?;
// 5. If status is false, throw a TypeError exception. // 5. If status is false, throw a TypeError exception.
if !status { if !status {
@ -334,7 +336,7 @@ impl Object {
} }
// 6. Return O. // 6. Return O.
Ok(obj) Ok(o)
} }
/// `Object.prototype.isPrototypeOf( proto )` /// `Object.prototype.isPrototypeOf( proto )`

15
boa/src/builtins/reflect/mod.rs

@ -244,7 +244,9 @@ impl Reflect {
.get(0) .get(0)
.and_then(|v| v.as_object()) .and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?; .ok_or_else(|| context.construct_type_error("target must be an object"))?;
target.__get_prototype_of__(context) Ok(target
.__get_prototype_of__(context)?
.map_or(JsValue::Null, JsValue::new))
} }
/// Returns `true` if the object has the property, `false` otherwise. /// Returns `true` if the object has the property, `false` otherwise.
@ -377,10 +379,11 @@ impl Reflect {
.get(0) .get(0)
.and_then(|v| v.as_object()) .and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?; .ok_or_else(|| context.construct_type_error("target must be an object"))?;
let proto = args.get_or_undefined(1); let proto = match args.get_or_undefined(1) {
if !proto.is_null() && !proto.is_object() { JsValue::Object(obj) => Some(obj.clone()),
return context.throw_type_error("proto must be an object or null"); JsValue::Null => None,
} _ => return context.throw_type_error("proto must be an object or null"),
Ok(target.__set_prototype_of__(proto.clone(), context)?.into()) };
Ok(target.__set_prototype_of__(proto, context)?.into())
} }
} }

2
boa/src/builtins/typed_array/integer_indexed_object.rs

@ -77,7 +77,7 @@ impl IntegerIndexed {
a.borrow_mut().data = ObjectData::integer_indexed(data); a.borrow_mut().data = ObjectData::integer_indexed(data);
// 10. Set A.[[Prototype]] to prototype. // 10. Set A.[[Prototype]] to prototype.
a.set_prototype_instance(prototype.into()); a.set_prototype(prototype.into());
// 11. Return A. // 11. Return A.
a a

4
boa/src/builtins/typed_array/mod.rs

@ -82,8 +82,8 @@ macro_rules! typed_array {
TypedArrayName::$ty.element_size(), TypedArrayName::$ty.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
.custom_prototype(typed_array_constructor.into()) .custom_prototype(typed_array_constructor)
.inherit(typed_array_constructor_proto.into()) .inherit(typed_array_constructor_proto)
.build() .build()
.into() .into()
} }

14
boa/src/environment/function_environment_record.rs

@ -17,7 +17,7 @@ use crate::{
lexical_environment::{Environment, EnvironmentType, VariableScope}, lexical_environment::{Environment, EnvironmentType, VariableScope},
}, },
gc::{empty_trace, Finalize, Trace}, gc::{empty_trace, Finalize, Trace},
object::JsObject, object::{JsObject, JsPrototype},
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -114,21 +114,23 @@ impl FunctionEnvironmentRecord {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-getsuperbase /// [spec]: https://tc39.es/ecma262/#sec-getsuperbase
pub fn get_super_base(&self) -> JsValue { pub fn get_super_base(&self, context: &mut Context) -> JsResult<Option<JsPrototype>> {
// 1. Let home be envRec.[[FunctionObject]].[[HomeObject]]. // 1. Let home be envRec.[[FunctionObject]].[[HomeObject]].
let home = &self.home_object; let home = &self.home_object;
// 2. If home has the value undefined, return undefined. // 2. If home has the value undefined, return undefined.
if home.is_undefined() { if home.is_undefined() {
JsValue::undefined() Ok(None)
} else { } else {
// 3. Assert: Type(home) is Object. // 3. Assert: Type(home) is Object.
assert!(home.is_object()); assert!(home.is_object());
// 4. Return ? home.[[GetPrototypeOf]](). // 4. Return ? home.[[GetPrototypeOf]]().
home.as_object() Ok(Some(
.expect("home_object must be an Object") home.as_object()
.prototype_instance() .expect("home_object must be an Object")
.__get_prototype_of__(context)?,
))
} }
} }
} }

9
boa/src/exec/tests.rs

@ -791,9 +791,12 @@ mod in_operator {
let bar_val = forward_val(&mut context, "bar").unwrap(); let bar_val = forward_val(&mut context, "bar").unwrap();
let bar_obj = bar_val.as_object().unwrap(); let bar_obj = bar_val.as_object().unwrap();
let foo_val = forward_val(&mut context, "Foo").unwrap(); let foo_val = forward_val(&mut context, "Foo").unwrap();
assert!(bar_obj assert_eq!(
.prototype_instance() &*bar_obj.prototype(),
.strict_equals(&foo_val.get_field("prototype", &mut context).unwrap())); &foo_val
.as_object()
.and_then(|obj| obj.get("prototype", &mut context).unwrap().as_object())
);
} }
} }

84
boa/src/object/internal_methods/mod.rs

@ -13,7 +13,7 @@ use crate::{
BoaProfiler, Context, JsResult, BoaProfiler, Context, JsResult,
}; };
use super::PROTOTYPE; use super::{JsPrototype, PROTOTYPE};
pub(super) mod arguments; pub(super) mod arguments;
pub(super) mod array; pub(super) mod array;
@ -31,7 +31,7 @@ impl JsObject {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof
#[inline] #[inline]
#[track_caller] #[track_caller]
pub(crate) fn __get_prototype_of__(&self, context: &mut Context) -> JsResult<JsValue> { pub(crate) fn __get_prototype_of__(&self, context: &mut Context) -> JsResult<JsPrototype> {
let func = self.borrow().data.internal_methods.__get_prototype_of__; let func = self.borrow().data.internal_methods.__get_prototype_of__;
func(self, context) func(self, context)
} }
@ -47,7 +47,7 @@ impl JsObject {
#[inline] #[inline]
pub(crate) fn __set_prototype_of__( pub(crate) fn __set_prototype_of__(
&mut self, &mut self,
val: JsValue, val: JsPrototype,
context: &mut Context, context: &mut Context,
) -> JsResult<bool> { ) -> JsResult<bool> {
let func = self.borrow().data.internal_methods.__set_prototype_of__; let func = self.borrow().data.internal_methods.__set_prototype_of__;
@ -245,8 +245,8 @@ pub(crate) static ORDINARY_INTERNAL_METHODS: InternalObjectMethods = InternalObj
/// For a guide on how to implement exotic internal methods, see `ORDINARY_INTERNAL_METHODS`. /// For a guide on how to implement exotic internal methods, see `ORDINARY_INTERNAL_METHODS`.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) struct InternalObjectMethods { pub(crate) struct InternalObjectMethods {
pub(crate) __get_prototype_of__: fn(&JsObject, &mut Context) -> JsResult<JsValue>, pub(crate) __get_prototype_of__: fn(&JsObject, &mut Context) -> JsResult<JsPrototype>,
pub(crate) __set_prototype_of__: fn(&JsObject, JsValue, &mut Context) -> JsResult<bool>, pub(crate) __set_prototype_of__: fn(&JsObject, JsPrototype, &mut Context) -> JsResult<bool>,
pub(crate) __is_extensible__: fn(&JsObject, &mut Context) -> JsResult<bool>, pub(crate) __is_extensible__: fn(&JsObject, &mut Context) -> JsResult<bool>,
pub(crate) __prevent_extensions__: fn(&JsObject, &mut Context) -> JsResult<bool>, pub(crate) __prevent_extensions__: fn(&JsObject, &mut Context) -> JsResult<bool>,
pub(crate) __get_own_property__: pub(crate) __get_own_property__:
@ -271,9 +271,9 @@ pub(crate) struct InternalObjectMethods {
pub(crate) fn ordinary_get_prototype_of( pub(crate) fn ordinary_get_prototype_of(
obj: &JsObject, obj: &JsObject,
_context: &mut Context, _context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsPrototype> {
// 1. Return O.[[Prototype]]. // 1. Return O.[[Prototype]].
Ok(obj.borrow().prototype.clone()) Ok(obj.prototype().as_ref().cloned())
} }
/// Abstract operation `OrdinarySetPrototypeOf`. /// Abstract operation `OrdinarySetPrototypeOf`.
@ -285,23 +285,23 @@ pub(crate) fn ordinary_get_prototype_of(
#[inline] #[inline]
pub(crate) fn ordinary_set_prototype_of( pub(crate) fn ordinary_set_prototype_of(
obj: &JsObject, obj: &JsObject,
val: JsValue, val: JsPrototype,
context: &mut Context, _: &mut Context,
) -> JsResult<bool> { ) -> JsResult<bool> {
// 1. Assert: Either Type(V) is Object or Type(V) is Null. // 1. Assert: Either Type(V) is Object or Type(V) is Null.
debug_assert!(val.is_object() || val.is_null()); {
// 2. Let current be O.[[Prototype]].
let current = obj.prototype();
// 2. Let current be O.[[Prototype]]. // 3. If SameValue(V, current) is true, return true.
let current = obj.__get_prototype_of__(context)?; if val == *current {
return Ok(true);
// 3. If SameValue(V, current) is true, return true. }
if JsValue::same_value(&current, &val) {
return Ok(true);
} }
// 4. Let extensible be O.[[Extensible]]. // 4. Let extensible be O.[[Extensible]].
// 5. If extensible is false, return false. // 5. If extensible is false, return false.
if !obj.__is_extensible__(context)? { if !obj.extensible() {
return Ok(false); return Ok(false);
} }
@ -309,37 +309,29 @@ pub(crate) fn ordinary_set_prototype_of(
let mut p = val.clone(); let mut p = val.clone();
// 7. Let done be false. // 7. Let done be false.
let mut done = false;
// 8. Repeat, while done is false, // 8. Repeat, while done is false,
while !done { // a. If p is null, set done to true.
match p { while let Some(proto) = p {
// a. If p is null, set done to true. // b. Else if SameValue(p, O) is true, return false.
JsValue::Null => done = true, if &proto == obj {
JsValue::Object(ref proto) => { return Ok(false);
// b. Else if SameValue(p, O) is true, return false. }
if JsObject::equals(proto, obj) { // c. Else,
return Ok(false); // i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined
} // in 10.1.1, set done to true.
// c. Else, else if proto.borrow().data.internal_methods.__get_prototype_of__ as usize
// i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined != ordinary_get_prototype_of as usize
// in 10.1.1, set done to true. {
else if proto.borrow().data.internal_methods.__get_prototype_of__ as usize break;
!= ordinary_get_prototype_of as usize }
{ // ii. Else, set p to p.[[Prototype]].
done = true; else {
} p = proto.prototype().clone();
// ii. Else, set p to p.[[Prototype]].
else {
p = proto.__get_prototype_of__(context)?;
}
}
_ => unreachable!(),
} }
} }
// 9. Set O.[[Prototype]] to V. // 9. Set O.[[Prototype]] to V.
obj.borrow_mut().prototype = val; obj.set_prototype(val);
// 10. Return true. // 10. Return true.
Ok(true) Ok(true)
@ -454,7 +446,7 @@ pub(crate) fn ordinary_has_property(
let parent = obj.__get_prototype_of__(context)?; let parent = obj.__get_prototype_of__(context)?;
// 5. If parent is not null, then // 5. If parent is not null, then
if let JsValue::Object(ref object) = parent { if let Some(object) = parent {
// a. Return ? parent.[[HasProperty]](P). // a. Return ? parent.[[HasProperty]](P).
object.__has_property__(key, context) object.__has_property__(key, context)
} else { } else {
@ -483,7 +475,7 @@ pub(crate) fn ordinary_get(
// If desc is undefined, then // If desc is undefined, then
None => { None => {
// a. Let parent be ? O.[[GetPrototypeOf]](). // a. Let parent be ? O.[[GetPrototypeOf]]().
if let Some(parent) = obj.__get_prototype_of__(context)?.as_object() { if let Some(parent) = obj.__get_prototype_of__(context)? {
// c. Return ? parent.[[Get]](P, Receiver). // c. Return ? parent.[[Get]](P, Receiver).
parent.__get__(key, receiver, context) parent.__get__(key, receiver, context)
} }
@ -537,7 +529,7 @@ pub(crate) fn ordinary_set(
// 2. If ownDesc is undefined, then // 2. If ownDesc is undefined, then
// a. Let parent be ? O.[[GetPrototypeOf]](). // a. Let parent be ? O.[[GetPrototypeOf]]().
// b. If parent is not null, then // b. If parent is not null, then
else if let Some(ref mut parent) = obj.__get_prototype_of__(context)?.as_object() { else if let Some(parent) = obj.__get_prototype_of__(context)? {
// i. Return ? parent.[[Set]](P, V, Receiver). // i. Return ? parent.[[Set]](P, V, Receiver).
return parent.__set__(key, value, receiver, context); return parent.__set__(key, value, receiver, context);
} }

63
boa/src/object/jsobject.rs

@ -2,7 +2,7 @@
//! //!
//! The `JsObject` is a garbage collected Object. //! The `JsObject` is a garbage collected Object.
use super::{NativeObject, Object}; use super::{JsPrototype, NativeObject, Object};
use crate::{ use crate::{
object::{ObjectData, ObjectKind}, object::{ObjectData, ObjectKind},
property::{PropertyDescriptor, PropertyKey}, property::{PropertyDescriptor, PropertyKey},
@ -40,21 +40,6 @@ pub type RefMut<'a, T, U> = GcCellRefMut<'a, T, U>;
#[derive(Trace, Finalize, Clone, Default)] #[derive(Trace, Finalize, Clone, Default)]
pub struct JsObject(Gc<GcCell<Object>>); pub struct JsObject(Gc<GcCell<Object>>);
/// The body of a JavaScript function.
///
/// This is needed for the call method since we cannot mutate the function itself since we
/// already borrow it so we get the function body clone it then drop the borrow and run the body
#[cfg(not(feature = "vm"))]
enum FunctionBody {
BuiltInFunction(NativeFunctionSignature),
BuiltInConstructor(NativeFunctionSignature),
Closure {
function: Box<dyn ClosureFunctionSignature>,
captures: Captures,
},
Ordinary(RcStatementList),
}
impl JsObject { impl JsObject {
/// Create a new `JsObject` from an internal `Object`. /// Create a new `JsObject` from an internal `Object`.
#[inline] #[inline]
@ -76,7 +61,7 @@ impl JsObject {
pub fn from_proto_and_data<O: Into<Option<JsObject>>>(prototype: O, data: ObjectData) -> Self { pub fn from_proto_and_data<O: Into<Option<JsObject>>>(prototype: O, data: ObjectData) -> Self {
Self::from_object(Object { Self::from_object(Object {
data, data,
prototype: prototype.into().map_or(JsValue::Null, JsValue::new), prototype: prototype.into(),
extensible: true, extensible: true,
properties: Default::default(), properties: Default::default(),
}) })
@ -160,6 +145,21 @@ impl JsObject {
use super::internal_methods::get_prototype_from_constructor; use super::internal_methods::get_prototype_from_constructor;
/// The body of a JavaScript function.
///
/// This is needed for the call method since we cannot mutate the function itself since we
/// already borrow it so we get the function body clone it then drop the borrow and run the body
#[cfg(not(feature = "vm"))]
enum FunctionBody {
BuiltInFunction(NativeFunctionSignature),
BuiltInConstructor(NativeFunctionSignature),
Closure {
function: Box<dyn ClosureFunctionSignature>,
captures: Captures,
},
Ordinary(RcStatementList),
}
let this_function_object = self.clone(); let this_function_object = self.clone();
let mut has_parameter_expressions = false; let mut has_parameter_expressions = false;
@ -515,8 +515,18 @@ impl JsObject {
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
#[inline] #[inline]
#[track_caller] #[track_caller]
pub fn prototype_instance(&self) -> JsValue { pub fn prototype(&self) -> Ref<'_, JsPrototype> {
self.borrow().prototype_instance().clone() Ref::map(self.borrow(), |obj| obj.prototype())
}
/// Get the extensibility of the object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
pub(crate) fn extensible(&self) -> bool {
self.borrow().extensible
} }
/// Set the prototype of the object. /// Set the prototype of the object.
@ -524,11 +534,10 @@ impl JsObject {
/// # Panics /// # Panics
/// ///
/// Panics if the object is currently mutably borrowed /// Panics if the object is currently mutably borrowed
/// or if th prototype is not an object or undefined.
#[inline] #[inline]
#[track_caller] #[track_caller]
pub fn set_prototype_instance(&self, prototype: JsValue) -> bool { pub fn set_prototype(&self, prototype: JsPrototype) -> bool {
self.borrow_mut().set_prototype_instance(prototype) self.borrow_mut().set_prototype(prototype)
} }
/// Checks if it's an `Array` object. /// Checks if it's an `Array` object.
@ -726,9 +735,9 @@ impl JsObject {
// a. Set O to ? O.[[GetPrototypeOf]](). // a. Set O to ? O.[[GetPrototypeOf]]().
// b. If O is null, return false. // b. If O is null, return false.
let mut object = object.__get_prototype_of__(context)?; let mut object = object.__get_prototype_of__(context)?;
while let Some(object_prototype) = object.as_object() { while let Some(object_prototype) = object {
// c. If SameValue(P, O) is true, return true. // c. If SameValue(P, O) is true, return true.
if JsObject::equals(&prototype, &object_prototype) { if prototype == object_prototype {
return Ok(true); return Ok(true);
} }
// a. Set O to ? O.[[GetPrototypeOf]](). // a. Set O to ? O.[[GetPrototypeOf]]().
@ -972,6 +981,12 @@ impl AsRef<GcCell<Object>> for JsObject {
} }
} }
impl PartialEq for JsObject {
fn eq(&self, other: &Self) -> bool {
JsObject::equals(self, other)
}
}
/// An error returned by [`JsObject::try_borrow`](struct.JsObject.html#method.try_borrow). /// An error returned by [`JsObject::try_borrow`](struct.JsObject.html#method.try_borrow).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BorrowError; pub struct BorrowError;

58
boa/src/object/mod.rs

@ -49,6 +49,8 @@ use self::internal_methods::{
/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object. /// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub static PROTOTYPE: &str = "prototype"; pub static PROTOTYPE: &str = "prototype";
pub type JsPrototype = Option<JsObject>;
/// This trait allows Rust types to be passed around as objects. /// This trait allows Rust types to be passed around as objects.
/// ///
/// This is automatically implemented, when a type implements `Debug`, `Any` and `Trace`. /// This is automatically implemented, when a type implements `Debug`, `Any` and `Trace`.
@ -72,14 +74,15 @@ impl<T: Any + Debug + Trace> NativeObject for T {
} }
} }
/// The internal representation of an JavaScript object. /// The internal representation of a JavaScript object.
#[derive(Debug, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
pub struct Object { pub struct Object {
/// The type of the object. /// The type of the object.
pub data: ObjectData, pub data: ObjectData,
/// The collection of properties contained in the object
properties: PropertyMap, properties: PropertyMap,
/// Instance prototype `__proto__`. /// Instance prototype `__proto__`.
prototype: JsValue, prototype: JsPrototype,
/// Whether it can have new properties added to it. /// Whether it can have new properties added to it.
extensible: bool, extensible: bool,
} }
@ -365,7 +368,7 @@ impl Default for Object {
Self { Self {
data: ObjectData::ordinary(), data: ObjectData::ordinary(),
properties: PropertyMap::default(), properties: PropertyMap::default(),
prototype: JsValue::null(), prototype: None,
extensible: true, extensible: true,
} }
} }
@ -936,7 +939,7 @@ impl Object {
/// Gets the prototype instance of this object. /// Gets the prototype instance of this object.
#[inline] #[inline]
pub fn prototype_instance(&self) -> &JsValue { pub fn prototype(&self) -> &JsPrototype {
&self.prototype &self.prototype
} }
@ -947,15 +950,15 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods /// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods
#[inline] #[inline]
#[track_caller] #[track_caller]
pub fn set_prototype_instance(&mut self, prototype: JsValue) -> bool { pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
assert!(prototype.is_null() || prototype.is_object()); let prototype = prototype.into();
if self.extensible { if self.extensible {
self.prototype = prototype; self.prototype = prototype;
true true
} else { } else {
// If target is non-extensible, [[SetPrototypeOf]] must return false // If target is non-extensible, [[SetPrototypeOf]] must return false
// unless V is the SameValue as the target's observed [[GetPrototypeOf]] value. // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value.
JsValue::same_value(&prototype, &self.prototype) self.prototype == prototype
} }
} }
@ -1264,13 +1267,7 @@ impl<'context> FunctionBuilder<'context> {
pub(crate) fn build_function_prototype(&mut self, object: &JsObject) { pub(crate) fn build_function_prototype(&mut self, object: &JsObject) {
let mut object = object.borrow_mut(); let mut object = object.borrow_mut();
object.data = ObjectData::function(self.function.take().unwrap()); object.data = ObjectData::function(self.function.take().unwrap());
object.set_prototype_instance( object.set_prototype(self.context.standard_objects().object_object().prototype());
self.context
.standard_objects()
.object_object()
.prototype()
.into(),
);
let property = PropertyDescriptor::builder() let property = PropertyDescriptor::builder()
.writable(false) .writable(false)
@ -1387,8 +1384,8 @@ pub struct ConstructorBuilder<'context> {
length: usize, length: usize,
callable: bool, callable: bool,
constructable: bool, constructable: bool,
inherit: Option<JsValue>, inherit: Option<JsPrototype>,
custom_prototype: Option<JsValue>, custom_prototype: Option<JsPrototype>,
} }
impl Debug for ConstructorBuilder<'_> { impl Debug for ConstructorBuilder<'_> {
@ -1642,9 +1639,8 @@ impl<'context> ConstructorBuilder<'context> {
/// ///
/// Default is `Object.prototype` /// Default is `Object.prototype`
#[inline] #[inline]
pub fn inherit(&mut self, prototype: JsValue) -> &mut Self { pub fn inherit<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
assert!(prototype.is_object() || prototype.is_null()); self.inherit = Some(prototype.into());
self.inherit = Some(prototype);
self self
} }
@ -1652,8 +1648,8 @@ impl<'context> ConstructorBuilder<'context> {
/// ///
/// Default is `Function.prototype` /// Default is `Function.prototype`
#[inline] #[inline]
pub fn custom_prototype(&mut self, prototype: JsValue) -> &mut Self { pub fn custom_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
self.custom_prototype = Some(prototype); self.custom_prototype = Some(prototype.into());
self self
} }
@ -1688,15 +1684,14 @@ impl<'context> ConstructorBuilder<'context> {
constructor.insert("length", length); constructor.insert("length", length);
constructor.insert("name", name); constructor.insert("name", name);
if let Some(proto) = &self.custom_prototype { if let Some(proto) = self.custom_prototype.take() {
constructor.set_prototype_instance(proto.clone()); constructor.set_prototype(proto);
} else { } else {
constructor.set_prototype_instance( constructor.set_prototype(
self.context self.context
.standard_objects() .standard_objects()
.function_object() .function_object()
.prototype() .prototype(),
.into(),
); );
} }
constructor.insert_property( constructor.insert_property(
@ -1721,15 +1716,10 @@ impl<'context> ConstructorBuilder<'context> {
); );
if let Some(proto) = self.inherit.take() { if let Some(proto) = self.inherit.take() {
prototype.set_prototype_instance(proto); prototype.set_prototype(proto);
} else { } else {
prototype.set_prototype_instance( prototype
self.context .set_prototype(self.context.standard_objects().object_object().prototype());
.standard_objects()
.object_object()
.prototype()
.into(),
);
} }
} }

6
boa/src/value/display.rs

@ -31,18 +31,18 @@ macro_rules! print_obj_value {
(internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => { (internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => {
{ {
let object = $obj.borrow(); let object = $obj.borrow();
if object.prototype_instance().is_object() { if let Some(object) = object.prototype() {
vec![format!( vec![format!(
"{:>width$}: {}", "{:>width$}: {}",
"__proto__", "__proto__",
$display_fn(object.prototype_instance(), $encounters, $indent.wrapping_add(4), true), $display_fn(&object.clone().into(), $encounters, $indent.wrapping_add(4), true),
width = $indent, width = $indent,
)] )]
} else { } else {
vec![format!( vec![format!(
"{:>width$}: {}", "{:>width$}: {}",
"__proto__", "__proto__",
object.prototype_instance().display(), JsValue::Null.display(),
width = $indent, width = $indent,
)] )]
} }

6
boa/src/value/mod.rs

@ -290,7 +290,11 @@ impl JsValue {
return property; return property;
} }
object.borrow().prototype_instance().get_property(key) object
.prototype()
.as_ref()
.map_or(JsValue::Null, |obj| obj.clone().into())
.get_property(key)
} }
_ => None, _ => None,
} }

Loading…
Cancel
Save