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()));
array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind)));
array_iterator
.as_object_mut()
.as_object()
.expect("array iterator object")
.set_prototype_instance(ctx.iterator_prototypes().array_iterator().into());
Ok(array_iterator)
@ -126,7 +126,7 @@ impl ArrayIterator {
let array_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx);
array_iterator
.as_object_mut()
.as_object()
.expect("array iterator prototype object")
.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(),
};
this.as_object_mut()
this.as_object()
.expect("this should be an array object")
.set_prototype_instance(prototype.into());
// 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
.as_object_mut()
.as_object()
.expect("array object")
.set_prototype_instance(context.standard_objects().array_object().prototype().into());
array.set_field("length", Value::from(0));
@ -281,7 +281,7 @@ impl Array {
_interpreter: &mut Context,
) -> Result<Value> {
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)),
}
}

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");
assert!(same_value(
&bool_instance
.as_object()
.unwrap()
.prototype_instance()
.clone(),
&bool_instance.as_object().unwrap().prototype_instance(),
&bool_prototype
));
}

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

@ -14,7 +14,7 @@
use crate::{
builtins::{Array, BuiltIn},
environment::lexical_environment::Environment,
object::{ConstructorBuilder, FunctionBuilder, Object, ObjectData, PROTOTYPE},
object::{ConstructorBuilder, FunctionBuilder, GcObject, Object, ObjectData},
property::{Attribute, DataDescriptor},
syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, Result, Value,
@ -172,7 +172,7 @@ impl Function {
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
let len = arguments_list.len();
let mut obj = Object::default();
let mut obj = GcObject::new(Object::default());
// Set length
let length = DataDescriptor::new(
len,
@ -195,61 +195,6 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
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`.
///
/// 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());
parent
.as_object_mut()
.as_object()
.unwrap()
.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);
Self {
iterator_prototype: iterator_prototype
.as_gc_object()
.as_object()
.expect("Iterator prototype is not an object"),
array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone())
.as_gc_object()
.as_object()
.expect("Array Iterator Prototype is not an object"),
string_iterator: StringIterator::create_prototype(ctx, iterator_prototype.clone())
.as_gc_object()
.as_object()
.expect("String Iterator Prototype is not an object"),
map_iterator: MapIterator::create_prototype(ctx, iterator_prototype)
.as_gc_object()
.as_object()
.expect("Map Iterator Prototype is not an object"),
}
}

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

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

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

@ -279,14 +279,12 @@ fn json_parse_sets_prototypes() {
.unwrap()
.as_object()
.unwrap()
.prototype_instance()
.clone();
.prototype_instance();
let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#)
.unwrap()
.as_object()
.unwrap()
.prototype_instance()
.clone();
.prototype_instance();
let global_object_prototype = engine
.global_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()));
map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind)));
map_iterator
.as_object_mut()
.as_object()
.expect("map iterator object")
.set_prototype_instance(ctx.iterator_prototypes().map_iterator().into());
Ok(map_iterator)
@ -143,7 +143,7 @@ impl MapIterator {
let map_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx);
map_iterator
.as_object_mut()
.as_object()
.expect("map iterator prototype object")
.set_prototype_instance(iterator_prototype);

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

@ -73,7 +73,7 @@ impl Map {
// Set Prototype
let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE);
this.as_object_mut()
this.as_object()
.expect("this is map object")
.set_prototype_instance(prototype);
// 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.
fn get_key_value(value: &Value) -> Option<(Value, 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 {
0 => (Value::Undefined, 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) {
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)?);
}
}
@ -169,7 +169,6 @@ impl Object {
for key in object.borrow().keys() {
let descriptor = {
let desc = object
.borrow()
.get_own_property(&key)
.expect("Expected property to be on object.");
Self::from_property_descriptor(desc, ctx)?
@ -239,16 +238,16 @@ impl Object {
/// Get the `prototype` of an object.
pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object");
Ok(obj.as_object().map_or_else(Value::undefined, |object| {
object.prototype_instance().clone()
}))
Ok(obj
.as_object()
.map_or_else(Value::undefined, |object| object.prototype_instance()))
}
/// Set the `prototype` of an object.
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).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)
}
@ -281,11 +280,11 @@ impl Object {
/// [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> {
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 {
let props = args.get(1).cloned().unwrap_or_else(Value::undefined);
obj.define_properties(props, ctx)?;
Ok(arg.clone())
Ok(arg)
} else {
ctx.throw_type_error("Expected an object")
}
@ -307,19 +306,21 @@ impl Object {
} else if this.is_null() {
Ok("[object Null]".into())
} else {
let gc_o = this.to_object(ctx)?;
let o = gc_o.borrow();
let builtin_tag = match &o.data {
ObjectData::Array => "Array",
// TODO: Arguments Exotic Objects are currently not supported
ObjectData::Function(_) => "Function",
ObjectData::Error => "Error",
ObjectData::Boolean(_) => "Boolean",
ObjectData::Number(_) => "Number",
ObjectData::String(_) => "String",
ObjectData::Date(_) => "Date",
ObjectData::RegExp(_) => "RegExp",
_ => "Object",
let o = this.to_object(ctx)?;
let builtin_tag = {
let o = o.borrow();
match &o.data {
ObjectData::Array => "Array",
// TODO: Arguments Exotic Objects are currently not supported
ObjectData::Function(_) => "Function",
ObjectData::Error => "Error",
ObjectData::Boolean(_) => "Boolean",
ObjectData::Number(_) => "Number",
ObjectData::String(_) => "String",
ObjectData::Date(_) => "Date",
ObjectData::RegExp(_) => "RegExp",
_ => "Object",
}
};
let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into());
@ -349,7 +350,6 @@ impl Object {
};
let own_property = this
.as_object()
.as_deref()
.expect("Cannot get THIS object")
.get_own_property(&prop.expect("cannot get prop").into());
if own_property.is_none() {
@ -370,7 +370,7 @@ impl Object {
};
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| {
Value::from(own_prop.enumerable())

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

@ -309,6 +309,7 @@ impl RegExp {
.to_string(ctx)?;
let mut last_index = this.get_field("lastIndex").to_index(ctx)?;
let result = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
let result =
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() {
@ -349,6 +350,7 @@ impl RegExp {
.to_string(ctx)?;
let mut last_index = this.get_field("lastIndex").to_index(ctx)?;
let result = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
let result = {
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
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 object = object.borrow();
let regex = object.as_regexp().unwrap();
(regex.matcher.clone(), regex.flags.clone())
} else {
@ -433,6 +436,7 @@ impl RegExp {
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let (body, flags) = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
(regex.original_source.clone(), regex.flags.clone())
} else {
@ -457,6 +461,7 @@ impl RegExp {
// TODO: it's returning an array, it should return an iterator
pub(crate) fn match_all(this: &Value, arg_str: String) -> Result<Value> {
let matches = if let Some(object) = this.as_object() {
let object = object.borrow();
let regex = object.as_regexp().unwrap();
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()));
string_iterator.set_data(ObjectData::StringIterator(Self::new(string)));
string_iterator
.as_object_mut()
.as_object()
.expect("array iterator object")
.set_prototype_instance(ctx.iterator_prototypes().string_iterator().into());
Ok(string_iterator)
@ -76,7 +76,7 @@ impl StringIterator {
let array_iterator = Value::new_object(Some(global));
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx);
array_iterator
.as_object_mut()
.as_object()
.expect("array iterator prototype object")
.set_prototype_instance(iterator_prototype);

2
boa/src/class.rs

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

25
boa/src/context.rs

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

2
boa/src/environment/global_environment_record.rs

@ -83,7 +83,7 @@ impl GlobalEnvironmentRecord {
};
global_object
.as_object_mut()
.as_object()
.expect("global object")
.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);
property.set_configurable(strict);
self.bindings
.as_object_mut()
.as_object()
.expect("binding object")
.insert(name, property);
}

294
boa/src/object/gcobject.rs

@ -2,7 +2,7 @@
//!
//! The `GcObject` is a garbage collected Object.
use super::{Object, PROTOTYPE};
use super::{NativeObject, Object, PROTOTYPE};
use crate::{
builtins::function::{
create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction,
@ -25,18 +25,20 @@ use std::{
result::Result as StdResult,
};
/// A wrapper type for an immutably borrowed `Object`.
pub type Ref<'object> = GcCellRef<'object, Object>;
/// A wrapper type for an immutably borrowed type T.
pub type Ref<'a, T> = GcCellRef<'a, T>;
/// A wrapper type for a mutably borrowed `Object`.
pub type RefMut<'object> = GcCellRefMut<'object, Object>;
/// A wrapper type for a mutably borrowed type T.
pub type RefMut<'a, T> = GcCellRefMut<'a, T>;
/// Garbage collected `Object`.
#[derive(Trace, Finalize, Clone, Default)]
pub struct GcObject(Gc<GcCell<Object>>);
// 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
/// 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
enum FunctionBody {
BuiltIn(NativeFunction),
Ordinary(RcStatementList),
@ -51,27 +53,28 @@ impl GcObject {
/// 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.
///
///# Panics
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn borrow(&self) -> Ref<'_> {
pub fn borrow(&self) -> Ref<'_, Object> {
self.try_borrow().expect("Object already mutably borrowed")
}
/// 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.
///
///# Panics
/// Panics if the object is currently borrowed.
#[inline]
#[track_caller]
pub fn borrow_mut(&self) -> RefMut<'_> {
pub fn borrow_mut(&self) -> RefMut<'_, Object> {
self.try_borrow_mut().expect("Object already borrowed")
}
@ -82,7 +85,7 @@ impl GcObject {
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
#[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)
}
@ -93,7 +96,7 @@ impl GcObject {
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
#[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)
}
@ -105,7 +108,8 @@ impl GcObject {
/// Call this object.
///
///# Panics
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
// <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.
///
///# Panics
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
#[track_caller]
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 body = if let Some(function) = self.borrow().as_function() {
@ -348,7 +353,7 @@ impl GcObject {
let rec_limiter = RecursionLimiter::new(self);
if rec_limiter.live {
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();
keys.sort_unstable();
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> {
let mut attribute = Attribute::empty();
let enumerable_key = PropertyKey::from("enumerable");
if self.borrow().has_property(&enumerable_key)
&& self.borrow().get(&enumerable_key).to_boolean()
{
if self.has_property(&enumerable_key) && self.get(&enumerable_key).to_boolean() {
attribute |= Attribute::ENUMERABLE;
}
let configurable_key = PropertyKey::from("configurable");
if self.borrow().has_property(&configurable_key)
&& self.borrow().get(&configurable_key).to_boolean()
{
if self.has_property(&configurable_key) && self.get(&configurable_key).to_boolean() {
attribute |= Attribute::CONFIGURABLE;
}
let mut value = None;
let value_key = PropertyKey::from("value");
if self.borrow().has_property(&value_key) {
value = Some(self.borrow().get(&value_key));
if self.has_property(&value_key) {
value = Some(self.get(&value_key));
}
let mut has_writable = false;
let writable_key = PropertyKey::from("writable");
if self.borrow().has_property(&writable_key) {
if self.has_property(&writable_key) {
has_writable = true;
if self.borrow().get(&writable_key).to_boolean() {
if self.get(&writable_key).to_boolean() {
attribute |= Attribute::WRITABLE;
}
}
let mut get = None;
let get_key = PropertyKey::from("get");
if self.borrow().has_property(&get_key) {
let getter = self.borrow().get(&get_key);
if self.has_property(&get_key) {
let getter = self.get(&get_key);
match getter {
Value::Object(ref object) if object.borrow().is_callable() => {
Value::Object(ref object) if object.is_callable() => {
get = Some(object.clone());
}
_ => {
@ -426,10 +432,10 @@ impl GcObject {
let mut set = None;
let set_key = PropertyKey::from("set");
if self.borrow().has_property(&set_key) {
let setter = self.borrow().get(&set_key);
if self.has_property(&set_key) {
let setter = self.get(&set_key);
match setter {
Value::Object(ref object) if object.borrow().is_callable() => {
Value::Object(ref object) if object.is_callable() => {
set = Some(object.clone());
}
_ => {
@ -450,6 +456,226 @@ impl GcObject {
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 {

95
boa/src/object/internal_methods.rs

@ -12,19 +12,20 @@ use crate::{
BoaProfiler, Context, Result,
};
impl Object {
impl GcObject {
/// Check if object has property.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
#[inline]
pub fn has_property(&self, key: &PropertyKey) -> bool {
let prop = self.get_own_property(key);
if prop.is_none() {
let parent = self.get_prototype_of();
return if let Value::Object(ref object) = parent {
object.borrow().has_property(key)
object.has_property(key)
} else {
false
};
@ -40,7 +41,7 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
#[inline]
pub fn is_extensible(&self) -> bool {
self.extensible
self.borrow().extensible
}
/// Disable extensibility.
@ -51,15 +52,16 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
#[inline]
pub fn prevent_extensions(&mut self) -> bool {
self.extensible = false;
self.borrow_mut().extensible = false;
true
}
/// Delete property.
#[inline]
pub fn delete(&mut self, key: &PropertyKey) -> bool {
match self.get_own_property(key) {
Some(desc) if desc.configurable() => {
self.remove_property(&key);
self.remove(&key);
true
}
Some(_) => false,
@ -216,13 +218,15 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [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> {
let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object");
let object = self.borrow();
let property = match key {
PropertyKey::Index(index) => self.indexed_properties.get(&index),
PropertyKey::String(ref st) => self.string_properties.get(st),
PropertyKey::Symbol(ref symbol) => self.symbol_properties.get(symbol),
PropertyKey::Index(index) => object.indexed_properties.get(&index),
PropertyKey::String(ref st) => object.string_properties.get(st),
PropertyKey::Symbol(ref symbol) => object.symbol_properties.get(symbol),
};
property.cloned()
@ -234,8 +238,9 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec](https://tc39.es/ecma262/#table-essential-internal-methods)
#[inline]
pub fn own_property_keys(&self) -> Vec<PropertyKey> {
self.keys().collect()
self.borrow().keys().collect()
}
/// The abstract operation ObjectDefineProperties
@ -244,15 +249,16 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties
#[inline]
pub fn define_properties(&mut self, props: Value, ctx: &mut Context) -> Result<()> {
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();
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() {
let desc_obj = props.borrow().get(&next_key);
let desc_obj = props.get(&next_key);
let desc = desc_obj.to_property_descriptor(ctx)?;
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
/// [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 {
// debug_assert!(val.is_object() || val.is_null());
// 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
#[inline]
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.
#[inline]
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.
#[inline]
pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
match key {
PropertyKey::Index(index) => self.indexed_properties.remove(&index),
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(_))
}
#[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`.
#[inline]
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 => {
let len = v
.borrow()
.get_own_property(&PropertyKey::from("length"))
// TODO: do this in a better way `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
// which are part of the Array
log_string_from(
&v.borrow()
.get_own_property(&i.into())
&v.get_own_property(&i.into())
// TODO: do this in a better way "unwrap"
.unwrap()
// 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) => {
let size = v
.borrow()
.get_own_property(&PropertyKey::from("size"))
// TODO: do this in a better way "unwrap"
.unwrap()

45
boa/src/value/mod.rs

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

Loading…
Cancel
Save