Browse Source

Move `Object` internal object methods to `GcObject` (#835)

pull/885/head
Halid Odat 4 years ago committed by GitHub
parent
commit
2cb24427f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      boa/src/builtins/array/array_iterator.rs
  2. 6
      boa/src/builtins/array/mod.rs
  3. 6
      boa/src/builtins/boolean/tests.rs
  4. 61
      boa/src/builtins/function/mod.rs
  5. 8
      boa/src/builtins/iterable/mod.rs
  6. 2
      boa/src/builtins/json/mod.rs
  7. 6
      boa/src/builtins/json/tests.rs
  8. 4
      boa/src/builtins/map/map_iterator.rs
  9. 4
      boa/src/builtins/map/mod.rs
  10. 46
      boa/src/builtins/object/mod.rs
  11. 5
      boa/src/builtins/regexp/mod.rs
  12. 4
      boa/src/builtins/string/string_iterator.rs
  13. 2
      boa/src/class.rs
  14. 25
      boa/src/context.rs
  15. 2
      boa/src/environment/global_environment_record.rs
  16. 2
      boa/src/environment/object_environment_record.rs
  17. 294
      boa/src/object/gcobject.rs
  18. 95
      boa/src/object/internal_methods.rs
  19. 8
      boa/src/object/mod.rs
  20. 5
      boa/src/value/display.rs
  21. 45
      boa/src/value/mod.rs
  22. 2
      boa/src/value/type.rs

4
boa/src/builtins/array/array_iterator.rs

@ -53,7 +53,7 @@ impl ArrayIterator {
let array_iterator = Value::new_object(Some(ctx.global_object())); let array_iterator = Value::new_object(Some(ctx.global_object()));
array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind))); array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind)));
array_iterator array_iterator
.as_object_mut() .as_object()
.expect("array iterator object") .expect("array iterator object")
.set_prototype_instance(ctx.iterator_prototypes().array_iterator().into()); .set_prototype_instance(ctx.iterator_prototypes().array_iterator().into());
Ok(array_iterator) Ok(array_iterator)
@ -126,7 +126,7 @@ impl ArrayIterator {
let array_iterator = Value::new_object(Some(global)); let array_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx); make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx);
array_iterator array_iterator
.as_object_mut() .as_object()
.expect("array iterator prototype object") .expect("array iterator prototype object")
.set_prototype_instance(iterator_prototype); .set_prototype_instance(iterator_prototype);

6
boa/src/builtins/array/mod.rs

@ -186,7 +186,7 @@ impl Array {
None => context.standard_objects().array_object().prototype(), None => context.standard_objects().array_object().prototype(),
}; };
this.as_object_mut() this.as_object()
.expect("this should be an array object") .expect("this should be an array object")
.set_prototype_instance(prototype.into()); .set_prototype_instance(prototype.into());
// This value is used by console.log and other routines to match Object type // This value is used by console.log and other routines to match Object type
@ -213,7 +213,7 @@ impl Array {
)); ));
array.set_data(ObjectData::Array); array.set_data(ObjectData::Array);
array array
.as_object_mut() .as_object()
.expect("array object") .expect("array object")
.set_prototype_instance(context.standard_objects().array_object().prototype().into()); .set_prototype_instance(context.standard_objects().array_object().prototype().into());
array.set_field("length", Value::from(0)); array.set_field("length", Value::from(0));
@ -281,7 +281,7 @@ impl Array {
_interpreter: &mut Context, _interpreter: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
match args.get(0).and_then(|x| x.as_object()) { match args.get(0).and_then(|x| x.as_object()) {
Some(object) => Ok(Value::from(object.is_array())), Some(object) => Ok(Value::from(object.borrow().is_array())),
None => Ok(Value::from(false)), None => Ok(Value::from(false)),
} }
} }

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

@ -59,11 +59,7 @@ fn instances_have_correct_proto_set() {
let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected"); let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected");
assert!(same_value( assert!(same_value(
&bool_instance &bool_instance.as_object().unwrap().prototype_instance(),
.as_object()
.unwrap()
.prototype_instance()
.clone(),
&bool_prototype &bool_prototype
)); ));
} }

61
boa/src/builtins/function/mod.rs

@ -14,7 +14,7 @@
use crate::{ use crate::{
builtins::{Array, BuiltIn}, builtins::{Array, BuiltIn},
environment::lexical_environment::Environment, environment::lexical_environment::Environment,
object::{ConstructorBuilder, FunctionBuilder, Object, ObjectData, PROTOTYPE}, object::{ConstructorBuilder, FunctionBuilder, GcObject, Object, ObjectData},
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
syntax::ast::node::{FormalParameter, RcStatementList}, syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
@ -172,7 +172,7 @@ impl Function {
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject> /// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
let len = arguments_list.len(); let len = arguments_list.len();
let mut obj = Object::default(); let mut obj = GcObject::new(Object::default());
// Set length // Set length
let length = DataDescriptor::new( let length = DataDescriptor::new(
len, len,
@ -195,61 +195,6 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
Value::from(obj) Value::from(obj)
} }
/// Creates a new constructor function
///
/// This utility function handling linking the new Constructor to the prototype.
/// So far this is only used by internal functions
pub fn make_constructor_fn(
name: &str,
length: usize,
body: NativeFunction,
global: &Value,
prototype: Value,
constructable: bool,
callable: bool,
) -> Value {
let _timer =
BoaProfiler::global().start_event(&format!("make_constructor_fn: {}", name), "init");
// Create the native function
let function = Function::BuiltIn(
body.into(),
FunctionFlags::from_parameters(callable, constructable),
);
// Get reference to Function.prototype
// Create the function object and point its instance prototype to Function.prototype
let mut constructor =
Object::function(function, global.get_field("Function").get_field(PROTOTYPE));
let length = DataDescriptor::new(
length,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert("length", length);
let name = DataDescriptor::new(
name,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert("name", name);
let constructor = Value::from(constructor);
prototype.as_object_mut().unwrap().insert_property(
"constructor",
constructor.clone(),
Attribute::all(),
);
constructor
.as_object_mut()
.expect("constructor object")
.insert_property(PROTOTYPE, prototype, Attribute::all());
constructor
}
/// Creates a new member function of a `Object` or `prototype`. /// Creates a new member function of a `Object` or `prototype`.
/// ///
/// A function registered using this macro can then be called from Javascript using: /// A function registered using this macro can then be called from Javascript using:
@ -290,7 +235,7 @@ pub fn make_builtin_fn<N>(
function.insert_property("length", length, Attribute::all()); function.insert_property("length", length, Attribute::all());
parent parent
.as_object_mut() .as_object()
.unwrap() .unwrap()
.insert_property(name, function, Attribute::all()); .insert_property(name, function, Attribute::all());
} }

8
boa/src/builtins/iterable/mod.rs

@ -20,16 +20,16 @@ impl IteratorPrototypes {
let iterator_prototype = create_iterator_prototype(ctx); let iterator_prototype = create_iterator_prototype(ctx);
Self { Self {
iterator_prototype: iterator_prototype iterator_prototype: iterator_prototype
.as_gc_object() .as_object()
.expect("Iterator prototype is not an object"), .expect("Iterator prototype is not an object"),
array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone()) array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone())
.as_gc_object() .as_object()
.expect("Array Iterator Prototype is not an object"), .expect("Array Iterator Prototype is not an object"),
string_iterator: StringIterator::create_prototype(ctx, iterator_prototype.clone()) string_iterator: StringIterator::create_prototype(ctx, iterator_prototype.clone())
.as_gc_object() .as_object()
.expect("String Iterator Prototype is not an object"), .expect("String Iterator Prototype is not an object"),
map_iterator: MapIterator::create_prototype(ctx, iterator_prototype) map_iterator: MapIterator::create_prototype(ctx, iterator_prototype)
.as_gc_object() .as_object()
.expect("Map Iterator Prototype is not an object"), .expect("Map Iterator Prototype is not an object"),
} }
} }

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

@ -154,6 +154,7 @@ impl Json {
.map(|obj| { .map(|obj| {
let object_to_return = Value::new_object(None); let object_to_return = Value::new_object(None);
for (key, val) in obj for (key, val) in obj
.borrow()
.iter() .iter()
// FIXME: handle accessor descriptors // FIXME: handle accessor descriptors
.map(|(k, v)| (k, v.as_data_descriptor().unwrap().value())) .map(|(k, v)| (k, v.as_data_descriptor().unwrap().value()))
@ -176,6 +177,7 @@ impl Json {
.ok_or_else(Value::undefined)? .ok_or_else(Value::undefined)?
} else if replacer_as_object.is_array() { } else if replacer_as_object.is_array() {
let mut obj_to_return = serde_json::Map::new(); let mut obj_to_return = serde_json::Map::new();
let replacer_as_object = replacer_as_object.borrow();
let fields = replacer_as_object.keys().filter_map(|key| { let fields = replacer_as_object.keys().filter_map(|key| {
if key == "length" { if key == "length" {
None None

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

@ -279,14 +279,12 @@ fn json_parse_sets_prototypes() {
.unwrap() .unwrap()
.as_object() .as_object()
.unwrap() .unwrap()
.prototype_instance() .prototype_instance();
.clone();
let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#) let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#)
.unwrap() .unwrap()
.as_object() .as_object()
.unwrap() .unwrap()
.prototype_instance() .prototype_instance();
.clone();
let global_object_prototype = engine let global_object_prototype = engine
.global_object() .global_object()
.get_field("Object") .get_field("Object")

4
boa/src/builtins/map/map_iterator.rs

@ -53,7 +53,7 @@ impl MapIterator {
let map_iterator = Value::new_object(Some(ctx.global_object())); let map_iterator = Value::new_object(Some(ctx.global_object()));
map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind))); map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind)));
map_iterator map_iterator
.as_object_mut() .as_object()
.expect("map iterator object") .expect("map iterator object")
.set_prototype_instance(ctx.iterator_prototypes().map_iterator().into()); .set_prototype_instance(ctx.iterator_prototypes().map_iterator().into());
Ok(map_iterator) Ok(map_iterator)
@ -143,7 +143,7 @@ impl MapIterator {
let map_iterator = Value::new_object(Some(global)); let map_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx); make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx);
map_iterator map_iterator
.as_object_mut() .as_object()
.expect("map iterator prototype object") .expect("map iterator prototype object")
.set_prototype_instance(iterator_prototype); .set_prototype_instance(iterator_prototype);

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

@ -73,7 +73,7 @@ impl Map {
// Set Prototype // Set Prototype
let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE); let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE);
this.as_object_mut() this.as_object()
.expect("this is map object") .expect("this is map object")
.set_prototype_instance(prototype); .set_prototype_instance(prototype);
// This value is used by console.log and other routines to match Object type // This value is used by console.log and other routines to match Object type
@ -354,7 +354,7 @@ impl Map {
/// Helper function to get a key-value pair from an array. /// Helper function to get a key-value pair from an array.
fn get_key_value(value: &Value) -> Option<(Value, Value)> { fn get_key_value(value: &Value) -> Option<(Value, Value)> {
if let Value::Object(object) = value { if let Value::Object(object) = value {
if object.borrow().is_array() { if object.is_array() {
let (key, value) = match value.get_field("length").as_number().unwrap() as i32 { let (key, value) = match value.get_field("length").as_number().unwrap() as i32 {
0 => (Value::Undefined, Value::Undefined), 0 => (Value::Undefined, Value::Undefined),
1 => (value.get_field("0"), Value::Undefined), 1 => (value.get_field("0"), Value::Undefined),

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

@ -140,7 +140,7 @@ impl Object {
if let Some(key) = args.get(1) { if let Some(key) = args.get(1) {
let key = key.to_property_key(ctx)?; let key = key.to_property_key(ctx)?;
if let Some(desc) = object.borrow().get_own_property(&key) { if let Some(desc) = object.get_own_property(&key) {
return Ok(Self::from_property_descriptor(desc, ctx)?); return Ok(Self::from_property_descriptor(desc, ctx)?);
} }
} }
@ -169,7 +169,6 @@ impl Object {
for key in object.borrow().keys() { for key in object.borrow().keys() {
let descriptor = { let descriptor = {
let desc = object let desc = object
.borrow()
.get_own_property(&key) .get_own_property(&key)
.expect("Expected property to be on object."); .expect("Expected property to be on object.");
Self::from_property_descriptor(desc, ctx)? Self::from_property_descriptor(desc, ctx)?
@ -239,16 +238,16 @@ impl Object {
/// Get the `prototype` of an object. /// Get the `prototype` of an object.
pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> { pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object"); let obj = args.get(0).expect("Cannot get object");
Ok(obj.as_object().map_or_else(Value::undefined, |object| { Ok(obj
object.prototype_instance().clone() .as_object()
})) .map_or_else(Value::undefined, |object| object.prototype_instance()))
} }
/// Set the `prototype` of an object. /// Set the `prototype` of an object.
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> { pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object").clone(); let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone();
obj.as_object_mut().unwrap().set_prototype_instance(proto); obj.as_object().unwrap().set_prototype_instance(proto);
Ok(obj) Ok(obj)
} }
@ -281,11 +280,11 @@ impl Object {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties
pub fn define_properties(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub fn define_properties(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let arg = args.get(0).cloned().unwrap_or_default(); let arg = args.get(0).cloned().unwrap_or_default();
let arg_obj = arg.as_object_mut(); let arg_obj = arg.as_object();
if let Some(mut obj) = arg_obj { if let Some(mut obj) = arg_obj {
let props = args.get(1).cloned().unwrap_or_else(Value::undefined); let props = args.get(1).cloned().unwrap_or_else(Value::undefined);
obj.define_properties(props, ctx)?; obj.define_properties(props, ctx)?;
Ok(arg.clone()) Ok(arg)
} else { } else {
ctx.throw_type_error("Expected an object") ctx.throw_type_error("Expected an object")
} }
@ -307,19 +306,21 @@ impl Object {
} else if this.is_null() { } else if this.is_null() {
Ok("[object Null]".into()) Ok("[object Null]".into())
} else { } else {
let gc_o = this.to_object(ctx)?; let o = this.to_object(ctx)?;
let o = gc_o.borrow(); let builtin_tag = {
let builtin_tag = match &o.data { let o = o.borrow();
ObjectData::Array => "Array", match &o.data {
// TODO: Arguments Exotic Objects are currently not supported ObjectData::Array => "Array",
ObjectData::Function(_) => "Function", // TODO: Arguments Exotic Objects are currently not supported
ObjectData::Error => "Error", ObjectData::Function(_) => "Function",
ObjectData::Boolean(_) => "Boolean", ObjectData::Error => "Error",
ObjectData::Number(_) => "Number", ObjectData::Boolean(_) => "Boolean",
ObjectData::String(_) => "String", ObjectData::Number(_) => "Number",
ObjectData::Date(_) => "Date", ObjectData::String(_) => "String",
ObjectData::RegExp(_) => "RegExp", ObjectData::Date(_) => "Date",
_ => "Object", ObjectData::RegExp(_) => "RegExp",
_ => "Object",
}
}; };
let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into()); let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into());
@ -349,7 +350,6 @@ impl Object {
}; };
let own_property = this let own_property = this
.as_object() .as_object()
.as_deref()
.expect("Cannot get THIS object") .expect("Cannot get THIS object")
.get_own_property(&prop.expect("cannot get prop").into()); .get_own_property(&prop.expect("cannot get prop").into());
if own_property.is_none() { if own_property.is_none() {
@ -370,7 +370,7 @@ impl Object {
}; };
let key = key.to_property_key(ctx)?; let key = key.to_property_key(ctx)?;
let own_property = this.to_object(ctx)?.borrow().get_own_property(&key); let own_property = this.to_object(ctx)?.get_own_property(&key);
Ok(own_property.map_or(Value::from(false), |own_prop| { Ok(own_property.map_or(Value::from(false), |own_prop| {
Value::from(own_prop.enumerable()) Value::from(own_prop.enumerable())

5
boa/src/builtins/regexp/mod.rs

@ -309,6 +309,7 @@ impl RegExp {
.to_string(ctx)?; .to_string(ctx)?;
let mut last_index = this.get_field("lastIndex").to_index(ctx)?; let mut last_index = this.get_field("lastIndex").to_index(ctx)?;
let result = if let Some(object) = this.as_object() { let result = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
let result = let result =
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() {
@ -349,6 +350,7 @@ impl RegExp {
.to_string(ctx)?; .to_string(ctx)?;
let mut last_index = this.get_field("lastIndex").to_index(ctx)?; let mut last_index = this.get_field("lastIndex").to_index(ctx)?;
let result = if let Some(object) = this.as_object() { let result = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
let result = { let result = {
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() {
@ -401,6 +403,7 @@ impl RegExp {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match
pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Context) -> Result<Value> { pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Context) -> Result<Value> {
let (matcher, flags) = if let Some(object) = this.as_object() { let (matcher, flags) = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
(regex.matcher.clone(), regex.flags.clone()) (regex.matcher.clone(), regex.flags.clone())
} else { } else {
@ -433,6 +436,7 @@ impl RegExp {
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> { pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let (body, flags) = if let Some(object) = this.as_object() { let (body, flags) = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
(regex.original_source.clone(), regex.flags.clone()) (regex.original_source.clone(), regex.flags.clone())
} else { } else {
@ -457,6 +461,7 @@ impl RegExp {
// TODO: it's returning an array, it should return an iterator // TODO: it's returning an array, it should return an iterator
pub(crate) fn match_all(this: &Value, arg_str: String) -> Result<Value> { pub(crate) fn match_all(this: &Value, arg_str: String) -> Result<Value> {
let matches = if let Some(object) = this.as_object() { let matches = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
let mut matches = Vec::new(); let mut matches = Vec::new();

4
boa/src/builtins/string/string_iterator.rs

@ -25,7 +25,7 @@ impl StringIterator {
let string_iterator = Value::new_object(Some(ctx.global_object())); let string_iterator = Value::new_object(Some(ctx.global_object()));
string_iterator.set_data(ObjectData::StringIterator(Self::new(string))); string_iterator.set_data(ObjectData::StringIterator(Self::new(string)));
string_iterator string_iterator
.as_object_mut() .as_object()
.expect("array iterator object") .expect("array iterator object")
.set_prototype_instance(ctx.iterator_prototypes().string_iterator().into()); .set_prototype_instance(ctx.iterator_prototypes().string_iterator().into());
Ok(string_iterator) Ok(string_iterator)
@ -76,7 +76,7 @@ impl StringIterator {
let array_iterator = Value::new_object(Some(global)); let array_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx); make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx);
array_iterator array_iterator
.as_object_mut() .as_object()
.expect("array iterator prototype object") .expect("array iterator prototype object")
.set_prototype_instance(iterator_prototype); .set_prototype_instance(iterator_prototype);

2
boa/src/class.rs

@ -43,7 +43,7 @@
//! class.method("speak", 0, |this, _args, _ctx| { //! class.method("speak", 0, |this, _args, _ctx| {
//! if let Some(object) = this.as_object() { //! if let Some(object) = this.as_object() {
//! if let Some(animal) = object.downcast_ref::<Animal>() { //! if let Some(animal) = object.downcast_ref::<Animal>() {
//! match animal { //! match &*animal {
//! Self::Cat => println!("meow"), //! Self::Cat => println!("meow"),
//! Self::Dog => println!("woof"), //! Self::Dog => println!("woof"),
//! Self::Other => println!(r"¯\_(ツ)_/¯"), //! Self::Other => println!(r"¯\_(ツ)_/¯"),

25
boa/src/context.rs

@ -482,15 +482,15 @@ impl Context {
// Every new function has a prototype property pre-made // Every new function has a prototype property pre-made
let proto = Value::new_object(Some(self.global_object())); let proto = Value::new_object(Some(self.global_object()));
let mut function = Object::function( let mut function = GcObject::new(Object::function(
Function::BuiltIn(body.into(), FunctionFlags::CALLABLE), Function::BuiltIn(body.into(), FunctionFlags::CALLABLE),
function_prototype, function_prototype,
); ));
function.set(PROTOTYPE.into(), proto); function.set(PROTOTYPE.into(), proto);
function.set("length".into(), length.into()); function.set("length".into(), length.into());
function.set("name".into(), name.into()); function.set("name".into(), name.into());
Ok(GcObject::new(function)) Ok(function)
} }
/// Register a global function. /// Register a global function.
@ -533,16 +533,13 @@ impl Context {
.expect("Could not get global object"), .expect("Could not get global object"),
)); ));
array.set_data(ObjectData::Array); array.set_data(ObjectData::Array);
array array.as_object().expect("object").set_prototype_instance(
.as_object_mut() self.realm()
.expect("object") .environment
.set_prototype_instance( .get_binding_value("Array")
self.realm() .expect("Array was not initialized")
.environment .get_field(PROTOTYPE),
.get_binding_value("Array") );
.expect("Array was not initialized")
.get_field(PROTOTYPE),
);
array.set_field("0", key); array.set_field("0", key);
array.set_field("1", value); array.set_field("1", value);
array.set_field("length", Value::from(2)); array.set_field("length", Value::from(2));
@ -611,7 +608,7 @@ impl Context {
let class = class_builder.build(); let class = class_builder.build();
let property = DataDescriptor::new(class, T::ATTRIBUTE); let property = DataDescriptor::new(class, T::ATTRIBUTE);
self.global_object() self.global_object()
.as_object_mut() .as_object()
.unwrap() .unwrap()
.insert(T::NAME, property); .insert(T::NAME, property);
Ok(()) Ok(())

2
boa/src/environment/global_environment_record.rs

@ -83,7 +83,7 @@ impl GlobalEnvironmentRecord {
}; };
global_object global_object
.as_object_mut() .as_object()
.expect("global object") .expect("global object")
.insert(name, desc); .insert(name, desc);
} }

2
boa/src/environment/object_environment_record.rs

@ -66,7 +66,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE); let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE);
property.set_configurable(strict); property.set_configurable(strict);
self.bindings self.bindings
.as_object_mut() .as_object()
.expect("binding object") .expect("binding object")
.insert(name, property); .insert(name, property);
} }

294
boa/src/object/gcobject.rs

@ -2,7 +2,7 @@
//! //!
//! The `GcObject` is a garbage collected Object. //! The `GcObject` is a garbage collected Object.
use super::{Object, PROTOTYPE}; use super::{NativeObject, Object, PROTOTYPE};
use crate::{ use crate::{
builtins::function::{ builtins::function::{
create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction, create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction,
@ -25,18 +25,20 @@ use std::{
result::Result as StdResult, result::Result as StdResult,
}; };
/// A wrapper type for an immutably borrowed `Object`. /// A wrapper type for an immutably borrowed type T.
pub type Ref<'object> = GcCellRef<'object, Object>; pub type Ref<'a, T> = GcCellRef<'a, T>;
/// A wrapper type for a mutably borrowed `Object`. /// A wrapper type for a mutably borrowed type T.
pub type RefMut<'object> = GcCellRefMut<'object, Object>; pub type RefMut<'a, T> = GcCellRefMut<'a, T>;
/// Garbage collected `Object`. /// Garbage collected `Object`.
#[derive(Trace, Finalize, Clone, Default)] #[derive(Trace, Finalize, Clone, Default)]
pub struct GcObject(Gc<GcCell<Object>>); pub struct GcObject(Gc<GcCell<Object>>);
// This is needed for the call method since we cannot mutate the function itself since we /// The body of a JavaScript function.
// already borrow it so we get the function body clone it then drop the borrow and run the body ///
/// 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
enum FunctionBody { enum FunctionBody {
BuiltIn(NativeFunction), BuiltIn(NativeFunction),
Ordinary(RcStatementList), Ordinary(RcStatementList),
@ -51,27 +53,28 @@ impl GcObject {
/// Immutably borrows the `Object`. /// Immutably borrows the `Object`.
/// ///
/// The borrow lasts until the returned `GcCellRef` exits scope. /// The borrow lasts until the returned `Ref` exits scope.
/// Multiple immutable borrows can be taken out at the same time. /// Multiple immutable borrows can be taken out at the same time.
/// ///
///# Panics /// # Panics
///
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
#[inline] #[inline]
#[track_caller] #[track_caller]
pub fn borrow(&self) -> Ref<'_> { pub fn borrow(&self) -> Ref<'_, Object> {
self.try_borrow().expect("Object already mutably borrowed") self.try_borrow().expect("Object already mutably borrowed")
} }
/// Mutably borrows the Object. /// Mutably borrows the Object.
/// ///
/// The borrow lasts until the returned `GcCellRefMut` exits scope. /// The borrow lasts until the returned `RefMut` exits scope.
/// The object cannot be borrowed while this borrow is active. /// The object cannot be borrowed while this borrow is active.
/// ///
///# Panics ///# Panics
/// Panics if the object is currently borrowed. /// Panics if the object is currently borrowed.
#[inline] #[inline]
#[track_caller] #[track_caller]
pub fn borrow_mut(&self) -> RefMut<'_> { pub fn borrow_mut(&self) -> RefMut<'_, Object> {
self.try_borrow_mut().expect("Object already borrowed") self.try_borrow_mut().expect("Object already borrowed")
} }
@ -82,7 +85,7 @@ impl GcObject {
/// ///
/// This is the non-panicking variant of [`borrow`](#method.borrow). /// This is the non-panicking variant of [`borrow`](#method.borrow).
#[inline] #[inline]
pub fn try_borrow(&self) -> StdResult<Ref<'_>, BorrowError> { pub fn try_borrow(&self) -> StdResult<Ref<'_, Object>, BorrowError> {
self.0.try_borrow().map_err(|_| BorrowError) self.0.try_borrow().map_err(|_| BorrowError)
} }
@ -93,7 +96,7 @@ impl GcObject {
/// ///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
#[inline] #[inline]
pub fn try_borrow_mut(&self) -> StdResult<RefMut<'_>, BorrowMutError> { pub fn try_borrow_mut(&self) -> StdResult<RefMut<'_, Object>, BorrowMutError> {
self.0.try_borrow_mut().map_err(|_| BorrowMutError) self.0.try_borrow_mut().map_err(|_| BorrowMutError)
} }
@ -105,7 +108,8 @@ impl GcObject {
/// Call this object. /// Call this object.
/// ///
///# Panics /// # Panics
///
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-prepareforordinarycall> // <https://tc39.es/ecma262/#sec-prepareforordinarycall>
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist> // <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
@ -188,12 +192,13 @@ impl GcObject {
/// Construct an instance of this object with the specified arguments. /// Construct an instance of this object with the specified arguments.
/// ///
///# Panics /// # Panics
///
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget> // <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
#[track_caller] #[track_caller]
pub fn construct(&self, args: &[Value], ctx: &mut Context) -> Result<Value> { pub fn construct(&self, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this: Value = Object::create(self.borrow().get(&PROTOTYPE.into())).into(); let this: Value = Object::create(self.get(&PROTOTYPE.into())).into();
let this_function_object = self.clone(); let this_function_object = self.clone();
let body = if let Some(function) = self.borrow().as_function() { let body = if let Some(function) = self.borrow().as_function() {
@ -348,7 +353,7 @@ impl GcObject {
let rec_limiter = RecursionLimiter::new(self); let rec_limiter = RecursionLimiter::new(self);
if rec_limiter.live { if rec_limiter.live {
Err(interpreter.construct_type_error("cyclic object value")) Err(interpreter.construct_type_error("cyclic object value"))
} else if self.borrow().is_array() { } else if self.is_array() {
let mut keys: Vec<u32> = self.borrow().index_property_keys().cloned().collect(); let mut keys: Vec<u32> = self.borrow().index_property_keys().cloned().collect();
keys.sort_unstable(); keys.sort_unstable();
let mut arr: Vec<JSONValue> = Vec::with_capacity(keys.len()); let mut arr: Vec<JSONValue> = Vec::with_capacity(keys.len());
@ -376,44 +381,45 @@ impl GcObject {
} }
} }
/// Convert the object to a `PropertyDescritptor`
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
pub fn to_property_descriptor(&self, context: &mut Context) -> Result<PropertyDescriptor> { pub fn to_property_descriptor(&self, context: &mut Context) -> Result<PropertyDescriptor> {
let mut attribute = Attribute::empty(); let mut attribute = Attribute::empty();
let enumerable_key = PropertyKey::from("enumerable"); let enumerable_key = PropertyKey::from("enumerable");
if self.borrow().has_property(&enumerable_key) if self.has_property(&enumerable_key) && self.get(&enumerable_key).to_boolean() {
&& self.borrow().get(&enumerable_key).to_boolean()
{
attribute |= Attribute::ENUMERABLE; attribute |= Attribute::ENUMERABLE;
} }
let configurable_key = PropertyKey::from("configurable"); let configurable_key = PropertyKey::from("configurable");
if self.borrow().has_property(&configurable_key) if self.has_property(&configurable_key) && self.get(&configurable_key).to_boolean() {
&& self.borrow().get(&configurable_key).to_boolean()
{
attribute |= Attribute::CONFIGURABLE; attribute |= Attribute::CONFIGURABLE;
} }
let mut value = None; let mut value = None;
let value_key = PropertyKey::from("value"); let value_key = PropertyKey::from("value");
if self.borrow().has_property(&value_key) { if self.has_property(&value_key) {
value = Some(self.borrow().get(&value_key)); value = Some(self.get(&value_key));
} }
let mut has_writable = false; let mut has_writable = false;
let writable_key = PropertyKey::from("writable"); let writable_key = PropertyKey::from("writable");
if self.borrow().has_property(&writable_key) { if self.has_property(&writable_key) {
has_writable = true; has_writable = true;
if self.borrow().get(&writable_key).to_boolean() { if self.get(&writable_key).to_boolean() {
attribute |= Attribute::WRITABLE; attribute |= Attribute::WRITABLE;
} }
} }
let mut get = None; let mut get = None;
let get_key = PropertyKey::from("get"); let get_key = PropertyKey::from("get");
if self.borrow().has_property(&get_key) { if self.has_property(&get_key) {
let getter = self.borrow().get(&get_key); let getter = self.get(&get_key);
match getter { match getter {
Value::Object(ref object) if object.borrow().is_callable() => { Value::Object(ref object) if object.is_callable() => {
get = Some(object.clone()); get = Some(object.clone());
} }
_ => { _ => {
@ -426,10 +432,10 @@ impl GcObject {
let mut set = None; let mut set = None;
let set_key = PropertyKey::from("set"); let set_key = PropertyKey::from("set");
if self.borrow().has_property(&set_key) { if self.has_property(&set_key) {
let setter = self.borrow().get(&set_key); let setter = self.get(&set_key);
match setter { match setter {
Value::Object(ref object) if object.borrow().is_callable() => { Value::Object(ref object) if object.is_callable() => {
set = Some(object.clone()); set = Some(object.clone());
} }
_ => { _ => {
@ -450,6 +456,226 @@ impl GcObject {
Ok(DataDescriptor::new(value.unwrap_or_else(Value::undefined), attribute).into()) Ok(DataDescriptor::new(value.unwrap_or_else(Value::undefined), attribute).into())
} }
} }
/// Reeturn `true` if it is a native object and the native type is `T`.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is<T>(&self) -> bool
where
T: NativeObject,
{
self.borrow().is::<T>()
}
/// Downcast a reference to the object,
/// if the object is type native object type `T`.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn downcast_ref<T>(&self) -> Option<Ref<'_, T>>
where
T: NativeObject,
{
let object = self.borrow();
if object.is::<T>() {
Some(Ref::map(object, |x| x.downcast_ref::<T>().unwrap()))
} else {
None
}
}
/// Downcast a mutable reference to the object,
/// if the object is type native object type `T`.
///
/// # Panics
///
/// Panics if the object is currently borrowed.
#[inline]
#[track_caller]
pub fn downcast_mut<T>(&mut self) -> Option<RefMut<'_, T>>
where
T: NativeObject,
{
let object = self.borrow_mut();
if object.is::<T>() {
Some(RefMut::map(object, |x| x.downcast_mut::<T>().unwrap()))
} else {
None
}
}
/// Get the prototype of the object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn prototype_instance(&self) -> Value {
self.borrow().prototype_instance().clone()
}
/// Set the prototype of the object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed
/// or if th prototype is not an object or undefined.
#[inline]
#[track_caller]
pub fn set_prototype_instance(&mut self, prototype: Value) {
self.borrow_mut().set_prototype_instance(prototype)
}
/// Checks if it an `Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_array(&self) -> bool {
self.borrow().is_array()
}
/// Checks if it is an `ArrayIterator` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_array_iterator(&self) -> bool {
self.borrow().is_array_iterator()
}
/// Checks if it is a `Map` object.pub
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_map(&self) -> bool {
self.borrow().is_map()
}
/// Checks if it a `String` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_string(&self) -> bool {
self.borrow().is_string()
}
/// Checks if it a `Function` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_function(&self) -> bool {
self.borrow().is_function()
}
/// Checks if it a Symbol object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_symbol(&self) -> bool {
self.borrow().is_symbol()
}
/// Checks if it an Error object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_error(&self) -> bool {
self.borrow().is_error()
}
/// Checks if it a Boolean object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_boolean(&self) -> bool {
self.borrow().is_boolean()
}
/// Checks if it a `Number` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_number(&self) -> bool {
self.borrow().is_number()
}
/// Checks if it a `BigInt` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_bigint(&self) -> bool {
self.borrow().is_bigint()
}
/// Checks if it a `RegExp` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_regexp(&self) -> bool {
self.borrow().is_regexp()
}
/// Checks if it an ordinary object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_ordinary(&self) -> bool {
self.borrow().is_ordinary()
}
/// Returns `true` if it holds an Rust type that implements `NativeObject`.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_native_object(&self) -> bool {
self.borrow().is_native_object()
}
} }
impl AsRef<GcCell<Object>> for GcObject { impl AsRef<GcCell<Object>> for GcObject {

95
boa/src/object/internal_methods.rs

@ -12,19 +12,20 @@ use crate::{
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
impl Object { impl GcObject {
/// Check if object has property. /// Check if object has property.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
#[inline]
pub fn has_property(&self, key: &PropertyKey) -> bool { pub fn has_property(&self, key: &PropertyKey) -> bool {
let prop = self.get_own_property(key); let prop = self.get_own_property(key);
if prop.is_none() { if prop.is_none() {
let parent = self.get_prototype_of(); let parent = self.get_prototype_of();
return if let Value::Object(ref object) = parent { return if let Value::Object(ref object) = parent {
object.borrow().has_property(key) object.has_property(key)
} else { } else {
false false
}; };
@ -40,7 +41,7 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
#[inline] #[inline]
pub fn is_extensible(&self) -> bool { pub fn is_extensible(&self) -> bool {
self.extensible self.borrow().extensible
} }
/// Disable extensibility. /// Disable extensibility.
@ -51,15 +52,16 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
#[inline] #[inline]
pub fn prevent_extensions(&mut self) -> bool { pub fn prevent_extensions(&mut self) -> bool {
self.extensible = false; self.borrow_mut().extensible = false;
true true
} }
/// Delete property. /// Delete property.
#[inline]
pub fn delete(&mut self, key: &PropertyKey) -> bool { pub fn delete(&mut self, key: &PropertyKey) -> bool {
match self.get_own_property(key) { match self.get_own_property(key) {
Some(desc) if desc.configurable() => { Some(desc) if desc.configurable() => {
self.remove_property(&key); self.remove(&key);
true true
} }
Some(_) => false, Some(_) => false,
@ -216,13 +218,15 @@ impl Object {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
#[inline]
pub fn get_own_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { pub fn get_own_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {
let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object");
let object = self.borrow();
let property = match key { let property = match key {
PropertyKey::Index(index) => self.indexed_properties.get(&index), PropertyKey::Index(index) => object.indexed_properties.get(&index),
PropertyKey::String(ref st) => self.string_properties.get(st), PropertyKey::String(ref st) => object.string_properties.get(st),
PropertyKey::Symbol(ref symbol) => self.symbol_properties.get(symbol), PropertyKey::Symbol(ref symbol) => object.symbol_properties.get(symbol),
}; };
property.cloned() property.cloned()
@ -234,8 +238,9 @@ impl Object {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec](https://tc39.es/ecma262/#table-essential-internal-methods) /// [spec](https://tc39.es/ecma262/#table-essential-internal-methods)
#[inline]
pub fn own_property_keys(&self) -> Vec<PropertyKey> { pub fn own_property_keys(&self) -> Vec<PropertyKey> {
self.keys().collect() self.borrow().keys().collect()
} }
/// The abstract operation ObjectDefineProperties /// The abstract operation ObjectDefineProperties
@ -244,15 +249,16 @@ impl Object {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties /// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties
#[inline]
pub fn define_properties(&mut self, props: Value, ctx: &mut Context) -> Result<()> { pub fn define_properties(&mut self, props: Value, ctx: &mut Context) -> Result<()> {
let props = props.to_object(ctx)?; let props = props.to_object(ctx)?;
let keys = props.borrow().own_property_keys(); let keys = props.own_property_keys();
let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new(); let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new();
for next_key in keys { for next_key in keys {
if let Some(prop_desc) = props.borrow().get_own_property(&next_key) { if let Some(prop_desc) = props.get_own_property(&next_key) {
if prop_desc.enumerable() { if prop_desc.enumerable() {
let desc_obj = props.borrow().get(&next_key); let desc_obj = props.get(&next_key);
let desc = desc_obj.to_property_descriptor(ctx)?; let desc = desc_obj.to_property_descriptor(ctx)?;
descriptors.push((next_key, desc)); descriptors.push((next_key, desc));
} }
@ -277,6 +283,7 @@ impl Object {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
#[inline]
pub fn set_prototype_of(&mut self, _val: Value) -> bool { pub fn set_prototype_of(&mut self, _val: Value) -> bool {
// debug_assert!(val.is_object() || val.is_null()); // debug_assert!(val.is_object() || val.is_null());
// let current = self.prototype.clone(); // let current = self.prototype.clone();
@ -316,9 +323,69 @@ impl Object {
/// [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]
pub fn get_prototype_of(&self) -> Value { pub fn get_prototype_of(&self) -> Value {
self.prototype.clone() self.borrow().prototype.clone()
}
/// Helper function for property insertion.
#[inline]
pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> Option<PropertyDescriptor>
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
self.borrow_mut().insert(key, property)
}
/// Helper function for property removal.
#[inline]
pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
self.borrow_mut().remove(key)
} }
/// Inserts a field in the object `properties` without checking if it's writable.
///
/// If a field was already in the object with the same name that a `Some` is returned
/// with that field, otherwise None is returned.
#[inline]
pub(crate) fn insert_property<K, V>(
&mut self,
key: K,
value: V,
attribute: Attribute,
) -> Option<PropertyDescriptor>
where
K: Into<PropertyKey>,
V: Into<Value>,
{
self.insert(key.into(), DataDescriptor::new(value, attribute))
}
/// It determines if Object is a callable function with a [[Call]] internal method.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-iscallable
#[inline]
#[track_caller]
pub fn is_callable(&self) -> bool {
self.borrow().is_callable()
}
/// It determines if Object is a function object with a [[Construct]] internal method.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isconstructor
#[inline]
#[track_caller]
pub fn is_constructable(&self) -> bool {
self.borrow().is_constructable()
}
}
impl Object {
/// Helper function for property insertion. /// Helper function for property insertion.
#[inline] #[inline]
pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> Option<PropertyDescriptor> pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> Option<PropertyDescriptor>
@ -340,7 +407,7 @@ impl Object {
/// Helper function for property removal. /// Helper function for property removal.
#[inline] #[inline]
pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> { pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
match key { match key {
PropertyKey::Index(index) => self.indexed_properties.remove(&index), PropertyKey::Index(index) => self.indexed_properties.remove(&index),
PropertyKey::String(ref string) => self.string_properties.remove(string), PropertyKey::String(ref string) => self.string_properties.remove(string),

8
boa/src/object/mod.rs

@ -483,6 +483,14 @@ impl Object {
matches!(self.data, ObjectData::NativeObject(_)) matches!(self.data, ObjectData::NativeObject(_))
} }
#[inline]
pub fn as_native_object(&self) -> Option<&dyn NativeObject> {
match self.data {
ObjectData::NativeObject(ref object) => Some(object.as_ref()),
_ => None,
}
}
/// Reeturn `true` if it is a native object and the native type is `T`. /// Reeturn `true` if it is a native object and the native type is `T`.
#[inline] #[inline]
pub fn is<T>(&self) -> bool pub fn is<T>(&self) -> bool

5
boa/src/value/display.rs

@ -93,7 +93,6 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children:
} }
ObjectData::Array => { ObjectData::Array => {
let len = v let len = v
.borrow()
.get_own_property(&PropertyKey::from("length")) .get_own_property(&PropertyKey::from("length"))
// TODO: do this in a better way `unwrap` // TODO: do this in a better way `unwrap`
.unwrap() .unwrap()
@ -114,8 +113,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children:
// Introduce recursive call to stringify any objects // Introduce recursive call to stringify any objects
// which are part of the Array // which are part of the Array
log_string_from( log_string_from(
&v.borrow() &v.get_own_property(&i.into())
.get_own_property(&i.into())
// TODO: do this in a better way "unwrap" // TODO: do this in a better way "unwrap"
.unwrap() .unwrap()
// FIXME: handle accessor descriptors // FIXME: handle accessor descriptors
@ -136,7 +134,6 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children:
} }
ObjectData::Map(ref map) => { ObjectData::Map(ref map) => {
let size = v let size = v
.borrow()
.get_own_property(&PropertyKey::from("size")) .get_own_property(&PropertyKey::from("size"))
// TODO: do this in a better way "unwrap" // TODO: do this in a better way "unwrap"
.unwrap() .unwrap()

45
boa/src/value/mod.rs

@ -14,7 +14,7 @@ use crate::{
property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
use gc::{Finalize, GcCellRef, GcCellRefMut, Trace}; use gc::{Finalize, Trace};
use serde_json::{Number as JSONNumber, Value as JSONValue}; use serde_json::{Number as JSONNumber, Value as JSONValue};
use std::{ use std::{
collections::HashSet, collections::HashSet,
@ -268,25 +268,9 @@ impl Value {
} }
#[inline] #[inline]
pub fn as_object(&self) -> Option<GcCellRef<'_, Object>> { pub fn as_object(&self) -> Option<GcObject> {
match *self { match *self {
Self::Object(ref o) => Some(o.borrow()), Self::Object(ref o) => Some(o.clone()),
_ => None,
}
}
#[inline]
pub fn as_gc_object(&self) -> Option<GcObject> {
match self {
Self::Object(o) => Some(o.clone()),
_ => None,
}
}
#[inline]
pub fn as_object_mut(&self) -> Option<GcCellRefMut<'_, Object>> {
match *self {
Self::Object(ref o) => Some(o.borrow_mut()),
_ => None, _ => None,
} }
} }
@ -307,7 +291,7 @@ impl Value {
/// Returns true if the value is a function /// Returns true if the value is a function
#[inline] #[inline]
pub fn is_function(&self) -> bool { pub fn is_function(&self) -> bool {
matches!(self, Self::Object(o) if o.borrow().is_function()) matches!(self, Self::Object(o) if o.is_function())
} }
/// Returns true if the value is undefined. /// Returns true if the value is undefined.
@ -434,8 +418,8 @@ impl Value {
where where
Key: Into<PropertyKey>, Key: Into<PropertyKey>,
{ {
self.as_object_mut() self.as_object()
.map(|mut x| x.remove_property(&key.into())) .map(|mut x| x.remove(&key.into()))
.is_some() .is_some()
} }
@ -450,13 +434,12 @@ impl Value {
let _timer = BoaProfiler::global().start_event("Value::get_property", "value"); let _timer = BoaProfiler::global().start_event("Value::get_property", "value");
match self { match self {
Self::Object(ref object) => { Self::Object(ref object) => {
let object = object.borrow();
let property = object.get_own_property(&key); let property = object.get_own_property(&key);
if property.is_some() { if property.is_some() {
return property; return property;
} }
object.prototype_instance().get_property(key) object.borrow().prototype_instance().get_property(key)
} }
_ => None, _ => None,
} }
@ -504,14 +487,14 @@ impl Value {
let _timer = BoaProfiler::global().start_event("Value::set_field", "value"); let _timer = BoaProfiler::global().start_event("Value::set_field", "value");
if let Self::Object(ref obj) = *self { if let Self::Object(ref obj) = *self {
if let PropertyKey::Index(index) = key { if let PropertyKey::Index(index) = key {
if obj.borrow().is_array() { if obj.is_array() {
let len = self.get_field("length").as_number().unwrap() as u32; let len = self.get_field("length").as_number().unwrap() as u32;
if len < index + 1 { if len < index + 1 {
self.set_field("length", index + 1); self.set_field("length", index + 1);
} }
} }
} }
obj.borrow_mut().set(key, value.clone()); obj.clone().set(key, value.clone());
} }
value value
} }
@ -531,7 +514,7 @@ impl Value {
K: Into<PropertyKey>, K: Into<PropertyKey>,
P: Into<PropertyDescriptor>, P: Into<PropertyDescriptor>,
{ {
if let Some(mut object) = self.as_object_mut() { if let Some(mut object) = self.as_object() {
object.insert(key.into(), property.into()); object.insert(key.into(), property.into());
} }
} }
@ -661,11 +644,13 @@ impl Value {
Value::String(ref string) => { Value::String(ref string) => {
let prototype = ctx.standard_objects().string_object().prototype(); let prototype = ctx.standard_objects().string_object().prototype();
let mut object = let mut object = GcObject::new(Object::with_prototype(
Object::with_prototype(prototype.into(), ObjectData::String(string.clone())); prototype.into(),
ObjectData::String(string.clone()),
));
// Make sure the correct length is set on our new string object // Make sure the correct length is set on our new string object
object.set("length".into(), string.chars().count().into()); object.set("length".into(), string.chars().count().into());
Ok(GcObject::new(object)) Ok(object)
} }
Value::Symbol(ref symbol) => { Value::Symbol(ref symbol) => {
let prototype = ctx.standard_objects().symbol_object().prototype(); let prototype = ctx.standard_objects().symbol_object().prototype();

2
boa/src/value/type.rs

@ -47,7 +47,7 @@ impl Value {
Self::Undefined => Type::Undefined, Self::Undefined => Type::Undefined,
Self::BigInt(_) => Type::BigInt, Self::BigInt(_) => Type::BigInt,
Self::Object(ref object) => { Self::Object(ref object) => {
if object.borrow().is_function() { if object.is_function() {
Type::Function Type::Function
} else { } else {
Type::Object Type::Object

Loading…
Cancel
Save