Browse Source

Refactor internal methods and make some builtins spec compliant (#1422)

pull/1390/head
Halid Odat 3 years ago committed by GitHub
parent
commit
f6d5733b02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa/src/builtins/array/array_iterator.rs
  2. 828
      boa/src/builtins/array/mod.rs
  3. 2
      boa/src/builtins/array/tests.rs
  4. 2
      boa/src/builtins/boolean/mod.rs
  5. 10
      boa/src/builtins/console/mod.rs
  6. 4
      boa/src/builtins/date/mod.rs
  7. 4
      boa/src/builtins/error/eval.rs
  8. 4
      boa/src/builtins/error/mod.rs
  9. 4
      boa/src/builtins/error/range.rs
  10. 4
      boa/src/builtins/error/reference.rs
  11. 4
      boa/src/builtins/error/syntax.rs
  12. 4
      boa/src/builtins/error/type.rs
  13. 4
      boa/src/builtins/error/uri.rs
  14. 6
      boa/src/builtins/function/mod.rs
  15. 2
      boa/src/builtins/json/mod.rs
  16. 2
      boa/src/builtins/map/map_iterator.rs
  17. 4
      boa/src/builtins/map/mod.rs
  18. 2
      boa/src/builtins/number/mod.rs
  19. 4
      boa/src/builtins/object/for_in_iterator.rs
  20. 104
      boa/src/builtins/object/mod.rs
  21. 31
      boa/src/builtins/reflect/mod.rs
  22. 114
      boa/src/builtins/regexp/mod.rs
  23. 2
      boa/src/builtins/regexp/regexp_string_iterator.rs
  24. 4
      boa/src/builtins/set/mod.rs
  25. 2
      boa/src/builtins/set/set_iterator.rs
  26. 32
      boa/src/builtins/string/mod.rs
  27. 2
      boa/src/builtins/string/string_iterator.rs
  28. 2
      boa/src/context.rs
  29. 158
      boa/src/object/gcobject.rs
  30. 379
      boa/src/object/internal_methods.rs
  31. 10
      boa/src/property/mod.rs
  32. 4
      boa/src/syntax/ast/node/operator/unary_op/mod.rs
  33. 32
      boa/src/value/conversions.rs
  34. 4
      boa/src/value/display.rs
  35. 17
      boa/src/value/mod.rs
  36. 4
      boa/src/value/tests.rs
  37. 12
      boa/src/vm/mod.rs

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

@ -123,7 +123,7 @@ impl ArrayIterator {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let mut array_iterator = context.construct_object();
let array_iterator = context.construct_object();
make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
array_iterator.set_prototype_instance(iterator_prototype);

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

File diff suppressed because it is too large Load Diff

2
boa/src/builtins/array/tests.rs

@ -711,7 +711,7 @@ fn fill() {
assert_eq!(
forward(&mut context, "a.fill().join()"),
String::from("\"undefined,undefined,undefined\"")
String::from("\",,\"")
);
// test object reference

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

@ -69,7 +69,7 @@ impl Boolean {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})

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

@ -35,14 +35,6 @@ pub enum LogMessage {
Error(String),
}
/// Helper function that returns the argument at a specified index.
fn get_arg_at_index<'a, T>(args: &'a [Value], index: usize) -> Option<T>
where
T: From<&'a Value> + Default,
{
args.get(index).map(|s| T::from(s))
}
/// Helper function for logging messages.
pub(crate) fn logger(msg: LogMessage, console_state: &Console) {
let indent = 2 * console_state.groups.len();
@ -193,7 +185,7 @@ impl Console {
/// [spec]: https://console.spec.whatwg.org/#assert
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert
pub(crate) fn assert(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
let assertion = args.get(0).map(Value::to_boolean).unwrap_or(false);
if !assertion {
let mut args: Vec<Value> = args.iter().skip(1).cloned().collect();

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

@ -376,13 +376,13 @@ impl Date {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().object_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = obj.into();
if args.is_empty() {

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

@ -65,13 +65,13 @@ impl EvalError {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -81,13 +81,13 @@ impl Error {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -62,13 +62,13 @@ impl RangeError {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -61,13 +61,13 @@ impl ReferenceError {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -64,13 +64,13 @@ impl SyntaxError {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -67,13 +67,13 @@ impl TypeError {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -63,13 +63,13 @@ impl UriError {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {

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

@ -176,14 +176,14 @@ 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 = GcObject::new(Object::default());
let obj = GcObject::new(Object::default());
// Set length
let length = DataDescriptor::new(
len,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
);
// Define length as a property
obj.ordinary_define_own_property("length", length.into());
obj.ordinary_define_own_property("length".into(), length.into());
let mut index: usize = 0;
while index < len {
let val = arguments_list.get(index).expect("Could not get argument");
@ -258,7 +258,7 @@ impl BuiltInFunctionObject {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})

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

@ -197,7 +197,7 @@ impl Json {
.map(|obj| {
let object_to_return = Value::object(Object::default());
for key in obj.borrow().keys() {
let val = obj.get(&key, obj.clone().into(), context)?;
let val = obj.__get__(&key, obj.clone().into(), context)?;
let this_arg = object.clone();
object_to_return.set_property(
key.to_owned(),

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

@ -149,7 +149,7 @@ impl MapIterator {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let mut map_iterator = context.construct_object();
let map_iterator = context.construct_object();
make_builtin_fn(Self::next, "next", &map_iterator, 0, context);
map_iterator.set_prototype_instance(iterator_prototype);

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

@ -118,14 +118,14 @@ impl Map {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or(map_prototype);
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);

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

@ -169,7 +169,7 @@ impl Number {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})

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

@ -87,7 +87,7 @@ impl ForInIterator {
while let Some(r) = iterator.remaining_keys.pop_front() {
if !iterator.visited_keys.contains(&r) {
if let Some(desc) =
object.get_own_property(&PropertyKey::from(r.clone()))
object.__get_own_property__(&PropertyKey::from(r.clone()))
{
iterator.visited_keys.insert(r.clone());
if desc.enumerable() {
@ -129,7 +129,7 @@ impl ForInIterator {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let mut for_in_iterator = context.construct_object();
let for_in_iterator = context.construct_object();
make_builtin_fn(Self::next, "next", &for_in_iterator, 0, context);
for_in_iterator.set_prototype_instance(iterator_prototype);

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

@ -87,7 +87,7 @@ impl Object {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
@ -165,7 +165,7 @@ impl Object {
if let Some(key) = args.get(1) {
let key = key.to_property_key(context)?;
if let Some(desc) = object.get_own_property(&key) {
if let Some(desc) = object.__get_own_property__(&key) {
return Ok(Self::from_property_descriptor(desc, context));
}
}
@ -197,7 +197,7 @@ impl Object {
for key in object.borrow().keys() {
let descriptor = {
let desc = object
.get_own_property(&key)
.__get_own_property__(&key)
.expect("Expected property to be on object.");
Self::from_property_descriptor(desc, context)
};
@ -318,7 +318,7 @@ impl Object {
let status = obj
.as_object()
.expect("obj was not an object")
.set_prototype_of(proto);
.__set_prototype_of__(proto);
// 5. If status is false, throw a TypeError exception.
if !status {
@ -411,38 +411,52 @@ impl Object {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
#[allow(clippy::wrong_self_convention)]
pub fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
// 1. If the this value is undefined, return "[object Undefined]".
if this.is_undefined() {
Ok("[object Undefined]".into())
} else if this.is_null() {
Ok("[object Null]".into())
} else {
let o = this.to_object(context)?;
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",
}
};
return Ok("[object Undefined]".into());
}
// 2. If the this value is null, return "[object Null]".
if this.is_null() {
return Ok("[object Null]".into());
}
// 3. Let O be ! ToObject(this value).
let o = this.to_object(context)?;
// TODO: 4. Let isArray be ? IsArray(O).
// TODO: 5. If isArray is true, let builtinTag be "Array".
// 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
// 7. Else if O has a [[Call]] internal method, let builtinTag be "Function".
// 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
// 9. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
// 10. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
// 11. Else if O has a [[StringData]] internal slot, let builtinTag be "String".
// 12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
// 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
// 14. Else, let builtinTag be "Object".
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(
&WellKnownSymbols::to_string_tag().into(),
o.clone().into(),
context,
)?;
// 15. Let tag be ? Get(O, @@toStringTag).
let tag = o.get(WellKnownSymbols::to_string_tag(), context)?;
let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag);
// 16. If Type(tag) is not String, set tag to builtinTag.
let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag);
Ok(format!("[object {}]", tag_str).into())
}
// 17. Return the string-concatenation of "[object ", tag, and "]".
Ok(format!("[object {}]", tag_str).into())
}
/// `Object.prototype.hasOwnPrototype( property )`
@ -463,7 +477,7 @@ impl Object {
.to_property_key(context)?;
let object = this.to_object(context)?;
Ok(object.has_own_property(key).into())
Ok(object.has_own_property(key, context)?.into())
}
/// `Object.prototype.propertyIsEnumerable( property )`
@ -488,7 +502,7 @@ impl Object {
};
let key = key.to_property_key(context)?;
let own_property = this.to_object(context)?.get_own_property(&key);
let own_property = this.to_object(context)?.__get_own_property__(&key);
Ok(own_property.map_or(Value::from(false), |own_prop| {
Value::from(own_prop.enumerable())
@ -507,31 +521,45 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-object.assign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
pub fn assign(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let mut to = args
//
//
// 1. Let to be ? ToObject(target).
let to = args
.get(0)
.cloned()
.unwrap_or_default()
.to_object(context)?;
// 2. If only one argument was passed, return to.
if args.len() == 1 {
return Ok(to.into());
}
// 3. For each element nextSource of sources, do
for source in &args[1..] {
// 3.a. If nextSource is neither undefined nor null, then
if !source.is_null_or_undefined() {
// 3.a.i. Let from be ! ToObject(nextSource).
let from = source.to_object(context).unwrap();
// 3.a.ii. Let keys be ? from.[[OwnPropertyKeys]]().
let keys = from.own_property_keys();
// 3.a.iii. For each element nextKey of keys, do
for key in keys {
if let Some(desc) = from.get_own_property(&key) {
// 3.a.iii.1. Let desc be ? from.[[GetOwnProperty]](nextKey).
if let Some(desc) = from.__get_own_property__(&key) {
// 3.a.iii.2. If desc is not undefined and desc.[[Enumerable]] is true, then
if desc.enumerable() {
let property = from.get(&key, from.clone().into(), context)?;
to.set(key, property, to.clone().into(), context)?;
// 3.a.iii.2.a. Let propValue be ? Get(from, nextKey).
let property = from.get(key.clone(), context)?;
// 3.a.iii.2.b. Perform ? Set(to, nextKey, propValue, true).
to.set(key, property, true, context)?;
}
}
}
}
}
// 4. Return to.
Ok(to.into())
}
}

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

@ -142,7 +142,7 @@ impl Reflect {
context: &mut Context,
) -> Result<Value> {
let undefined = Value::undefined();
let mut target = args
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
@ -154,7 +154,7 @@ impl Reflect {
.to_property_descriptor(context)?;
target
.define_own_property(key, prop_desc, context)
.__define_own_property__(key, prop_desc, context)
.map(|b| b.into())
}
@ -172,13 +172,13 @@ impl Reflect {
context: &mut Context,
) -> Result<Value> {
let undefined = Value::undefined();
let mut target = args
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
let key = args.get(1).unwrap_or(&undefined).to_property_key(context)?;
Ok(target.delete(&key).into())
Ok(target.__delete__(&key).into())
}
/// Gets a property of an object.
@ -191,17 +191,22 @@ impl Reflect {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get
pub(crate) fn get(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let undefined = Value::undefined();
// 1. If Type(target) is not Object, throw a TypeError exception.
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
// 2. Let key be ? ToPropertyKey(propertyKey).
let key = args.get(1).unwrap_or(&undefined).to_property_key(context)?;
// 3. If receiver is not present, then
let receiver = if let Some(receiver) = args.get(2).cloned() {
receiver
} else {
// 3.a. Set receiver to target.
target.clone().into()
};
target.get(&key, receiver, context)
// 4. Return ? target.[[Get]](key, receiver).
target.__get__(&key, receiver, context)
}
/// Gets a property of an object.
@ -243,7 +248,7 @@ impl Reflect {
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
Ok(target.get_prototype_of())
Ok(target.__get_prototype_of__())
}
/// Returns `true` if the object has the property, `false` otherwise.
@ -263,7 +268,7 @@ impl Reflect {
.get(1)
.unwrap_or(&Value::undefined())
.to_property_key(context)?;
Ok(target.has_property(&key).into())
Ok(target.__has_property__(&key).into())
}
/// Returns `true` if the object is extensible, `false` otherwise.
@ -279,7 +284,7 @@ impl Reflect {
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
Ok(target.is_extensible().into())
Ok(target.__is_extensible__().into())
}
/// Returns an array of object own property keys.
@ -332,7 +337,7 @@ impl Reflect {
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
Ok(target.prevent_extensions().into())
Ok(target.__prevent_extensions__().into())
}
/// Sets a property of an object.
@ -345,7 +350,7 @@ impl Reflect {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set
pub(crate) fn set(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let undefined = Value::undefined();
let mut target = args
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
@ -356,7 +361,9 @@ impl Reflect {
} else {
target.clone().into()
};
Ok(target.set(key, value.clone(), receiver, context)?.into())
Ok(target
.__set__(key, value.clone(), receiver, context)?
.into())
}
/// Sets the prototype of an object.
@ -381,6 +388,6 @@ impl Reflect {
if !proto.is_null() && !proto.is_object() {
return context.throw_type_error("proto must be an object or null");
}
Ok(target.set_prototype_of(proto.clone()).into())
Ok(target.__set_prototype_of__(proto.clone()).into())
}
}

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

@ -15,7 +15,7 @@ use crate::{
builtins::{array::Array, BuiltIn},
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, FunctionBuilder, GcObject, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor},
property::Attribute,
symbol::WellKnownSymbols,
value::{IntegerOrInfinity, Value},
BoaProfiler, Context, JsString, Result,
@ -197,7 +197,7 @@ impl RegExp {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), ctx)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), ctx)
.map(|o| o.as_object())
.transpose()
})
@ -454,45 +454,46 @@ impl RegExp {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags
/// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
pub(crate) fn get_flags(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
// 1. Let R be the this value.
// 2. If Type(R) is not Object, throw a TypeError exception.
if let Some(object) = this.as_object() {
// 3. Let result be the empty String.
let mut result = String::new();
if object
.get(&"global".into(), this.clone(), context)?
.to_boolean()
{
// 4. Let global be ! ToBoolean(? Get(R, "global")).
// 5. If global is true, append the code unit 0x0067 (LATIN SMALL LETTER G) as the last code unit of result.
if object.get("global", context)?.to_boolean() {
result.push('g');
}
if object
.get(&"ignoreCase".into(), this.clone(), context)?
.to_boolean()
{
// 6. Let ignoreCase be ! ToBoolean(? Get(R, "ignoreCase")).
// 7. If ignoreCase is true, append the code unit 0x0069 (LATIN SMALL LETTER I) as the last code unit of result.
if object.get("ignoreCase", context)?.to_boolean() {
result.push('i');
}
if object
.get(&"multiline".into(), this.clone(), context)?
.to_boolean()
{
// 8. Let multiline be ! ToBoolean(? Get(R, "multiline")).
// 9. If multiline is true, append the code unit 0x006D (LATIN SMALL LETTER M) as the last code unit of result.
if object.get("multiline", context)?.to_boolean() {
result.push('m');
}
if object
.get(&"dotAll".into(), this.clone(), context)?
.to_boolean()
{
// 10. Let dotAll be ! ToBoolean(? Get(R, "dotAll")).
// 11. If dotAll is true, append the code unit 0x0073 (LATIN SMALL LETTER S) as the last code unit of result.
if object.get("dotAll", context)?.to_boolean() {
result.push('s');
}
if object
.get(&"unicode".into(), this.clone(), context)?
.to_boolean()
{
// 12. Let unicode be ! ToBoolean(? Get(R, "unicode")).
// 13. If unicode is true, append the code unit 0x0075 (LATIN SMALL LETTER U) as the last code unit of result.
if object.get("unicode", context)?.to_boolean() {
result.push('u');
}
if object
.get(&"sticky".into(), this.clone(), context)?
.to_boolean()
{
// 14. Let sticky be ! ToBoolean(? Get(R, "sticky")).
// 15. If sticky is true, append the code unit 0x0079 (LATIN SMALL LETTER Y) as the last code unit of result.
if object.get("sticky", context)?.to_boolean() {
result.push('y');
}
// 16. Return result.
return Ok(result.into());
}
@ -819,24 +820,21 @@ impl RegExp {
}
// 16. Let n be the number of elements in r's captures List. (This is the same value as 22.2.2.1's NcapturingParens.)
let n = match_value.captures.len() as u64;
// 17. Assert: n < 23^2 - 1.
let n: u32 = match_value.captures.len() as u32;
assert!(n < 23u64.pow(2) - 1);
// 18. Let A be ! ArrayCreate(n + 1).
// 19. Assert: The mathematical value of A's "length" property is n + 1.
let a = Array::array_create(n + 1, None, context);
let a = Array::array_create(n + 1, None, context)?;
// 20. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)).
a.set_property(
"index",
DataDescriptor::new(match_value.start(), Attribute::all()),
);
a.create_data_property_or_throw("index", match_value.start(), context)
.unwrap();
// 21. Perform ! CreateDataPropertyOrThrow(A, "input", S).
a.set_property(
"input",
DataDescriptor::new(input.clone(), Attribute::all()),
);
a.create_data_property_or_throw("input", input.clone(), context)
.unwrap();
// 22. Let matchedSubstr be the substring of S from lastIndex to e.
let matched_substr = if let Some(s) = input.get(match_value.range()) {
@ -846,7 +844,8 @@ impl RegExp {
};
// 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr).
a.set_property("0", DataDescriptor::new(matched_substr, Attribute::all()));
a.create_data_property_or_throw(0, matched_substr, context)
.unwrap();
// TODO: named capture groups
// 24. If R contains any GroupName, then
@ -856,7 +855,8 @@ impl RegExp {
let groups = Value::undefined();
// 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
a.set_property("groups", DataDescriptor::new(groups, Attribute::all()));
a.create_data_property_or_throw("groups", groups, context)
.unwrap();
// 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do
for i in 1..=n {
@ -878,7 +878,8 @@ impl RegExp {
};
// e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue).
a.set_property(i, DataDescriptor::new(captured_value, Attribute::all()));
a.create_data_property_or_throw(i, captured_value, context)
.unwrap();
// TODO: named capture groups
// f. If the ith capture of R was defined with a GroupName, then
@ -887,7 +888,7 @@ impl RegExp {
}
// 28. Return A.
Ok(a)
Ok(a.into())
}
/// `RegExp.prototype[ @@match ]( string )`
@ -933,7 +934,7 @@ impl RegExp {
this.set_field("lastIndex", Value::from(0), true, context)?;
// d. Let A be ! ArrayCreate(0).
let a = Array::array_create(0, None, context);
let a = Array::array_create(0, None, context).unwrap();
// e. Let n be 0.
let mut n = 0;
@ -951,14 +952,15 @@ impl RegExp {
if n == 0 {
return Ok(Value::null());
} else {
return Ok(a);
return Ok(a.into());
}
} else {
// 1. Let matchStr be ? ToString(? Get(result, "0")).
let match_str = result.get_field("0", context)?.to_string(context)?;
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr).
Array::add_to_array_object(&a, &[match_str.clone().into()], context)?;
a.create_data_property_or_throw(n, match_str.clone(), context)
.unwrap();
// 3. If matchStr is the empty String, then
if match_str.is_empty() {
@ -1412,7 +1414,7 @@ impl RegExp {
RegExp::constructor(&constructor, &[this.clone(), new_flags.into()], context)?;
// 11. Let A be ! ArrayCreate(0).
let a = Array::array_create(0, None, context);
let a = Array::array_create(0, None, context).unwrap();
// 12. Let lengthA be 0.
let mut length_a = 0;
@ -1427,7 +1429,7 @@ impl RegExp {
// 14. If lim is 0, return A.
if lim == 0 {
return Ok(a);
return Ok(a.into());
}
// 15. Let size be the length of S.
@ -1440,14 +1442,15 @@ impl RegExp {
// b. If z is not null, return A.
if !result.is_null() {
return Ok(a);
return Ok(a.into());
}
// c. Perform ! CreateDataPropertyOrThrow(A, "0", S).
Array::add_to_array_object(&a, &[Value::from(arg_str)], context)?;
a.create_data_property_or_throw(0, arg_str, context)
.unwrap();
// d. Return A.
return Ok(a);
return Ok(a.into());
}
// 17. Let p be 0.
@ -1488,14 +1491,15 @@ impl RegExp {
let arg_str_substring: String = arg_str.chars().skip(p).take(q - p).collect();
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
Array::add_to_array_object(&a, &[Value::from(arg_str_substring)], context)?;
a.create_data_property_or_throw(length_a, arg_str_substring, context)
.unwrap();
// 3. Set lengthA to lengthA + 1.
length_a += 1;
// 4. If lengthA = lim, return A.
if length_a == lim {
return Ok(a);
return Ok(a.into());
}
// 5. Set p to e.
@ -1519,14 +1523,15 @@ impl RegExp {
let next_capture = result.get_field(i.to_string(), context)?;
// b. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), nextCapture).
Array::add_to_array_object(&a, &[next_capture], context)?;
a.create_data_property_or_throw(length_a, next_capture, context)
.unwrap();
// d. Set lengthA to lengthA + 1.
length_a += 1;
// e. If lengthA = lim, return A.
if length_a == lim {
return Ok(a);
return Ok(a.into());
}
}
@ -1540,10 +1545,11 @@ impl RegExp {
let arg_str_substring: String = arg_str.chars().skip(p).take(size - p).collect();
// 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
Array::add_to_array_object(&a, &[Value::from(arg_str_substring)], context)?;
a.create_data_property_or_throw(length_a, arg_str_substring, context)
.unwrap();
// 22. Return A.
Ok(a)
Ok(a.into())
}
}

2
boa/src/builtins/regexp/regexp_string_iterator.rs

@ -154,7 +154,7 @@ impl RegExpStringIterator {
let _timer = BoaProfiler::global().start_event("RegExp String Iterator", "init");
// Create prototype
let mut result = context.construct_object();
let result = context.construct_object();
make_builtin_fn(Self::next, "next", &result, 0, context);
result.set_prototype_instance(iterator_prototype);

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

@ -128,14 +128,14 @@ impl Set {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or(set_prototype);
let mut obj = context.construct_object();
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let set = Value::from(obj);

2
boa/src/builtins/set/set_iterator.rs

@ -139,7 +139,7 @@ impl SetIterator {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let mut set_iterator = context.construct_object();
let set_iterator = context.construct_object();
make_builtin_fn(Self::next, "next", &set_iterator, 0, context);
set_iterator.set_prototype_instance(iterator_prototype);

32
boa/src/builtins/string/mod.rs

@ -178,7 +178,7 @@ impl String {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
obj.__get__(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
@ -1156,7 +1156,7 @@ impl String {
/// `String.prototype.split ( separator, limit )`
///
/// The split() method divides a String into an ordered list of substrings, puts these substrings into an array, and returns the array.
/// The division is done by searching for a pattern; where the pattern is provided as the first parameter in the method's call.
/// The division is done by searching for a pattern; where the pattern is provided as the first parameter in the method's call.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -1189,7 +1189,7 @@ impl String {
let this_str = this.to_string(context)?;
// 4. Let A be ! ArrayCreate(0).
let a = Array::array_create(0, None, context);
let a = Array::array_create(0, None, context).unwrap();
// 5. Let lengthA be 0.
let mut length_a = 0;
@ -1206,16 +1206,17 @@ impl String {
// 8. If lim = 0, return A.
if lim == 0 {
return Ok(a);
return Ok(a.into());
}
// 9. If separator is undefined, then
if separator.is_undefined() {
// a. Perform ! CreateDataPropertyOrThrow(A, "0", S).
Array::add_to_array_object(&a, &[Value::from(this_str)], context)?;
a.create_data_property_or_throw(0, this_str, context)
.unwrap();
// b. Return A.
return Ok(a);
return Ok(a.into());
}
// 10. Let s be the length of S.
@ -1226,11 +1227,12 @@ impl String {
// a. If R is not the empty String, then
if !separator_str.is_empty() {
// i. Perform ! CreateDataPropertyOrThrow(A, "0", S).
Array::add_to_array_object(&a, &[Value::from(this_str)], context)?;
a.create_data_property_or_throw(0, this_str, context)
.unwrap();
}
// b. Return A.
return Ok(a);
return Ok(a.into());
}
// 12. Let p be 0.
@ -1264,18 +1266,15 @@ impl String {
);
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
Array::add_to_array_object(
&a,
&[Value::from(this_str_substring)],
context,
)?;
a.create_data_property_or_throw(length_a, this_str_substring, context)
.unwrap();
// 3. Set lengthA to lengthA + 1.
length_a += 1;
// 4. If lengthA = lim, return A.
if length_a == lim {
return Ok(a);
return Ok(a.into());
}
// 5. Set p to e.
@ -1298,10 +1297,11 @@ impl String {
);
// 16. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
Array::add_to_array_object(&a, &[Value::from(this_str_substring)], context)?;
a.create_data_property_or_throw(length_a, this_str_substring, context)
.unwrap();
// 17. Return A.
Ok(a)
Ok(a.into())
}
/// String.prototype.valueOf()

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

@ -74,7 +74,7 @@ impl StringIterator {
let _timer = BoaProfiler::global().start_event("String Iterator", "init");
// Create prototype
let mut array_iterator = context.construct_object();
let array_iterator = context.construct_object();
make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
array_iterator.set_prototype_instance(iterator_prototype);

2
boa/src/context.rs

@ -562,7 +562,7 @@ impl Context {
#[inline]
pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool {
if let Some(obj) = obj.as_object() {
obj.has_property(key)
obj.__has_property__(key)
} else {
false
}

158
boa/src/object/gcobject.rs

@ -135,7 +135,7 @@ impl GcObject {
let body = if let Some(function) = self.borrow().as_function() {
if construct && !function.is_constructable() {
let name = self
.get(&"name".into(), self.clone().into(), context)?
.__get__(&"name".into(), self.clone().into(), context)?
.display()
.to_string();
return context.throw_type_error(format!("{} is not a constructor", name));
@ -161,7 +161,7 @@ impl GcObject {
// prototype as prototype for the new object
// see <https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor>
// see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>
let proto = this_target.as_object().unwrap().get(
let proto = this_target.as_object().unwrap().__get__(
&PROTOTYPE.into(),
this_target.clone(),
context,
@ -458,50 +458,61 @@ impl GcObject {
///
/// Panics if the object is currently mutably borrowed.
pub fn to_property_descriptor(&self, context: &mut Context) -> Result<PropertyDescriptor> {
// 1. If Type(Obj) is not Object, throw a TypeError exception.
// 2. Let desc be a new Property Descriptor that initially has no fields.
let mut attribute = Attribute::empty();
let enumerable_key = PropertyKey::from("enumerable");
if self.has_property(&enumerable_key)
&& self
.get(&enumerable_key, self.clone().into(), context)?
.to_boolean()
{
// 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
let has_enumerable = self.has_property("enumerable", context)?;
// 4. If hasEnumerable is true, then
// a. Let enumerable be ! ToBoolean(? Get(Obj, "enumerable")).
// b. Set desc.[[Enumerable]] to enumerable.
if has_enumerable && self.get("enumerable", context)?.to_boolean() {
attribute |= Attribute::ENUMERABLE;
}
let configurable_key = PropertyKey::from("configurable");
if self.has_property(&configurable_key)
&& self
.get(&configurable_key, self.clone().into(), context)?
.to_boolean()
{
// 5. Let hasConfigurable be ? HasProperty(Obj, "configurable").
let has_configurable = self.has_property("configurable", context)?;
// 6. If hasConfigurable is true, then
// a. Let configurable be ! ToBoolean(? Get(Obj, "configurable")).
// b. Set desc.[[Configurable]] to configurable.
if has_configurable && self.get("configurable", context)?.to_boolean() {
attribute |= Attribute::CONFIGURABLE;
}
let mut value = None;
let value_key = PropertyKey::from("value");
if self.has_property(&value_key) {
value = Some(self.get(&value_key, self.clone().into(), context)?);
// 7. Let hasValue be ? HasProperty(Obj, "value").
let has_value = self.has_property("value", context)?;
// 8. If hasValue is true, then
if has_value {
// a. Let value be ? Get(Obj, "value").
// b. Set desc.[[Value]] to value.
value = Some(self.get("value", context)?);
}
let mut has_writable = false;
let writable_key = PropertyKey::from("writable");
if self.has_property(&writable_key) {
has_writable = true;
if self
.get(&writable_key, self.clone().into(), context)?
.to_boolean()
{
// 9. Let hasWritable be ? HasProperty(Obj, ).
let has_writable = self.has_property("writable", context)?;
// 10. If hasWritable is true, then
if has_writable {
// a. Let writable be ! ToBoolean(? Get(Obj, "writable")).
if self.get("writable", context)?.to_boolean() {
// b. Set desc.[[Writable]] to writable.
attribute |= Attribute::WRITABLE;
}
}
// 11. Let hasGet be ? HasProperty(Obj, "get").
let has_get = self.has_property("get", context)?;
// 12. If hasGet is true, then
let mut get = None;
let get_key = PropertyKey::from("get");
if self.has_property(&get_key) {
let getter = self.get(&get_key, self.clone().into(), context)?;
if has_get {
// a. Let getter be ? Get(Obj, "get").
let getter = self.get("get", context)?;
// b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception.
match getter {
Value::Object(ref object) if object.is_callable() => {
// c. Set desc.[[Get]] to getter.
get = Some(object.clone());
}
_ => {
@ -512,12 +523,17 @@ impl GcObject {
}
}
// 13. Let hasSet be ? HasProperty(Obj, "set").
let has_set = self.has_property("set", context)?;
// 14. If hasSet is true, then
let mut set = None;
let set_key = PropertyKey::from("set");
if self.has_property(&set_key) {
let setter = self.get(&set_key, self.clone().into(), context)?;
if has_set {
// 14.a. Let setter be ? Get(Obj, "set").
let setter = self.get("set", context)?;
// 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception.
match setter {
Value::Object(ref object) if object.is_callable() => {
// 14.c. Set desc.[[Set]] to setter.
set = Some(object.clone());
}
_ => {
@ -528,7 +544,10 @@ impl GcObject {
};
}
// 15. If desc.[[Get]] is present or desc.[[Set]] is present, then
// 16. Return desc.
if get.is_some() || set.is_some() {
// 15.a. If desc.[[Value]] is present or desc.[[Writable]] is present, throw a TypeError exception.
if value.is_some() || has_writable {
return Err(context.construct_type_error("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"));
}
@ -614,7 +633,7 @@ impl GcObject {
/// or if th prototype is not an object or undefined.
#[inline]
#[track_caller]
pub fn set_prototype_instance(&mut self, prototype: Value) -> bool {
pub fn set_prototype_instance(&self, prototype: Value) -> bool {
self.borrow_mut().set_prototype_instance(prototype)
}
@ -772,13 +791,17 @@ impl GcObject {
where
K: Into<PropertyKey>,
{
let key = key.into();
let value = self.get(&key, self.clone().into(), context)?;
// 1. Assert: IsPropertyKey(P) is true.
// 2. Let func be ? GetV(V, P).
let value = self.get(key, context)?;
// 3. If func is either undefined or null, return undefined.
if value.is_null_or_undefined() {
return Ok(None);
}
// 4. If IsCallable(func) is false, throw a TypeError exception.
// 5. Return func.
match value.as_object() {
Some(object) if object.is_callable() => Ok(Some(object)),
_ => Err(context
@ -798,25 +821,31 @@ impl GcObject {
context: &mut Context,
value: &Value,
) -> Result<bool> {
// 1. If IsCallable(C) is false, return false.
if !self.is_callable() {
return Ok(false);
}
// TODO: If C has a [[BoundTargetFunction]] internal slot, then
// Let BC be C.[[BoundTargetFunction]].
// Return ? InstanceofOperator(O, BC).
// TODO: 2. If C has a [[BoundTargetFunction]] internal slot, then
// a. Let BC be C.[[BoundTargetFunction]].
// b. Return ? InstanceofOperator(O, BC).
// 3. If Type(O) is not Object, return false.
if let Some(object) = value.as_object() {
if let Some(prototype) = self
.get(&"prototype".into(), self.clone().into(), context)?
.as_object()
{
let mut object = object.get_prototype_of();
// 4. Let P be ? Get(C, "prototype").
// 5. If Type(P) is not Object, throw a TypeError exception.
if let Some(prototype) = self.get("prototype", context)?.as_object() {
// 6. Repeat,
// a. Set O to ? O.[[GetPrototypeOf]]().
// b. If O is null, return false.
let mut object = object.__get_prototype_of__();
while let Some(object_prototype) = object.as_object() {
// c. If SameValue(P, O) is true, return true.
if GcObject::equals(&prototype, &object_prototype) {
return Ok(true);
}
object = object_prototype.get_prototype_of();
// a. Set O to ? O.[[GetPrototypeOf]]().
object = object_prototype.__get_prototype_of__();
}
Ok(false)
@ -828,43 +857,6 @@ impl GcObject {
Ok(false)
}
}
#[inline]
#[track_caller]
pub fn has_own_property<K>(&self, key: K) -> bool
where
K: Into<PropertyKey>,
{
let key = key.into();
self.get_own_property(&key).is_some()
}
/// Defines the property or throws a `TypeError` if the operation fails.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
#[inline]
pub(crate) fn define_property_or_throw<K, P>(
&mut self,
key: K,
desc: P,
context: &mut Context,
) -> Result<()>
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
let key = key.into();
let desc = desc.into();
let success = self.define_own_property(key.clone(), desc, context)?;
if !success {
Err(context.construct_type_error(format!("Cannot redefine property: {}", key)))
} else {
Ok(())
}
}
/// `7.3.22 SpeciesConstructor ( O, defaultConstructor )`
///
@ -884,11 +876,7 @@ impl GcObject {
// 1. Assert: Type(O) is Object.
// 2. Let C be ? Get(O, "constructor").
let c = self.clone().get(
&PropertyKey::from("constructor"),
self.clone().into(),
context,
)?;
let c = self.clone().get("constructor", context)?;
// 3. If C is undefined, return defaultConstructor.
if c.is_undefined() {

379
boa/src/object/internal_methods.rs

@ -18,14 +18,226 @@ impl GcObject {
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
/// [spec]: https://tc39.es/ecma262/#sec-hasproperty
// NOTE: for now context is not used but it will in the future.
#[inline]
pub fn has_property(&self, key: &PropertyKey) -> bool {
let prop = self.get_own_property(key);
pub fn has_property<K>(&self, key: K, _context: &mut Context) -> Result<bool>
where
K: Into<PropertyKey>,
{
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Return ? O.[[HasProperty]](P).
Ok(self.__has_property__(&key.into()))
}
/// Check if it is extensible.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isextensible-o
// NOTE: for now context is not used but it will in the future.
#[inline]
pub fn is_extensible(&self, _context: &mut Context) -> Result<bool> {
// 1. Assert: Type(O) is Object.
// 2. Return ? O.[[IsExtensible]]().
Ok(self.__is_extensible__())
}
/// Delete property, if deleted return `true`.
#[inline]
pub fn delete<K>(&self, key: K) -> bool
where
K: Into<PropertyKey>,
{
self.__delete__(&key.into())
}
/// Defines the property or throws a `TypeError` if the operation fails.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
#[inline]
pub fn delete_property_or_throw<K>(&self, key: K, context: &mut Context) -> Result<bool>
where
K: Into<PropertyKey>,
{
let key = key.into();
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Let success be ? O.[[Delete]](P).
let success = self.__delete__(&key);
// 4. If success is false, throw a TypeError exception.
if !success {
return Err(context.construct_type_error(format!("cannot delete property: {}", key)));
}
// 5. Return success.
Ok(success)
}
/// Check if object has an own property.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-hasownproperty
#[inline]
pub fn has_own_property<K>(&self, key: K, _context: &mut Context) -> Result<bool>
where
K: Into<PropertyKey>,
{
let key = key.into();
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Let desc be ? O.[[GetOwnProperty]](P).
let desc = self.__get_own_property__(&key);
// 4. If desc is undefined, return false.
// 5. Return true.
Ok(desc.is_some())
}
/// Get property from object or throw.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-get-o-p
#[inline]
pub fn get<K>(&self, key: K, context: &mut Context) -> Result<Value>
where
K: Into<PropertyKey>,
{
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Return ? O.[[Get]](P, O).
self.__get__(&key.into(), self.clone().into(), context)
}
/// set property of object or throw if bool flag is passed.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw
#[inline]
pub fn set<K, V>(&self, key: K, value: V, throw: bool, context: &mut Context) -> Result<bool>
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let key = key.into();
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Assert: Type(Throw) is Boolean.
// 4. Let success be ? O.[[Set]](P, V, O).
let success = self.__set__(key.clone(), value.into(), self.clone().into(), context)?;
// 5. If success is false and Throw is true, throw a TypeError exception.
if !success && throw {
return Err(
context.construct_type_error(format!("cannot set non-writable property: {}", key))
);
}
// 6. Return success.
Ok(success)
}
/// Define property or throw.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
#[inline]
pub fn define_property_or_throw<K, P>(
&mut self,
key: K,
desc: P,
context: &mut Context,
) -> Result<bool>
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
let key = key.into();
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Let success be ? O.[[DefineOwnProperty]](P, desc).
let success = self.__define_own_property__(key.clone(), desc.into(), context)?;
// 4. If success is false, throw a TypeError exception.
if !success {
return Err(context.construct_type_error(format!("cannot redefine property: {}", key)));
}
// 5. Return success.
Ok(success)
}
/// Create data property
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow
pub fn create_data_property<K, V>(
&self,
key: K,
value: V,
context: &mut Context,
) -> Result<bool>
where
K: Into<PropertyKey>,
V: Into<Value>,
{
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
let new_desc = DataDescriptor::new(
value,
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
);
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
self.__define_own_property__(key.into(), new_desc.into(), context)
}
/// Create data property or throw
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow
pub fn create_data_property_or_throw<K, V>(
&self,
key: K,
value: V,
context: &mut Context,
) -> Result<bool>
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let key = key.into();
// 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true.
// 3. Let success be ? CreateDataProperty(O, P, V).
let success = self.create_data_property(key.clone(), value, context)?;
// 4. If success is false, throw a TypeError exception.
if !success {
return Err(context.construct_type_error(format!("cannot redefine property: {}", key)));
}
// 5. Return success.
Ok(success)
}
/// `[[hasProperty]]`
#[inline]
pub(crate) fn __has_property__(&self, key: &PropertyKey) -> bool {
let prop = self.__get_own_property__(key);
if prop.is_none() {
let parent = self.get_prototype_of();
let parent = self.__get_prototype_of__();
return if let Value::Object(ref object) = parent {
object.has_property(key)
object.__has_property__(key)
} else {
false
};
@ -40,7 +252,7 @@ impl GcObject {
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
#[inline]
pub fn is_extensible(&self) -> bool {
pub(crate) fn __is_extensible__(&self) -> bool {
self.borrow().extensible
}
@ -51,15 +263,15 @@ impl GcObject {
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
#[inline]
pub fn prevent_extensions(&mut self) -> bool {
pub fn __prevent_extensions__(&mut self) -> bool {
self.borrow_mut().extensible = false;
true
}
/// Delete property.
#[inline]
pub fn delete(&mut self, key: &PropertyKey) -> bool {
match self.get_own_property(key) {
pub(crate) fn __delete__(&self, key: &PropertyKey) -> bool {
match self.__get_own_property__(key) {
Some(desc) if desc.configurable() => {
self.remove(&key);
true
@ -70,13 +282,17 @@ impl GcObject {
}
/// `[[Get]]`
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver>
pub fn get(&self, key: &PropertyKey, receiver: Value, context: &mut Context) -> Result<Value> {
match self.get_own_property(key) {
pub fn __get__(
&self,
key: &PropertyKey,
receiver: Value,
context: &mut Context,
) -> Result<Value> {
match self.__get_own_property__(key) {
None => {
// parent will either be null or an Object
if let Some(parent) = self.get_prototype_of().as_object() {
Ok(parent.get(key, receiver, context)?)
if let Some(parent) = self.__get_prototype_of__().as_object() {
Ok(parent.__get__(key, receiver, context)?)
} else {
Ok(Value::undefined())
}
@ -92,9 +308,8 @@ impl GcObject {
}
/// `[[Set]]`
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
pub fn set(
&mut self,
pub fn __set__(
&self,
key: PropertyKey,
value: Value,
receiver: Value,
@ -103,10 +318,10 @@ impl GcObject {
let _timer = BoaProfiler::global().start_event("Object::set", "object");
// Fetch property key
let own_desc = if let Some(desc) = self.get_own_property(&key) {
let own_desc = if let Some(desc) = self.__get_own_property__(&key) {
desc
} else if let Some(ref mut parent) = self.get_prototype_of().as_object() {
return parent.set(key, value, receiver, context);
} else if let Some(ref mut parent) = self.__get_prototype_of__().as_object() {
return parent.__set__(key, value, receiver, context);
} else {
DataDescriptor::new(Value::undefined(), Attribute::all()).into()
};
@ -117,14 +332,14 @@ impl GcObject {
return Ok(false);
}
if let Some(ref mut receiver) = receiver.as_object() {
if let Some(ref existing_desc) = receiver.get_own_property(&key) {
if let Some(ref existing_desc) = receiver.__get_own_property__(&key) {
match existing_desc {
PropertyDescriptor::Accessor(_) => Ok(false),
PropertyDescriptor::Data(existing_data_desc) => {
if !existing_data_desc.writable() {
return Ok(false);
}
receiver.define_own_property(
receiver.__define_own_property__(
key,
DataDescriptor::new(value, existing_data_desc.attributes())
.into(),
@ -133,7 +348,7 @@ impl GcObject {
}
}
} else {
receiver.define_own_property(
receiver.__define_own_property__(
key,
DataDescriptor::new(value, Attribute::all()).into(),
context,
@ -151,21 +366,13 @@ impl GcObject {
}
}
/// Define an own property.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
pub fn define_own_property<K>(
&mut self,
key: K,
/// `[[defineOwnProperty]]`
pub fn __define_own_property__(
&self,
key: PropertyKey,
desc: PropertyDescriptor,
context: &mut Context,
) -> Result<bool>
where
K: Into<PropertyKey>,
{
) -> Result<bool> {
if self.is_array() {
self.array_define_own_property(key, desc, context)
} else {
@ -179,16 +386,12 @@ impl GcObject {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty
pub fn ordinary_define_own_property<K>(&mut self, key: K, desc: PropertyDescriptor) -> bool
where
K: Into<PropertyKey>,
{
pub fn ordinary_define_own_property(&self, key: PropertyKey, desc: PropertyDescriptor) -> bool {
let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object");
let key = key.into();
let extensible = self.is_extensible();
let extensible = self.__is_extensible__();
let current = if let Some(desc) = self.get_own_property(&key) {
let current = if let Some(desc) = self.__get_own_property__(&key) {
desc
} else {
if !extensible {
@ -294,25 +497,21 @@ impl GcObject {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc
fn array_define_own_property<K>(
&mut self,
key: K,
fn array_define_own_property(
&self,
key: PropertyKey,
desc: PropertyDescriptor,
context: &mut Context,
) -> Result<bool>
where
K: Into<PropertyKey>,
{
let key = key.into();
) -> Result<bool> {
match key {
PropertyKey::String(ref s) if s == "length" => {
match desc {
PropertyDescriptor::Accessor(_) => {
return Ok(self.ordinary_define_own_property("length", desc))
return Ok(self.ordinary_define_own_property("length".into(), desc))
}
PropertyDescriptor::Data(ref d) => {
if d.value().is_undefined() {
return Ok(self.ordinary_define_own_property("length", desc));
return Ok(self.ordinary_define_own_property("length".into(), desc));
}
let new_len = d.value().to_u32(context)?;
let number_len = d.value().to_number(context)?;
@ -322,11 +521,13 @@ impl GcObject {
}
let mut new_len_desc =
PropertyDescriptor::Data(DataDescriptor::new(new_len, d.attributes()));
let old_len_desc = self.get_own_property(&"length".into()).unwrap();
let old_len_desc = self.__get_own_property__(&"length".into()).unwrap();
let old_len_desc = old_len_desc.as_data_descriptor().unwrap();
let old_len = old_len_desc.value();
if new_len >= old_len.to_u32(context)? {
return Ok(self.ordinary_define_own_property("length", new_len_desc));
return Ok(
self.ordinary_define_own_property("length".into(), new_len_desc)
);
}
if !old_len_desc.writable() {
return Ok(false);
@ -342,7 +543,8 @@ impl GcObject {
));
false
};
if !self.ordinary_define_own_property("length", new_len_desc.clone()) {
if !self.ordinary_define_own_property("length".into(), new_len_desc.clone())
{
return Ok(false);
}
let keys_to_delete = {
@ -356,7 +558,7 @@ impl GcObject {
keys
};
for key in keys_to_delete.into_iter().rev() {
if !self.delete(&key.into()) {
if !self.__delete__(&key.into()) {
let mut new_len_desc_attribute = new_len_desc.attributes();
if !new_writable {
new_len_desc_attribute.set_writable(false);
@ -365,7 +567,7 @@ impl GcObject {
key + 1,
new_len_desc_attribute,
));
self.ordinary_define_own_property("length", new_len_desc);
self.ordinary_define_own_property("length".into(), new_len_desc);
return Ok(false);
}
}
@ -376,14 +578,14 @@ impl GcObject {
new_len,
new_desc_attr,
));
self.ordinary_define_own_property("length", new_desc);
self.ordinary_define_own_property("length".into(), new_desc);
}
}
}
Ok(true)
}
PropertyKey::Index(index) => {
let old_len_desc = self.get_own_property(&"length".into()).unwrap();
let old_len_desc = self.__get_own_property__(&"length".into()).unwrap();
let old_len_data_desc = old_len_desc.as_data_descriptor().unwrap();
let old_len = old_len_data_desc.value().to_u32(context)?;
if index >= old_len && !old_len_data_desc.writable() {
@ -395,7 +597,7 @@ impl GcObject {
index + 1,
old_len_data_desc.attributes(),
));
self.ordinary_define_own_property("length", desc);
self.ordinary_define_own_property("length".into(), desc);
}
Ok(true)
} else {
@ -409,7 +611,7 @@ impl GcObject {
/// Gets own property of 'Object'
///
#[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 object = self.borrow();
@ -516,9 +718,9 @@ impl GcObject {
let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new();
for next_key in keys {
if let Some(prop_desc) = props.get_own_property(&next_key) {
if let Some(prop_desc) = props.__get_own_property__(&next_key) {
if prop_desc.enumerable() {
let desc_obj = props.get(&next_key, props.clone().into(), context)?;
let desc_obj = props.__get__(&next_key, props.clone().into(), context)?;
let desc = desc_obj.to_property_descriptor(context)?;
descriptors.push((next_key, desc));
}
@ -526,7 +728,7 @@ impl GcObject {
}
for (p, d) in descriptors {
self.define_own_property(p, d, context)?;
self.__define_own_property__(p, d, context)?;
}
Ok(())
@ -544,13 +746,13 @@ impl GcObject {
/// [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 {
pub fn __set_prototype_of__(&mut self, val: Value) -> bool {
debug_assert!(val.is_object() || val.is_null());
let current = self.get_prototype_of();
let current = self.__get_prototype_of__();
if Value::same_value(&current, &val) {
return true;
}
if !self.is_extensible() {
if !self.__is_extensible__() {
return false;
}
let mut p = val.clone();
@ -564,7 +766,7 @@ impl GcObject {
let prototype = p
.as_object()
.expect("prototype should be null or object")
.get_prototype_of();
.__get_prototype_of__();
p = prototype;
}
}
@ -582,14 +784,14 @@ impl GcObject {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
#[inline]
#[track_caller]
pub fn get_prototype_of(&self) -> Value {
pub fn __get_prototype_of__(&self) -> Value {
self.borrow().prototype.clone()
}
/// Helper function for property insertion.
#[inline]
#[track_caller]
pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> Option<PropertyDescriptor>
pub(crate) fn insert<K, P>(&self, key: K, property: P) -> Option<PropertyDescriptor>
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
@ -600,7 +802,7 @@ impl GcObject {
/// Helper function for property removal.
#[inline]
#[track_caller]
pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
pub(crate) fn remove(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {
self.borrow_mut().remove(key)
}
@ -610,7 +812,7 @@ impl GcObject {
/// with that field, otherwise None is returned.
#[inline]
pub fn insert_property<K, V>(
&mut self,
&self,
key: K,
value: V,
attribute: Attribute,
@ -663,6 +865,7 @@ impl GcObject {
element_types: &[Type],
context: &mut Context,
) -> Result<Vec<Value>> {
// 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».
let types = if element_types.is_empty() {
&[
Type::Undefined,
@ -672,24 +875,44 @@ impl GcObject {
Type::Symbol,
Type::Number,
Type::BigInt,
Type::Symbol,
Type::Object,
]
} else {
element_types
};
let len = self
.get(&"length".into(), self.clone().into(), context)?
.to_length(context)?;
// TODO: 2. If Type(obj) is not Object, throw a TypeError exception.
// 3. Let len be ? LengthOfArrayLike(obj).
let len = self.length_of_array_like(context)?;
// 4. Let list be a new empty List.
let mut list = Vec::with_capacity(len);
// 5. Let index be 0.
// 6. Repeat, while index < len,
for index in 0..len {
let next = self.get(&index.into(), self.clone().into(), context)?;
// a. Let indexName be ! ToString(𝔽(index)).
// b. Let next be ? Get(obj, indexName).
let next = self.get(index, context)?;
// c. If Type(next) is not an element of elementTypes, throw a TypeError exception.
if !types.contains(&next.get_type()) {
return Err(context.construct_type_error("bad type"));
}
// d. Append next as the last element of list.
list.push(next.clone());
// e. Set index to index + 1.
}
// 7. Return list.
Ok(list)
}
pub(crate) fn length_of_array_like(&self, context: &mut Context) -> Result<usize> {
// 1. Assert: Type(obj) is Object.
// 2. Return ℝ(? ToLength(? Get(obj, "length"))).
self.get("length", context)?.to_length(context)
}
}
impl Object {

10
boa/src/property/mod.rs

@ -452,6 +452,16 @@ impl From<usize> for PropertyKey {
}
}
impl From<u64> for PropertyKey {
fn from(value: u64) -> Self {
if let Ok(index) = u32::try_from(value) {
PropertyKey::Index(index)
} else {
PropertyKey::String(JsString::from(value.to_string()))
}
}
}
impl From<isize> for PropertyKey {
fn from(value: isize) -> Self {
if let Ok(index) = u32::try_from(value) {

4
boa/src/syntax/ast/node/operator/unary_op/mod.rs

@ -94,14 +94,14 @@ impl Executable for UnaryOp {
.obj()
.run(context)?
.to_object(context)?
.delete(&get_const_field.field().into()),
.__delete__(&get_const_field.field().into()),
),
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(context)?;
let field = &get_field.field().run(context)?;
let res = obj
.to_object(context)?
.delete(&field.to_property_key(context)?);
.__delete__(&field.to_property_key(context)?);
return Ok(Value::boolean(res));
}
Node::Identifier(_) => Value::boolean(false),

32
boa/src/value/conversions.rs

@ -68,6 +68,7 @@ impl Display for TryFromCharError {
}
impl From<f64> for Value {
#[inline]
fn from(value: f64) -> Self {
Self::rational(value)
}
@ -85,32 +86,45 @@ impl From<u32> for Value {
}
impl From<i32> for Value {
#[inline]
fn from(value: i32) -> Value {
Value::integer(value)
}
}
impl From<JsBigInt> for Value {
#[inline]
fn from(value: JsBigInt) -> Self {
Value::BigInt(value)
}
}
impl From<usize> for Value {
#[inline]
fn from(value: usize) -> Value {
Value::integer(value as i32)
if let Ok(value) = i32::try_from(value) {
Value::integer(value)
} else {
Value::rational(value as f64)
}
}
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::boolean(value)
impl From<u64> for Value {
#[inline]
fn from(value: u64) -> Value {
if let Ok(value) = i32::try_from(value) {
Value::integer(value)
} else {
Value::rational(value as f64)
}
}
}
impl From<&Value> for bool {
fn from(value: &Value) -> Self {
value.to_boolean()
impl From<bool> for Value {
#[inline]
fn from(value: bool) -> Self {
Value::boolean(value)
}
}
@ -148,6 +162,7 @@ impl From<Object> for Value {
}
impl From<GcObject> for Value {
#[inline]
fn from(object: GcObject) -> Self {
let _timer = BoaProfiler::global().start_event("From<GcObject>", "value");
Value::Object(object)
@ -158,12 +173,14 @@ impl From<GcObject> for Value {
pub struct TryFromObjectError;
impl Display for TryFromObjectError {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Could not convert value to an Object type")
}
}
impl From<()> for Value {
#[inline]
fn from(_: ()) -> Self {
Value::null()
}
@ -173,6 +190,7 @@ impl<T> From<Option<T>> for Value
where
T: Into<Value>,
{
#[inline]
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),

4
boa/src/value/display.rs

@ -102,7 +102,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children:
}
ObjectData::Array => {
let len = v
.get_own_property(&PropertyKey::from("length"))
.__get_own_property__(&PropertyKey::from("length"))
// TODO: do this in a better way `unwrap`
.unwrap()
// FIXME: handle accessor descriptors
@ -123,7 +123,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.get_own_property(&i.into())
&v.__get_own_property__(&i.into())
// FIXME: handle accessor descriptors
.and_then(|p| p.as_data_descriptor().map(|d| d.value()))
.unwrap_or_default(),

17
boa/src/value/mod.rs

@ -426,9 +426,7 @@ impl Value {
where
Key: Into<PropertyKey>,
{
self.as_object()
.map(|mut x| x.remove(&key.into()))
.is_some()
self.as_object().map(|x| x.remove(&key.into())).is_some()
}
/// Resolve the property in the object.
@ -442,7 +440,7 @@ impl Value {
let _timer = BoaProfiler::global().start_event("Value::get_property", "value");
match self {
Self::Object(ref object) => {
let property = object.get_own_property(&key);
let property = object.__get_own_property__(&key);
if property.is_some() {
return property;
}
@ -461,7 +459,8 @@ impl Value {
{
let _timer = BoaProfiler::global().start_event("Value::get_field", "value");
if let Self::Object(ref obj) = *self {
obj.clone().get(&key.into(), obj.clone().into(), context)
obj.clone()
.__get__(&key.into(), obj.clone().into(), context)
} else {
Ok(Value::undefined())
}
@ -475,7 +474,7 @@ impl Value {
{
let _timer = BoaProfiler::global().start_event("Value::has_field", "value");
self.as_object()
.map(|object| object.has_property(&key.into()))
.map(|object| object.__has_property__(&key.into()))
.unwrap_or(false)
}
@ -512,7 +511,7 @@ impl Value {
// 4. Let success be ? O.[[Set]](P, V, O).
let success = obj
.clone()
.set(key, value.clone(), obj.clone().into(), context)?;
.__set__(key, value.clone(), obj.clone().into(), context)?;
// 5. If success is false and Throw is true, throw a TypeError exception.
// 6. Return success.
@ -540,7 +539,7 @@ impl Value {
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
if let Some(mut object) = self.as_object() {
if let Some(object) = self.as_object() {
object.insert(key.into(), property.into());
}
}
@ -699,7 +698,7 @@ impl Value {
Value::String(ref string) => {
let prototype = context.standard_objects().string_object().prototype();
let mut object = GcObject::new(Object::with_prototype(
let object = GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::String(string.clone()),
));

4
boa/src/value/tests.rs

@ -267,7 +267,7 @@ fn string_length_is_not_enumerable() {
let object = Value::from("foo").to_object(&mut context).unwrap();
let length_desc = object
.get_own_property(&PropertyKey::from("length"))
.__get_own_property__(&PropertyKey::from("length"))
.unwrap();
assert!(!length_desc.enumerable());
}
@ -279,7 +279,7 @@ fn string_length_is_in_utf16_codeunits() {
// 😀 is one Unicode code point, but 2 UTF-16 code units
let object = Value::from("😀").to_object(&mut context).unwrap();
let length_desc = object
.get_own_property(&PropertyKey::from("length"))
.__get_own_property__(&PropertyKey::from("length"))
.unwrap();
assert_eq!(
length_desc

12
boa/src/vm/mod.rs

@ -370,7 +370,7 @@ impl<'a> Vm<'a> {
};
let name = self.code.names[index as usize].clone();
let result = object.get(&name.into(), value, self.context)?;
let result = object.get(name, self.context)?;
self.push(result)
}
@ -384,7 +384,7 @@ impl<'a> Vm<'a> {
};
let key = key.to_property_key(self.context)?;
let result = object.get(&key, value, self.context)?;
let result = object.get(key, self.context)?;
self.push(result)
}
@ -393,7 +393,7 @@ impl<'a> Vm<'a> {
let object = self.pop();
let value = self.pop();
let mut object = if let Some(object) = object.as_object() {
let object = if let Some(object) = object.as_object() {
object
} else {
object.to_object(self.context)?
@ -401,20 +401,20 @@ impl<'a> Vm<'a> {
let name = self.code.names[index as usize].clone();
object.set(name.into(), value, object.clone().into(), self.context)?;
object.set(name, value, true, self.context)?;
}
Opcode::SetPropertyByValue => {
let object = self.pop();
let key = self.pop();
let value = self.pop();
let mut object = if let Some(object) = object.as_object() {
let object = if let Some(object) = object.as_object() {
object
} else {
object.to_object(self.context)?
};
let key = key.to_property_key(self.context)?;
object.set(key, value, object.clone().into(), self.context)?;
object.set(key, value, true, self.context)?;
}
Opcode::Throw => {
let value = self.pop();

Loading…
Cancel
Save