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

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

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
symbol::JsSymbol,
value::{IntegerOrInfinity, Numeric},
Context, JsArgs, JsResult, JsValue,
@ -62,7 +63,12 @@ impl IntrinsicObject for ArrayBuffer {
.build();
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(
JsSymbol::species(),
Some(get_species),

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

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
symbol::JsSymbol,
value::JsValue,
Context, JsArgs, JsResult,
@ -49,9 +50,19 @@ impl IntrinsicObject for DataView {
.build();
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.accessor("buffer", Some(get_buffer), None, flag_attributes)
.accessor("byteLength", Some(get_byte_length), None, flag_attributes)
.accessor("byteOffset", Some(get_byte_offset), None, flag_attributes)
.accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes)
.accessor(
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_uint64, "getBigUint64", 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").
let func = o.get("toISOString", context)?;
let func = o.get(utf16!("toISOString"), context)?;
func.call(this, &[], context)
}

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

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

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

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

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;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute)
.method(Self::to_string, "toString", 0)
.build();
}
@ -175,7 +175,7 @@ impl BuiltInConstructor for Error {
let msg = message.to_string(context)?;
// 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).
@ -194,12 +194,12 @@ impl Error {
) -> JsResult<()> {
// 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then
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").
let cause = options.get("cause", context)?;
let cause = options.get(utf16!("cause"), context)?;
// 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},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue,
};
use boa_profiler::Profiler;
@ -32,8 +33,8 @@ impl IntrinsicObject for RangeError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute)
.build();
}
@ -71,7 +72,7 @@ impl BuiltInConstructor for RangeError {
let msg = message.to_string(context)?;
// 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).

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

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

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

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

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

@ -24,6 +24,7 @@ use crate::{
native_function::NativeFunction,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue,
};
use boa_profiler::Profiler;
@ -42,8 +43,8 @@ impl IntrinsicObject for TypeError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute)
.build();
}
@ -81,7 +82,7 @@ impl BuiltInConstructor for TypeError {
let msg = message.to_string(context)?;
// 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).
@ -108,8 +109,8 @@ impl IntrinsicObject for ThrowTypeError {
let obj = BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
.prototype(intrinsics.constructors().function().prototype())
.static_property("name", "ThrowTypeError", Attribute::empty())
.static_property("length", 0, Attribute::empty())
.static_property(utf16!("name"), "ThrowTypeError", Attribute::empty())
.static_property(utf16!("length"), 0, Attribute::empty())
.build();
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},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
Context, JsArgs, JsResult, JsValue,
};
use boa_profiler::Profiler;
@ -33,8 +34,8 @@ impl IntrinsicObject for UriError {
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.prototype(intrinsics.constructors().error().constructor())
.inherits(Some(intrinsics.constructors().error().prototype()))
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute)
.build();
}
@ -72,7 +73,7 @@ impl BuiltInConstructor for UriError {
let msg = message.to_string(context)?;
// 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).

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

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

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

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

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

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

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

@ -13,6 +13,7 @@ use crate::{
error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
string::utf16,
Context, JsResult, JsString, JsValue,
};
@ -182,7 +183,12 @@ pub(crate) fn to_date_time_options(
// 4. If required is "date" or "any", then
if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(required) {
// 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).
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",
// "fractionalSecondDigits" », do
for property in [
"dayPeriod",
"hour",
"minute",
"second",
"fractionalSecondDigits",
utf16!("dayPeriod"),
utf16!("hour"),
utf16!("minute"),
utf16!("second"),
utf16!("fractionalSecondDigits"),
] {
// i. Let value be ? Get(options, prop).
let value = options.get(property, context)?;
@ -215,10 +221,10 @@ pub(crate) fn to_date_time_options(
}
// 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").
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.
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
if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(defaults) {
// 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").
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
if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(defaults) {
// 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").
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},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
};
@ -110,8 +111,9 @@ impl BuiltInConstructor for ListFormat {
// 5. Let opt be a new Record.
// 6. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
let matcher = get_option::<LocaleMatcher>(&options, "localeMatcher", false, context)?
.unwrap_or_default();
let matcher =
get_option::<LocaleMatcher>(&options, utf16!("localeMatcher"), false, context)?
.unwrap_or_default();
// 7. Set opt.[[localeMatcher]] to matcher.
// 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").
// 12. Set listFormat.[[Type]] to type.
let typ =
get_option::<ListFormatType>(&options, "type", false, context)?.unwrap_or_default();
let typ = get_option::<ListFormatType>(&options, utf16!("type"), false, context)?
.unwrap_or_default();
// 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long").
// 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);
// 15. Let dataLocale be r.[[dataLocale]].
@ -361,11 +363,11 @@ impl ListFormat {
);
// 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");
// 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");
// d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O).
@ -418,11 +420,11 @@ impl ListFormat {
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
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");
options
.create_data_property_or_throw(
"type",
utf16!("type"),
match lf.typ {
ListFormatType::Conjunction => "conjunction",
ListFormatType::Disjunction => "disjunction",
@ -433,7 +435,7 @@ impl ListFormat {
.expect("operation must not fail per the spec");
options
.create_data_property_or_throw(
"style",
utf16!("style"),
match lf.style {
ListLength::Wide => "long",
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 icu_collator::CaseFirst;
use icu_datetime::options::preferences::HourCycle;
@ -93,21 +94,66 @@ impl IntrinsicObject for Locale {
.method(Self::maximize, "maximize", 0)
.method(Self::minimize, "minimize", 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(
"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),
None,
Attribute::CONFIGURABLE,
)
.accessor("language", Some(language), None, Attribute::CONFIGURABLE)
.accessor("script", Some(script), None, Attribute::CONFIGURABLE)
.accessor("region", Some(region), None, Attribute::CONFIGURABLE)
.accessor(
utf16!("language"),
Some(language),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("script"),
Some(script),
None,
Attribute::CONFIGURABLE,
)
.accessor(
utf16!("region"),
Some(region),
None,
Attribute::CONFIGURABLE,
)
.build();
}
@ -197,7 +243,7 @@ impl BuiltInConstructor for Locale {
// 3. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
// 4. Let language be ? GetOption(options, "language", string, empty, undefined).
// 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.
.map(|s| s.to_std_string_escaped().parse::<Language>())
.transpose()
@ -205,7 +251,7 @@ impl BuiltInConstructor for Locale {
// 6. Let script be ? GetOption(options, "script", string, empty, undefined).
// 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>())
.transpose()
// 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).
// 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>())
.transpose()
// 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
// 15. Set opt.[[ca]] to calendar.
// 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).
// 17. If collation is not undefined, then
// 18. Set opt.[[co]] to collation.
// 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).
// 20. Set opt.[[hc]] to hc.
let hc =
get_option::<HourCycle>(options, "hourCycle", false, context)?.map(|hc| match hc {
HourCycle::H24 => value!("h24"),
HourCycle::H23 => value!("h23"),
HourCycle::H12 => value!("h12"),
HourCycle::H11 => value!("h11"),
});
get_option::<HourCycle>(options, utf16!("hourCycle"), false, context)?.map(
|hc| match hc {
HourCycle::H24 => value!("h24"),
HourCycle::H23 => value!("h23"),
HourCycle::H12 => value!("h12"),
HourCycle::H11 => value!("h11"),
},
);
// 21. Let kf be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined).
// 22. Set opt.[[kf]] to kf.
let kf =
get_option::<CaseFirst>(options, "caseFirst", false, context)?.map(|kf| match kf {
CaseFirst::UpperFirst => value!("upper"),
CaseFirst::LowerFirst => value!("lower"),
CaseFirst::Off => value!("false"),
_ => unreachable!(),
});
get_option::<CaseFirst>(options, utf16!("caseFirst"), false, context)?.map(
|kf| match kf {
CaseFirst::UpperFirst => value!("upper"),
CaseFirst::LowerFirst => value!("lower"),
CaseFirst::Off => value!("false"),
_ => unreachable!(),
},
);
// 23. Let kn be ? GetOption(options, "numeric", boolean, empty, undefined).
// 24. If kn is not undefined, set kn to ! ToString(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 {
value!("true")
} else {
@ -302,7 +352,7 @@ impl BuiltInConstructor for Locale {
// 27. If numberingSystem is not undefined, then
// 28. Set opt.[[nu]] to numberingSystem.
// 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).
// 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},
object::JsObject,
string::utf16,
Context, JsNativeError, JsResult, JsValue,
};
@ -497,8 +498,8 @@ where
let options = coerce_options_to_object(options, context)?;
// 2. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
let matcher =
get_option::<LocaleMatcher>(&options, "localeMatcher", false, context)?.unwrap_or_default();
let matcher = get_option::<LocaleMatcher>(&options, utf16!("localeMatcher"), false, context)?
.unwrap_or_default();
let elements = match matcher {
// 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
pub(super) fn get_option<T: OptionType>(
options: &JsObject,
property: &str,
property: &[u16],
required: bool,
context: &mut Context<'_>,
) -> JsResult<Option<T>> {
@ -157,7 +157,7 @@ pub(super) fn get_option<T: OptionType>(
#[allow(unused)]
pub(super) fn get_number_option(
options: &JsObject,
property: &str,
property: &[u16],
minimum: f64,
maximum: f64,
fallback: Option<f64>,

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

@ -7,6 +7,7 @@ use crate::{
context::intrinsics::Intrinsics,
native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, ObjectData},
string::utf16,
Context, JsArgs, JsNativeError, JsResult, JsValue,
};
use boa_gc::{Finalize, Trace};
@ -70,7 +71,7 @@ impl AsyncFromSyncIterator {
// 3. Let nextMethod be ! Get(asyncIterator, "next").
let next_method = async_iterator
.get("next", context)
.get(utf16!("next"), context)
.expect("async from sync iterator prototype must have next method");
// 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");
// 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).
if_abrupt_reject_promise!(r#return, promise_capability, context);
@ -253,7 +254,7 @@ impl AsyncFromSyncIterator {
.expect("cannot fail with promise constructor");
// 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).
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,
error::JsNativeError,
object::JsObject,
string::utf16,
symbol::JsSymbol,
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);
// 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");
// 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");
// 5. Return obj.
obj.into()
@ -265,7 +266,7 @@ impl JsValue {
})?;
// 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 }.
// 7. Return iteratorRecord.
@ -301,7 +302,7 @@ impl IteratorResult {
#[inline]
pub fn complete(&self, context: &mut Context<'_>) -> JsResult<bool> {
// 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 )`
@ -317,7 +318,7 @@ impl IteratorResult {
#[inline]
pub fn value(&self, context: &mut Context<'_>) -> JsResult<JsValue> {
// 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;
// 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
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.
// 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");
// d. Return ? InternalizeJSONProperty(root, rootName, reviver).
@ -448,7 +448,7 @@ impl Json {
// 10. Perform ! CreateDataPropertyOrThrow(wrapper, the empty String, value).
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");
// 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
if value.is_object() || value.is_bigint() {
// 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
if let Some(obj) = to_json.as_object() {

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

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

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

@ -13,7 +13,7 @@
use crate::{
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;
@ -32,14 +32,18 @@ impl IntrinsicObject for Math {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::with_intrinsic::<Self>(intrinsics)
.static_property("E", std::f64::consts::E, attribute)
.static_property("LN10", std::f64::consts::LN_10, attribute)
.static_property("LN2", std::f64::consts::LN_2, attribute)
.static_property("LOG10E", std::f64::consts::LOG10_E, attribute)
.static_property("LOG2E", std::f64::consts::LOG2_E, attribute)
.static_property("PI", std::f64::consts::PI, attribute)
.static_property("SQRT1_2", std::f64::consts::FRAC_1_SQRT_2, attribute)
.static_property("SQRT2", std::f64::consts::SQRT_2, attribute)
.static_property(utf16!("E"), std::f64::consts::E, attribute)
.static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute)
.static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute)
.static_property(utf16!("LOG10E"), std::f64::consts::LOG10_E, attribute)
.static_property(utf16!("LOG2E"), std::f64::consts::LOG2_E, attribute)
.static_property(utf16!("PI"), std::f64::consts::PI, attribute)
.static_property(
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::acos, "acos", 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,
},
property::{Attribute, PropertyDescriptor, PropertyKey},
string::utf16,
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();
global_object.define_property_or_throw(
"globalThis",
utf16!("globalThis"),
PropertyDescriptor::builder()
.value(context.realm.global_this().clone())
.writable(true)
@ -286,13 +287,17 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
.enumerable(false)
.configurable(false);
global_object.define_property_or_throw(
"Infinity",
utf16!("Infinity"),
restricted.clone().value(f64::INFINITY),
context,
)?;
global_object.define_property_or_throw("NaN", restricted.clone().value(f64::NAN), context)?;
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()),
context,
)?;
@ -357,7 +362,7 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
let object = Console::init(context);
let global_object = context.global_object().clone();
global_object.define_property_or_throw(
"console",
utf16!("console"),
PropertyDescriptor::builder()
.value(object)
.writable(true)
@ -475,8 +480,8 @@ impl<S: ApplyToObject + IsConstructor> ApplyToObject for Callable<S> {
{
let mut constructor = object.borrow_mut();
constructor.data = ObjectData::function(function);
constructor.insert("length", length);
constructor.insert("name", name);
constructor.insert(utf16!("length"), length);
constructor.insert(utf16!("name"), name);
}
}
}

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

@ -19,6 +19,7 @@ use crate::{
error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
value::{AbstractRelation, IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult,
};
@ -50,21 +51,29 @@ impl IntrinsicObject for Number {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.static_property("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(utf16!("EPSILON"), f64::EPSILON, attribute)
.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(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
"parseFloat",
utf16!("parseFloat"),
intrinsics.objects().parse_float(),
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)
.inherits(None)
.accessor(
"__proto__",
utf16!("__proto__"),
Some(legacy_proto_getter),
Some(legacy_setter_proto),
Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
@ -526,42 +526,42 @@ impl Object {
// 4. If Desc has a [[Value]] field, then
if let Some(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");
}
// 5. If Desc has a [[Writable]] field, then
if let Some(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");
}
// 6. If Desc has a [[Get]] field, then
if let Some(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");
}
// 7. If Desc has a [[Set]] field, then
if let Some(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");
}
// 8. If Desc has an [[Enumerable]] field, then
if let Some(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");
}
// 9. If Desc has a [[Configurable]] field, then
if let Some(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");
}
@ -843,7 +843,7 @@ impl Object {
) -> JsResult<JsValue> {
// 1. Let O be the this value.
// 2. Return ? Invoke(O, "toString").
this.invoke("toString", &[], context)
this.invoke(utf16!("toString"), &[], context)
}
/// `Object.prototype.hasOwnProperty( property )`

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

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

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

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

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

@ -167,7 +167,7 @@ impl Reflect {
.into();
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)
}

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

@ -97,7 +97,7 @@ impl IntrinsicObject for RegExp {
None,
Attribute::CONFIGURABLE,
)
.property("lastIndex", 0, Attribute::all())
.property(utf16!("lastIndex"), 0, Attribute::all())
.method(Self::test, "test", 1)
.method(Self::exec, "exec", 1)
.method(Self::to_string, "toString", 0)
@ -110,15 +110,30 @@ impl IntrinsicObject for RegExp {
.method(Self::replace, (JsSymbol::replace(), "[Symbol.replace]"), 2)
.method(Self::search, (JsSymbol::search(), "[Symbol.search]"), 1)
.method(Self::split, (JsSymbol::split(), "[Symbol.split]"), 2)
.accessor("hasIndices", Some(get_has_indices), None, flag_attributes)
.accessor("global", Some(get_global), None, flag_attributes)
.accessor("ignoreCase", Some(get_ignore_case), None, flag_attributes)
.accessor("multiline", Some(get_multiline), None, flag_attributes)
.accessor("dotAll", Some(get_dot_all), None, flag_attributes)
.accessor("unicode", Some(get_unicode), None, flag_attributes)
.accessor("sticky", Some(get_sticky), None, flag_attributes)
.accessor("flags", Some(get_flags), None, flag_attributes)
.accessor("source", Some(get_source), None, flag_attributes)
.accessor(
utf16!("hasIndices"),
Some(get_has_indices),
None,
flag_attributes,
)
.accessor(utf16!("global"), Some(get_global), None, flag_attributes)
.accessor(
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();
}
@ -219,7 +234,7 @@ impl RegExp {
// 2. Perform ! DefinePropertyOrThrow(obj, "lastIndex", PropertyDescriptor { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
obj.define_property_or_throw(
"lastIndex",
utf16!("lastIndex"),
PropertyDescriptorBuilder::new()
.writable(true)
.enumerable(false)
@ -298,7 +313,7 @@ impl RegExp {
obj.borrow_mut().data = ObjectData::reg_exp(Box::new(regexp));
// 16. Perform ? Set(obj, "lastIndex", +0𝔽, true).
obj.set("lastIndex", 0, true, context)?;
obj.set(utf16!("lastIndex"), 0, true, context)?;
// 16. Return obj.
Ok(obj.into())
@ -528,41 +543,41 @@ impl RegExp {
// 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.
if object.get("hasIndices", context)?.to_boolean() {
if object.get(utf16!("hasIndices"), context)?.to_boolean() {
result.push('d');
}
// 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.
if object.get("global", context)?.to_boolean() {
if object.get(utf16!("global"), context)?.to_boolean() {
result.push('g');
}
// 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.
if object.get("ignoreCase", context)?.to_boolean() {
if object.get(utf16!("ignoreCase"), context)?.to_boolean() {
result.push('i');
}
// 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.
if object.get("multiline", context)?.to_boolean() {
if object.get(utf16!("multiline"), context)?.to_boolean() {
result.push('m');
}
// 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.
if object.get("dotAll", context)?.to_boolean() {
if object.get(utf16!("dotAll"), context)?.to_boolean() {
result.push('s');
}
// 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.
if object.get("unicode", context)?.to_boolean() {
if object.get(utf16!("unicode"), context)?.to_boolean() {
result.push('u');
}
// 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.
if object.get("sticky", context)?.to_boolean() {
if object.get(utf16!("sticky"), context)?.to_boolean() {
result.push('y');
}
@ -747,7 +762,7 @@ impl RegExp {
// 2. Assert: Type(S) is String.
// 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
if let Some(exec) = exec.as_callable() {
@ -805,7 +820,7 @@ impl RegExp {
let length = input.len() as u64;
// 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]].
let flags = &rx.original_flags;
@ -835,7 +850,7 @@ impl RegExp {
// i. If global is true or sticky is true, then
if global || sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
this.set("lastIndex", 0, true, context)?;
this.set(utf16!("lastIndex"), 0, true, context)?;
}
// ii. Return null.
@ -863,7 +878,7 @@ impl RegExp {
// i. If sticky is true, then
if sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
this.set("lastIndex", 0, true, context)?;
this.set(utf16!("lastIndex"), 0, true, context)?;
// 2. Return null.
return Ok(None);
@ -880,7 +895,7 @@ impl RegExp {
// i. If sticky is true, then
if sticky {
// 1. Perform ? Set(R, "lastIndex", +0𝔽, true).
this.set("lastIndex", 0, true, context)?;
this.set(utf16!("lastIndex"), 0, true, context)?;
// 2. Return null.
return Ok(None);
@ -916,7 +931,7 @@ impl RegExp {
if global || sticky {
// a. Perform ? Set(R, "lastIndex", 𝔽(e), true).
this.set(
"lastIndex",
utf16!("lastIndex"),
lossy_input[..e].encode_utf16().count(),
true,
context,
@ -933,11 +948,11 @@ impl RegExp {
let a = Array::array_create(n + 1, None, context)?;
// 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");
// 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");
// 22. Let matchedSubstr be the substring of S from lastIndex to e.
@ -975,7 +990,7 @@ impl RegExp {
};
// 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");
// 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)?;
// 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
#[allow(clippy::if_not_else)]
@ -1039,10 +1054,10 @@ impl RegExp {
// a. Assert: global is true.
// 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).
rx.set("lastIndex", 0, true, context)?;
rx.set(utf16!("lastIndex"), 0, true, context)?;
// d. Let A be ! ArrayCreate(0).
let a =
@ -1060,7 +1075,7 @@ impl RegExp {
// iii. Else,
if let Some(result) = result {
// 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).
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
if match_str.is_empty() {
// 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).
let next_index = advance_string_index(&arg_str, this_index, unicode);
// 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.
@ -1157,16 +1173,18 @@ impl RegExp {
let c = regexp.species_constructor(StandardConstructors::regexp, context)?;
// 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 »).
let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?;
// 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).
matcher.set("lastIndex", last_index, true, context)?;
matcher.set(utf16!("lastIndex"), last_index, true, context)?;
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
@ -1228,16 +1246,16 @@ impl RegExp {
};
// 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
let mut unicode = false;
if global {
// 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).
rx.set("lastIndex", 0, true, context)?;
rx.set(utf16!("lastIndex"), 0, true, context)?;
}
// 9. Let results be a new empty List.
@ -1262,18 +1280,18 @@ impl RegExp {
}
// iii. Else,
// 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
if match_str.is_empty() {
// 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).
let next_index = advance_string_index(&arg_str, this_index, unicode);
// 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 {
break;
@ -1295,14 +1313,14 @@ impl RegExp {
let n_captures = std::cmp::max(result_length - 1, 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.
let match_length = matched.len();
// e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")).
let position = result
.get("index", context)?
.get(utf16!("index"), context)?
.to_integer_or_infinity(context)?;
// 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").
let mut named_captures = result.get("groups", context)?;
let mut named_captures = result.get(utf16!("groups"), context)?;
// k. If functionalReplace is true, then
let replacement = match replace {
@ -1430,31 +1448,31 @@ impl RegExp {
let arg_str = args.get_or_undefined(0).to_string(context)?;
// 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
if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) {
// 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).
let result = Self::abstract_exec(rx, arg_str, context)?;
// 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
if !JsValue::same_value(&current_last_index, &previous_last_index) {
// 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𝔽.
// 10. Return ? Get(result, "index").
result.map_or_else(
|| 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)?;
// 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.
// 7. Else, let unicodeMatching be false.
@ -1556,7 +1574,7 @@ impl RegExp {
// 19. Repeat, while q < size,
while q < size {
// 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).
let result = Self::abstract_exec(&splitter, arg_str.clone(), context)?;
@ -1565,7 +1583,9 @@ impl RegExp {
// d. Else,
if let Some(result) = result {
// 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).
e = std::cmp::min(e, size);

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

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

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

@ -16,6 +16,7 @@ use crate::{
error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind},
string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue,
};
@ -74,13 +75,18 @@ impl IntrinsicObject for Set {
.method(Self::for_each, "forEach", 1)
.method(Self::has, "has", 1)
.property(
"keys",
utf16!("keys"),
values_function.clone(),
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(
"values",
utf16!("values"),
values_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
@ -132,7 +138,7 @@ impl BuiltInConstructor for Set {
}
// 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.
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;
BuiltInBuilder::from_standard_constructor::<Self>(intrinsics)
.property("length", 0, attribute)
.property(utf16!("length"), 0, attribute)
.static_method(Self::raw, "raw", 1)
.static_method(Self::from_char_code, "fromCharCode", 1)
.static_method(Self::from_code_point, "fromCodePoint", 1)
@ -190,7 +190,7 @@ impl String {
// 8. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: 𝔽(length),
// [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
s.define_property_or_throw(
"length",
utf16!("length"),
PropertyDescriptor::builder()
.value(len)
.writable(false)
@ -300,7 +300,7 @@ impl String {
let cooked = args.get_or_undefined(0).to_object(context)?;
// 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).
let literal_segments = raw.length_of_array_like(context)?;
@ -1040,7 +1040,7 @@ impl String {
// b. If isRegExp is true, then
if is_reg_exp_object(obj, context)? {
// i. Let flags be ? Get(searchValue, "flags").
let flags = obj.get("flags", context)?;
let flags = obj.get(utf16!("flags"), context)?;
// ii. Perform ? RequireObjectCoercible(flags).
flags.require_object_coercible()?;
@ -1972,7 +1972,7 @@ impl String {
if let Some(regexp_obj) = regexp.as_object() {
if is_reg_exp_object(regexp_obj, context)? {
// 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).
flags.require_object_coercible()?;

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

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

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

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

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

@ -12,6 +12,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue,
};
@ -88,7 +89,7 @@ impl BuiltInConstructor for WeakSet {
}
// 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.
let adder = adder

9
boa_engine/src/error.rs

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

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

@ -4,6 +4,7 @@ use crate::{
builtins::Map,
error::JsNativeError,
object::{JsFunction, JsMapIterator, JsObject, JsObjectType, ObjectData},
string::utf16,
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 = map
.get("set", context)
.get(utf16!("set"), context)
.expect("creating a map with the default prototype must not fail");
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,
native_function::{NativeFunction, NativeFunctionPointer},
object::{FunctionObjectBuilder, JsObject, JsObjectType, ObjectData},
string::utf16,
Context, JsResult, JsValue,
};
@ -379,7 +380,7 @@ impl JsProxyBuilder {
.length(3)
.build();
handler
.create_data_property_or_throw("apply", f, context)
.create_data_property_or_throw(utf16!("apply"), f, context)
.expect("new object should be writable");
}
if let Some(construct) = self.construct {
@ -387,7 +388,7 @@ impl JsProxyBuilder {
.length(3)
.build();
handler
.create_data_property_or_throw("construct", f, context)
.create_data_property_or_throw(utf16!("construct"), f, context)
.expect("new object should be writable");
}
if let Some(define_property) = self.define_property {
@ -396,7 +397,7 @@ impl JsProxyBuilder {
.length(3)
.build();
handler
.create_data_property_or_throw("defineProperty", f, context)
.create_data_property_or_throw(utf16!("defineProperty"), f, context)
.expect("new object should be writable");
}
if let Some(delete_property) = self.delete_property {
@ -405,7 +406,7 @@ impl JsProxyBuilder {
.length(2)
.build();
handler
.create_data_property_or_throw("deleteProperty", f, context)
.create_data_property_or_throw(utf16!("deleteProperty"), f, context)
.expect("new object should be writable");
}
if let Some(get) = self.get {
@ -413,7 +414,7 @@ impl JsProxyBuilder {
.length(3)
.build();
handler
.create_data_property_or_throw("get", f, context)
.create_data_property_or_throw(utf16!("get"), f, context)
.expect("new object should be writable");
}
if let Some(get_own_property_descriptor) = self.get_own_property_descriptor {
@ -424,7 +425,7 @@ impl JsProxyBuilder {
.length(2)
.build();
handler
.create_data_property_or_throw("getOwnPropertyDescriptor", f, context)
.create_data_property_or_throw(utf16!("getOwnPropertyDescriptor"), f, context)
.expect("new object should be writable");
}
if let Some(get_prototype_of) = self.get_prototype_of {
@ -433,7 +434,7 @@ impl JsProxyBuilder {
.length(1)
.build();
handler
.create_data_property_or_throw("getPrototypeOf", f, context)
.create_data_property_or_throw(utf16!("getPrototypeOf"), f, context)
.expect("new object should be writable");
}
if let Some(has) = self.has {
@ -441,7 +442,7 @@ impl JsProxyBuilder {
.length(2)
.build();
handler
.create_data_property_or_throw("has", f, context)
.create_data_property_or_throw(utf16!("has"), f, context)
.expect("new object should be writable");
}
if let Some(is_extensible) = self.is_extensible {
@ -449,7 +450,7 @@ impl JsProxyBuilder {
.length(1)
.build();
handler
.create_data_property_or_throw("isExtensible", f, context)
.create_data_property_or_throw(utf16!("isExtensible"), f, context)
.expect("new object should be writable");
}
if let Some(own_keys) = self.own_keys {
@ -457,7 +458,7 @@ impl JsProxyBuilder {
.length(1)
.build();
handler
.create_data_property_or_throw("ownKeys", f, context)
.create_data_property_or_throw(utf16!("ownKeys"), f, context)
.expect("new object should be writable");
}
if let Some(prevent_extensions) = self.prevent_extensions {
@ -468,7 +469,7 @@ impl JsProxyBuilder {
.length(1)
.build();
handler
.create_data_property_or_throw("preventExtensions", f, context)
.create_data_property_or_throw(utf16!("preventExtensions"), f, context)
.expect("new object should be writable");
}
if let Some(set) = self.set {
@ -476,7 +477,7 @@ impl JsProxyBuilder {
.length(4)
.build();
handler
.create_data_property_or_throw("set", f, context)
.create_data_property_or_throw(utf16!("set"), f, context)
.expect("new object should be writable");
}
if let Some(set_prototype_of) = self.set_prototype_of {
@ -485,7 +486,7 @@ impl JsProxyBuilder {
.length(2)
.build();
handler
.create_data_property_or_throw("setPrototypeOf", f, context)
.create_data_property_or_throw(utf16!("setPrototypeOf"), f, context)
.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)]
pub(crate) fn arguments_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 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]].
obj.borrow()
.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
pub(crate) fn array_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. Assert: IsPropertyKey(P) is true.
match key {
match *key {
// 2. If P is "length", then
PropertyKey::String(ref s) if s == utf16!("length") => {
// 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
PropertyKey::Index(index) if index < u32::MAX => {
// a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let old_len_desc = super::ordinary_get_own_property(obj, &"length".into(), context)?
.expect("the property descriptor must exist");
let old_len_desc =
super::ordinary_get_own_property(obj, &utf16!("length").into(), context)?
.expect("the property descriptor must exist");
// b. Assert: ! IsDataDescriptor(oldLenDesc) is true.
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).
let succeeded = super::ordinary_define_own_property(
obj,
"length".into(),
&utf16!("length").into(),
old_len_desc.into(),
context,
)?;
@ -113,7 +114,7 @@ fn array_set_length(
// 1. If Desc.[[Value]] is absent, then
let Some(new_len_val) = desc.value() else {
// 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]]).
@ -139,7 +140,7 @@ fn array_set_length(
.maybe_configurable(desc.configurable());
// 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");
// 8. Assert: ! IsDataDescriptor(oldLenDesc) is true.
@ -156,7 +157,7 @@ fn array_set_length(
// a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
return super::ordinary_define_own_property(
obj,
"length".into(),
&utf16!("length").into(),
new_len_desc.build(),
context,
);
@ -186,7 +187,7 @@ fn array_set_length(
// 16. If succeeded is false, return false.
if !super::ordinary_define_own_property(
obj,
"length".into(),
&utf16!("length").into(),
new_len_desc.clone().build(),
context,
)
@ -223,7 +224,7 @@ fn array_set_length(
// iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
super::ordinary_define_own_property(
obj,
"length".into(),
&utf16!("length").into(),
new_len_desc.build(),
context,
)
@ -240,7 +241,7 @@ fn array_set_length(
// PropertyDescriptor { [[Writable]]: false }).
let succeeded = super::ordinary_define_own_property(
obj,
"length".into(),
&utf16!("length").into(),
PropertyDescriptor::builder().writable(false).build(),
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)]
pub(crate) fn global_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
let _timer = Profiler::global().start_event("Object::global_define_own_property", "object");
// 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).
let extensible = obj.__is_extensible__(context)?;
// 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
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
pub(crate) fn integer_indexed_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
// 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.
// 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.

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
pub(crate) fn __define_own_property__(
&self,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
@ -305,7 +305,7 @@ pub(crate) struct InternalObjectMethods {
pub(crate) __get_own_property__:
fn(&JsObject, &PropertyKey, &mut Context<'_>) -> JsResult<Option<PropertyDescriptor>>,
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) __get__: fn(&JsObject, &PropertyKey, JsValue, &mut Context<'_>) -> JsResult<JsValue>,
pub(crate) __set__:
@ -462,13 +462,13 @@ pub(crate) fn ordinary_get_own_property(
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty
pub(crate) fn ordinary_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
let _timer = Profiler::global().start_event("Object::ordinary_define_own_property", "object");
// 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).
let extensible = obj.__is_extensible__(context)?;
@ -629,7 +629,7 @@ pub(crate) fn ordinary_set(
// iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
return receiver.__define_own_property__(
key,
&key,
PropertyDescriptor::builder().value(value).build(),
context,
);
@ -762,7 +762,7 @@ pub(crate) fn is_compatible_property_descriptor(
///
/// [spec]: https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
pub(crate) fn validate_and_apply_property_descriptor(
obj_and_key: Option<(&JsObject, PropertyKey)>,
obj_and_key: Option<(&JsObject, &PropertyKey)>,
extensible: bool,
desc: PropertyDescriptor,
current: Option<PropertyDescriptor>,
@ -782,7 +782,7 @@ pub(crate) fn validate_and_apply_property_descriptor(
if let Some((obj, key)) = obj_and_key {
obj.borrow_mut().properties.insert(
&key,
key,
// c. If IsGenericDescriptor(Desc) is true or IsDataDescriptor(Desc) is true, then
if desc.is_generic_descriptor() || desc.is_data_descriptor() {
// 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
// property named P of object O to the value of the field.
current.fill_with(&desc);
obj.borrow_mut().properties.insert(&key, current);
obj.borrow_mut().properties.insert(key, current);
}
// 10. Return true.

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

@ -3,6 +3,7 @@ use crate::{
error::JsNativeError,
object::{InternalObjectMethods, JsObject, JsPrototype},
property::{PropertyDescriptor, PropertyKey},
string::utf16,
value::Type,
Context, JsResult, JsValue,
};
@ -65,7 +66,7 @@ pub(crate) fn proxy_exotic_get_prototype_of(
.try_data()?;
// 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
// a. Return ? target.[[GetPrototypeOf]]().
return target.__get_prototype_of__(context);
@ -127,7 +128,7 @@ pub(crate) fn proxy_exotic_set_prototype_of(
.try_data()?;
// 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
// a. Return ? target.[[SetPrototypeOf]](V).
return target.__set_prototype_of__(val, context);
@ -190,7 +191,7 @@ pub(crate) fn proxy_exotic_is_extensible(
.try_data()?;
// 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
// a. Return ? IsExtensible(target).
return target.is_extensible(context);
@ -236,7 +237,7 @@ pub(crate) fn proxy_exotic_prevent_extensions(
.try_data()?;
// 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
// a. Return ? target.[[PreventExtensions]]().
return target.__prevent_extensions__(context);
@ -282,7 +283,7 @@ pub(crate) fn proxy_exotic_get_own_property(
.try_data()?;
// 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
// a. Return ? target.[[GetOwnProperty]](P).
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
pub(crate) fn proxy_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
@ -406,7 +407,7 @@ pub(crate) fn proxy_exotic_define_own_property(
.try_data()?;
// 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
// a. Return ? target.[[DefineOwnProperty]](P, Desc).
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).
let target_desc = target.__get_own_property__(&key, context)?;
let target_desc = target.__get_own_property__(key, context)?;
// 11. Let extensibleTarget be ? IsExtensible(target).
let extensible_target = target.is_extensible(context)?;
@ -517,7 +518,7 @@ pub(crate) fn proxy_exotic_has_property(
.try_data()?;
// 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
// a. Return ? target.[[HasProperty]](P).
return target.has_property(key.clone(), context);
@ -583,7 +584,7 @@ pub(crate) fn proxy_exotic_get(
.try_data()?;
// 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
// a. Return ? target.[[Get]](P, Receiver).
return target.__get__(key, receiver, context);
@ -652,7 +653,7 @@ pub(crate) fn proxy_exotic_set(
.try_data()?;
// 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
// a. Return ? target.[[Set]](P, V, Receiver).
return target.__set__(key, value, receiver, context);
@ -733,7 +734,7 @@ pub(crate) fn proxy_exotic_delete(
.try_data()?;
// 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
// a. Return ? target.[[Delete]](P).
return target.__delete__(key, context);
@ -799,7 +800,7 @@ pub(crate) fn proxy_exotic_own_property_keys(
.try_data()?;
// 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
// a. Return ? target.[[OwnPropertyKeys]]().
return target.__own_property_keys__(context);
@ -935,7 +936,7 @@ fn proxy_exotic_call(
.try_data()?;
// 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
// a. Return ? Call(target, thisArgument, argumentsList).
return target.call(this, args, context);
@ -978,7 +979,7 @@ fn proxy_exotic_construct(
assert!(target.is_constructor());
// 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
// a. Return ? Construct(target, argumentsList, newTarget).
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
pub(crate) fn string_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
key: &PropertyKey,
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. Assert: IsPropertyKey(P) is true.
// 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
if let Some(string_desc) = string_desc {

29
boa_engine/src/object/jsobject.rs

@ -7,6 +7,7 @@ use crate::{
error::JsNativeError,
object::{ObjectData, ObjectKind},
property::{PropertyDescriptor, PropertyKey},
string::utf16,
value::PreferredType,
Context, JsResult, JsValue,
};
@ -180,9 +181,9 @@ impl JsObject {
// 4. Else,
// a. Let methodNames be « "valueOf", "toString" ».
let method_names = if hint == PreferredType::String {
["toString", "valueOf"]
[utf16!("toString"), utf16!("valueOf")]
} else {
["valueOf", "toString"]
[utf16!("valueOf"), utf16!("toString")]
};
// 5. For each name in methodNames in List order, do
@ -532,41 +533,41 @@ impl JsObject {
// 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
// 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")).
// 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").
// 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")).
// 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").
// 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").
// 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, ).
// 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")).
// 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").
// 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").
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.
// todo: extract IsCallable to be callable from Value
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").
// 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").
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.
// todo: extract IsCallable to be callable from Value
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,
native_function::NativeFunction,
property::{Attribute, PropertyDescriptor, PropertyKey},
string::utf16,
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.
pub static CONSTRUCTOR: &str = "constructor";
/// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object.
pub const CONSTRUCTOR: &[u16] = utf16!("constructor");
/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub static PROTOTYPE: &str = "prototype";
/// Const `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub const PROTOTYPE: &[u16] = utf16!("prototype");
/// Common field names.
/// A type alias for an object prototype.
///
@ -2025,8 +2028,8 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> {
.writable(false)
.enumerable(false)
.configurable(true);
function.insert_property("length", property.clone().value(self.length));
function.insert_property("name", property.value(self.name));
function.insert_property(utf16!("length"), property.clone().value(self.length));
function.insert_property(utf16!("name"), property.value(self.name));
JsFunction::from_object_unchecked(function)
}
@ -2395,8 +2398,8 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> {
{
let mut constructor = self.object.borrow_mut();
constructor.data = ObjectData::function(function);
constructor.insert("length", length);
constructor.insert("name", name);
constructor.insert(utf16!("length"), length);
constructor.insert(utf16!("name"), name);
if let Some(proto) = self.custom_prototype.take() {
constructor.set_prototype(proto);
@ -2425,7 +2428,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> {
{
let mut prototype = self.prototype.borrow_mut();
prototype.insert(
"constructor",
CONSTRUCTOR,
PropertyDescriptor::builder()
.value(self.object.clone())
.writable(true)

11
boa_engine/src/object/operations.rs

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

11
boa_engine/src/property/mod.rs

@ -569,6 +569,17 @@ pub enum PropertyKey {
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 {
#[inline]
fn from(string: JsString) -> Self {

12
boa_engine/src/value/display.rs

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

27
boa_engine/src/value/serde_json.rs

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

23
boa_engine/src/vm/code_block.rs

@ -12,9 +12,12 @@ use crate::{
context::intrinsics::StandardConstructors,
environments::{BindingLocator, CompileTimeEnvironment},
error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR},
object::{
internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR,
PROTOTYPE,
},
property::PropertyDescriptor,
string::utf16,
vm::{CallFrame, Opcode},
Context, JsResult, JsString, JsValue,
};
@ -612,14 +615,14 @@ pub(crate) fn create_function_object(
.build();
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");
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");
if !r#async && !arrow && !method {
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");
}
@ -705,14 +708,14 @@ pub(crate) fn create_generator_function_object(
if !method {
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");
}
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");
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");
constructor
@ -1117,7 +1120,7 @@ impl JsObject {
std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = this_function_object
.get("prototype", context)
.get(PROTOTYPE, context)
.expect("GeneratorFunction must have a prototype property")
.as_object()
.map_or_else(|| context.intrinsics().objects().generator(), Clone::clone);
@ -1255,7 +1258,7 @@ impl JsObject {
std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = this_function_object
.get("prototype", context)
.get(PROTOTYPE, context)
.expect("AsyncGeneratorFunction must have a prototype property")
.as_object()
.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)
.cloned();
class.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.maybe_get(Some(function))
.maybe_set(set)
@ -103,7 +103,7 @@ impl Operation for DefineClassGetterByName {
.and_then(PropertyDescriptor::set)
.cloned();
class_proto.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.maybe_get(Some(function))
.maybe_set(set)
@ -209,7 +209,7 @@ impl Operation for DefineClassGetterByValue {
.and_then(PropertyDescriptor::set)
.cloned();
class_proto.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.maybe_get(Some(function))
.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());
}
class.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.value(function)
.writable(true)
@ -93,7 +93,7 @@ impl Operation for DefineClassMethodByName {
function_mut.set_class_object(class);
}
class_proto.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.value(function)
.writable(true)
@ -189,7 +189,7 @@ impl Operation for DefineClassMethodByValue {
function_mut.set_class_object(class);
}
class_proto.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.value(function)
.writable(true)

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

@ -45,7 +45,7 @@ impl Operation for DefineClassStaticSetterByName {
.and_then(PropertyDescriptor::get)
.cloned();
class.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.maybe_set(Some(function))
.maybe_get(get)
@ -103,7 +103,7 @@ impl Operation for DefineClassSetterByName {
.and_then(PropertyDescriptor::get)
.cloned();
class_proto.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.maybe_set(Some(function))
.maybe_get(get)
@ -209,7 +209,7 @@ impl Operation for DefineClassSetterByValue {
.and_then(PropertyDescriptor::get)
.cloned();
class_proto.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.maybe_set(Some(function))
.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())
.into_common::<JsString>(false);
object.__define_own_property__(
name.into(),
&name.into(),
PropertyDescriptor::builder()
.value(value)
.writable(true)
@ -65,7 +65,7 @@ impl Operation for DefineOwnPropertyByValue {
};
let key = key.to_property_key(context)?;
let success = object.__define_own_property__(
key,
&key,
PropertyDescriptor::builder()
.value(value)
.writable(true)

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

@ -4,6 +4,7 @@ use crate::{
iterable::IteratorRecord,
},
error::JsNativeError,
string::utf16,
vm::{
call_frame::{FinallyReturn, GeneratorResumeKind},
opcode::Operation,
@ -151,14 +152,14 @@ impl Operation for GeneratorNextDelegate {
let result = result.as_object().ok_or_else(|| {
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 {
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);
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(next_method.clone());
context.vm.push(done);
@ -166,21 +167,21 @@ impl Operation for GeneratorNextDelegate {
Ok(ShouldExit::Yield)
}
GeneratorResumeKind::Throw => {
let throw = iterator.get_method("throw", context)?;
let throw = iterator.get_method(utf16!("throw"), context)?;
if let Some(throw) = throw {
let result = throw.call(&iterator.clone().into(), &[received], context)?;
let result_object = result.as_object().ok_or_else(|| {
JsNativeError::typ()
.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 {
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);
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(next_method.clone());
context.vm.push(done);
@ -196,21 +197,21 @@ impl Operation for GeneratorNextDelegate {
.into())
}
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 {
let result = r#return.call(&iterator.clone().into(), &[received], context)?;
let result_object = result.as_object().ok_or_else(|| {
JsNativeError::typ()
.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 {
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);
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(next_method.clone());
context.vm.push(done);

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

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

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

@ -1,5 +1,6 @@
use crate::{
builtins::{iterable::IteratorRecord, Array},
string::utf16,
vm::{opcode::Operation, ShouldExit},
Context, JsResult,
};
@ -67,7 +68,7 @@ impl Operation for PushElisionToArray {
.length_of_array_like(context)
.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);
Ok(ShouldExit::False)
}

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

@ -1,6 +1,7 @@
use crate::{
builtins::function::{ConstructorKind, Function},
error::JsNativeError,
object::PROTOTYPE,
vm::{opcode::Operation, ShouldExit},
Context, JsResult, JsValue,
};
@ -26,7 +27,7 @@ impl Operation for PushClassPrototype {
let superclass = context.vm.pop();
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() {
return Err(JsNativeError::typ()
.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::{
object::PrivateElement,
property::PropertyDescriptor,
string::utf16,
vm::{opcode::Operation, ShouldExit},
Context, JsResult,
};
@ -30,7 +31,7 @@ impl Operation for PushClassPrivateMethod {
.configurable(true)
.build();
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");
let class = context.vm.pop();

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

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

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

@ -1,6 +1,7 @@
use crate::{
object::PrivateElement,
property::PropertyDescriptor,
string::utf16,
vm::{opcode::Operation, ShouldExit},
Context, JsResult,
};
@ -80,7 +81,7 @@ impl Operation for SetPrivateMethod {
.configurable(true)
.build();
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");
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)
.cloned();
object.__define_own_property__(
name,
&name,
PropertyDescriptor::builder()
.maybe_get(Some(value))
.maybe_set(set)
@ -144,7 +144,7 @@ impl Operation for SetPropertyGetterByValue {
.and_then(PropertyDescriptor::set)
.cloned();
object.__define_own_property__(
name,
&name,
PropertyDescriptor::builder()
.maybe_get(Some(value))
.maybe_set(set)
@ -185,7 +185,7 @@ impl Operation for SetPropertySetterByName {
.and_then(PropertyDescriptor::get)
.cloned();
object.__define_own_property__(
name,
&name,
PropertyDescriptor::builder()
.maybe_set(Some(value))
.maybe_get(get)
@ -221,7 +221,7 @@ impl Operation for SetPropertySetterByValue {
.and_then(PropertyDescriptor::get)
.cloned();
object.__define_own_property__(
name,
&name,
PropertyDescriptor::builder()
.maybe_set(Some(value))
.maybe_get(get)

Loading…
Cancel
Save