Browse Source

Fast path for static property keys (#2604)

When a rust string literal is given for a property key, boa checks if it can be parsed as an index and converts the string into a utf16 slice. This PR rewrites each hard-coded property key as a utf16 slice so that we can bypass those conversions at runtime.

This improves QuickJS benchmark score 5% on average.
Richards: 35.4 -> 37.0
DeltaBlue: 35.0 -> 38.1
Crypto: 57.6 -> 59.6
RayTrace: 137 -> 146
EarleyBoyer: 131 -> 138
Splay: 98.3 -> 104
NavierStokes: 10.2 -> 10.2
pull/2607/head
Choongwoo Han 2 years ago
parent
commit
4750e8d53a
  1. 62
      boa_engine/src/builtins/array/mod.rs
  2. 8
      boa_engine/src/builtins/array_buffer/mod.rs
  3. 17
      boa_engine/src/builtins/dataview/mod.rs
  4. 2
      boa_engine/src/builtins/date/mod.rs
  5. 9
      boa_engine/src/builtins/error/aggregate.rs
  6. 7
      boa_engine/src/builtins/error/eval.rs
  7. 12
      boa_engine/src/builtins/error/mod.rs
  8. 7
      boa_engine/src/builtins/error/range.rs
  9. 7
      boa_engine/src/builtins/error/reference.rs
  10. 7
      boa_engine/src/builtins/error/syntax.rs
  11. 11
      boa_engine/src/builtins/error/type.rs
  12. 7
      boa_engine/src/builtins/error/uri.rs
  13. 9
      boa_engine/src/builtins/function/arguments.rs
  14. 16
      boa_engine/src/builtins/function/mod.rs
  15. 62
      boa_engine/src/builtins/intl/collator/mod.rs
  16. 26
      boa_engine/src/builtins/intl/date_time_format.rs
  17. 22
      boa_engine/src/builtins/intl/list_format/mod.rs
  18. 108
      boa_engine/src/builtins/intl/locale/mod.rs
  19. 5
      boa_engine/src/builtins/intl/locale/utils.rs
  20. 4
      boa_engine/src/builtins/intl/options.rs
  21. 7
      boa_engine/src/builtins/iterable/async_from_sync_iterator.rs
  22. 13
      boa_engine/src/builtins/iterable/mod.rs
  23. 6
      boa_engine/src/builtins/json/mod.rs
  24. 12
      boa_engine/src/builtins/map/mod.rs
  25. 22
      boa_engine/src/builtins/math/mod.rs
  26. 19
      boa_engine/src/builtins/mod.rs
  27. 29
      boa_engine/src/builtins/number/mod.rs
  28. 16
      boa_engine/src/builtins/object/mod.rs
  29. 39
      boa_engine/src/builtins/promise/mod.rs
  30. 5
      boa_engine/src/builtins/proxy/mod.rs
  31. 2
      boa_engine/src/builtins/reflect/mod.rs
  32. 128
      boa_engine/src/builtins/regexp/mod.rs
  33. 7
      boa_engine/src/builtins/regexp/regexp_string_iterator.rs
  34. 14
      boa_engine/src/builtins/set/mod.rs
  35. 10
      boa_engine/src/builtins/string/mod.rs
  36. 41
      boa_engine/src/builtins/symbol/mod.rs
  37. 15
      boa_engine/src/builtins/typed_array/mod.rs
  38. 3
      boa_engine/src/builtins/weak_map/mod.rs
  39. 3
      boa_engine/src/builtins/weak_set/mod.rs
  40. 9
      boa_engine/src/error.rs
  41. 3
      boa_engine/src/object/builtins/jsmap.rs
  42. 27
      boa_engine/src/object/builtins/jsproxy.rs
  43. 4
      boa_engine/src/object/internal_methods/arguments.rs
  44. 23
      boa_engine/src/object/internal_methods/array.rs
  45. 6
      boa_engine/src/object/internal_methods/global.rs
  46. 4
      boa_engine/src/object/internal_methods/integer_indexed.rs
  47. 16
      boa_engine/src/object/internal_methods/mod.rs
  48. 31
      boa_engine/src/object/internal_methods/proxy.rs
  49. 4
      boa_engine/src/object/internal_methods/string.rs
  50. 29
      boa_engine/src/object/jsobject.rs
  51. 21
      boa_engine/src/object/mod.rs
  52. 11
      boa_engine/src/object/operations.rs
  53. 11
      boa_engine/src/property/mod.rs
  54. 12
      boa_engine/src/value/display.rs
  55. 27
      boa_engine/src/value/serde_json.rs
  56. 23
      boa_engine/src/vm/code_block.rs
  57. 6
      boa_engine/src/vm/opcode/define/class/getter.rs
  58. 6
      boa_engine/src/vm/opcode/define/class/method.rs
  59. 6
      boa_engine/src/vm/opcode/define/class/setter.rs
  60. 4
      boa_engine/src/vm/opcode/define/own_property.rs
  61. 23
      boa_engine/src/vm/opcode/generator/mod.rs
  62. 3
      boa_engine/src/vm/opcode/iteration/for_in.rs
  63. 3
      boa_engine/src/vm/opcode/push/array.rs
  64. 3
      boa_engine/src/vm/opcode/push/class/mod.rs
  65. 3
      boa_engine/src/vm/opcode/push/class/private.rs
  66. 6
      boa_engine/src/vm/opcode/set/class_prototype.rs
  67. 3
      boa_engine/src/vm/opcode/set/private.rs
  68. 8
      boa_engine/src/vm/opcode/set/property.rs

62
boa_engine/src/builtins/array/mod.rs

@ -66,12 +66,12 @@ impl IntrinsicObject for Array {
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(
"length", utf16!("length"),
0, 0,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
.property( .property(
"values", utf16!("values"),
values_function.clone(), values_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
@ -191,7 +191,7 @@ impl BuiltInConstructor for Array {
}; };
// e. Perform ! Set(array, "length", intLen, true). // e. Perform ! Set(array, "length", intLen, true).
array array
.set("length", int_len, true, context) .set(utf16!("length"), int_len, true, context)
.expect("this Set call must not fail"); .expect("this Set call must not fail");
// f. Return array. // f. Return array.
Ok(array.into()) Ok(array.into())
@ -250,7 +250,7 @@ impl Array {
// 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). // 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
crate::object::internal_methods::ordinary_define_own_property( crate::object::internal_methods::ordinary_define_own_property(
&array, &array,
"length".into(), &utf16!("length").into(),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(length) .value(length)
.writable(true) .writable(true)
@ -291,7 +291,7 @@ impl Array {
.properties_mut() .properties_mut()
.override_indexed_properties(elements); .override_indexed_properties(elements);
array array
.set("length", length, true, context) .set(utf16!("length"), length, true, context)
.expect("Should not fail"); .expect("Should not fail");
// 5. Return array. // 5. Return array.
@ -475,7 +475,7 @@ impl Array {
} }
// 13. Perform ? Set(A, "length", 𝔽(len), true). // 13. Perform ? Set(A, "length", 𝔽(len), true).
a.set("length", len, true, context)?; a.set(utf16!("length"), len, true, context)?;
// 14. Return A. // 14. Return A.
return Ok(a.into()); return Ok(a.into());
@ -508,7 +508,7 @@ impl Array {
// iv. If next is false, then // iv. If next is false, then
let Some(next) = next else { let Some(next) = next else {
// 1. Perform ? Set(A, "length", 𝔽(k), true). // 1. Perform ? Set(A, "length", 𝔽(k), true).
a.set("length", k, true, context)?; a.set(utf16!("length"), k, true, context)?;
// 2. Return A. // 2. Return A.
return Ok(a.into()); return Ok(a.into());
@ -609,7 +609,7 @@ impl Array {
} }
// 8. Perform ? Set(A, "length", lenNumber, true). // 8. Perform ? Set(A, "length", lenNumber, true).
a.set("length", len, true, context)?; a.set(utf16!("length"), len, true, context)?;
// 9. Return A. // 9. Return A.
Ok(a.into()) Ok(a.into())
@ -733,7 +733,7 @@ impl Array {
} }
} }
// 6. Perform ? Set(A, "length", 𝔽(n), true). // 6. Perform ? Set(A, "length", 𝔽(n), true).
arr.set("length", n, true, context)?; arr.set(utf16!("length"), n, true, context)?;
// 7. Return A. // 7. Return A.
Ok(JsValue::new(arr)) Ok(JsValue::new(arr))
@ -778,7 +778,7 @@ impl Array {
len += 1; len += 1;
} }
// 6. Perform ? Set(O, "length", 𝔽(len), true). // 6. Perform ? Set(O, "length", 𝔽(len), true).
o.set("length", len, true, context)?; o.set(utf16!("length"), len, true, context)?;
// 7. Return 𝔽(len). // 7. Return 𝔽(len).
Ok(len.into()) Ok(len.into())
} }
@ -805,7 +805,7 @@ impl Array {
// 3. If len = 0, then // 3. If len = 0, then
if len == 0 { if len == 0 {
// a. Perform ? Set(O, "length", +0𝔽, true). // a. Perform ? Set(O, "length", +0𝔽, true).
o.set("length", 0, true, context)?; o.set(utf16!("length"), 0, true, context)?;
// b. Return undefined. // b. Return undefined.
Ok(JsValue::undefined()) Ok(JsValue::undefined())
// 4. Else, // 4. Else,
@ -820,7 +820,7 @@ impl Array {
// e. Perform ? DeletePropertyOrThrow(O, index). // e. Perform ? DeletePropertyOrThrow(O, index).
o.delete_property_or_throw(index, context)?; o.delete_property_or_throw(index, context)?;
// f. Perform ? Set(O, "length", newLen, true). // f. Perform ? Set(O, "length", newLen, true).
o.set("length", new_len, true, context)?; o.set(utf16!("length"), new_len, true, context)?;
// g. Return element. // g. Return element.
Ok(element) Ok(element)
} }
@ -946,7 +946,7 @@ impl Array {
// 1. Let array be ? ToObject(this value). // 1. Let array be ? ToObject(this value).
let array = this.to_object(context)?; let array = this.to_object(context)?;
// 2. Let func be ? Get(array, "join"). // 2. Let func be ? Get(array, "join").
let func = array.get("join", context)?; let func = array.get(utf16!("join"), context)?;
// 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%. // 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%.
// 4. Return ? Call(func, array). // 4. Return ? Call(func, array).
if let Some(func) = func.as_callable() { if let Some(func) = func.as_callable() {
@ -1061,7 +1061,7 @@ impl Array {
// 3. If len = 0, then // 3. If len = 0, then
if len == 0 { if len == 0 {
// a. Perform ? Set(O, "length", +0𝔽, true). // a. Perform ? Set(O, "length", +0𝔽, true).
o.set("length", 0, true, context)?; o.set(utf16!("length"), 0, true, context)?;
// b. Return undefined. // b. Return undefined.
return Ok(JsValue::undefined()); return Ok(JsValue::undefined());
} }
@ -1093,7 +1093,7 @@ impl Array {
// 7. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(len - 1))). // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(len - 1))).
o.delete_property_or_throw(len - 1, context)?; o.delete_property_or_throw(len - 1, context)?;
// 8. Perform ? Set(O, "length", 𝔽(len - 1), true). // 8. Perform ? Set(O, "length", 𝔽(len - 1), true).
o.set("length", len - 1, true, context)?; o.set(utf16!("length"), len - 1, true, context)?;
// 9. Return first. // 9. Return first.
Ok(first) Ok(first)
} }
@ -1163,7 +1163,7 @@ impl Array {
} }
} }
// 5. Perform ? Set(O, "length", 𝔽(len + argCount), true). // 5. Perform ? Set(O, "length", 𝔽(len + argCount), true).
o.set("length", len + arg_count, true, context)?; o.set(utf16!("length"), len + arg_count, true, context)?;
// 6. Return 𝔽(len + argCount). // 6. Return 𝔽(len + argCount).
Ok((len + arg_count).into()) Ok((len + arg_count).into())
} }
@ -2042,7 +2042,7 @@ impl Array {
} }
// 15. Perform ? Set(A, "length", 𝔽(n), true). // 15. Perform ? Set(A, "length", 𝔽(n), true).
a.set("length", n, true, context)?; a.set(utf16!("length"), n, true, context)?;
// 16. Return A. // 16. Return A.
Ok(a.into()) Ok(a.into())
@ -2102,7 +2102,7 @@ impl Array {
if !next.is_null_or_undefined() { if !next.is_null_or_undefined() {
// i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)). // i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)).
let s = next let s = next
.invoke("toLocaleString", args, context)? .invoke(utf16!("toLocaleString"), args, context)?
.to_string(context)?; .to_string(context)?;
// ii. Set R to the string-concatenation of R and S. // ii. Set R to the string-concatenation of R and S.
@ -2197,7 +2197,7 @@ impl Array {
} }
// 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true). // 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true).
arr.set("length", actual_delete_count, true, context)?; arr.set(utf16!("length"), actual_delete_count, true, context)?;
// 15. Let itemCount be the number of elements in items. // 15. Let itemCount be the number of elements in items.
let item_count = items.len() as u64; let item_count = items.len() as u64;
@ -2283,7 +2283,7 @@ impl Array {
// 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true).
o.set( o.set(
"length", utf16!("length"),
len - actual_delete_count + item_count, len - actual_delete_count + item_count,
true, true,
context, context,
@ -2980,27 +2980,27 @@ impl Array {
{ {
let mut obj = unscopable_list.borrow_mut(); let mut obj = unscopable_list.borrow_mut();
// 2. Perform ! CreateDataPropertyOrThrow(unscopableList, "at", true). // 2. Perform ! CreateDataPropertyOrThrow(unscopableList, "at", true).
obj.insert("at", true_prop.clone()); obj.insert(utf16!("at"), true_prop.clone());
// 3. Perform ! CreateDataPropertyOrThrow(unscopableList, "copyWithin", true). // 3. Perform ! CreateDataPropertyOrThrow(unscopableList, "copyWithin", true).
obj.insert("copyWithin", true_prop.clone()); obj.insert(utf16!("copyWithin"), true_prop.clone());
// 4. Perform ! CreateDataPropertyOrThrow(unscopableList, "entries", true). // 4. Perform ! CreateDataPropertyOrThrow(unscopableList, "entries", true).
obj.insert("entries", true_prop.clone()); obj.insert(utf16!("entries"), true_prop.clone());
// 5. Perform ! CreateDataPropertyOrThrow(unscopableList, "fill", true). // 5. Perform ! CreateDataPropertyOrThrow(unscopableList, "fill", true).
obj.insert("fill", true_prop.clone()); obj.insert(utf16!("fill"), true_prop.clone());
// 6. Perform ! CreateDataPropertyOrThrow(unscopableList, "find", true). // 6. Perform ! CreateDataPropertyOrThrow(unscopableList, "find", true).
obj.insert("find", true_prop.clone()); obj.insert(utf16!("find"), true_prop.clone());
// 7. Perform ! CreateDataPropertyOrThrow(unscopableList, "findIndex", true). // 7. Perform ! CreateDataPropertyOrThrow(unscopableList, "findIndex", true).
obj.insert("findIndex", true_prop.clone()); obj.insert(utf16!("findIndex"), true_prop.clone());
// 8. Perform ! CreateDataPropertyOrThrow(unscopableList, "flat", true). // 8. Perform ! CreateDataPropertyOrThrow(unscopableList, "flat", true).
obj.insert("flat", true_prop.clone()); obj.insert(utf16!("flat"), true_prop.clone());
// 9. Perform ! CreateDataPropertyOrThrow(unscopableList, "flatMap", true). // 9. Perform ! CreateDataPropertyOrThrow(unscopableList, "flatMap", true).
obj.insert("flatMap", true_prop.clone()); obj.insert(utf16!("flatMap"), true_prop.clone());
// 10. Perform ! CreateDataPropertyOrThrow(unscopableList, "includes", true). // 10. Perform ! CreateDataPropertyOrThrow(unscopableList, "includes", true).
obj.insert("includes", true_prop.clone()); obj.insert(utf16!("includes"), true_prop.clone());
// 11. Perform ! CreateDataPropertyOrThrow(unscopableList, "keys", true). // 11. Perform ! CreateDataPropertyOrThrow(unscopableList, "keys", true).
obj.insert("keys", true_prop.clone()); obj.insert(utf16!("keys"), true_prop.clone());
// 12. Perform ! CreateDataPropertyOrThrow(unscopableList, "values", true). // 12. Perform ! CreateDataPropertyOrThrow(unscopableList, "values", true).
obj.insert("values", true_prop); obj.insert(utf16!("values"), true_prop);
} }
// 13. Return unscopableList. // 13. Return unscopableList.

8
boa_engine/src/builtins/array_buffer/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, Numeric}, value::{IntegerOrInfinity, Numeric},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
@ -62,7 +63,12 @@ impl IntrinsicObject for ArrayBuffer {
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.accessor("byteLength", Some(get_byte_length), None, flag_attributes) .accessor(
utf16!("byteLength"),
Some(get_byte_length),
None,
flag_attributes,
)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),

17
boa_engine/src/builtins/dataview/mod.rs

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
@ -49,9 +50,19 @@ impl IntrinsicObject for DataView {
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.accessor("buffer", Some(get_buffer), None, flag_attributes) .accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes)
.accessor("byteLength", Some(get_byte_length), None, flag_attributes) .accessor(
.accessor("byteOffset", Some(get_byte_offset), None, flag_attributes) utf16!("byteLength"),
Some(get_byte_length),
None,
flag_attributes,
)
.accessor(
utf16!("byteOffset"),
Some(get_byte_offset),
None,
flag_attributes,
)
.method(Self::get_big_int64, "getBigInt64", 1) .method(Self::get_big_int64, "getBigInt64", 1)
.method(Self::get_big_uint64, "getBigUint64", 1) .method(Self::get_big_uint64, "getBigUint64", 1)
.method(Self::get_float32, "getFloat32", 1) .method(Self::get_float32, "getFloat32", 1)

2
boa_engine/src/builtins/date/mod.rs

@ -1241,7 +1241,7 @@ impl Date {
} }
// 4. Return ? Invoke(O, "toISOString"). // 4. Return ? Invoke(O, "toISOString").
let func = o.get("toISOString", context)?; let func = o.get(utf16!("toISOString"), context)?;
func.call(this, &[], context) func.call(this, &[], context)
} }

9
boa_engine/src/builtins/error/aggregate.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptorBuilder}, property::{Attribute, PropertyDescriptorBuilder},
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,8 +33,8 @@ impl IntrinsicObject for AggregateError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -74,7 +75,7 @@ impl BuiltInConstructor for AggregateError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).
@ -91,7 +92,7 @@ impl BuiltInConstructor for AggregateError {
// [[Value]]: CreateArrayFromList(errorsList) // [[Value]]: CreateArrayFromList(errorsList)
// }). // }).
o.define_property_or_throw( o.define_property_or_throw(
"errors", utf16!("errors"),
PropertyDescriptorBuilder::new() PropertyDescriptorBuilder::new()
.configurable(true) .configurable(true)
.enumerable(false) .enumerable(false)

7
boa_engine/src/builtins/error/eval.rs

@ -16,6 +16,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -34,8 +35,8 @@ impl IntrinsicObject for EvalError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -73,7 +74,7 @@ impl BuiltInConstructor for EvalError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).

12
boa_engine/src/builtins/error/mod.rs

@ -132,8 +132,8 @@ impl IntrinsicObject for Error {
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
.build(); .build();
} }
@ -175,7 +175,7 @@ impl BuiltInConstructor for Error {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).
@ -194,12 +194,12 @@ impl Error {
) -> JsResult<()> { ) -> JsResult<()> {
// 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then // 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then
if let Some(options) = options.as_object() { if let Some(options) = options.as_object() {
if options.has_property("cause", context)? { if options.has_property(utf16!("cause"), context)? {
// a. Let cause be ? Get(options, "cause"). // a. Let cause be ? Get(options, "cause").
let cause = options.get("cause", context)?; let cause = options.get(utf16!("cause"), context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
o.create_non_enumerable_data_property_or_throw("cause", cause, context); o.create_non_enumerable_data_property_or_throw(utf16!("cause"), cause, context);
} }
} }

7
boa_engine/src/builtins/error/range.rs

@ -14,6 +14,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,8 +33,8 @@ impl IntrinsicObject for RangeError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -71,7 +72,7 @@ impl BuiltInConstructor for RangeError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).

7
boa_engine/src/builtins/error/reference.rs

@ -14,6 +14,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -31,8 +32,8 @@ impl IntrinsicObject for ReferenceError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -73,7 +74,7 @@ impl BuiltInConstructor for ReferenceError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).

7
boa_engine/src/builtins/error/syntax.rs

@ -16,6 +16,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -34,8 +35,8 @@ impl IntrinsicObject for SyntaxError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -76,7 +77,7 @@ impl BuiltInConstructor for SyntaxError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).

11
boa_engine/src/builtins/error/type.rs

@ -24,6 +24,7 @@ use crate::{
native_function::NativeFunction, native_function::NativeFunction,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -42,8 +43,8 @@ impl IntrinsicObject for TypeError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -81,7 +82,7 @@ impl BuiltInConstructor for TypeError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).
@ -108,8 +109,8 @@ impl IntrinsicObject for ThrowTypeError {
let obj = BuiltInBuilder::with_intrinsic::<Self>(intrinsics) let obj = BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
.prototype(intrinsics.constructors().function().prototype()) .prototype(intrinsics.constructors().function().prototype())
.static_property("name", "ThrowTypeError", Attribute::empty()) .static_property(utf16!("name"), "ThrowTypeError", Attribute::empty())
.static_property("length", 0, Attribute::empty()) .static_property(utf16!("length"), 0, Attribute::empty())
.build(); .build();
let mut obj = obj.borrow_mut(); let mut obj = obj.borrow_mut();

7
boa_engine/src/builtins/error/uri.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -33,8 +34,8 @@ impl IntrinsicObject for UriError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor()) .prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype())) .inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property("message", "", attribute) .property(utf16!("message"), "", attribute)
.build(); .build();
} }
@ -72,7 +73,7 @@ impl BuiltInConstructor for UriError {
let msg = message.to_string(context)?; let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
} }
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).

9
boa_engine/src/builtins/function/arguments.rs

@ -2,6 +2,7 @@ use crate::{
environments::DeclarativeEnvironment, environments::DeclarativeEnvironment,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::PropertyDescriptor, property::PropertyDescriptor,
string::utf16,
symbol::{self, JsSymbol}, symbol::{self, JsSymbol},
Context, JsValue, Context, JsValue,
}; };
@ -87,7 +88,7 @@ impl Arguments {
// 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len),
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
obj.define_property_or_throw( obj.define_property_or_throw(
"length", utf16!("length"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(len) .value(len)
.writable(true) .writable(true)
@ -129,7 +130,7 @@ impl Arguments {
// [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, // [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false,
// [[Configurable]]: false }). // [[Configurable]]: false }).
obj.define_property_or_throw( obj.define_property_or_throw(
"callee", utf16!("callee"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.get(throw_type_error.clone()) .get(throw_type_error.clone())
.set(throw_type_error) .set(throw_type_error)
@ -249,7 +250,7 @@ impl Arguments {
// 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), // 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len),
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
obj.define_property_or_throw( obj.define_property_or_throw(
"length", utf16!("length"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(len) .value(len)
.writable(true) .writable(true)
@ -277,7 +278,7 @@ impl Arguments {
// 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { // 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {
// [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). // [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
obj.define_property_or_throw( obj.define_property_or_throw(
"callee", utf16!("callee"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(func.clone()) .value(func.clone())
.writable(true) .writable(true)

16
boa_engine/src/builtins/function/mod.rs

@ -419,13 +419,13 @@ impl IntrinsicObject for BuiltInFunctionObject {
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
.property(JsSymbol::has_instance(), has_instance, Attribute::default()) .property(JsSymbol::has_instance(), has_instance, Attribute::default())
.accessor( .accessor(
"caller", utf16!("caller"),
Some(throw_type_error.clone()), Some(throw_type_error.clone()),
Some(throw_type_error.clone()), Some(throw_type_error.clone()),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
"arguments", utf16!("arguments"),
Some(throw_type_error.clone()), Some(throw_type_error.clone()),
Some(throw_type_error), Some(throw_type_error),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
@ -734,9 +734,9 @@ impl BuiltInFunctionObject {
// 5. Let targetHasLength be ? HasOwnProperty(Target, "length"). // 5. Let targetHasLength be ? HasOwnProperty(Target, "length").
// 6. If targetHasLength is true, then // 6. If targetHasLength is true, then
if target.has_own_property("length", context)? { if target.has_own_property(utf16!("length"), context)? {
// a. Let targetLen be ? Get(Target, "length"). // a. Let targetLen be ? Get(Target, "length").
let target_len = target.get("length", context)?; let target_len = target.get(utf16!("length"), context)?;
// b. If Type(targetLen) is Number, then // b. If Type(targetLen) is Number, then
if target_len.is_number() { if target_len.is_number() {
// 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen). // 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen).
@ -761,7 +761,7 @@ impl BuiltInFunctionObject {
// 7. Perform ! SetFunctionLength(F, L). // 7. Perform ! SetFunctionLength(F, L).
f.define_property_or_throw( f.define_property_or_throw(
"length", utf16!("length"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(l) .value(l)
.writable(false) .writable(false)
@ -772,7 +772,7 @@ impl BuiltInFunctionObject {
.expect("defining the `length` property for a new object should not fail"); .expect("defining the `length` property for a new object should not fail");
// 8. Let targetName be ? Get(Target, "name"). // 8. Let targetName be ? Get(Target, "name").
let target_name = target.get("name", context)?; let target_name = target.get(utf16!("name"), context)?;
// 9. If Type(targetName) is not String, set targetName to the empty String. // 9. If Type(targetName) is not String, set targetName to the empty String.
let target_name = target_name let target_name = target_name
@ -825,7 +825,7 @@ impl BuiltInFunctionObject {
let value = this let value = this
.as_object() .as_object()
.expect("checked that `this` was an object above") .expect("checked that `this` was an object above")
.get("name", &mut *context)?; .get(utf16!("name"), &mut *context)?;
if value.is_null_or_undefined() { if value.is_null_or_undefined() {
None None
} else { } else {
@ -923,7 +923,7 @@ pub(crate) fn set_function_name(
// [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).
function function
.define_property_or_throw( .define_property_or_throw(
"name", utf16!("name"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(name) .value(name)
.writable(false) .writable(false)

62
boa_engine/src/builtins/intl/collator/mod.rs

@ -22,6 +22,7 @@ use crate::{
JsObject, ObjectData, JsObject, ObjectData,
}, },
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
@ -173,7 +174,12 @@ impl IntrinsicObject for Collator {
"Intl.Collator", "Intl.Collator",
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor("compare", Some(compare), None, Attribute::CONFIGURABLE) .accessor(
utf16!("compare"),
Some(compare),
None,
Attribute::CONFIGURABLE,
)
.method(Self::resolved_options, "resolvedOptions", 0) .method(Self::resolved_options, "resolvedOptions", 0)
.build(); .build();
} }
@ -234,29 +240,31 @@ impl BuiltInConstructor for Collator {
// a. Let localeData be %Collator%.[[SortLocaleData]]. // a. Let localeData be %Collator%.[[SortLocaleData]].
// 6. Else, // 6. Else,
// a. Let localeData be %Collator%.[[SearchLocaleData]]. // a. Let localeData be %Collator%.[[SearchLocaleData]].
let usage = get_option::<Usage>(&options, "usage", false, context)?.unwrap_or_default(); let usage =
get_option::<Usage>(&options, utf16!("usage"), false, context)?.unwrap_or_default();
// 7. Let opt be a new Record. // 7. Let opt be a new Record.
// 8. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 8. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
// 9. Set opt.[[localeMatcher]] to matcher. // 9. Set opt.[[localeMatcher]] to matcher.
let matcher = get_option::<LocaleMatcher>(&options, "localeMatcher", false, context)? let matcher =
.unwrap_or_default(); get_option::<LocaleMatcher>(&options, utf16!("localeMatcher"), false, context)?
.unwrap_or_default();
// 10. Let collation be ? GetOption(options, "collation", string, empty, undefined). // 10. Let collation be ? GetOption(options, "collation", string, empty, undefined).
// 11. If collation is not undefined, then // 11. If collation is not undefined, then
// a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
// 12. Set opt.[[co]] to collation. // 12. Set opt.[[co]] to collation.
let collation = get_option::<Value>(&options, "collation", false, context)?; let collation = get_option::<Value>(&options, utf16!("collation"), false, context)?;
// 13. Let numeric be ? GetOption(options, "numeric", boolean, empty, undefined). // 13. Let numeric be ? GetOption(options, "numeric", boolean, empty, undefined).
// 14. If numeric is not undefined, then // 14. If numeric is not undefined, then
// a. Let numeric be ! ToString(numeric). // a. Let numeric be ! ToString(numeric).
// 15. Set opt.[[kn]] to numeric. // 15. Set opt.[[kn]] to numeric.
let numeric = get_option::<bool>(&options, "numeric", false, context)?; let numeric = get_option::<bool>(&options, utf16!("numeric"), false, context)?;
// 16. Let caseFirst be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined). // 16. Let caseFirst be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined).
// 17. Set opt.[[kf]] to caseFirst. // 17. Set opt.[[kf]] to caseFirst.
let case_first = get_option::<CaseFirst>(&options, "caseFirst", false, context)?; let case_first = get_option::<CaseFirst>(&options, utf16!("caseFirst"), false, context)?;
let mut intl_options = IntlOptions { let mut intl_options = IntlOptions {
matcher, matcher,
@ -305,20 +313,22 @@ impl BuiltInConstructor for Collator {
// 26. Let sensitivity be ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », undefined). // 26. Let sensitivity be ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », undefined).
// 28. Set collator.[[Sensitivity]] to sensitivity. // 28. Set collator.[[Sensitivity]] to sensitivity.
let sensitivity = get_option::<Sensitivity>(&options, "sensitivity", false, context)? let sensitivity =
// 27. If sensitivity is undefined, then get_option::<Sensitivity>(&options, utf16!("sensitivity"), false, context)?
// a. If usage is "sort", then // 27. If sensitivity is undefined, then
// i. Let sensitivity be "variant". // a. If usage is "sort", then
// b. Else, // i. Let sensitivity be "variant".
// i. Let dataLocale be r.[[dataLocale]]. // b. Else,
// ii. Let dataLocaleData be localeData.[[<dataLocale>]]. // i. Let dataLocale be r.[[dataLocale]].
// iii. Let sensitivity be dataLocaleData.[[sensitivity]]. // ii. Let dataLocaleData be localeData.[[<dataLocale>]].
.or_else(|| (usage == Usage::Sort).then_some(Sensitivity::Variant)); // iii. Let sensitivity be dataLocaleData.[[sensitivity]].
.or_else(|| (usage == Usage::Sort).then_some(Sensitivity::Variant));
// 29. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", boolean, empty, false). // 29. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", boolean, empty, false).
// 30. Set collator.[[IgnorePunctuation]] to ignorePunctuation. // 30. Set collator.[[IgnorePunctuation]] to ignorePunctuation.
let ignore_punctuation = let ignore_punctuation =
get_option::<bool>(&options, "ignorePunctuation", false, context)?.unwrap_or_default(); get_option::<bool>(&options, utf16!("ignorePunctuation"), false, context)?
.unwrap_or_default();
let (strength, case_level) = sensitivity.map(Sensitivity::to_collator_options).unzip(); let (strength, case_level) = sensitivity.map(Sensitivity::to_collator_options).unzip();
@ -504,11 +514,11 @@ impl Collator {
// i. Perform ! CreateDataPropertyOrThrow(options, p, v). // i. Perform ! CreateDataPropertyOrThrow(options, p, v).
// 5. Return options. // 5. Return options.
options options
.create_data_property_or_throw("locale", collator.locale.to_string(), context) .create_data_property_or_throw(utf16!("locale"), collator.locale.to_string(), context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
"usage", utf16!("usage"),
match collator.usage { match collator.usage {
Usage::Search => "search", Usage::Search => "search",
Usage::Sort => "sort", Usage::Sort => "sort",
@ -518,7 +528,7 @@ impl Collator {
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
"sensitivity", utf16!("sensitivity"),
match collator.sensitivity { match collator.sensitivity {
Sensitivity::Base => "base", Sensitivity::Base => "base",
Sensitivity::Accent => "accent", Sensitivity::Accent => "accent",
@ -530,21 +540,25 @@ impl Collator {
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
"ignorePunctuation", utf16!("ignorePunctuation"),
collator.ignore_punctuation, collator.ignore_punctuation,
context, context,
) )
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw("collation", collator.collation.to_string(), context) .create_data_property_or_throw(
utf16!("collation"),
collator.collation.to_string(),
context,
)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw("numeric", collator.numeric, context) .create_data_property_or_throw(utf16!("numeric"), collator.numeric, context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
if let Some(kf) = collator.case_first { if let Some(kf) = collator.case_first {
options options
.create_data_property_or_throw( .create_data_property_or_throw(
"caseFirst", utf16!("caseFirst"),
match kf { match kf {
CaseFirst::Off => "false", CaseFirst::Off => "false",
CaseFirst::LowerFirst => "lower", CaseFirst::LowerFirst => "lower",

26
boa_engine/src/builtins/intl/date_time_format.rs

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
string::utf16,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -182,7 +183,12 @@ pub(crate) fn to_date_time_options(
// 4. If required is "date" or "any", then // 4. If required is "date" or "any", then
if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(required) { if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(required) {
// a. For each property name prop of « "weekday", "year", "month", "day" », do // a. For each property name prop of « "weekday", "year", "month", "day" », do
for property in ["weekday", "year", "month", "day"] { for property in [
utf16!("weekday"),
utf16!("year"),
utf16!("month"),
utf16!("day"),
] {
// i. Let value be ? Get(options, prop). // i. Let value be ? Get(options, prop).
let value = options.get(property, context)?; let value = options.get(property, context)?;
@ -198,11 +204,11 @@ pub(crate) fn to_date_time_options(
// a. For each property name prop of « "dayPeriod", "hour", "minute", "second", // a. For each property name prop of « "dayPeriod", "hour", "minute", "second",
// "fractionalSecondDigits" », do // "fractionalSecondDigits" », do
for property in [ for property in [
"dayPeriod", utf16!("dayPeriod"),
"hour", utf16!("hour"),
"minute", utf16!("minute"),
"second", utf16!("second"),
"fractionalSecondDigits", utf16!("fractionalSecondDigits"),
] { ] {
// i. Let value be ? Get(options, prop). // i. Let value be ? Get(options, prop).
let value = options.get(property, context)?; let value = options.get(property, context)?;
@ -215,10 +221,10 @@ pub(crate) fn to_date_time_options(
} }
// 6. Let dateStyle be ? Get(options, "dateStyle"). // 6. Let dateStyle be ? Get(options, "dateStyle").
let date_style = options.get("dateStyle", context)?; let date_style = options.get(utf16!("dateStyle"), context)?;
// 7. Let timeStyle be ? Get(options, "timeStyle"). // 7. Let timeStyle be ? Get(options, "timeStyle").
let time_style = options.get("timeStyle", context)?; let time_style = options.get(utf16!("timeStyle"), context)?;
// 8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false. // 8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false.
if !date_style.is_undefined() || !time_style.is_undefined() { if !date_style.is_undefined() || !time_style.is_undefined() {
@ -244,7 +250,7 @@ pub(crate) fn to_date_time_options(
// 11. If needDefaults is true and defaults is either "date" or "all", then // 11. If needDefaults is true and defaults is either "date" or "all", then
if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(defaults) { if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(defaults) {
// a. For each property name prop of « "year", "month", "day" », do // a. For each property name prop of « "year", "month", "day" », do
for property in ["year", "month", "day"] { for property in [utf16!("year"), utf16!("month"), utf16!("day")] {
// i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
options.create_data_property_or_throw(property, "numeric", context)?; options.create_data_property_or_throw(property, "numeric", context)?;
} }
@ -253,7 +259,7 @@ pub(crate) fn to_date_time_options(
// 12. If needDefaults is true and defaults is either "time" or "all", then // 12. If needDefaults is true and defaults is either "time" or "all", then
if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(defaults) { if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(defaults) {
// a. For each property name prop of « "hour", "minute", "second" », do // a. For each property name prop of « "hour", "minute", "second" », do
for property in ["hour", "minute", "second"] { for property in [utf16!("hour"), utf16!("minute"), utf16!("second")] {
// i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
options.create_data_property_or_throw(property, "numeric", context)?; options.create_data_property_or_throw(property, "numeric", context)?;
} }

22
boa_engine/src/builtins/intl/list_format/mod.rs

@ -10,6 +10,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
@ -110,8 +111,9 @@ impl BuiltInConstructor for ListFormat {
// 5. Let opt be a new Record. // 5. Let opt be a new Record.
// 6. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 6. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
let matcher = get_option::<LocaleMatcher>(&options, "localeMatcher", false, context)? let matcher =
.unwrap_or_default(); get_option::<LocaleMatcher>(&options, utf16!("localeMatcher"), false, context)?
.unwrap_or_default();
// 7. Set opt.[[localeMatcher]] to matcher. // 7. Set opt.[[localeMatcher]] to matcher.
// 8. Let localeData be %ListFormat%.[[LocaleData]]. // 8. Let localeData be %ListFormat%.[[LocaleData]].
@ -128,12 +130,12 @@ impl BuiltInConstructor for ListFormat {
// 11. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction"). // 11. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction").
// 12. Set listFormat.[[Type]] to type. // 12. Set listFormat.[[Type]] to type.
let typ = let typ = get_option::<ListFormatType>(&options, utf16!("type"), false, context)?
get_option::<ListFormatType>(&options, "type", false, context)?.unwrap_or_default(); .unwrap_or_default();
// 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long"). // 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long").
// 14. Set listFormat.[[Style]] to style. // 14. Set listFormat.[[Style]] to style.
let style = get_option::<ListLength>(&options, "style", false, context)? let style = get_option::<ListLength>(&options, utf16!("style"), false, context)?
.unwrap_or(ListLength::Wide); .unwrap_or(ListLength::Wide);
// 15. Let dataLocale be r.[[dataLocale]]. // 15. Let dataLocale be r.[[dataLocale]].
@ -361,11 +363,11 @@ impl ListFormat {
); );
// b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
o.create_data_property_or_throw("type", part.typ(), context) o.create_data_property_or_throw(utf16!("type"), part.typ(), context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
// c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
o.create_data_property_or_throw("value", part.value(), context) o.create_data_property_or_throw(utf16!("value"), part.value(), context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
// d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O). // d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O).
@ -418,11 +420,11 @@ impl ListFormat {
// c. Assert: v is not undefined. // c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v). // d. Perform ! CreateDataPropertyOrThrow(options, p, v).
options options
.create_data_property_or_throw("locale", lf.locale.to_string(), context) .create_data_property_or_throw(utf16!("locale"), lf.locale.to_string(), context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
"type", utf16!("type"),
match lf.typ { match lf.typ {
ListFormatType::Conjunction => "conjunction", ListFormatType::Conjunction => "conjunction",
ListFormatType::Disjunction => "disjunction", ListFormatType::Disjunction => "disjunction",
@ -433,7 +435,7 @@ impl ListFormat {
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
"style", utf16!("style"),
match lf.style { match lf.style {
ListLength::Wide => "long", ListLength::Wide => "long",
ListLength::Short => "short", ListLength::Short => "short",

108
boa_engine/src/builtins/intl/locale/mod.rs

@ -1,3 +1,4 @@
use crate::string::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use icu_collator::CaseFirst; use icu_collator::CaseFirst;
use icu_datetime::options::preferences::HourCycle; use icu_datetime::options::preferences::HourCycle;
@ -93,21 +94,66 @@ impl IntrinsicObject for Locale {
.method(Self::maximize, "maximize", 0) .method(Self::maximize, "maximize", 0)
.method(Self::minimize, "minimize", 0) .method(Self::minimize, "minimize", 0)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
.accessor("baseName", Some(base_name), None, Attribute::CONFIGURABLE)
.accessor("calendar", Some(calendar), None, Attribute::CONFIGURABLE)
.accessor("caseFirst", Some(case_first), None, Attribute::CONFIGURABLE)
.accessor("collation", Some(collation), None, Attribute::CONFIGURABLE)
.accessor("hourCycle", Some(hour_cycle), None, Attribute::CONFIGURABLE)
.accessor("numeric", Some(numeric), None, Attribute::CONFIGURABLE)
.accessor( .accessor(
"numberingSystem", utf16!("baseName"),
Some(base_name),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("calendar"),
Some(calendar),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("caseFirst"),
Some(case_first),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("collation"),
Some(collation),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("hourCycle"),
Some(hour_cycle),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("numeric"),
Some(numeric),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("numberingSystem"),
Some(numbering_system), Some(numbering_system),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor("language", Some(language), None, Attribute::CONFIGURABLE) .accessor(
.accessor("script", Some(script), None, Attribute::CONFIGURABLE) utf16!("language"),
.accessor("region", Some(region), None, Attribute::CONFIGURABLE) Some(language),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("script"),
Some(script),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("region"),
Some(region),
None,
Attribute::CONFIGURABLE,
)
.build(); .build();
} }
@ -197,7 +243,7 @@ impl BuiltInConstructor for Locale {
// 3. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. // 3. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
// 4. Let language be ? GetOption(options, "language", string, empty, undefined). // 4. Let language be ? GetOption(options, "language", string, empty, undefined).
// 5. If language is not undefined, then // 5. If language is not undefined, then
let language = get_option::<JsString>(options, "language", false, context)? let language = get_option::<JsString>(options, utf16!("language"), false, context)?
// a. If language does not match the unicode_language_subtag production, throw a RangeError exception. // a. If language does not match the unicode_language_subtag production, throw a RangeError exception.
.map(|s| s.to_std_string_escaped().parse::<Language>()) .map(|s| s.to_std_string_escaped().parse::<Language>())
.transpose() .transpose()
@ -205,7 +251,7 @@ impl BuiltInConstructor for Locale {
// 6. Let script be ? GetOption(options, "script", string, empty, undefined). // 6. Let script be ? GetOption(options, "script", string, empty, undefined).
// 7. If script is not undefined, then // 7. If script is not undefined, then
let script = get_option::<JsString>(options, "script", false, context)? let script = get_option::<JsString>(options, utf16!("script"), false, context)?
.map(|s| s.to_std_string_escaped().parse::<Script>()) .map(|s| s.to_std_string_escaped().parse::<Script>())
.transpose() .transpose()
// a. If script does not match the unicode_script_subtag production, throw a RangeError exception. // a. If script does not match the unicode_script_subtag production, throw a RangeError exception.
@ -213,7 +259,7 @@ impl BuiltInConstructor for Locale {
// 8. Let region be ? GetOption(options, "region", string, empty, undefined). // 8. Let region be ? GetOption(options, "region", string, empty, undefined).
// 9. If region is not undefined, then // 9. If region is not undefined, then
let region = get_option::<JsString>(options, "region", false, context)? let region = get_option::<JsString>(options, utf16!("region"), false, context)?
.map(|s| s.to_std_string_escaped().parse::<Region>()) .map(|s| s.to_std_string_escaped().parse::<Region>())
.transpose() .transpose()
// a. If region does not match the unicode_region_subtag production, throw a RangeError exception. // a. If region does not match the unicode_region_subtag production, throw a RangeError exception.
@ -259,38 +305,42 @@ impl BuiltInConstructor for Locale {
// 14. If calendar is not undefined, then // 14. If calendar is not undefined, then
// 15. Set opt.[[ca]] to calendar. // 15. Set opt.[[ca]] to calendar.
// a. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. // a. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
let ca = get_option::<Value>(options, "calendar", false, context)?; let ca = get_option::<Value>(options, utf16!("calendar"), false, context)?;
// 16. Let collation be ? GetOption(options, "collation", string, empty, undefined). // 16. Let collation be ? GetOption(options, "collation", string, empty, undefined).
// 17. If collation is not undefined, then // 17. If collation is not undefined, then
// 18. Set opt.[[co]] to collation. // 18. Set opt.[[co]] to collation.
// a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
let co = get_option::<Value>(options, "collation", false, context)?; let co = get_option::<Value>(options, utf16!("collation"), false, context)?;
// 19. Let hc be ? GetOption(options, "hourCycle", string, « "h11", "h12", "h23", "h24" », undefined). // 19. Let hc be ? GetOption(options, "hourCycle", string, « "h11", "h12", "h23", "h24" », undefined).
// 20. Set opt.[[hc]] to hc. // 20. Set opt.[[hc]] to hc.
let hc = let hc =
get_option::<HourCycle>(options, "hourCycle", false, context)?.map(|hc| match hc { get_option::<HourCycle>(options, utf16!("hourCycle"), false, context)?.map(
HourCycle::H24 => value!("h24"), |hc| match hc {
HourCycle::H23 => value!("h23"), HourCycle::H24 => value!("h24"),
HourCycle::H12 => value!("h12"), HourCycle::H23 => value!("h23"),
HourCycle::H11 => value!("h11"), HourCycle::H12 => value!("h12"),
}); HourCycle::H11 => value!("h11"),
},
);
// 21. Let kf be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined). // 21. Let kf be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined).
// 22. Set opt.[[kf]] to kf. // 22. Set opt.[[kf]] to kf.
let kf = let kf =
get_option::<CaseFirst>(options, "caseFirst", false, context)?.map(|kf| match kf { get_option::<CaseFirst>(options, utf16!("caseFirst"), false, context)?.map(
CaseFirst::UpperFirst => value!("upper"), |kf| match kf {
CaseFirst::LowerFirst => value!("lower"), CaseFirst::UpperFirst => value!("upper"),
CaseFirst::Off => value!("false"), CaseFirst::LowerFirst => value!("lower"),
_ => unreachable!(), CaseFirst::Off => value!("false"),
}); _ => unreachable!(),
},
);
// 23. Let kn be ? GetOption(options, "numeric", boolean, empty, undefined). // 23. Let kn be ? GetOption(options, "numeric", boolean, empty, undefined).
// 24. If kn is not undefined, set kn to ! ToString(kn). // 24. If kn is not undefined, set kn to ! ToString(kn).
// 25. Set opt.[[kn]] to kn. // 25. Set opt.[[kn]] to kn.
let kn = get_option::<bool>(options, "numeric", false, context)?.map(|b| { let kn = get_option::<bool>(options, utf16!("numeric"), false, context)?.map(|b| {
if b { if b {
value!("true") value!("true")
} else { } else {
@ -302,7 +352,7 @@ impl BuiltInConstructor for Locale {
// 27. If numberingSystem is not undefined, then // 27. If numberingSystem is not undefined, then
// 28. Set opt.[[nu]] to numberingSystem. // 28. Set opt.[[nu]] to numberingSystem.
// a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
let nu = get_option::<Value>(options, "numberingSystem", false, context)?; let nu = get_option::<Value>(options, utf16!("numberingSystem"), false, context)?;
// 29. Let r be ! ApplyUnicodeExtensionToTag(tag, opt, relevantExtensionKeys). // 29. Let r be ! ApplyUnicodeExtensionToTag(tag, opt, relevantExtensionKeys).
// 30. Set locale.[[Locale]] to r.[[locale]]. // 30. Set locale.[[Locale]] to r.[[locale]].

5
boa_engine/src/builtins/intl/locale/utils.rs

@ -8,6 +8,7 @@ use crate::{
}, },
context::{icu::Icu, BoaProvider}, context::{icu::Icu, BoaProvider},
object::JsObject, object::JsObject,
string::utf16,
Context, JsNativeError, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
}; };
@ -497,8 +498,8 @@ where
let options = coerce_options_to_object(options, context)?; let options = coerce_options_to_object(options, context)?;
// 2. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 2. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
let matcher = let matcher = get_option::<LocaleMatcher>(&options, utf16!("localeMatcher"), false, context)?
get_option::<LocaleMatcher>(&options, "localeMatcher", false, context)?.unwrap_or_default(); .unwrap_or_default();
let elements = match matcher { let elements = match matcher {
// 4. Else, // 4. Else,

4
boa_engine/src/builtins/intl/options.rs

@ -120,7 +120,7 @@ impl OptionType for CaseFirst {
/// [spec]: https://tc39.es/ecma402/#sec-getoption /// [spec]: https://tc39.es/ecma402/#sec-getoption
pub(super) fn get_option<T: OptionType>( pub(super) fn get_option<T: OptionType>(
options: &JsObject, options: &JsObject,
property: &str, property: &[u16],
required: bool, required: bool,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<Option<T>> { ) -> JsResult<Option<T>> {
@ -157,7 +157,7 @@ pub(super) fn get_option<T: OptionType>(
#[allow(unused)] #[allow(unused)]
pub(super) fn get_number_option( pub(super) fn get_number_option(
options: &JsObject, options: &JsObject,
property: &str, property: &[u16],
minimum: f64, minimum: f64,
maximum: f64, maximum: f64,
fallback: Option<f64>, fallback: Option<f64>,

7
boa_engine/src/builtins/iterable/async_from_sync_iterator.rs

@ -7,6 +7,7 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, ObjectData}, object::{FunctionObjectBuilder, JsObject, ObjectData},
string::utf16,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -70,7 +71,7 @@ impl AsyncFromSyncIterator {
// 3. Let nextMethod be ! Get(asyncIterator, "next"). // 3. Let nextMethod be ! Get(asyncIterator, "next").
let next_method = async_iterator let next_method = async_iterator
.get("next", context) .get(utf16!("next"), context)
.expect("async from sync iterator prototype must have next method"); .expect("async from sync iterator prototype must have next method");
// 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }. // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
@ -155,7 +156,7 @@ impl AsyncFromSyncIterator {
.expect("cannot fail with promise constructor"); .expect("cannot fail with promise constructor");
// 5. Let return be Completion(GetMethod(syncIterator, "return")). // 5. Let return be Completion(GetMethod(syncIterator, "return")).
let r#return = sync_iterator.get_method("return", context); let r#return = sync_iterator.get_method(utf16!("return"), context);
// 6. IfAbruptRejectPromise(return, promiseCapability). // 6. IfAbruptRejectPromise(return, promiseCapability).
if_abrupt_reject_promise!(r#return, promise_capability, context); if_abrupt_reject_promise!(r#return, promise_capability, context);
@ -253,7 +254,7 @@ impl AsyncFromSyncIterator {
.expect("cannot fail with promise constructor"); .expect("cannot fail with promise constructor");
// 5. Let throw be Completion(GetMethod(syncIterator, "throw")). // 5. Let throw be Completion(GetMethod(syncIterator, "throw")).
let throw = sync_iterator.get_method("throw", context); let throw = sync_iterator.get_method(utf16!("throw"), context);
// 6. IfAbruptRejectPromise(throw, promiseCapability). // 6. IfAbruptRejectPromise(throw, promiseCapability).
if_abrupt_reject_promise!(throw, promise_capability, context); if_abrupt_reject_promise!(throw, promise_capability, context);

13
boa_engine/src/builtins/iterable/mod.rs

@ -5,6 +5,7 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -188,10 +189,10 @@ pub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Conte
let obj = JsObject::with_object_proto(context); let obj = JsObject::with_object_proto(context);
// 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value). // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
obj.create_data_property_or_throw("value", value, context) obj.create_data_property_or_throw(utf16!("value"), value, context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
obj.create_data_property_or_throw("done", done, context) obj.create_data_property_or_throw(utf16!("done"), done, context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 5. Return obj. // 5. Return obj.
obj.into() obj.into()
@ -265,7 +266,7 @@ impl JsValue {
})?; })?;
// 5. Let nextMethod be ? GetV(iterator, "next"). // 5. Let nextMethod be ? GetV(iterator, "next").
let next_method = iterator.get_v("next", context)?; let next_method = iterator.get_v(utf16!("next"), context)?;
// 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. // 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 7. Return iteratorRecord. // 7. Return iteratorRecord.
@ -301,7 +302,7 @@ impl IteratorResult {
#[inline] #[inline]
pub fn complete(&self, context: &mut Context<'_>) -> JsResult<bool> { pub fn complete(&self, context: &mut Context<'_>) -> JsResult<bool> {
// 1. Return ToBoolean(? Get(iterResult, "done")). // 1. Return ToBoolean(? Get(iterResult, "done")).
Ok(self.object.get("done", context)?.to_boolean()) Ok(self.object.get(utf16!("done"), context)?.to_boolean())
} }
/// `IteratorValue ( iterResult )` /// `IteratorValue ( iterResult )`
@ -317,7 +318,7 @@ impl IteratorResult {
#[inline] #[inline]
pub fn value(&self, context: &mut Context<'_>) -> JsResult<JsValue> { pub fn value(&self, context: &mut Context<'_>) -> JsResult<JsValue> {
// 1. Return ? Get(iterResult, "value"). // 1. Return ? Get(iterResult, "value").
self.object.get("value", context) self.object.get(utf16!("value"), context)
} }
} }
@ -478,7 +479,7 @@ impl IteratorRecord {
let iterator = &self.iterator; let iterator = &self.iterator;
// 3. Let innerResult be Completion(GetMethod(iterator, "return")). // 3. Let innerResult be Completion(GetMethod(iterator, "return")).
let inner_result = iterator.get_method("return", context); let inner_result = iterator.get_method(utf16!("return"), context);
// 4. If innerResult.[[Type]] is normal, then // 4. If innerResult.[[Type]] is normal, then
let inner_result = match inner_result { let inner_result = match inner_result {

6
boa_engine/src/builtins/json/mod.rs

@ -216,7 +216,7 @@ impl Json {
// b. Let rootName be the empty String. // b. Let rootName be the empty String.
// c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). // c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered).
root.create_data_property_or_throw("", unfiltered, context) root.create_data_property_or_throw(utf16!(""), unfiltered, context)
.expect("CreateDataPropertyOrThrow should never throw here"); .expect("CreateDataPropertyOrThrow should never throw here");
// d. Return ? InternalizeJSONProperty(root, rootName, reviver). // d. Return ? InternalizeJSONProperty(root, rootName, reviver).
@ -448,7 +448,7 @@ impl Json {
// 10. Perform ! CreateDataPropertyOrThrow(wrapper, the empty String, value). // 10. Perform ! CreateDataPropertyOrThrow(wrapper, the empty String, value).
wrapper wrapper
.create_data_property_or_throw("", args.get_or_undefined(0).clone(), context) .create_data_property_or_throw(utf16!(""), args.get_or_undefined(0).clone(), context)
.expect("CreateDataPropertyOrThrow should never fail here"); .expect("CreateDataPropertyOrThrow should never fail here");
// 11. Let state be the Record { [[ReplacerFunction]]: ReplacerFunction, [[Stack]]: stack, [[Indent]]: indent, [[Gap]]: gap, [[PropertyList]]: PropertyList }. // 11. Let state be the Record { [[ReplacerFunction]]: ReplacerFunction, [[Stack]]: stack, [[Indent]]: indent, [[Gap]]: gap, [[PropertyList]]: PropertyList }.
@ -486,7 +486,7 @@ impl Json {
// 2. If Type(value) is Object or BigInt, then // 2. If Type(value) is Object or BigInt, then
if value.is_object() || value.is_bigint() { if value.is_object() || value.is_bigint() {
// a. Let toJSON be ? GetV(value, "toJSON"). // a. Let toJSON be ? GetV(value, "toJSON").
let to_json = value.get_v("toJSON", context)?; let to_json = value.get_v(utf16!("toJSON"), context)?;
// b. If IsCallable(toJSON) is true, then // b. If IsCallable(toJSON) is true, then
if let Some(obj) = to_json.as_object() { if let Some(obj) = to_json.as_object() {

12
boa_engine/src/builtins/map/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -62,7 +63,7 @@ impl IntrinsicObject for Map {
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(
"entries", utf16!("entries"),
entries_function.clone(), entries_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
@ -84,7 +85,12 @@ impl IntrinsicObject for Map {
.method(Self::keys, "keys", 0) .method(Self::keys, "keys", 0)
.method(Self::set, "set", 2) .method(Self::set, "set", 2)
.method(Self::values, "values", 0) .method(Self::values, "values", 0)
.accessor("size", Some(get_size), None, Attribute::CONFIGURABLE) .accessor(
utf16!("size"),
Some(get_size),
None,
Attribute::CONFIGURABLE,
)
.build(); .build();
} }
@ -138,7 +144,7 @@ impl BuiltInConstructor for Map {
}; };
// 5. Let adder be ? Get(map, "set"). // 5. Let adder be ? Get(map, "set").
let adder = map.get("set", context)?; let adder = map.get(utf16!("set"), context)?;
// 6. Return ? AddEntriesFromIterable(map, iterable, adder). // 6. Return ? AddEntriesFromIterable(map, iterable, adder).
add_entries_from_iterable(&map, iterable, &adder, context) add_entries_from_iterable(&map, iterable, &adder, context)

22
boa_engine/src/builtins/math/mod.rs

@ -13,7 +13,7 @@
use crate::{ use crate::{
builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject, builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject,
property::Attribute, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, property::Attribute, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,14 +32,18 @@ impl IntrinsicObject for Math {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::with_intrinsic::<Self>(intrinsics) BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
.static_property("E", std::f64::consts::E, attribute) .static_property(utf16!("E"), std::f64::consts::E, attribute)
.static_property("LN10", std::f64::consts::LN_10, attribute) .static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute)
.static_property("LN2", std::f64::consts::LN_2, attribute) .static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute)
.static_property("LOG10E", std::f64::consts::LOG10_E, attribute) .static_property(utf16!("LOG10E"), std::f64::consts::LOG10_E, attribute)
.static_property("LOG2E", std::f64::consts::LOG2_E, attribute) .static_property(utf16!("LOG2E"), std::f64::consts::LOG2_E, attribute)
.static_property("PI", std::f64::consts::PI, attribute) .static_property(utf16!("PI"), std::f64::consts::PI, attribute)
.static_property("SQRT1_2", std::f64::consts::FRAC_1_SQRT_2, attribute) .static_property(
.static_property("SQRT2", std::f64::consts::SQRT_2, attribute) utf16!("SQRT1_2"),
std::f64::consts::FRAC_1_SQRT_2,
attribute,
)
.static_property(utf16!("SQRT2"), std::f64::consts::SQRT_2, attribute)
.static_method(Self::abs, "abs", 1) .static_method(Self::abs, "abs", 1)
.static_method(Self::acos, "acos", 1) .static_method(Self::acos, "acos", 1)
.static_method(Self::acosh, "acosh", 1) .static_method(Self::acosh, "acosh", 1)

19
boa_engine/src/builtins/mod.rs

@ -97,6 +97,7 @@ use crate::{
FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE, FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE,
}, },
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
string::utf16,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -273,7 +274,7 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
let global_object = context.global_object().clone(); let global_object = context.global_object().clone();
global_object.define_property_or_throw( global_object.define_property_or_throw(
"globalThis", utf16!("globalThis"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(context.realm.global_this().clone()) .value(context.realm.global_this().clone())
.writable(true) .writable(true)
@ -286,13 +287,17 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
.enumerable(false) .enumerable(false)
.configurable(false); .configurable(false);
global_object.define_property_or_throw( global_object.define_property_or_throw(
"Infinity", utf16!("Infinity"),
restricted.clone().value(f64::INFINITY), restricted.clone().value(f64::INFINITY),
context, context,
)?; )?;
global_object.define_property_or_throw("NaN", restricted.clone().value(f64::NAN), context)?;
global_object.define_property_or_throw( global_object.define_property_or_throw(
"undefined", utf16!("NaN"),
restricted.clone().value(f64::NAN),
context,
)?;
global_object.define_property_or_throw(
utf16!("undefined"),
restricted.value(JsValue::undefined()), restricted.value(JsValue::undefined()),
context, context,
)?; )?;
@ -357,7 +362,7 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
let object = Console::init(context); let object = Console::init(context);
let global_object = context.global_object().clone(); let global_object = context.global_object().clone();
global_object.define_property_or_throw( global_object.define_property_or_throw(
"console", utf16!("console"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(object) .value(object)
.writable(true) .writable(true)
@ -475,8 +480,8 @@ impl<S: ApplyToObject + IsConstructor> ApplyToObject for Callable<S> {
{ {
let mut constructor = object.borrow_mut(); let mut constructor = object.borrow_mut();
constructor.data = ObjectData::function(function); constructor.data = ObjectData::function(function);
constructor.insert("length", length); constructor.insert(utf16!("length"), length);
constructor.insert("name", name); constructor.insert(utf16!("name"), name);
} }
} }
} }

29
boa_engine/src/builtins/number/mod.rs

@ -19,6 +19,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
value::{AbstractRelation, IntegerOrInfinity, JsValue}, value::{AbstractRelation, IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
}; };
@ -50,21 +51,29 @@ impl IntrinsicObject for Number {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.static_property("EPSILON", f64::EPSILON, attribute) .static_property(utf16!("EPSILON"), f64::EPSILON, attribute)
.static_property("MAX_SAFE_INTEGER", Self::MAX_SAFE_INTEGER, attribute)
.static_property("MIN_SAFE_INTEGER", Self::MIN_SAFE_INTEGER, attribute)
.static_property("MAX_VALUE", Self::MAX_VALUE, attribute)
.static_property("MIN_VALUE", Self::MIN_VALUE, attribute)
.static_property("NEGATIVE_INFINITY", f64::NEG_INFINITY, attribute)
.static_property("POSITIVE_INFINITY", f64::INFINITY, attribute)
.static_property("NaN", f64::NAN, attribute)
.static_property( .static_property(
"parseInt", utf16!("MAX_SAFE_INTEGER"),
Self::MAX_SAFE_INTEGER,
attribute,
)
.static_property(
utf16!("MIN_SAFE_INTEGER"),
Self::MIN_SAFE_INTEGER,
attribute,
)
.static_property(utf16!("MAX_VALUE"), Self::MAX_VALUE, attribute)
.static_property(utf16!("MIN_VALUE"), Self::MIN_VALUE, attribute)
.static_property(utf16!("NEGATIVE_INFINITY"), f64::NEG_INFINITY, attribute)
.static_property(utf16!("POSITIVE_INFINITY"), f64::INFINITY, attribute)
.static_property(utf16!("NaN"), f64::NAN, attribute)
.static_property(
utf16!("parseInt"),
intrinsics.objects().parse_int(), intrinsics.objects().parse_int(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.static_property( .static_property(
"parseFloat", utf16!("parseFloat"),
intrinsics.objects().parse_float(), intrinsics.objects().parse_float(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )

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

@ -60,7 +60,7 @@ impl IntrinsicObject for Object {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.inherits(None) .inherits(None)
.accessor( .accessor(
"__proto__", utf16!("__proto__"),
Some(legacy_proto_getter), Some(legacy_proto_getter),
Some(legacy_setter_proto), Some(legacy_setter_proto),
Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
@ -526,42 +526,42 @@ impl Object {
// 4. If Desc has a [[Value]] field, then // 4. If Desc has a [[Value]] field, then
if let Some(value) = desc.value() { if let Some(value) = desc.value() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]). // a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]).
obj.create_data_property_or_throw("value", value.clone(), context) obj.create_data_property_or_throw(utf16!("value"), value.clone(), context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
} }
// 5. If Desc has a [[Writable]] field, then // 5. If Desc has a [[Writable]] field, then
if let Some(writable) = desc.writable() { if let Some(writable) = desc.writable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]). // a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]).
obj.create_data_property_or_throw("writable", writable, context) obj.create_data_property_or_throw(utf16!("writable"), writable, context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
} }
// 6. If Desc has a [[Get]] field, then // 6. If Desc has a [[Get]] field, then
if let Some(get) = desc.get() { if let Some(get) = desc.get() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]). // a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]).
obj.create_data_property_or_throw("get", get.clone(), context) obj.create_data_property_or_throw(utf16!("get"), get.clone(), context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
} }
// 7. If Desc has a [[Set]] field, then // 7. If Desc has a [[Set]] field, then
if let Some(set) = desc.set() { if let Some(set) = desc.set() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]). // a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]).
obj.create_data_property_or_throw("set", set.clone(), context) obj.create_data_property_or_throw(utf16!("set"), set.clone(), context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
} }
// 8. If Desc has an [[Enumerable]] field, then // 8. If Desc has an [[Enumerable]] field, then
if let Some(enumerable) = desc.enumerable() { if let Some(enumerable) = desc.enumerable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]). // a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]).
obj.create_data_property_or_throw("enumerable", enumerable, context) obj.create_data_property_or_throw(utf16!("enumerable"), enumerable, context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
} }
// 9. If Desc has a [[Configurable]] field, then // 9. If Desc has a [[Configurable]] field, then
if let Some(configurable) = desc.configurable() { if let Some(configurable) = desc.configurable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]). // a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]).
obj.create_data_property_or_throw("configurable", configurable, context) obj.create_data_property_or_throw(utf16!("configurable"), configurable, context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
} }
@ -843,7 +843,7 @@ impl Object {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. Return ? Invoke(O, "toString"). // 2. Return ? Invoke(O, "toString").
this.invoke("toString", &[], context) this.invoke(utf16!("toString"), &[], context)
} }
/// `Object.prototype.hasOwnProperty( property )` /// `Object.prototype.hasOwnProperty( property )`

39
boa_engine/src/builtins/promise/mod.rs

@ -15,6 +15,7 @@ use crate::{
JsObject, ObjectData, CONSTRUCTOR, JsObject, ObjectData, CONSTRUCTOR,
}, },
property::{Attribute, PropertyDescriptorBuilder}, property::{Attribute, PropertyDescriptorBuilder},
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult,
@ -622,7 +623,7 @@ impl Promise {
// s. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »). // s. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »).
next_promise.invoke( next_promise.invoke(
"then", utf16!("then"),
&[on_fulfilled.into(), result_capability.reject.clone().into()], &[on_fulfilled.into(), result_capability.reject.clone().into()],
context, context,
)?; )?;
@ -826,12 +827,12 @@ impl Promise {
let obj = JsObject::with_object_proto(context); let obj = JsObject::with_object_proto(context);
// 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled"). // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled").
obj.create_data_property_or_throw("status", "fulfilled", context) obj.create_data_property_or_throw(utf16!("status"), "fulfilled", context)
.expect("cannot fail per spec"); .expect("cannot fail per spec");
// 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x).
obj.create_data_property_or_throw( obj.create_data_property_or_throw(
"value", utf16!("value"),
args.get_or_undefined(0).clone(), args.get_or_undefined(0).clone(),
context, context,
) )
@ -912,12 +913,12 @@ impl Promise {
let obj = JsObject::with_object_proto(context); let obj = JsObject::with_object_proto(context);
// 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected"). // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected").
obj.create_data_property_or_throw("status", "rejected", context) obj.create_data_property_or_throw(utf16!("status"), "rejected", context)
.expect("cannot fail per spec"); .expect("cannot fail per spec");
// 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x).
obj.create_data_property_or_throw( obj.create_data_property_or_throw(
"reason", utf16!("reason"),
args.get_or_undefined(0).clone(), args.get_or_undefined(0).clone(),
context, context,
) )
@ -968,7 +969,11 @@ impl Promise {
remaining_elements_count.set(remaining_elements_count.get() + 1); remaining_elements_count.set(remaining_elements_count.get() + 1);
// ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). // ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »).
next_promise.invoke("then", &[on_fulfilled.into(), on_rejected.into()], context)?; next_promise.invoke(
utf16!("then"),
&[on_fulfilled.into(), on_rejected.into()],
context,
)?;
// ac. Set index to index + 1. // ac. Set index to index + 1.
index += 1; index += 1;
@ -1106,7 +1111,7 @@ impl Promise {
// 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }). // 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).
error error
.define_property_or_throw( .define_property_or_throw(
"errors", utf16!("errors"),
PropertyDescriptorBuilder::new() PropertyDescriptorBuilder::new()
.configurable(true) .configurable(true)
.enumerable(false) .enumerable(false)
@ -1203,7 +1208,7 @@ impl Promise {
// b. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }). // b. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).
error error
.define_property_or_throw( .define_property_or_throw(
"errors", utf16!("errors"),
PropertyDescriptorBuilder::new() PropertyDescriptorBuilder::new()
.configurable(true) .configurable(true)
.enumerable(false) .enumerable(false)
@ -1245,7 +1250,7 @@ impl Promise {
// s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »). // s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »).
next_promise.invoke( next_promise.invoke(
"then", utf16!("then"),
&[result_capability.resolve.clone().into(), on_rejected.into()], &[result_capability.resolve.clone().into(), on_rejected.into()],
context, context,
)?; )?;
@ -1334,7 +1339,7 @@ impl Promise {
}; };
// 9. Let then be Completion(Get(resolution, "then")). // 9. Let then be Completion(Get(resolution, "then")).
let then_action = match then.get("then", context) { let then_action = match then.get(utf16!("then"), context) {
// 10. If then is an abrupt completion, then // 10. If then is an abrupt completion, then
Err(e) => { Err(e) => {
// a. Perform RejectPromise(promise, then.[[Value]]). // a. Perform RejectPromise(promise, then.[[Value]]).
@ -1677,7 +1682,7 @@ impl Promise {
// i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »). // i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »).
next_promise.invoke( next_promise.invoke(
"then", utf16!("then"),
&[ &[
result_capability.resolve.clone().into(), result_capability.resolve.clone().into(),
result_capability.reject.clone().into(), result_capability.reject.clone().into(),
@ -1778,7 +1783,7 @@ impl Promise {
let promise = this; let promise = this;
// 2. Return ? Invoke(promise, "then", « undefined, onRejected »). // 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
promise.invoke( promise.invoke(
"then", utf16!("then"),
&[JsValue::undefined(), on_rejected.clone()], &[JsValue::undefined(), on_rejected.clone()],
context, context,
) )
@ -1820,7 +1825,7 @@ impl Promise {
// a. Let thenFinally be onFinally. // a. Let thenFinally be onFinally.
// b. Let catchFinally be onFinally. // b. Let catchFinally be onFinally.
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
let then = promise.get("then", context)?; let then = promise.get(utf16!("then"), context)?;
return then.call(this, &[on_finally.clone(), on_finally.clone()], context); return then.call(this, &[on_finally.clone(), on_finally.clone()], context);
}; };
@ -1872,7 +1877,7 @@ impl Promise {
let value_thunk = return_value.length(0).name("").build(); let value_thunk = return_value.length(0).name("").build();
// v. Return ? Invoke(promise, "then", « valueThunk »). // v. Return ? Invoke(promise, "then", « valueThunk »).
promise.invoke("then", &[value_thunk.into()], context) promise.invoke(utf16!("then"), &[value_thunk.into()], context)
}, },
FinallyCaptures { FinallyCaptures {
on_finally: on_finally.clone(), on_finally: on_finally.clone(),
@ -1924,7 +1929,7 @@ impl Promise {
let thrower = throw_reason.length(0).name("").build(); let thrower = throw_reason.length(0).name("").build();
// v. Return ? Invoke(promise, "then", « thrower »). // v. Return ? Invoke(promise, "then", « thrower »).
promise.invoke("then", &[thrower.into()], context) promise.invoke(utf16!("then"), &[thrower.into()], context)
}, },
FinallyCaptures { FinallyCaptures {
on_finally: on_finally.clone(), on_finally: on_finally.clone(),
@ -1940,7 +1945,7 @@ impl Promise {
}; };
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
let then = promise.get("then", context)?; let then = promise.get(utf16!("then"), context)?;
then.call(this, &[then_finally, catch_finally], context) then.call(this, &[then_finally, catch_finally], context)
} }
@ -2157,7 +2162,7 @@ impl Promise {
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
// 1. Let promiseResolve be ? Get(promiseConstructor, "resolve"). // 1. Let promiseResolve be ? Get(promiseConstructor, "resolve").
let promise_resolve = promise_constructor.get("resolve", context)?; let promise_resolve = promise_constructor.get(utf16!("resolve"), context)?;
// 2. If IsCallable(promiseResolve) is false, throw a TypeError exception. // 2. If IsCallable(promiseResolve) is false, throw a TypeError exception.
promise_resolve.as_callable().cloned().ok_or_else(|| { promise_resolve.as_callable().cloned().ok_or_else(|| {

5
boa_engine/src/builtins/proxy/mod.rs

@ -18,6 +18,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData}, object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData},
string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -187,12 +188,12 @@ impl Proxy {
// 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p). // 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p).
result result
.create_data_property_or_throw("proxy", p, context) .create_data_property_or_throw(utf16!("proxy"), p, context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
// 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker). // 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker).
result result
.create_data_property_or_throw("revoke", revoker, context) .create_data_property_or_throw(utf16!("revoke"), revoker, context)
.expect("CreateDataPropertyOrThrow cannot fail here"); .expect("CreateDataPropertyOrThrow cannot fail here");
// 8. Return result. // 8. Return result.

2
boa_engine/src/builtins/reflect/mod.rs

@ -167,7 +167,7 @@ impl Reflect {
.into(); .into();
target target
.__define_own_property__(key, prop_desc.to_property_descriptor(context)?, context) .__define_own_property__(&key, prop_desc.to_property_descriptor(context)?, context)
.map(Into::into) .map(Into::into)
} }

128
boa_engine/src/builtins/regexp/mod.rs

@ -97,7 +97,7 @@ impl IntrinsicObject for RegExp {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property("lastIndex", 0, Attribute::all()) .property(utf16!("lastIndex"), 0, Attribute::all())
.method(Self::test, "test", 1) .method(Self::test, "test", 1)
.method(Self::exec, "exec", 1) .method(Self::exec, "exec", 1)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
@ -110,15 +110,30 @@ impl IntrinsicObject for RegExp {
.method(Self::replace, (JsSymbol::replace(), "[Symbol.replace]"), 2) .method(Self::replace, (JsSymbol::replace(), "[Symbol.replace]"), 2)
.method(Self::search, (JsSymbol::search(), "[Symbol.search]"), 1) .method(Self::search, (JsSymbol::search(), "[Symbol.search]"), 1)
.method(Self::split, (JsSymbol::split(), "[Symbol.split]"), 2) .method(Self::split, (JsSymbol::split(), "[Symbol.split]"), 2)
.accessor("hasIndices", Some(get_has_indices), None, flag_attributes) .accessor(
.accessor("global", Some(get_global), None, flag_attributes) utf16!("hasIndices"),
.accessor("ignoreCase", Some(get_ignore_case), None, flag_attributes) Some(get_has_indices),
.accessor("multiline", Some(get_multiline), None, flag_attributes) None,
.accessor("dotAll", Some(get_dot_all), None, flag_attributes) flag_attributes,
.accessor("unicode", Some(get_unicode), None, flag_attributes) )
.accessor("sticky", Some(get_sticky), None, flag_attributes) .accessor(utf16!("global"), Some(get_global), None, flag_attributes)
.accessor("flags", Some(get_flags), None, flag_attributes) .accessor(
.accessor("source", Some(get_source), None, flag_attributes) utf16!("ignoreCase"),
Some(get_ignore_case),
None,
flag_attributes,
)
.accessor(
utf16!("multiline"),
Some(get_multiline),
None,
flag_attributes,
)
.accessor(utf16!("dotAll"), Some(get_dot_all), None, flag_attributes)
.accessor(utf16!("unicode"), Some(get_unicode), None, flag_attributes)
.accessor(utf16!("sticky"), Some(get_sticky), None, flag_attributes)
.accessor(utf16!("flags"), Some(get_flags), None, flag_attributes)
.accessor(utf16!("source"), Some(get_source), None, flag_attributes)
.build(); .build();
} }
@ -219,7 +234,7 @@ impl RegExp {
// 2. Perform ! DefinePropertyOrThrow(obj, "lastIndex", PropertyDescriptor { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). // 2. Perform ! DefinePropertyOrThrow(obj, "lastIndex", PropertyDescriptor { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
obj.define_property_or_throw( obj.define_property_or_throw(
"lastIndex", utf16!("lastIndex"),
PropertyDescriptorBuilder::new() PropertyDescriptorBuilder::new()
.writable(true) .writable(true)
.enumerable(false) .enumerable(false)
@ -298,7 +313,7 @@ impl RegExp {
obj.borrow_mut().data = ObjectData::reg_exp(Box::new(regexp)); obj.borrow_mut().data = ObjectData::reg_exp(Box::new(regexp));
// 16. Perform ? Set(obj, "lastIndex", +0𝔽, true). // 16. Perform ? Set(obj, "lastIndex", +0𝔽, true).
obj.set("lastIndex", 0, true, context)?; obj.set(utf16!("lastIndex"), 0, true, context)?;
// 16. Return obj. // 16. Return obj.
Ok(obj.into()) Ok(obj.into())
@ -528,41 +543,41 @@ impl RegExp {
// 4. Let hasIndices be ToBoolean(? Get(R, "hasIndices")). // 4. Let hasIndices be ToBoolean(? Get(R, "hasIndices")).
// 5. If hasIndices is true, append the code unit 0x0064 (LATIN SMALL LETTER D) as the last code unit of result. // 5. If hasIndices is true, append the code unit 0x0064 (LATIN SMALL LETTER D) as the last code unit of result.
if object.get("hasIndices", context)?.to_boolean() { if object.get(utf16!("hasIndices"), context)?.to_boolean() {
result.push('d'); result.push('d');
} }
// 6. Let global be ! ToBoolean(? Get(R, "global")). // 6. Let global be ! ToBoolean(? Get(R, "global")).
// 7. If global is true, append the code unit 0x0067 (LATIN SMALL LETTER G) as the last code unit of result. // 7. 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() { if object.get(utf16!("global"), context)?.to_boolean() {
result.push('g'); result.push('g');
} }
// 8. Let ignoreCase be ! ToBoolean(? Get(R, "ignoreCase")). // 8. Let ignoreCase be ! ToBoolean(? Get(R, "ignoreCase")).
// 9. If ignoreCase is true, append the code unit 0x0069 (LATIN SMALL LETTER I) as the last code unit of result. // 9. 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() { if object.get(utf16!("ignoreCase"), context)?.to_boolean() {
result.push('i'); result.push('i');
} }
// 10. Let multiline be ! ToBoolean(? Get(R, "multiline")). // 10. Let multiline be ! ToBoolean(? Get(R, "multiline")).
// 11. If multiline is true, append the code unit 0x006D (LATIN SMALL LETTER M) as the last code unit of result. // 11. 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() { if object.get(utf16!("multiline"), context)?.to_boolean() {
result.push('m'); result.push('m');
} }
// 12. Let dotAll be ! ToBoolean(? Get(R, "dotAll")). // 12. Let dotAll be ! ToBoolean(? Get(R, "dotAll")).
// 13. If dotAll is true, append the code unit 0x0073 (LATIN SMALL LETTER S) as the last code unit of result. // 13. 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() { if object.get(utf16!("dotAll"), context)?.to_boolean() {
result.push('s'); result.push('s');
} }
// 14. Let unicode be ! ToBoolean(? Get(R, "unicode")). // 14. Let unicode be ! ToBoolean(? Get(R, "unicode")).
// 15. If unicode is true, append the code unit 0x0075 (LATIN SMALL LETTER U) as the last code unit of result. // 15. 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() { if object.get(utf16!("unicode"), context)?.to_boolean() {
result.push('u'); result.push('u');
} }
// 16. Let sticky be ! ToBoolean(? Get(R, "sticky")). // 16. Let sticky be ! ToBoolean(? Get(R, "sticky")).
// 17. If sticky is true, append the code unit 0x0079 (LATIN SMALL LETTER Y) as the last code unit of result. // 17. 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() { if object.get(utf16!("sticky"), context)?.to_boolean() {
result.push('y'); result.push('y');
} }
@ -747,7 +762,7 @@ impl RegExp {
// 2. Assert: Type(S) is String. // 2. Assert: Type(S) is String.
// 3. Let exec be ? Get(R, "exec"). // 3. Let exec be ? Get(R, "exec").
let exec = this.get("exec", context)?; let exec = this.get(utf16!("exec"), context)?;
// 4. If IsCallable(exec) is true, then // 4. If IsCallable(exec) is true, then
if let Some(exec) = exec.as_callable() { if let Some(exec) = exec.as_callable() {
@ -805,7 +820,7 @@ impl RegExp {
let length = input.len() as u64; let length = input.len() as u64;
// 4. Let lastIndex be ℝ(? ToLength(? Get(R, "lastIndex"))). // 4. Let lastIndex be ℝ(? ToLength(? Get(R, "lastIndex"))).
let mut last_index = this.get("lastIndex", context)?.to_length(context)?; let mut last_index = this.get(utf16!("lastIndex"), context)?.to_length(context)?;
// 5. Let flags be R.[[OriginalFlags]]. // 5. Let flags be R.[[OriginalFlags]].
let flags = &rx.original_flags; let flags = &rx.original_flags;
@ -835,7 +850,7 @@ impl RegExp {
// i. If global is true or sticky is true, then // i. If global is true or sticky is true, then
if global || sticky { if global || sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true). // 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
this.set("lastIndex", 0, true, context)?; this.set(utf16!("lastIndex"), 0, true, context)?;
} }
// ii. Return null. // ii. Return null.
@ -863,7 +878,7 @@ impl RegExp {
// i. If sticky is true, then // i. If sticky is true, then
if sticky { if sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true). // 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
this.set("lastIndex", 0, true, context)?; this.set(utf16!("lastIndex"), 0, true, context)?;
// 2. Return null. // 2. Return null.
return Ok(None); return Ok(None);
@ -880,7 +895,7 @@ impl RegExp {
// i. If sticky is true, then // i. If sticky is true, then
if sticky { if sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true). // 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
this.set("lastIndex", 0, true, context)?; this.set(utf16!("lastIndex"), 0, true, context)?;
// 2. Return null. // 2. Return null.
return Ok(None); return Ok(None);
@ -916,7 +931,7 @@ impl RegExp {
if global || sticky { if global || sticky {
// a. Perform ? Set(R, "lastIndex", 𝔽(e), true). // a. Perform ? Set(R, "lastIndex", 𝔽(e), true).
this.set( this.set(
"lastIndex", utf16!("lastIndex"),
lossy_input[..e].encode_utf16().count(), lossy_input[..e].encode_utf16().count(),
true, true,
context, context,
@ -933,11 +948,11 @@ impl RegExp {
let a = Array::array_create(n + 1, None, context)?; let a = Array::array_create(n + 1, None, context)?;
// 20. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)). // 20. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)).
a.create_data_property_or_throw("index", match_value.start(), context) a.create_data_property_or_throw(utf16!("index"), match_value.start(), context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 21. Perform ! CreateDataPropertyOrThrow(A, "input", S). // 21. Perform ! CreateDataPropertyOrThrow(A, "input", S).
a.create_data_property_or_throw("input", input.clone(), context) a.create_data_property_or_throw(utf16!("input"), input.clone(), context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 22. Let matchedSubstr be the substring of S from lastIndex to e. // 22. Let matchedSubstr be the substring of S from lastIndex to e.
@ -975,7 +990,7 @@ impl RegExp {
}; };
// 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). // 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
a.create_data_property_or_throw("groups", groups, context) a.create_data_property_or_throw(utf16!("groups"), groups, context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do // 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do
@ -1026,7 +1041,7 @@ impl RegExp {
let arg_str = args.get_or_undefined(0).to_string(context)?; let arg_str = args.get_or_undefined(0).to_string(context)?;
// 4. Let global be ! ToBoolean(? Get(rx, "global")). // 4. Let global be ! ToBoolean(? Get(rx, "global")).
let global = rx.get("global", context)?.to_boolean(); let global = rx.get(utf16!("global"), context)?.to_boolean();
// 5. If global is false, then // 5. If global is false, then
#[allow(clippy::if_not_else)] #[allow(clippy::if_not_else)]
@ -1039,10 +1054,10 @@ impl RegExp {
// a. Assert: global is true. // a. Assert: global is true.
// b. Let fullUnicode be ! ToBoolean(? Get(rx, "unicode")). // b. Let fullUnicode be ! ToBoolean(? Get(rx, "unicode")).
let unicode = rx.get("unicode", context)?.to_boolean(); let unicode = rx.get(utf16!("unicode"), context)?.to_boolean();
// c. Perform ? Set(rx, "lastIndex", +0𝔽, true). // c. Perform ? Set(rx, "lastIndex", +0𝔽, true).
rx.set("lastIndex", 0, true, context)?; rx.set(utf16!("lastIndex"), 0, true, context)?;
// d. Let A be ! ArrayCreate(0). // d. Let A be ! ArrayCreate(0).
let a = let a =
@ -1060,7 +1075,7 @@ impl RegExp {
// iii. Else, // iii. Else,
if let Some(result) = result { if let Some(result) = result {
// 1. Let matchStr be ? ToString(? Get(result, "0")). // 1. Let matchStr be ? ToString(? Get(result, "0")).
let match_str = result.get("0", context)?.to_string(context)?; let match_str = result.get(0, context)?.to_string(context)?;
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr). // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr).
a.create_data_property_or_throw(n, match_str.clone(), context) a.create_data_property_or_throw(n, match_str.clone(), context)
@ -1069,13 +1084,14 @@ impl RegExp {
// 3. If matchStr is the empty String, then // 3. If matchStr is the empty String, then
if match_str.is_empty() { if match_str.is_empty() {
// a. Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))). // a. Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))).
let this_index = rx.get("lastIndex", context)?.to_length(context)?; let this_index =
rx.get(utf16!("lastIndex"), context)?.to_length(context)?;
// b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). // b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
let next_index = advance_string_index(&arg_str, this_index, unicode); let next_index = advance_string_index(&arg_str, this_index, unicode);
// c. Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true). // c. Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true).
rx.set("lastIndex", JsValue::new(next_index), true, context)?; rx.set(utf16!("lastIndex"), JsValue::new(next_index), true, context)?;
} }
// 4. Set n to n + 1. // 4. Set n to n + 1.
@ -1157,16 +1173,18 @@ impl RegExp {
let c = regexp.species_constructor(StandardConstructors::regexp, context)?; let c = regexp.species_constructor(StandardConstructors::regexp, context)?;
// 5. Let flags be ? ToString(? Get(R, "flags")). // 5. Let flags be ? ToString(? Get(R, "flags")).
let flags = regexp.get("flags", context)?.to_string(context)?; let flags = regexp.get(utf16!("flags"), context)?.to_string(context)?;
// 6. Let matcher be ? Construct(C, « R, flags »). // 6. Let matcher be ? Construct(C, « R, flags »).
let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?; let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?;
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
let last_index = regexp.get("lastIndex", context)?.to_length(context)?; let last_index = regexp
.get(utf16!("lastIndex"), context)?
.to_length(context)?;
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
matcher.set("lastIndex", last_index, true, context)?; matcher.set(utf16!("lastIndex"), last_index, true, context)?;
// 9. If flags contains "g", let global be true. // 9. If flags contains "g", let global be true.
// 10. Else, let global be false. // 10. Else, let global be false.
@ -1228,16 +1246,16 @@ impl RegExp {
}; };
// 7. Let global be ! ToBoolean(? Get(rx, "global")). // 7. Let global be ! ToBoolean(? Get(rx, "global")).
let global = rx.get("global", context)?.to_boolean(); let global = rx.get(utf16!("global"), context)?.to_boolean();
// 8. If global is true, then // 8. If global is true, then
let mut unicode = false; let mut unicode = false;
if global { if global {
// a. Let fullUnicode be ! ToBoolean(? Get(rx, "unicode")). // a. Let fullUnicode be ! ToBoolean(? Get(rx, "unicode")).
unicode = rx.get("unicode", context)?.to_boolean(); unicode = rx.get(utf16!("unicode"), context)?.to_boolean();
// b. Perform ? Set(rx, "lastIndex", +0𝔽, true). // b. Perform ? Set(rx, "lastIndex", +0𝔽, true).
rx.set("lastIndex", 0, true, context)?; rx.set(utf16!("lastIndex"), 0, true, context)?;
} }
// 9. Let results be a new empty List. // 9. Let results be a new empty List.
@ -1262,18 +1280,18 @@ impl RegExp {
} }
// iii. Else, // iii. Else,
// 1. Let matchStr be ? ToString(? Get(result, "0")). // 1. Let matchStr be ? ToString(? Get(result, "0")).
let match_str = result.get("0", context)?.to_string(context)?; let match_str = result.get(0, context)?.to_string(context)?;
// 2. If matchStr is the empty String, then // 2. If matchStr is the empty String, then
if match_str.is_empty() { if match_str.is_empty() {
// a. Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))). // a. Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))).
let this_index = rx.get("lastIndex", context)?.to_length(context)?; let this_index = rx.get(utf16!("lastIndex"), context)?.to_length(context)?;
// b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). // b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
let next_index = advance_string_index(&arg_str, this_index, unicode); let next_index = advance_string_index(&arg_str, this_index, unicode);
// c. Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true). // c. Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true).
rx.set("lastIndex", JsValue::new(next_index), true, context)?; rx.set(utf16!("lastIndex"), JsValue::new(next_index), true, context)?;
} }
} else { } else {
break; break;
@ -1295,14 +1313,14 @@ impl RegExp {
let n_captures = std::cmp::max(result_length - 1, 0); let n_captures = std::cmp::max(result_length - 1, 0);
// c. Let matched be ? ToString(? Get(result, "0")). // c. Let matched be ? ToString(? Get(result, "0")).
let matched = result.get("0", context)?.to_string(context)?; let matched = result.get(0, context)?.to_string(context)?;
// d. Let matchLength be the number of code units in matched. // d. Let matchLength be the number of code units in matched.
let match_length = matched.len(); let match_length = matched.len();
// e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")). // e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")).
let position = result let position = result
.get("index", context)? .get(utf16!("index"), context)?
.to_integer_or_infinity(context)?; .to_integer_or_infinity(context)?;
// f. Set position to the result of clamping position between 0 and lengthS. // f. Set position to the result of clamping position between 0 and lengthS.
@ -1331,7 +1349,7 @@ impl RegExp {
} }
// j. Let namedCaptures be ? Get(result, "groups"). // j. Let namedCaptures be ? Get(result, "groups").
let mut named_captures = result.get("groups", context)?; let mut named_captures = result.get(utf16!("groups"), context)?;
// k. If functionalReplace is true, then // k. If functionalReplace is true, then
let replacement = match replace { let replacement = match replace {
@ -1430,31 +1448,31 @@ impl RegExp {
let arg_str = args.get_or_undefined(0).to_string(context)?; let arg_str = args.get_or_undefined(0).to_string(context)?;
// 4. Let previousLastIndex be ? Get(rx, "lastIndex"). // 4. Let previousLastIndex be ? Get(rx, "lastIndex").
let previous_last_index = rx.get("lastIndex", context)?; let previous_last_index = rx.get(utf16!("lastIndex"), context)?;
// 5. If SameValue(previousLastIndex, +0𝔽) is false, then // 5. If SameValue(previousLastIndex, +0𝔽) is false, then
if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) { if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) {
// a. Perform ? Set(rx, "lastIndex", +0𝔽, true). // a. Perform ? Set(rx, "lastIndex", +0𝔽, true).
rx.set("lastIndex", 0, true, context)?; rx.set(utf16!("lastIndex"), 0, true, context)?;
} }
// 6. Let result be ? RegExpExec(rx, S). // 6. Let result be ? RegExpExec(rx, S).
let result = Self::abstract_exec(rx, arg_str, context)?; let result = Self::abstract_exec(rx, arg_str, context)?;
// 7. Let currentLastIndex be ? Get(rx, "lastIndex"). // 7. Let currentLastIndex be ? Get(rx, "lastIndex").
let current_last_index = rx.get("lastIndex", context)?; let current_last_index = rx.get(utf16!("lastIndex"), context)?;
// 8. If SameValue(currentLastIndex, previousLastIndex) is false, then // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
if !JsValue::same_value(&current_last_index, &previous_last_index) { if !JsValue::same_value(&current_last_index, &previous_last_index) {
// a. Perform ? Set(rx, "lastIndex", previousLastIndex, true). // a. Perform ? Set(rx, "lastIndex", previousLastIndex, true).
rx.set("lastIndex", previous_last_index, true, context)?; rx.set(utf16!("lastIndex"), previous_last_index, true, context)?;
} }
// 9. If result is null, return -1𝔽. // 9. If result is null, return -1𝔽.
// 10. Return ? Get(result, "index"). // 10. Return ? Get(result, "index").
result.map_or_else( result.map_or_else(
|| Ok(JsValue::new(-1)), || Ok(JsValue::new(-1)),
|result| result.get("index", context), |result| result.get(utf16!("index"), context),
) )
} }
@ -1487,7 +1505,7 @@ impl RegExp {
let constructor = rx.species_constructor(StandardConstructors::regexp, context)?; let constructor = rx.species_constructor(StandardConstructors::regexp, context)?;
// 5. Let flags be ? ToString(? Get(rx, "flags")). // 5. Let flags be ? ToString(? Get(rx, "flags")).
let flags = rx.get("flags", context)?.to_string(context)?; let flags = rx.get(utf16!("flags"), context)?.to_string(context)?;
// 6. If flags contains "u", let unicodeMatching be true. // 6. If flags contains "u", let unicodeMatching be true.
// 7. Else, let unicodeMatching be false. // 7. Else, let unicodeMatching be false.
@ -1556,7 +1574,7 @@ impl RegExp {
// 19. Repeat, while q < size, // 19. Repeat, while q < size,
while q < size { while q < size {
// a. Perform ? Set(splitter, "lastIndex", 𝔽(q), true). // a. Perform ? Set(splitter, "lastIndex", 𝔽(q), true).
splitter.set("lastIndex", JsValue::new(q), true, context)?; splitter.set(utf16!("lastIndex"), JsValue::new(q), true, context)?;
// b. Let z be ? RegExpExec(splitter, S). // b. Let z be ? RegExpExec(splitter, S).
let result = Self::abstract_exec(&splitter, arg_str.clone(), context)?; let result = Self::abstract_exec(&splitter, arg_str.clone(), context)?;
@ -1565,7 +1583,9 @@ impl RegExp {
// d. Else, // d. Else,
if let Some(result) = result { if let Some(result) = result {
// i. Let e be ℝ(? ToLength(? Get(splitter, "lastIndex"))). // i. Let e be ℝ(? ToLength(? Get(splitter, "lastIndex"))).
let mut e = splitter.get("lastIndex", context)?.to_length(context)?; let mut e = splitter
.get(utf16!("lastIndex"), context)?
.to_length(context)?;
// ii. Set e to min(e, size). // ii. Set e to min(e, size).
e = std::cmp::min(e, size); e = std::cmp::min(e, size);

7
boa_engine/src/builtins/regexp/regexp_string_iterator.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -144,14 +145,14 @@ impl RegExpStringIterator {
} }
// iv. Let matchStr be ? ToString(? Get(match, "0")). // iv. Let matchStr be ? ToString(? Get(match, "0")).
let m_str = m.get("0", context)?.to_string(context)?; let m_str = m.get(0, context)?.to_string(context)?;
// v. If matchStr is the empty String, then // v. If matchStr is the empty String, then
if m_str.is_empty() { if m_str.is_empty() {
// 1. Let thisIndex be ℝ(? ToLength(? Get(R, "lastIndex"))). // 1. Let thisIndex be ℝ(? ToLength(? Get(R, "lastIndex"))).
let this_index = iterator let this_index = iterator
.matcher .matcher
.get("lastIndex", context)? .get(utf16!("lastIndex"), context)?
.to_length(context)?; .to_length(context)?;
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode). // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
@ -161,7 +162,7 @@ impl RegExpStringIterator {
// 3. Perform ? Set(R, "lastIndex", 𝔽(nextIndex), true). // 3. Perform ? Set(R, "lastIndex", 𝔽(nextIndex), true).
iterator iterator
.matcher .matcher
.set("lastIndex", next_index, true, context)?; .set(utf16!("lastIndex"), next_index, true, context)?;
} }
// vi. Perform ? Yield(match). // vi. Perform ? Yield(match).

14
boa_engine/src/builtins/set/mod.rs

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -74,13 +75,18 @@ impl IntrinsicObject for Set {
.method(Self::for_each, "forEach", 1) .method(Self::for_each, "forEach", 1)
.method(Self::has, "has", 1) .method(Self::has, "has", 1)
.property( .property(
"keys", utf16!("keys"),
values_function.clone(), values_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.accessor("size", Some(size_getter), None, Attribute::CONFIGURABLE) .accessor(
utf16!("size"),
Some(size_getter),
None,
Attribute::CONFIGURABLE,
)
.property( .property(
"values", utf16!("values"),
values_function.clone(), values_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
@ -132,7 +138,7 @@ impl BuiltInConstructor for Set {
} }
// 5. Let adder be ? Get(set, "add"). // 5. Let adder be ? Get(set, "add").
let adder = set.get("add", context)?; let adder = set.get(utf16!("add"), context)?;
// 6. If IsCallable(adder) is false, throw a TypeError exception. // 6. If IsCallable(adder) is false, throw a TypeError exception.
let adder = adder.as_callable().ok_or_else(|| { let adder = adder.as_callable().ok_or_else(|| {

10
boa_engine/src/builtins/string/mod.rs

@ -71,7 +71,7 @@ impl IntrinsicObject for String {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.property("length", 0, attribute) .property(utf16!("length"), 0, attribute)
.static_method(Self::raw, "raw", 1) .static_method(Self::raw, "raw", 1)
.static_method(Self::from_char_code, "fromCharCode", 1) .static_method(Self::from_char_code, "fromCharCode", 1)
.static_method(Self::from_code_point, "fromCodePoint", 1) .static_method(Self::from_code_point, "fromCodePoint", 1)
@ -190,7 +190,7 @@ impl String {
// 8. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: 𝔽(length), // 8. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: 𝔽(length),
// [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
s.define_property_or_throw( s.define_property_or_throw(
"length", utf16!("length"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(len) .value(len)
.writable(false) .writable(false)
@ -300,7 +300,7 @@ impl String {
let cooked = args.get_or_undefined(0).to_object(context)?; let cooked = args.get_or_undefined(0).to_object(context)?;
// 3. Let raw be ? ToObject(? Get(cooked, "raw")). // 3. Let raw be ? ToObject(? Get(cooked, "raw")).
let raw = cooked.get("raw", context)?.to_object(context)?; let raw = cooked.get(utf16!("raw"), context)?.to_object(context)?;
// 4. Let literalSegments be ? LengthOfArrayLike(raw). // 4. Let literalSegments be ? LengthOfArrayLike(raw).
let literal_segments = raw.length_of_array_like(context)?; let literal_segments = raw.length_of_array_like(context)?;
@ -1040,7 +1040,7 @@ impl String {
// b. If isRegExp is true, then // b. If isRegExp is true, then
if is_reg_exp_object(obj, context)? { if is_reg_exp_object(obj, context)? {
// i. Let flags be ? Get(searchValue, "flags"). // i. Let flags be ? Get(searchValue, "flags").
let flags = obj.get("flags", context)?; let flags = obj.get(utf16!("flags"), context)?;
// ii. Perform ? RequireObjectCoercible(flags). // ii. Perform ? RequireObjectCoercible(flags).
flags.require_object_coercible()?; flags.require_object_coercible()?;
@ -1972,7 +1972,7 @@ impl String {
if let Some(regexp_obj) = regexp.as_object() { if let Some(regexp_obj) = regexp.as_object() {
if is_reg_exp_object(regexp_obj, context)? { if is_reg_exp_object(regexp_obj, context)? {
// i. Let flags be ? Get(regexp, "flags"). // i. Let flags be ? Get(regexp, "flags").
let flags = regexp_obj.get("flags", context)?; let flags = regexp_obj.get(utf16!("flags"), context)?;
// ii. Perform ? RequireObjectCoercible(flags). // ii. Perform ? RequireObjectCoercible(flags).
flags.require_object_coercible()?; flags.require_object_coercible()?;

41
boa_engine/src/builtins/symbol/mod.rs

@ -27,6 +27,7 @@ use crate::{
js_string, js_string,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
@ -123,23 +124,35 @@ impl IntrinsicObject for Symbol {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics) BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.static_method(Self::for_, "for", 1) .static_method(Self::for_, "for", 1)
.static_method(Self::key_for, "keyFor", 1) .static_method(Self::key_for, "keyFor", 1)
.static_property("asyncIterator", symbol_async_iterator, attribute) .static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute)
.static_property("hasInstance", symbol_has_instance, attribute) .static_property(utf16!("hasInstance"), symbol_has_instance, attribute)
.static_property("isConcatSpreadable", symbol_is_concat_spreadable, attribute) .static_property(
.static_property("iterator", symbol_iterator, attribute) utf16!("isConcatSpreadable"),
.static_property("match", symbol_match, attribute) symbol_is_concat_spreadable,
.static_property("matchAll", symbol_match_all, attribute) attribute,
.static_property("replace", symbol_replace, attribute) )
.static_property("search", symbol_search, attribute) .static_property(utf16!("iterator"), symbol_iterator, attribute)
.static_property("species", symbol_species, attribute) .static_property(utf16!("match"), symbol_match, attribute)
.static_property("split", symbol_split, attribute) .static_property(utf16!("matchAll"), symbol_match_all, attribute)
.static_property("toPrimitive", symbol_to_primitive.clone(), attribute) .static_property(utf16!("replace"), symbol_replace, attribute)
.static_property("toStringTag", symbol_to_string_tag.clone(), attribute) .static_property(utf16!("search"), symbol_search, attribute)
.static_property("unscopables", symbol_unscopables, attribute) .static_property(utf16!("species"), symbol_species, attribute)
.static_property(utf16!("split"), symbol_split, attribute)
.static_property(
utf16!("toPrimitive"),
symbol_to_primitive.clone(),
attribute,
)
.static_property(
utf16!("toStringTag"),
symbol_to_string_tag.clone(),
attribute,
)
.static_property(utf16!("unscopables"), symbol_unscopables, attribute)
.method(Self::to_string, "toString", 0) .method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, "valueOf", 0)
.accessor( .accessor(
"description", utf16!("description"),
Some(get_description), Some(get_description),
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,

15
boa_engine/src/builtins/typed_array/mod.rs

@ -25,6 +25,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
@ -64,12 +65,12 @@ macro_rules! typed_array {
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(
"BYTES_PER_ELEMENT", utf16!("BYTES_PER_ELEMENT"),
TypedArrayKind::$variant.element_size(), TypedArrayKind::$variant.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
.static_property( .static_property(
"BYTES_PER_ELEMENT", utf16!("BYTES_PER_ELEMENT"),
TypedArrayKind::$variant.element_size(), TypedArrayKind::$variant.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
@ -275,7 +276,7 @@ impl IntrinsicObject for TypedArray {
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(
"length", utf16!("length"),
0, 0,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
@ -285,25 +286,25 @@ impl IntrinsicObject for TypedArray {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
"buffer", utf16!("buffer"),
Some(get_buffer), Some(get_buffer),
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
) )
.accessor( .accessor(
"byteLength", utf16!("byteLength"),
Some(get_byte_length), Some(get_byte_length),
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
) )
.accessor( .accessor(
"byteOffset", utf16!("byteOffset"),
Some(get_byte_offset), Some(get_byte_offset),
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
) )
.accessor( .accessor(
"length", utf16!("length"),
Some(get_length), Some(get_length),
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,

3
boa_engine/src/builtins/weak_map/mod.rs

@ -15,6 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
@ -92,7 +93,7 @@ impl BuiltInConstructor for WeakMap {
} }
// 5. Let adder be ? Get(map, "set"). // 5. Let adder be ? Get(map, "set").
let adder = map.get("set", context)?; let adder = map.get(utf16!("set"), context)?;
// 6. If IsCallable(adder) is false, throw a TypeError exception. // 6. If IsCallable(adder) is false, throw a TypeError exception.
if !adder.is_callable() { if !adder.is_callable() {

3
boa_engine/src/builtins/weak_set/mod.rs

@ -12,6 +12,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
@ -88,7 +89,7 @@ impl BuiltInConstructor for WeakSet {
} }
// 5. Let adder be ? Get(set, "add"). // 5. Let adder be ? Get(set, "add").
let adder = weak_set.get("add", context)?; let adder = weak_set.get(utf16!("add"), context)?;
// 6. If IsCallable(adder) is false, throw a TypeError exception. // 6. If IsCallable(adder) is false, throw a TypeError exception.
let adder = adder let adder = adder

9
boa_engine/src/error.rs

@ -5,6 +5,7 @@ use crate::{
object::JsObject, object::JsObject,
object::ObjectData, object::ObjectData,
property::PropertyDescriptor, property::PropertyDescriptor,
string::utf16,
Context, JsString, JsValue, Context, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -249,7 +250,7 @@ impl JsError {
ErrorKind::Syntax => JsNativeErrorKind::Syntax, ErrorKind::Syntax => JsNativeErrorKind::Syntax,
ErrorKind::Uri => JsNativeErrorKind::Uri, ErrorKind::Uri => JsNativeErrorKind::Uri,
ErrorKind::Aggregate => { ErrorKind::Aggregate => {
let errors = obj.get("errors", context).map_err(|e| { let errors = obj.get(utf16!("errors"), context).map_err(|e| {
TryNativeError::InaccessibleProperty { TryNativeError::InaccessibleProperty {
property: "errors", property: "errors",
source: e, source: e,
@ -670,11 +671,11 @@ impl JsNativeError {
let o = JsObject::from_proto_and_data(prototype, ObjectData::error(tag)); let o = JsObject::from_proto_and_data(prototype, ObjectData::error(tag));
o.create_non_enumerable_data_property_or_throw("message", &**message, context); o.create_non_enumerable_data_property_or_throw(utf16!("message"), &**message, context);
if let Some(cause) = cause { if let Some(cause) = cause {
o.create_non_enumerable_data_property_or_throw( o.create_non_enumerable_data_property_or_throw(
"cause", utf16!("cause"),
cause.to_opaque(context), cause.to_opaque(context),
context, context,
); );
@ -687,7 +688,7 @@ impl JsNativeError {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let errors = Array::create_array_from_list(errors, context); let errors = Array::create_array_from_list(errors, context);
o.define_property_or_throw( o.define_property_or_throw(
"errors", utf16!("errors"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.configurable(true) .configurable(true)
.enumerable(false) .enumerable(false)

3
boa_engine/src/object/builtins/jsmap.rs

@ -4,6 +4,7 @@ use crate::{
builtins::Map, builtins::Map,
error::JsNativeError, error::JsNativeError,
object::{JsFunction, JsMapIterator, JsObject, JsObjectType, ObjectData}, object::{JsFunction, JsMapIterator, JsObject, JsObjectType, ObjectData},
string::utf16,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -121,7 +122,7 @@ impl JsMap {
// Let adder be Get(map, "set") per spec. This action should not fail with default map. // Let adder be Get(map, "set") per spec. This action should not fail with default map.
let adder = map let adder = map
.get("set", context) .get(utf16!("set"), context)
.expect("creating a map with the default prototype must not fail"); .expect("creating a map with the default prototype must not fail");
let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?; let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?;

27
boa_engine/src/object/builtins/jsproxy.rs

@ -5,6 +5,7 @@ use crate::{
builtins::Proxy, builtins::Proxy,
native_function::{NativeFunction, NativeFunctionPointer}, native_function::{NativeFunction, NativeFunctionPointer},
object::{FunctionObjectBuilder, JsObject, JsObjectType, ObjectData}, object::{FunctionObjectBuilder, JsObject, JsObjectType, ObjectData},
string::utf16,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -379,7 +380,7 @@ impl JsProxyBuilder {
.length(3) .length(3)
.build(); .build();
handler handler
.create_data_property_or_throw("apply", f, context) .create_data_property_or_throw(utf16!("apply"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(construct) = self.construct { if let Some(construct) = self.construct {
@ -387,7 +388,7 @@ impl JsProxyBuilder {
.length(3) .length(3)
.build(); .build();
handler handler
.create_data_property_or_throw("construct", f, context) .create_data_property_or_throw(utf16!("construct"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(define_property) = self.define_property { if let Some(define_property) = self.define_property {
@ -396,7 +397,7 @@ impl JsProxyBuilder {
.length(3) .length(3)
.build(); .build();
handler handler
.create_data_property_or_throw("defineProperty", f, context) .create_data_property_or_throw(utf16!("defineProperty"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(delete_property) = self.delete_property { if let Some(delete_property) = self.delete_property {
@ -405,7 +406,7 @@ impl JsProxyBuilder {
.length(2) .length(2)
.build(); .build();
handler handler
.create_data_property_or_throw("deleteProperty", f, context) .create_data_property_or_throw(utf16!("deleteProperty"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(get) = self.get { if let Some(get) = self.get {
@ -413,7 +414,7 @@ impl JsProxyBuilder {
.length(3) .length(3)
.build(); .build();
handler handler
.create_data_property_or_throw("get", f, context) .create_data_property_or_throw(utf16!("get"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(get_own_property_descriptor) = self.get_own_property_descriptor { if let Some(get_own_property_descriptor) = self.get_own_property_descriptor {
@ -424,7 +425,7 @@ impl JsProxyBuilder {
.length(2) .length(2)
.build(); .build();
handler handler
.create_data_property_or_throw("getOwnPropertyDescriptor", f, context) .create_data_property_or_throw(utf16!("getOwnPropertyDescriptor"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(get_prototype_of) = self.get_prototype_of { if let Some(get_prototype_of) = self.get_prototype_of {
@ -433,7 +434,7 @@ impl JsProxyBuilder {
.length(1) .length(1)
.build(); .build();
handler handler
.create_data_property_or_throw("getPrototypeOf", f, context) .create_data_property_or_throw(utf16!("getPrototypeOf"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(has) = self.has { if let Some(has) = self.has {
@ -441,7 +442,7 @@ impl JsProxyBuilder {
.length(2) .length(2)
.build(); .build();
handler handler
.create_data_property_or_throw("has", f, context) .create_data_property_or_throw(utf16!("has"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(is_extensible) = self.is_extensible { if let Some(is_extensible) = self.is_extensible {
@ -449,7 +450,7 @@ impl JsProxyBuilder {
.length(1) .length(1)
.build(); .build();
handler handler
.create_data_property_or_throw("isExtensible", f, context) .create_data_property_or_throw(utf16!("isExtensible"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(own_keys) = self.own_keys { if let Some(own_keys) = self.own_keys {
@ -457,7 +458,7 @@ impl JsProxyBuilder {
.length(1) .length(1)
.build(); .build();
handler handler
.create_data_property_or_throw("ownKeys", f, context) .create_data_property_or_throw(utf16!("ownKeys"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(prevent_extensions) = self.prevent_extensions { if let Some(prevent_extensions) = self.prevent_extensions {
@ -468,7 +469,7 @@ impl JsProxyBuilder {
.length(1) .length(1)
.build(); .build();
handler handler
.create_data_property_or_throw("preventExtensions", f, context) .create_data_property_or_throw(utf16!("preventExtensions"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(set) = self.set { if let Some(set) = self.set {
@ -476,7 +477,7 @@ impl JsProxyBuilder {
.length(4) .length(4)
.build(); .build();
handler handler
.create_data_property_or_throw("set", f, context) .create_data_property_or_throw(utf16!("set"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }
if let Some(set_prototype_of) = self.set_prototype_of { if let Some(set_prototype_of) = self.set_prototype_of {
@ -485,7 +486,7 @@ impl JsProxyBuilder {
.length(2) .length(2)
.build(); .build();
handler handler
.create_data_property_or_throw("setPrototypeOf", f, context) .create_data_property_or_throw(utf16!("setPrototypeOf"), f, context)
.expect("new object should be writable"); .expect("new object should be writable");
} }

4
boa_engine/src/object/internal_methods/arguments.rs

@ -68,12 +68,12 @@ pub(crate) fn arguments_exotic_get_own_property(
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub(crate) fn arguments_exotic_define_own_property( pub(crate) fn arguments_exotic_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
// 2. Let isMapped be HasOwnProperty(map, P). // 2. Let isMapped be HasOwnProperty(map, P).
let mapped = if let PropertyKey::Index(index) = key { let mapped = if let &PropertyKey::Index(index) = key {
// 1. Let map be args.[[ParameterMap]]. // 1. Let map be args.[[ParameterMap]].
obj.borrow() obj.borrow()
.as_mapped_arguments() .as_mapped_arguments()

23
boa_engine/src/object/internal_methods/array.rs

@ -27,12 +27,12 @@ pub(crate) static ARRAY_EXOTIC_INTERNAL_METHODS: InternalObjectMethods = Interna
/// [spec]: https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc /// [spec]: https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc
pub(crate) fn array_exotic_define_own_property( pub(crate) fn array_exotic_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
// 1. Assert: IsPropertyKey(P) is true. // 1. Assert: IsPropertyKey(P) is true.
match key { match *key {
// 2. If P is "length", then // 2. If P is "length", then
PropertyKey::String(ref s) if s == utf16!("length") => { PropertyKey::String(ref s) if s == utf16!("length") => {
// a. Return ? ArraySetLength(A, Desc). // a. Return ? ArraySetLength(A, Desc).
@ -42,8 +42,9 @@ pub(crate) fn array_exotic_define_own_property(
// 3. Else if P is an array index, then // 3. Else if P is an array index, then
PropertyKey::Index(index) if index < u32::MAX => { PropertyKey::Index(index) if index < u32::MAX => {
// a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). // a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)? let old_len_desc =
.expect("the property descriptor must exist"); super::ordinary_get_own_property(obj, &utf16!("length").into(), context)?
.expect("the property descriptor must exist");
// b. Assert: ! IsDataDescriptor(oldLenDesc) is true. // b. Assert: ! IsDataDescriptor(oldLenDesc) is true.
debug_assert!(old_len_desc.is_data_descriptor()); debug_assert!(old_len_desc.is_data_descriptor());
@ -78,7 +79,7 @@ pub(crate) fn array_exotic_define_own_property(
// ii. Set succeeded to OrdinaryDefineOwnProperty(A, "length", oldLenDesc). // ii. Set succeeded to OrdinaryDefineOwnProperty(A, "length", oldLenDesc).
let succeeded = super::ordinary_define_own_property( let succeeded = super::ordinary_define_own_property(
obj, obj,
"length".into(), &utf16!("length").into(),
old_len_desc.into(), old_len_desc.into(),
context, context,
)?; )?;
@ -113,7 +114,7 @@ fn array_set_length(
// 1. If Desc.[[Value]] is absent, then // 1. If Desc.[[Value]] is absent, then
let Some(new_len_val) = desc.value() else { let Some(new_len_val) = desc.value() else {
// a. Return OrdinaryDefineOwnProperty(A, "length", Desc). // a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
return super::ordinary_define_own_property(obj, "length".into(), desc, context); return super::ordinary_define_own_property(obj, &utf16!("length").into(), desc, context);
}; };
// 3. Let newLen be ? ToUint32(Desc.[[Value]]). // 3. Let newLen be ? ToUint32(Desc.[[Value]]).
@ -139,7 +140,7 @@ fn array_set_length(
.maybe_configurable(desc.configurable()); .maybe_configurable(desc.configurable());
// 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). // 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)? let old_len_desc = super::ordinary_get_own_property(obj, &utf16!("length").into(), context)?
.expect("the property descriptor must exist"); .expect("the property descriptor must exist");
// 8. Assert: ! IsDataDescriptor(oldLenDesc) is true. // 8. Assert: ! IsDataDescriptor(oldLenDesc) is true.
@ -156,7 +157,7 @@ fn array_set_length(
// a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc). // a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
return super::ordinary_define_own_property( return super::ordinary_define_own_property(
obj, obj,
"length".into(), &utf16!("length").into(),
new_len_desc.build(), new_len_desc.build(),
context, context,
); );
@ -186,7 +187,7 @@ fn array_set_length(
// 16. If succeeded is false, return false. // 16. If succeeded is false, return false.
if !super::ordinary_define_own_property( if !super::ordinary_define_own_property(
obj, obj,
"length".into(), &utf16!("length").into(),
new_len_desc.clone().build(), new_len_desc.clone().build(),
context, context,
) )
@ -223,7 +224,7 @@ fn array_set_length(
// iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc). // iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
super::ordinary_define_own_property( super::ordinary_define_own_property(
obj, obj,
"length".into(), &utf16!("length").into(),
new_len_desc.build(), new_len_desc.build(),
context, context,
) )
@ -240,7 +241,7 @@ fn array_set_length(
// PropertyDescriptor { [[Writable]]: false }). // PropertyDescriptor { [[Writable]]: false }).
let succeeded = super::ordinary_define_own_property( let succeeded = super::ordinary_define_own_property(
obj, obj,
"length".into(), &utf16!("length").into(),
PropertyDescriptor::builder().writable(false).build(), PropertyDescriptor::builder().writable(false).build(),
context, context,
) )

6
boa_engine/src/object/internal_methods/global.rs

@ -60,20 +60,20 @@ pub(crate) fn global_get_own_property(
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub(crate) fn global_define_own_property( pub(crate) fn global_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
let _timer = Profiler::global().start_event("Object::global_define_own_property", "object"); let _timer = Profiler::global().start_event("Object::global_define_own_property", "object");
// 1. Let current be ? O.[[GetOwnProperty]](P). // 1. Let current be ? O.[[GetOwnProperty]](P).
let current = global_get_own_property(obj, &key, context)?; let current = global_get_own_property(obj, key, context)?;
// 2. Let extensible be ? IsExtensible(O). // 2. Let extensible be ? IsExtensible(O).
let extensible = obj.__is_extensible__(context)?; let extensible = obj.__is_extensible__(context)?;
// 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). // 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
Ok(validate_and_apply_property_descriptor( Ok(validate_and_apply_property_descriptor(
&key, extensible, desc, current, context, key, extensible, desc, current, context,
)) ))
} }

4
boa_engine/src/object/internal_methods/integer_indexed.rs

@ -89,14 +89,14 @@ pub(crate) fn integer_indexed_exotic_has_property(
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc /// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
pub(crate) fn integer_indexed_exotic_define_own_property( pub(crate) fn integer_indexed_exotic_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
// 1. If Type(P) is String, then // 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P). // a. Let numericIndex be ! CanonicalNumericIndexString(P).
// b. If numericIndex is not undefined, then // b. If numericIndex is not undefined, then
if let PropertyKey::Index(index) = key { if let &PropertyKey::Index(index) = key {
// i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false. // i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false.
// ii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, return false. // ii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, return false.
// iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false. // iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.

16
boa_engine/src/object/internal_methods/mod.rs

@ -114,7 +114,7 @@ impl JsObject {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
pub(crate) fn __define_own_property__( pub(crate) fn __define_own_property__(
&self, &self,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
@ -305,7 +305,7 @@ pub(crate) struct InternalObjectMethods {
pub(crate) __get_own_property__: pub(crate) __get_own_property__:
fn(&JsObject, &PropertyKey, &mut Context<'_>) -> JsResult<Option<PropertyDescriptor>>, fn(&JsObject, &PropertyKey, &mut Context<'_>) -> JsResult<Option<PropertyDescriptor>>,
pub(crate) __define_own_property__: pub(crate) __define_own_property__:
fn(&JsObject, PropertyKey, PropertyDescriptor, &mut Context<'_>) -> JsResult<bool>, fn(&JsObject, &PropertyKey, PropertyDescriptor, &mut Context<'_>) -> JsResult<bool>,
pub(crate) __has_property__: fn(&JsObject, &PropertyKey, &mut Context<'_>) -> JsResult<bool>, pub(crate) __has_property__: fn(&JsObject, &PropertyKey, &mut Context<'_>) -> JsResult<bool>,
pub(crate) __get__: fn(&JsObject, &PropertyKey, JsValue, &mut Context<'_>) -> JsResult<JsValue>, pub(crate) __get__: fn(&JsObject, &PropertyKey, JsValue, &mut Context<'_>) -> JsResult<JsValue>,
pub(crate) __set__: pub(crate) __set__:
@ -462,13 +462,13 @@ pub(crate) fn ordinary_get_own_property(
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty /// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty
pub(crate) fn ordinary_define_own_property( pub(crate) fn ordinary_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
let _timer = Profiler::global().start_event("Object::ordinary_define_own_property", "object"); let _timer = Profiler::global().start_event("Object::ordinary_define_own_property", "object");
// 1. Let current be ? O.[[GetOwnProperty]](P). // 1. Let current be ? O.[[GetOwnProperty]](P).
let current = obj.__get_own_property__(&key, context)?; let current = obj.__get_own_property__(key, context)?;
// 2. Let extensible be ? IsExtensible(O). // 2. Let extensible be ? IsExtensible(O).
let extensible = obj.__is_extensible__(context)?; let extensible = obj.__is_extensible__(context)?;
@ -629,7 +629,7 @@ pub(crate) fn ordinary_set(
// iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }. // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc). // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
return receiver.__define_own_property__( return receiver.__define_own_property__(
key, &key,
PropertyDescriptor::builder().value(value).build(), PropertyDescriptor::builder().value(value).build(),
context, context,
); );
@ -762,7 +762,7 @@ pub(crate) fn is_compatible_property_descriptor(
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor /// [spec]: https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
pub(crate) fn validate_and_apply_property_descriptor( pub(crate) fn validate_and_apply_property_descriptor(
obj_and_key: Option<(&JsObject, PropertyKey)>, obj_and_key: Option<(&JsObject, &PropertyKey)>,
extensible: bool, extensible: bool,
desc: PropertyDescriptor, desc: PropertyDescriptor,
current: Option<PropertyDescriptor>, current: Option<PropertyDescriptor>,
@ -782,7 +782,7 @@ pub(crate) fn validate_and_apply_property_descriptor(
if let Some((obj, key)) = obj_and_key { if let Some((obj, key)) = obj_and_key {
obj.borrow_mut().properties.insert( obj.borrow_mut().properties.insert(
&key, key,
// c. If IsGenericDescriptor(Desc) is true or IsDataDescriptor(Desc) is true, then // c. If IsGenericDescriptor(Desc) is true or IsDataDescriptor(Desc) is true, then
if desc.is_generic_descriptor() || desc.is_data_descriptor() { if desc.is_generic_descriptor() || desc.is_data_descriptor() {
// i. If O is not undefined, create an own data property named P of // i. If O is not undefined, create an own data property named P of
@ -899,7 +899,7 @@ pub(crate) fn validate_and_apply_property_descriptor(
// a. For each field of Desc that is present, set the corresponding attribute of the // a. For each field of Desc that is present, set the corresponding attribute of the
// property named P of object O to the value of the field. // property named P of object O to the value of the field.
current.fill_with(&desc); current.fill_with(&desc);
obj.borrow_mut().properties.insert(&key, current); obj.borrow_mut().properties.insert(key, current);
} }
// 10. Return true. // 10. Return true.

31
boa_engine/src/object/internal_methods/proxy.rs

@ -3,6 +3,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{InternalObjectMethods, JsObject, JsPrototype}, object::{InternalObjectMethods, JsObject, JsPrototype},
property::{PropertyDescriptor, PropertyKey}, property::{PropertyDescriptor, PropertyKey},
string::utf16,
value::Type, value::Type,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -65,7 +66,7 @@ pub(crate) fn proxy_exotic_get_prototype_of(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
let Some(trap) = handler.get_method("getPrototypeOf", context)? else { let Some(trap) = handler.get_method(utf16!("getPrototypeOf"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[GetPrototypeOf]](). // a. Return ? target.[[GetPrototypeOf]]().
return target.__get_prototype_of__(context); return target.__get_prototype_of__(context);
@ -127,7 +128,7 @@ pub(crate) fn proxy_exotic_set_prototype_of(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "setPrototypeOf"). // 5. Let trap be ? GetMethod(handler, "setPrototypeOf").
let Some(trap) = handler.get_method("setPrototypeOf", context)? else { let Some(trap) = handler.get_method(utf16!("setPrototypeOf"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[SetPrototypeOf]](V). // a. Return ? target.[[SetPrototypeOf]](V).
return target.__set_prototype_of__(val, context); return target.__set_prototype_of__(val, context);
@ -190,7 +191,7 @@ pub(crate) fn proxy_exotic_is_extensible(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "isExtensible"). // 5. Let trap be ? GetMethod(handler, "isExtensible").
let Some(trap) = handler.get_method("isExtensible", context)? else { let Some(trap) = handler.get_method(utf16!("isExtensible"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? IsExtensible(target). // a. Return ? IsExtensible(target).
return target.is_extensible(context); return target.is_extensible(context);
@ -236,7 +237,7 @@ pub(crate) fn proxy_exotic_prevent_extensions(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "preventExtensions"). // 5. Let trap be ? GetMethod(handler, "preventExtensions").
let Some(trap) = handler.get_method("preventExtensions", context)? else { let Some(trap) = handler.get_method(utf16!("preventExtensions"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[PreventExtensions]](). // a. Return ? target.[[PreventExtensions]]().
return target.__prevent_extensions__(context); return target.__prevent_extensions__(context);
@ -282,7 +283,7 @@ pub(crate) fn proxy_exotic_get_own_property(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
let Some(trap) = handler.get_method("getOwnPropertyDescriptor", context)? else { let Some(trap) = handler.get_method(utf16!("getOwnPropertyDescriptor"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[GetOwnProperty]](P). // a. Return ? target.[[GetOwnProperty]](P).
return target.__get_own_property__(key, context); return target.__get_own_property__(key, context);
@ -391,7 +392,7 @@ pub(crate) fn proxy_exotic_get_own_property(
/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc /// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc
pub(crate) fn proxy_exotic_define_own_property( pub(crate) fn proxy_exotic_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
@ -406,7 +407,7 @@ pub(crate) fn proxy_exotic_define_own_property(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "defineProperty"). // 5. Let trap be ? GetMethod(handler, "defineProperty").
let Some(trap) = handler.get_method("defineProperty", context)? else { let Some(trap) = handler.get_method(utf16!("defineProperty"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[DefineOwnProperty]](P, Desc). // a. Return ? target.[[DefineOwnProperty]](P, Desc).
return target.__define_own_property__(key, desc, context); return target.__define_own_property__(key, desc, context);
@ -429,7 +430,7 @@ pub(crate) fn proxy_exotic_define_own_property(
} }
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P). // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
let target_desc = target.__get_own_property__(&key, context)?; let target_desc = target.__get_own_property__(key, context)?;
// 11. Let extensibleTarget be ? IsExtensible(target). // 11. Let extensibleTarget be ? IsExtensible(target).
let extensible_target = target.is_extensible(context)?; let extensible_target = target.is_extensible(context)?;
@ -517,7 +518,7 @@ pub(crate) fn proxy_exotic_has_property(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "has"). // 5. Let trap be ? GetMethod(handler, "has").
let Some(trap) = handler.get_method("has", context)? else { let Some(trap) = handler.get_method(utf16!("has"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[HasProperty]](P). // a. Return ? target.[[HasProperty]](P).
return target.has_property(key.clone(), context); return target.has_property(key.clone(), context);
@ -583,7 +584,7 @@ pub(crate) fn proxy_exotic_get(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "get"). // 5. Let trap be ? GetMethod(handler, "get").
let Some(trap) = handler.get_method("get", context)? else { let Some(trap) = handler.get_method(utf16!("get"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[Get]](P, Receiver). // a. Return ? target.[[Get]](P, Receiver).
return target.__get__(key, receiver, context); return target.__get__(key, receiver, context);
@ -652,7 +653,7 @@ pub(crate) fn proxy_exotic_set(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "set"). // 5. Let trap be ? GetMethod(handler, "set").
let Some(trap) = handler.get_method("set", context)? else { let Some(trap) = handler.get_method(utf16!("set"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[Set]](P, V, Receiver). // a. Return ? target.[[Set]](P, V, Receiver).
return target.__set__(key, value, receiver, context); return target.__set__(key, value, receiver, context);
@ -733,7 +734,7 @@ pub(crate) fn proxy_exotic_delete(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "deleteProperty"). // 5. Let trap be ? GetMethod(handler, "deleteProperty").
let Some(trap) = handler.get_method("deleteProperty", context)? else { let Some(trap) = handler.get_method(utf16!("deleteProperty"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[Delete]](P). // a. Return ? target.[[Delete]](P).
return target.__delete__(key, context); return target.__delete__(key, context);
@ -799,7 +800,7 @@ pub(crate) fn proxy_exotic_own_property_keys(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "ownKeys"). // 5. Let trap be ? GetMethod(handler, "ownKeys").
let Some(trap) = handler.get_method("ownKeys", context)? else { let Some(trap) = handler.get_method(utf16!("ownKeys"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? target.[[OwnPropertyKeys]](). // a. Return ? target.[[OwnPropertyKeys]]().
return target.__own_property_keys__(context); return target.__own_property_keys__(context);
@ -935,7 +936,7 @@ fn proxy_exotic_call(
.try_data()?; .try_data()?;
// 5. Let trap be ? GetMethod(handler, "apply"). // 5. Let trap be ? GetMethod(handler, "apply").
let Some(trap) = handler.get_method("apply", context)? else { let Some(trap) = handler.get_method(utf16!("apply"), context)? else {
// 6. If trap is undefined, then // 6. If trap is undefined, then
// a. Return ? Call(target, thisArgument, argumentsList). // a. Return ? Call(target, thisArgument, argumentsList).
return target.call(this, args, context); return target.call(this, args, context);
@ -978,7 +979,7 @@ fn proxy_exotic_construct(
assert!(target.is_constructor()); assert!(target.is_constructor());
// 6. Let trap be ? GetMethod(handler, "construct"). // 6. Let trap be ? GetMethod(handler, "construct").
let Some(trap) = handler.get_method("construct", context)? else { let Some(trap) = handler.get_method(utf16!("construct"), context)? else {
// 7. If trap is undefined, then // 7. If trap is undefined, then
// a. Return ? Construct(target, argumentsList, newTarget). // a. Return ? Construct(target, argumentsList, newTarget).
return target.construct(args, Some(new_target), context); return target.construct(args, Some(new_target), context);

4
boa_engine/src/object/internal_methods/string.rs

@ -52,13 +52,13 @@ pub(crate) fn string_exotic_get_own_property(
/// [spec]: https://tc39.es/ecma262/#sec-string-exotic-objects-defineownproperty-p-desc /// [spec]: https://tc39.es/ecma262/#sec-string-exotic-objects-defineownproperty-p-desc
pub(crate) fn string_exotic_define_own_property( pub(crate) fn string_exotic_define_own_property(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
context: &mut Context<'_>, context: &mut Context<'_>,
) -> JsResult<bool> { ) -> JsResult<bool> {
// 1. Assert: IsPropertyKey(P) is true. // 1. Assert: IsPropertyKey(P) is true.
// 2. Let stringDesc be ! StringGetOwnProperty(S, P). // 2. Let stringDesc be ! StringGetOwnProperty(S, P).
let string_desc = string_get_own_property(obj, &key); let string_desc = string_get_own_property(obj, key);
// 3. If stringDesc is not undefined, then // 3. If stringDesc is not undefined, then
if let Some(string_desc) = string_desc { if let Some(string_desc) = string_desc {

29
boa_engine/src/object/jsobject.rs

@ -7,6 +7,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{ObjectData, ObjectKind}, object::{ObjectData, ObjectKind},
property::{PropertyDescriptor, PropertyKey}, property::{PropertyDescriptor, PropertyKey},
string::utf16,
value::PreferredType, value::PreferredType,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -180,9 +181,9 @@ impl JsObject {
// 4. Else, // 4. Else,
// a. Let methodNames be « "valueOf", "toString" ». // a. Let methodNames be « "valueOf", "toString" ».
let method_names = if hint == PreferredType::String { let method_names = if hint == PreferredType::String {
["toString", "valueOf"] [utf16!("toString"), utf16!("valueOf")]
} else { } else {
["valueOf", "toString"] [utf16!("valueOf"), utf16!("toString")]
}; };
// 5. For each name in methodNames in List order, do // 5. For each name in methodNames in List order, do
@ -532,41 +533,41 @@ impl JsObject {
// 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable"). // 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
// 4. If hasEnumerable is true, then ... // 4. If hasEnumerable is true, then ...
if self.has_property("enumerable", context)? { if self.has_property(utf16!("enumerable"), context)? {
// a. Let enumerable be ! ToBoolean(? Get(Obj, "enumerable")). // a. Let enumerable be ! ToBoolean(? Get(Obj, "enumerable")).
// b. Set desc.[[Enumerable]] to enumerable. // b. Set desc.[[Enumerable]] to enumerable.
desc = desc.enumerable(self.get("enumerable", context)?.to_boolean()); desc = desc.enumerable(self.get(utf16!("enumerable"), context)?.to_boolean());
} }
// 5. Let hasConfigurable be ? HasProperty(Obj, "configurable"). // 5. Let hasConfigurable be ? HasProperty(Obj, "configurable").
// 6. If hasConfigurable is true, then ... // 6. If hasConfigurable is true, then ...
if self.has_property("configurable", context)? { if self.has_property(utf16!("configurable"), context)? {
// a. Let configurable be ! ToBoolean(? Get(Obj, "configurable")). // a. Let configurable be ! ToBoolean(? Get(Obj, "configurable")).
// b. Set desc.[[Configurable]] to configurable. // b. Set desc.[[Configurable]] to configurable.
desc = desc.configurable(self.get("configurable", context)?.to_boolean()); desc = desc.configurable(self.get(utf16!("configurable"), context)?.to_boolean());
} }
// 7. Let hasValue be ? HasProperty(Obj, "value"). // 7. Let hasValue be ? HasProperty(Obj, "value").
// 8. If hasValue is true, then ... // 8. If hasValue is true, then ...
if self.has_property("value", context)? { if self.has_property(utf16!("value"), context)? {
// a. Let value be ? Get(Obj, "value"). // a. Let value be ? Get(Obj, "value").
// b. Set desc.[[Value]] to value. // b. Set desc.[[Value]] to value.
desc = desc.value(self.get("value", context)?); desc = desc.value(self.get(utf16!("value"), context)?);
} }
// 9. Let hasWritable be ? HasProperty(Obj, ). // 9. Let hasWritable be ? HasProperty(Obj, ).
// 10. If hasWritable is true, then ... // 10. If hasWritable is true, then ...
if self.has_property("writable", context)? { if self.has_property(utf16!("writable"), context)? {
// a. Let writable be ! ToBoolean(? Get(Obj, "writable")). // a. Let writable be ! ToBoolean(? Get(Obj, "writable")).
// b. Set desc.[[Writable]] to writable. // b. Set desc.[[Writable]] to writable.
desc = desc.writable(self.get("writable", context)?.to_boolean()); desc = desc.writable(self.get(utf16!("writable"), context)?.to_boolean());
} }
// 11. Let hasGet be ? HasProperty(Obj, "get"). // 11. Let hasGet be ? HasProperty(Obj, "get").
// 12. If hasGet is true, then // 12. If hasGet is true, then
let get = if self.has_property("get", context)? { let get = if self.has_property(utf16!("get"), context)? {
// a. Let getter be ? Get(Obj, "get"). // a. Let getter be ? Get(Obj, "get").
let getter = self.get("get", context)?; let getter = self.get(utf16!("get"), context)?;
// b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception. // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception.
// todo: extract IsCallable to be callable from Value // todo: extract IsCallable to be callable from Value
if !getter.is_undefined() && getter.as_object().map_or(true, |o| !o.is_callable()) { if !getter.is_undefined() && getter.as_object().map_or(true, |o| !o.is_callable()) {
@ -582,9 +583,9 @@ impl JsObject {
// 13. Let hasSet be ? HasProperty(Obj, "set"). // 13. Let hasSet be ? HasProperty(Obj, "set").
// 14. If hasSet is true, then // 14. If hasSet is true, then
let set = if self.has_property("set", context)? { let set = if self.has_property(utf16!("set"), context)? {
// 14.a. Let setter be ? Get(Obj, "set"). // 14.a. Let setter be ? Get(Obj, "set").
let setter = self.get("set", context)?; let setter = self.get(utf16!("set"), context)?;
// 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception. // 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception.
// todo: extract IsCallable to be callable from Value // todo: extract IsCallable to be callable from Value
if !setter.is_undefined() && setter.as_object().map_or(true, |o| !o.is_callable()) { if !setter.is_undefined() && setter.as_object().map_or(true, |o| !o.is_callable()) {

21
boa_engine/src/object/mod.rs

@ -51,6 +51,7 @@ use crate::{
js_string, js_string,
native_function::NativeFunction, native_function::NativeFunction,
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
string::utf16,
Context, JsBigInt, JsString, JsSymbol, JsValue, Context, JsBigInt, JsString, JsSymbol, JsValue,
}; };
@ -81,11 +82,13 @@ pub(crate) trait JsObjectType:
{ {
} }
/// Static `constructor`, usually set on prototypes as a key to point to their respective constructor object. /// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object.
pub static CONSTRUCTOR: &str = "constructor"; pub const CONSTRUCTOR: &[u16] = utf16!("constructor");
/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object. /// Const `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub static PROTOTYPE: &str = "prototype"; pub const PROTOTYPE: &[u16] = utf16!("prototype");
/// Common field names.
/// A type alias for an object prototype. /// A type alias for an object prototype.
/// ///
@ -2025,8 +2028,8 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> {
.writable(false) .writable(false)
.enumerable(false) .enumerable(false)
.configurable(true); .configurable(true);
function.insert_property("length", property.clone().value(self.length)); function.insert_property(utf16!("length"), property.clone().value(self.length));
function.insert_property("name", property.value(self.name)); function.insert_property(utf16!("name"), property.value(self.name));
JsFunction::from_object_unchecked(function) JsFunction::from_object_unchecked(function)
} }
@ -2395,8 +2398,8 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> {
{ {
let mut constructor = self.object.borrow_mut(); let mut constructor = self.object.borrow_mut();
constructor.data = ObjectData::function(function); constructor.data = ObjectData::function(function);
constructor.insert("length", length); constructor.insert(utf16!("length"), length);
constructor.insert("name", name); constructor.insert(utf16!("name"), name);
if let Some(proto) = self.custom_prototype.take() { if let Some(proto) = self.custom_prototype.take() {
constructor.set_prototype(proto); constructor.set_prototype(proto);
@ -2425,7 +2428,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> {
{ {
let mut prototype = self.prototype.borrow_mut(); let mut prototype = self.prototype.borrow_mut();
prototype.insert( prototype.insert(
"constructor", CONSTRUCTOR,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(self.object.clone()) .value(self.object.clone())
.writable(true) .writable(true)

11
boa_engine/src/object/operations.rs

@ -2,8 +2,9 @@ use crate::{
builtins::{function::ClassFieldDefinition, Array}, builtins::{function::ClassFieldDefinition, Array},
context::intrinsics::{StandardConstructor, StandardConstructors}, context::intrinsics::{StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{JsObject, PrivateElement}, object::{JsObject, PrivateElement, PROTOTYPE},
property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind}, property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind},
string::utf16,
value::Type, value::Type,
Context, JsResult, JsSymbol, JsValue, Context, JsResult, JsSymbol, JsValue,
}; };
@ -133,7 +134,7 @@ impl JsObject {
.enumerable(true) .enumerable(true)
.configurable(true); .configurable(true);
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc). // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
self.__define_own_property__(key.into(), new_desc.into(), context) self.__define_own_property__(&key.into(), new_desc.into(), context)
} }
// todo: CreateMethodProperty // todo: CreateMethodProperty
@ -226,7 +227,7 @@ impl JsObject {
// 1. Assert: Type(O) is Object. // 1. Assert: Type(O) is Object.
// 2. Assert: IsPropertyKey(P) is true. // 2. Assert: IsPropertyKey(P) is true.
// 3. Let success be ? O.[[DefineOwnProperty]](P, desc). // 3. Let success be ? O.[[DefineOwnProperty]](P, desc).
let success = self.__define_own_property__(key.clone(), desc.into(), context)?; let success = self.__define_own_property__(&key, desc.into(), context)?;
// 4. If success is false, throw a TypeError exception. // 4. If success is false, throw a TypeError exception.
if !success { if !success {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
@ -475,7 +476,7 @@ impl JsObject {
pub(crate) fn length_of_array_like(&self, context: &mut Context<'_>) -> JsResult<u64> { pub(crate) fn length_of_array_like(&self, context: &mut Context<'_>) -> JsResult<u64> {
// 1. Assert: Type(obj) is Object. // 1. Assert: Type(obj) is Object.
// 2. Return ℝ(? ToLength(? Get(obj, "length"))). // 2. Return ℝ(? ToLength(? Get(obj, "length"))).
self.get("length", context)?.to_length(context) self.get(utf16!("length"), context)?.to_length(context)
} }
/// `7.3.22 SpeciesConstructor ( O, defaultConstructor )` /// `7.3.22 SpeciesConstructor ( O, defaultConstructor )`
@ -1184,7 +1185,7 @@ impl JsValue {
}; };
// 4. Let P be ? Get(C, "prototype"). // 4. Let P be ? Get(C, "prototype").
let prototype = function.get("prototype", context)?; let prototype = function.get(PROTOTYPE, context)?;
// 5. If Type(P) is not Object, throw a TypeError exception. // 5. If Type(P) is not Object, throw a TypeError exception.
let prototype = prototype.as_object().ok_or_else(|| { let prototype = prototype.as_object().ok_or_else(|| {

11
boa_engine/src/property/mod.rs

@ -569,6 +569,17 @@ pub enum PropertyKey {
Index(u32), Index(u32),
} }
impl From<&[u16]> for PropertyKey {
#[inline]
fn from(string: &[u16]) -> Self {
debug_assert!(String::from_utf16(string)
.expect("should be ascii string")
.parse::<u32>()
.is_err());
Self::String(string.into())
}
}
impl From<JsString> for PropertyKey { impl From<JsString> for PropertyKey {
#[inline] #[inline]
fn from(string: JsString) -> Self { fn from(string: JsString) -> Self {

12
boa_engine/src/value/display.rs

@ -1,11 +1,11 @@
use std::borrow::Cow; use std::borrow::Cow;
use crate::{ use crate::{
builtins::promise::PromiseState, js_string, object::ObjectKind, property::PropertyDescriptor, builtins::promise::PromiseState, object::ObjectKind, property::PropertyDescriptor,
JsError, JsString, string::utf16, JsError, JsString,
}; };
use super::{fmt, Display, HashSet, JsValue, PropertyKey}; use super::{fmt, Display, HashSet, JsValue};
/// This object is used for displaying a `Value`. /// This object is used for displaying a `Value`.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -123,7 +123,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children
let len = v let len = v
.borrow() .borrow()
.properties() .properties()
.get(&PropertyKey::from(js_string!("length"))) .get(&utf16!("length").into())
.expect("array object must have 'length' property") .expect("array object must have 'length' property")
// FIXME: handle accessor descriptors // FIXME: handle accessor descriptors
.expect_value() .expect_value()
@ -202,7 +202,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children
} }
ObjectKind::Error(_) => { ObjectKind::Error(_) => {
let name: Cow<'static, str> = v let name: Cow<'static, str> = v
.get_property(&"name".into()) .get_property(&utf16!("name").into())
.as_ref() .as_ref()
.and_then(PropertyDescriptor::value) .and_then(PropertyDescriptor::value)
.map_or_else( .map_or_else(
@ -217,7 +217,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children
}, },
); );
let message = v let message = v
.get_property(&"message".into()) .get_property(&utf16!("message").into())
.as_ref() .as_ref()
.and_then(PropertyDescriptor::value) .and_then(PropertyDescriptor::value)
.map(|v| { .map(|v| {

27
boa_engine/src/value/serde_json.rs

@ -71,7 +71,7 @@ impl JsValue {
.writable(true) .writable(true)
.enumerable(true) .enumerable(true)
.configurable(true); .configurable(true);
js_obj.borrow_mut().insert(key.as_str(), property); js_obj.borrow_mut().insert(key.clone(), property);
} }
Ok(js_obj.into()) Ok(js_obj.into())
@ -171,7 +171,7 @@ mod tests {
use boa_parser::Source; use boa_parser::Source;
use crate::object::JsArray; use crate::object::JsArray;
use crate::{Context, JsValue}; use crate::{string::utf16, Context, JsValue};
#[test] #[test]
fn ut_json_conversions() { fn ut_json_conversions() {
@ -199,17 +199,26 @@ mod tests {
let value = JsValue::from_json(&json, &mut context).unwrap(); let value = JsValue::from_json(&json, &mut context).unwrap();
let obj = value.as_object().unwrap(); let obj = value.as_object().unwrap();
assert_eq!(obj.get("name", &mut context).unwrap(), "John Doe".into()); assert_eq!(
assert_eq!(obj.get("age", &mut context).unwrap(), 43_i32.into()); obj.get(utf16!("name"), &mut context).unwrap(),
assert_eq!(obj.get("minor", &mut context).unwrap(), false.into()); "John Doe".into()
assert_eq!(obj.get("adult", &mut context).unwrap(), true.into()); );
assert_eq!(obj.get(utf16!("age"), &mut context).unwrap(), 43_i32.into());
assert_eq!(
obj.get(utf16!("minor"), &mut context).unwrap(),
false.into()
);
assert_eq!(obj.get(utf16!("adult"), &mut context).unwrap(), true.into());
{ {
let extra = obj.get("extra", &mut context).unwrap(); let extra = obj.get(utf16!("extra"), &mut context).unwrap();
let extra = extra.as_object().unwrap(); let extra = extra.as_object().unwrap();
assert!(extra.get("address", &mut context).unwrap().is_null()); assert!(extra
.get(utf16!("address"), &mut context)
.unwrap()
.is_null());
} }
{ {
let phones = obj.get("phones", &mut context).unwrap(); let phones = obj.get(utf16!("phones"), &mut context).unwrap();
let phones = phones.as_object().unwrap(); let phones = phones.as_object().unwrap();
let arr = JsArray::from_object(phones.clone()).unwrap(); let arr = JsArray::from_object(phones.clone()).unwrap();

23
boa_engine/src/vm/code_block.rs

@ -12,9 +12,12 @@ use crate::{
context::intrinsics::StandardConstructors, context::intrinsics::StandardConstructors,
environments::{BindingLocator, CompileTimeEnvironment}, environments::{BindingLocator, CompileTimeEnvironment},
error::JsNativeError, error::JsNativeError,
js_string, object::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR,
PROTOTYPE,
},
property::PropertyDescriptor, property::PropertyDescriptor,
string::utf16,
vm::{CallFrame, Opcode}, vm::{CallFrame, Opcode},
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -612,14 +615,14 @@ pub(crate) fn create_function_object(
.build(); .build();
constructor constructor
.define_property_or_throw(js_string!("length"), length_property, context) .define_property_or_throw(utf16!("length"), length_property, context)
.expect("failed to define the length property of the function"); .expect("failed to define the length property of the function");
constructor constructor
.define_property_or_throw(js_string!("name"), name_property, context) .define_property_or_throw(utf16!("name"), name_property, context)
.expect("failed to define the name property of the function"); .expect("failed to define the name property of the function");
if !r#async && !arrow && !method { if !r#async && !arrow && !method {
constructor constructor
.define_property_or_throw(js_string!("prototype"), prototype_property, context) .define_property_or_throw(PROTOTYPE, prototype_property, context)
.expect("failed to define the prototype property of the function"); .expect("failed to define the prototype property of the function");
} }
@ -705,14 +708,14 @@ pub(crate) fn create_generator_function_object(
if !method { if !method {
constructor constructor
.define_property_or_throw(js_string!("prototype"), prototype_property, context) .define_property_or_throw(PROTOTYPE, prototype_property, context)
.expect("failed to define the prototype property of the generator function"); .expect("failed to define the prototype property of the generator function");
} }
constructor constructor
.define_property_or_throw(js_string!("name"), name_property, context) .define_property_or_throw(utf16!("name"), name_property, context)
.expect("failed to define the name property of the generator function"); .expect("failed to define the name property of the generator function");
constructor constructor
.define_property_or_throw(js_string!("length"), length_property, context) .define_property_or_throw(utf16!("length"), length_property, context)
.expect("failed to define the length property of the generator function"); .expect("failed to define the length property of the generator function");
constructor constructor
@ -1117,7 +1120,7 @@ impl JsObject {
std::mem::swap(&mut context.vm.stack, &mut stack); std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = this_function_object let prototype = this_function_object
.get("prototype", context) .get(PROTOTYPE, context)
.expect("GeneratorFunction must have a prototype property") .expect("GeneratorFunction must have a prototype property")
.as_object() .as_object()
.map_or_else(|| context.intrinsics().objects().generator(), Clone::clone); .map_or_else(|| context.intrinsics().objects().generator(), Clone::clone);
@ -1255,7 +1258,7 @@ impl JsObject {
std::mem::swap(&mut context.vm.stack, &mut stack); std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = this_function_object let prototype = this_function_object
.get("prototype", context) .get(PROTOTYPE, context)
.expect("AsyncGeneratorFunction must have a prototype property") .expect("AsyncGeneratorFunction must have a prototype property")
.as_object() .as_object()
.map_or_else( .map_or_else(

6
boa_engine/src/vm/opcode/define/class/getter.rs

@ -45,7 +45,7 @@ impl Operation for DefineClassStaticGetterByName {
.and_then(PropertyDescriptor::set) .and_then(PropertyDescriptor::set)
.cloned(); .cloned();
class.__define_own_property__( class.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_get(Some(function)) .maybe_get(Some(function))
.maybe_set(set) .maybe_set(set)
@ -103,7 +103,7 @@ impl Operation for DefineClassGetterByName {
.and_then(PropertyDescriptor::set) .and_then(PropertyDescriptor::set)
.cloned(); .cloned();
class_proto.__define_own_property__( class_proto.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_get(Some(function)) .maybe_get(Some(function))
.maybe_set(set) .maybe_set(set)
@ -209,7 +209,7 @@ impl Operation for DefineClassGetterByValue {
.and_then(PropertyDescriptor::set) .and_then(PropertyDescriptor::set)
.cloned(); .cloned();
class_proto.__define_own_property__( class_proto.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_get(Some(function)) .maybe_get(Some(function))
.maybe_set(set) .maybe_set(set)

6
boa_engine/src/vm/opcode/define/class/method.rs

@ -40,7 +40,7 @@ impl Operation for DefineClassStaticMethodByName {
function_mut.set_class_object(class.clone()); function_mut.set_class_object(class.clone());
} }
class.__define_own_property__( class.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(function) .value(function)
.writable(true) .writable(true)
@ -93,7 +93,7 @@ impl Operation for DefineClassMethodByName {
function_mut.set_class_object(class); function_mut.set_class_object(class);
} }
class_proto.__define_own_property__( class_proto.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(function) .value(function)
.writable(true) .writable(true)
@ -189,7 +189,7 @@ impl Operation for DefineClassMethodByValue {
function_mut.set_class_object(class); function_mut.set_class_object(class);
} }
class_proto.__define_own_property__( class_proto.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(function) .value(function)
.writable(true) .writable(true)

6
boa_engine/src/vm/opcode/define/class/setter.rs

@ -45,7 +45,7 @@ impl Operation for DefineClassStaticSetterByName {
.and_then(PropertyDescriptor::get) .and_then(PropertyDescriptor::get)
.cloned(); .cloned();
class.__define_own_property__( class.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_set(Some(function)) .maybe_set(Some(function))
.maybe_get(get) .maybe_get(get)
@ -103,7 +103,7 @@ impl Operation for DefineClassSetterByName {
.and_then(PropertyDescriptor::get) .and_then(PropertyDescriptor::get)
.cloned(); .cloned();
class_proto.__define_own_property__( class_proto.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_set(Some(function)) .maybe_set(Some(function))
.maybe_get(get) .maybe_get(get)
@ -209,7 +209,7 @@ impl Operation for DefineClassSetterByValue {
.and_then(PropertyDescriptor::get) .and_then(PropertyDescriptor::get)
.cloned(); .cloned();
class_proto.__define_own_property__( class_proto.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_set(Some(function)) .maybe_set(Some(function))
.maybe_get(get) .maybe_get(get)

4
boa_engine/src/vm/opcode/define/own_property.rs

@ -30,7 +30,7 @@ impl Operation for DefineOwnPropertyByName {
.resolve_expect(name.sym()) .resolve_expect(name.sym())
.into_common::<JsString>(false); .into_common::<JsString>(false);
object.__define_own_property__( object.__define_own_property__(
name.into(), &name.into(),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(value) .value(value)
.writable(true) .writable(true)
@ -65,7 +65,7 @@ impl Operation for DefineOwnPropertyByValue {
}; };
let key = key.to_property_key(context)?; let key = key.to_property_key(context)?;
let success = object.__define_own_property__( let success = object.__define_own_property__(
key, &key,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(value) .value(value)
.writable(true) .writable(true)

23
boa_engine/src/vm/opcode/generator/mod.rs

@ -4,6 +4,7 @@ use crate::{
iterable::IteratorRecord, iterable::IteratorRecord,
}, },
error::JsNativeError, error::JsNativeError,
string::utf16,
vm::{ vm::{
call_frame::{FinallyReturn, GeneratorResumeKind}, call_frame::{FinallyReturn, GeneratorResumeKind},
opcode::Operation, opcode::Operation,
@ -151,14 +152,14 @@ impl Operation for GeneratorNextDelegate {
let result = result.as_object().ok_or_else(|| { let result = result.as_object().ok_or_else(|| {
JsNativeError::typ().with_message("generator next method returned non-object") JsNativeError::typ().with_message("generator next method returned non-object")
})?; })?;
let done = result.get("done", context)?.to_boolean(); let done = result.get(utf16!("done"), context)?.to_boolean();
if done { if done {
context.vm.frame_mut().pc = done_address as usize; context.vm.frame_mut().pc = done_address as usize;
let value = result.get("value", context)?; let value = result.get(utf16!("value"), context)?;
context.vm.push(value); context.vm.push(value);
return Ok(ShouldExit::False); return Ok(ShouldExit::False);
} }
let value = result.get("value", context)?; let value = result.get(utf16!("value"), context)?;
context.vm.push(iterator.clone()); context.vm.push(iterator.clone());
context.vm.push(next_method.clone()); context.vm.push(next_method.clone());
context.vm.push(done); context.vm.push(done);
@ -166,21 +167,21 @@ impl Operation for GeneratorNextDelegate {
Ok(ShouldExit::Yield) Ok(ShouldExit::Yield)
} }
GeneratorResumeKind::Throw => { GeneratorResumeKind::Throw => {
let throw = iterator.get_method("throw", context)?; let throw = iterator.get_method(utf16!("throw"), context)?;
if let Some(throw) = throw { if let Some(throw) = throw {
let result = throw.call(&iterator.clone().into(), &[received], context)?; let result = throw.call(&iterator.clone().into(), &[received], context)?;
let result_object = result.as_object().ok_or_else(|| { let result_object = result.as_object().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("generator throw method returned non-object") .with_message("generator throw method returned non-object")
})?; })?;
let done = result_object.get("done", context)?.to_boolean(); let done = result_object.get(utf16!("done"), context)?.to_boolean();
if done { if done {
context.vm.frame_mut().pc = done_address as usize; context.vm.frame_mut().pc = done_address as usize;
let value = result_object.get("value", context)?; let value = result_object.get(utf16!("value"), context)?;
context.vm.push(value); context.vm.push(value);
return Ok(ShouldExit::False); return Ok(ShouldExit::False);
} }
let value = result_object.get("value", context)?; let value = result_object.get(utf16!("value"), context)?;
context.vm.push(iterator.clone()); context.vm.push(iterator.clone());
context.vm.push(next_method.clone()); context.vm.push(next_method.clone());
context.vm.push(done); context.vm.push(done);
@ -196,21 +197,21 @@ impl Operation for GeneratorNextDelegate {
.into()) .into())
} }
GeneratorResumeKind::Return => { GeneratorResumeKind::Return => {
let r#return = iterator.get_method("return", context)?; let r#return = iterator.get_method(utf16!("return"), context)?;
if let Some(r#return) = r#return { if let Some(r#return) = r#return {
let result = r#return.call(&iterator.clone().into(), &[received], context)?; let result = r#return.call(&iterator.clone().into(), &[received], context)?;
let result_object = result.as_object().ok_or_else(|| { let result_object = result.as_object().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("generator return method returned non-object") .with_message("generator return method returned non-object")
})?; })?;
let done = result_object.get("done", context)?.to_boolean(); let done = result_object.get(utf16!("done"), context)?.to_boolean();
if done { if done {
context.vm.frame_mut().pc = done_address as usize; context.vm.frame_mut().pc = done_address as usize;
let value = result_object.get("value", context)?; let value = result_object.get(utf16!("value"), context)?;
context.vm.push(value); context.vm.push(value);
return Ok(ShouldExit::True); return Ok(ShouldExit::True);
} }
let value = result_object.get("value", context)?; let value = result_object.get(utf16!("value"), context)?;
context.vm.push(iterator.clone()); context.vm.push(iterator.clone());
context.vm.push(next_method.clone()); context.vm.push(next_method.clone());
context.vm.push(done); context.vm.push(done);

3
boa_engine/src/vm/opcode/iteration/for_in.rs

@ -1,6 +1,7 @@
use crate::{ use crate::{
builtins::{iterable::IteratorRecord, object::for_in_iterator::ForInIterator}, builtins::{iterable::IteratorRecord, object::for_in_iterator::ForInIterator},
error::JsNativeError, error::JsNativeError,
js_string,
property::PropertyDescriptor, property::PropertyDescriptor,
vm::{opcode::Operation, ShouldExit}, vm::{opcode::Operation, ShouldExit},
Context, JsResult, JsValue, Context, JsResult, JsValue,
@ -29,7 +30,7 @@ impl Operation for ForInLoopInitIterator {
let object = object.to_object(context)?; let object = object.to_object(context)?;
let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context); let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context);
let next_method = iterator let next_method = iterator
.get_property("next") .get_property(js_string!("next"))
.as_ref() .as_ref()
.map(PropertyDescriptor::expect_value) .map(PropertyDescriptor::expect_value)
.cloned() .cloned()

3
boa_engine/src/vm/opcode/push/array.rs

@ -1,5 +1,6 @@
use crate::{ use crate::{
builtins::{iterable::IteratorRecord, Array}, builtins::{iterable::IteratorRecord, Array},
string::utf16,
vm::{opcode::Operation, ShouldExit}, vm::{opcode::Operation, ShouldExit},
Context, JsResult, Context, JsResult,
}; };
@ -67,7 +68,7 @@ impl Operation for PushElisionToArray {
.length_of_array_like(context) .length_of_array_like(context)
.expect("arrays should always have a 'length' property"); .expect("arrays should always have a 'length' property");
o.set("length", len + 1, true, context)?; o.set(utf16!("length"), len + 1, true, context)?;
context.vm.push(array); context.vm.push(array);
Ok(ShouldExit::False) Ok(ShouldExit::False)
} }

3
boa_engine/src/vm/opcode/push/class/mod.rs

@ -1,6 +1,7 @@
use crate::{ use crate::{
builtins::function::{ConstructorKind, Function}, builtins::function::{ConstructorKind, Function},
error::JsNativeError, error::JsNativeError,
object::PROTOTYPE,
vm::{opcode::Operation, ShouldExit}, vm::{opcode::Operation, ShouldExit},
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -26,7 +27,7 @@ impl Operation for PushClassPrototype {
let superclass = context.vm.pop(); let superclass = context.vm.pop();
if let Some(superclass) = superclass.as_constructor() { if let Some(superclass) = superclass.as_constructor() {
let proto = superclass.get("prototype", context)?; let proto = superclass.get(PROTOTYPE, context)?;
if !proto.is_object() && !proto.is_null() { if !proto.is_object() && !proto.is_null() {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("superclass prototype must be an object or null") .with_message("superclass prototype must be an object or null")

3
boa_engine/src/vm/opcode/push/class/private.rs

@ -1,6 +1,7 @@
use crate::{ use crate::{
object::PrivateElement, object::PrivateElement,
property::PropertyDescriptor, property::PropertyDescriptor,
string::utf16,
vm::{opcode::Operation, ShouldExit}, vm::{opcode::Operation, ShouldExit},
Context, JsResult, Context, JsResult,
}; };
@ -30,7 +31,7 @@ impl Operation for PushClassPrivateMethod {
.configurable(true) .configurable(true)
.build(); .build();
method_object method_object
.__define_own_property__("name".into(), desc, context) .__define_own_property__(&utf16!("name").into(), desc, context)
.expect("failed to set name property on private method"); .expect("failed to set name property on private method");
let class = context.vm.pop(); let class = context.vm.pop();

6
boa_engine/src/vm/opcode/set/class_prototype.rs

@ -1,5 +1,5 @@
use crate::{ use crate::{
object::{JsObject, ObjectData, CONSTRUCTOR}, object::{JsObject, ObjectData, CONSTRUCTOR, PROTOTYPE},
property::PropertyDescriptorBuilder, property::PropertyDescriptorBuilder,
vm::{opcode::Operation, ShouldExit}, vm::{opcode::Operation, ShouldExit},
Context, JsResult, JsValue, Context, JsResult, JsValue,
@ -39,7 +39,7 @@ impl Operation for SetClassPrototype {
let class_object = class.as_object().expect("class must be object"); let class_object = class.as_object().expect("class must be object");
class_object class_object
.define_property_or_throw( .define_property_or_throw(
"prototype", PROTOTYPE,
PropertyDescriptorBuilder::new() PropertyDescriptorBuilder::new()
.value(proto.clone()) .value(proto.clone())
.writable(false) .writable(false)
@ -57,7 +57,7 @@ impl Operation for SetClassPrototype {
proto proto
.__define_own_property__( .__define_own_property__(
CONSTRUCTOR.into(), &CONSTRUCTOR.into(),
PropertyDescriptorBuilder::new() PropertyDescriptorBuilder::new()
.value(class) .value(class)
.writable(true) .writable(true)

3
boa_engine/src/vm/opcode/set/private.rs

@ -1,6 +1,7 @@
use crate::{ use crate::{
object::PrivateElement, object::PrivateElement,
property::PropertyDescriptor, property::PropertyDescriptor,
string::utf16,
vm::{opcode::Operation, ShouldExit}, vm::{opcode::Operation, ShouldExit},
Context, JsResult, Context, JsResult,
}; };
@ -80,7 +81,7 @@ impl Operation for SetPrivateMethod {
.configurable(true) .configurable(true)
.build(); .build();
value value
.__define_own_property__("name".into(), desc, context) .__define_own_property__(&utf16!("name").into(), desc, context)
.expect("failed to set name property on private method"); .expect("failed to set name property on private method");
let object = context.vm.pop(); let object = context.vm.pop();

8
boa_engine/src/vm/opcode/set/property.rs

@ -108,7 +108,7 @@ impl Operation for SetPropertyGetterByName {
.and_then(PropertyDescriptor::set) .and_then(PropertyDescriptor::set)
.cloned(); .cloned();
object.__define_own_property__( object.__define_own_property__(
name, &name,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_get(Some(value)) .maybe_get(Some(value))
.maybe_set(set) .maybe_set(set)
@ -144,7 +144,7 @@ impl Operation for SetPropertyGetterByValue {
.and_then(PropertyDescriptor::set) .and_then(PropertyDescriptor::set)
.cloned(); .cloned();
object.__define_own_property__( object.__define_own_property__(
name, &name,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_get(Some(value)) .maybe_get(Some(value))
.maybe_set(set) .maybe_set(set)
@ -185,7 +185,7 @@ impl Operation for SetPropertySetterByName {
.and_then(PropertyDescriptor::get) .and_then(PropertyDescriptor::get)
.cloned(); .cloned();
object.__define_own_property__( object.__define_own_property__(
name, &name,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_set(Some(value)) .maybe_set(Some(value))
.maybe_get(get) .maybe_get(get)
@ -221,7 +221,7 @@ impl Operation for SetPropertySetterByValue {
.and_then(PropertyDescriptor::get) .and_then(PropertyDescriptor::get)
.cloned(); .cloned();
object.__define_own_property__( object.__define_own_property__(
name, &name,
PropertyDescriptor::builder() PropertyDescriptor::builder()
.maybe_set(Some(value)) .maybe_set(Some(value))
.maybe_get(get) .maybe_get(get)

Loading…
Cancel
Save