Browse Source

Remove direct conversion from `&str` to `JsValue`/`PropertyKey`. (#3319)

* Remove direct conversion from `&str` to `JsValue`/`PropertyKey`.

* Allow unused static strings

* Introduce DHAT to benchmark data usage

* Create new release profile

* Fix docs
pull/3323/head
José Julián Espina 1 year ago committed by GitHub
parent
commit
b80409d87d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .gitignore
  2. 50
      Cargo.lock
  3. 4
      Cargo.toml
  4. 2
      boa_cli/Cargo.toml
  5. 31
      boa_cli/src/debug/function.rs
  6. 10
      boa_cli/src/debug/gc.rs
  7. 5
      boa_cli/src/debug/limits.rs
  8. 18
      boa_cli/src/debug/mod.rs
  9. 7
      boa_cli/src/debug/object.rs
  10. 5
      boa_cli/src/debug/optimizer.rs
  11. 6
      boa_cli/src/debug/realm.rs
  12. 8
      boa_cli/src/debug/shape.rs
  13. 17
      boa_cli/src/main.rs
  14. 1
      boa_engine/Cargo.toml
  15. 7
      boa_engine/src/builtins/array/array_iterator.rs
  16. 83
      boa_engine/src/builtins/array/mod.rs
  17. 22
      boa_engine/src/builtins/array/tests.rs
  18. 19
      boa_engine/src/builtins/array_buffer/mod.rs
  19. 7
      boa_engine/src/builtins/async_function/mod.rs
  20. 14
      boa_engine/src/builtins/async_generator/mod.rs
  21. 7
      boa_engine/src/builtins/async_generator_function/mod.rs
  22. 22
      boa_engine/src/builtins/bigint/mod.rs
  23. 10
      boa_engine/src/builtins/bigint/tests.rs
  24. 14
      boa_engine/src/builtins/boolean/mod.rs
  25. 64
      boa_engine/src/builtins/dataview/mod.rs
  26. 200
      boa_engine/src/builtins/date/mod.rs
  27. 22
      boa_engine/src/builtins/date/tests.rs
  28. 11
      boa_engine/src/builtins/error/aggregate.rs
  29. 11
      boa_engine/src/builtins/error/eval.rs
  30. 12
      boa_engine/src/builtins/error/mod.rs
  31. 11
      boa_engine/src/builtins/error/range.rs
  32. 13
      boa_engine/src/builtins/error/reference.rs
  33. 11
      boa_engine/src/builtins/error/syntax.rs
  34. 66
      boa_engine/src/builtins/error/tests.rs
  35. 13
      boa_engine/src/builtins/error/type.rs
  36. 11
      boa_engine/src/builtins/error/uri.rs
  37. 8
      boa_engine/src/builtins/escape/mod.rs
  38. 5
      boa_engine/src/builtins/eval/mod.rs
  39. 18
      boa_engine/src/builtins/function/mod.rs
  40. 14
      boa_engine/src/builtins/function/tests.rs
  41. 14
      boa_engine/src/builtins/generator/mod.rs
  42. 7
      boa_engine/src/builtins/generator_function/mod.rs
  43. 55
      boa_engine/src/builtins/intl/collator/mod.rs
  44. 10
      boa_engine/src/builtins/intl/date_time_format.rs
  45. 56
      boa_engine/src/builtins/intl/list_format/mod.rs
  46. 58
      boa_engine/src/builtins/intl/locale/mod.rs
  47. 5
      boa_engine/src/builtins/intl/locale/utils.rs
  48. 16
      boa_engine/src/builtins/intl/mod.rs
  49. 33
      boa_engine/src/builtins/intl/plural_rules/mod.rs
  50. 21
      boa_engine/src/builtins/intl/segmenter/mod.rs
  51. 2
      boa_engine/src/builtins/intl/segmenter/segments.rs
  52. 9
      boa_engine/src/builtins/iterable/async_from_sync_iterator.rs
  53. 10
      boa_engine/src/builtins/json/mod.rs
  54. 66
      boa_engine/src/builtins/json/tests.rs
  55. 7
      boa_engine/src/builtins/map/map_iterator.rs
  56. 33
      boa_engine/src/builtins/map/mod.rs
  57. 18
      boa_engine/src/builtins/map/tests.rs
  58. 96
      boa_engine/src/builtins/math/mod.rs
  59. 22
      boa_engine/src/builtins/mod.rs
  60. 12
      boa_engine/src/builtins/number/globals.rs
  61. 99
      boa_engine/src/builtins/number/mod.rs
  62. 239
      boa_engine/src/builtins/number/tests.rs
  63. 5
      boa_engine/src/builtins/object/for_in_iterator.rs
  64. 110
      boa_engine/src/builtins/object/mod.rs
  65. 29
      boa_engine/src/builtins/object/tests.rs
  66. 45
      boa_engine/src/builtins/promise/mod.rs
  67. 11
      boa_engine/src/builtins/proxy/mod.rs
  68. 34
      boa_engine/src/builtins/reflect/mod.rs
  69. 9
      boa_engine/src/builtins/reflect/tests.rs
  70. 120
      boa_engine/src/builtins/regexp/mod.rs
  71. 7
      boa_engine/src/builtins/regexp/regexp_string_iterator.rs
  72. 26
      boa_engine/src/builtins/regexp/tests.rs
  73. 27
      boa_engine/src/builtins/set/mod.rs
  74. 7
      boa_engine/src/builtins/set/set_iterator.rs
  75. 134
      boa_engine/src/builtins/string/mod.rs
  76. 6
      boa_engine/src/builtins/string/string_iterator.rs
  77. 131
      boa_engine/src/builtins/string/tests.rs
  78. 50
      boa_engine/src/builtins/symbol/mod.rs
  79. 4
      boa_engine/src/builtins/symbol/tests.rs
  80. 194
      boa_engine/src/builtins/typed_array/mod.rs
  81. 10
      boa_engine/src/builtins/uri/mod.rs
  82. 12
      boa_engine/src/builtins/weak/weak_ref.rs
  83. 17
      boa_engine/src/builtins/weak_map/mod.rs
  84. 15
      boa_engine/src/builtins/weak_set/mod.rs
  85. 9
      boa_engine/src/class.rs
  86. 2
      boa_engine/src/context/intrinsics.rs
  87. 35
      boa_engine/src/context/mod.rs
  88. 40
      boa_engine/src/error.rs
  89. 6
      boa_engine/src/object/builtins/jsdate.rs
  90. 70
      boa_engine/src/object/builtins/jsmap.rs
  91. 49
      boa_engine/src/object/builtins/jspromise.rs
  92. 30
      boa_engine/src/object/builtins/jsregexp.rs
  93. 2
      boa_engine/src/object/internal_methods/integer_indexed.rs
  94. 3
      boa_engine/src/object/jsobject.rs
  95. 31
      boa_engine/src/object/mod.rs
  96. 2
      boa_engine/src/optimizer/pass/constant_folding.rs
  97. 24
      boa_engine/src/property/mod.rs
  98. 289
      boa_engine/src/string/common.rs
  99. 2
      boa_engine/src/string/mod.rs
  100. 26
      boa_engine/src/symbol.rs
  101. Some files were not shown because too many files have changed in this diff Show More

4
.gitignore vendored

@ -39,3 +39,7 @@ chrome_profiler.json
# e2e test # e2e test
playwright-report playwright-report
test-results test-results
# dhat
dhat-*.json
perf.data*

50
Cargo.lock generated

@ -342,6 +342,7 @@ dependencies = [
"boa_runtime", "boa_runtime",
"clap", "clap",
"colored", "colored",
"dhat",
"jemallocator", "jemallocator",
"phf", "phf",
"pollster", "pollster",
@ -389,6 +390,7 @@ dependencies = [
"num-traits", "num-traits",
"num_enum", "num_enum",
"once_cell", "once_cell",
"paste",
"pollster", "pollster",
"rand", "rand",
"regress", "regress",
@ -954,6 +956,22 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "dhat"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2aaf837aaf456f6706cb46386ba8dffd4013a757e36f4ea05c20dd46b209a3"
dependencies = [
"backtrace",
"lazy_static",
"mintex",
"parking_lot 0.12.1",
"rustc-hash",
"serde",
"serde_json",
"thousands",
]
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.4" version = "0.2.4"
@ -1882,6 +1900,16 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "mintex"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd7c5ba1c3b5a23418d7bbf98c71c3d4946a0125002129231da8d6b723d559cb"
dependencies = [
"once_cell",
"sys-info",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.8" version = "0.8.8"
@ -2083,6 +2111,12 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.0" version = "2.3.0"
@ -2831,6 +2865,16 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "sys-info"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "sys-locale" name = "sys-locale"
version = "0.3.1" version = "0.3.1"
@ -2892,6 +2936,12 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "thousands"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.7" version = "1.1.7"

4
Cargo.toml

@ -101,6 +101,10 @@ lto = "fat"
# Makes sure that all code is compiled together, for LTO # Makes sure that all code is compiled together, for LTO
codegen-units = 1 codegen-units = 1
[profile.release-dbg]
inherits = "release"
debug = true
# The test profile, used for `cargo test`. # The test profile, used for `cargo test`.
[profile.test] [profile.test]
# Enables thin local LTO and some optimizations. # Enables thin local LTO and some optimizations.

2
boa_cli/Cargo.toml

@ -25,9 +25,11 @@ colored.workspace = true
regex.workspace = true regex.workspace = true
phf = { workspace = true, features = ["macros"] } phf = { workspace = true, features = ["macros"] }
pollster.workspace = true pollster.workspace = true
dhat = { version = "0.3.2", optional = true }
[features] [features]
default = ["boa_engine/annex-b", "boa_engine/experimental", "boa_engine/intl"] default = ["boa_engine/annex-b", "boa_engine/experimental", "boa_engine/intl"]
dhat = ["dep:dhat"]
[target.x86_64-unknown-linux-gnu.dependencies] [target.x86_64-unknown-linux-gnu.dependencies]
jemallocator.workspace = true jemallocator.workspace = true

31
boa_cli/src/debug/function.rs

@ -1,4 +1,5 @@
use boa_engine::{ use boa_engine::{
js_string,
object::ObjectInitializer, object::ObjectInitializer,
vm::flowgraph::{Direction, Graph}, vm::flowgraph::{Direction, Graph},
Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction,
@ -67,8 +68,10 @@ fn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> Js
let mut direction = Direction::LeftToRight; let mut direction = Direction::LeftToRight;
if let Some(arguments) = args.get(1) { if let Some(arguments) = args.get(1) {
if let Some(arguments) = arguments.as_object() { if let Some(arguments) = arguments.as_object() {
format = flowgraph_parse_format_option(&arguments.get("format", context)?)?; format = flowgraph_parse_format_option(&arguments.get(js_string!("format"), context)?)?;
direction = flowgraph_parse_direction_option(&arguments.get("direction", context)?)?; direction = flowgraph_parse_direction_option(
&arguments.get(js_string!("direction"), context)?,
)?;
} else if value.is_string() { } else if value.is_string() {
format = flowgraph_parse_format_option(value)?; format = flowgraph_parse_format_option(value)?;
} else { } else {
@ -97,7 +100,7 @@ fn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> Js
FlowgraphFormat::Mermaid => graph.to_mermaid_format(), FlowgraphFormat::Mermaid => graph.to_mermaid_format(),
}; };
Ok(JsValue::new(result)) Ok(JsValue::new(js_string!(result)))
} }
fn bytecode(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { fn bytecode(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> {
@ -122,7 +125,7 @@ fn bytecode(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResul
JsNativeError::typ().with_message("native functions do not have bytecode") JsNativeError::typ().with_message("native functions do not have bytecode")
})?; })?;
Ok(code.to_interned_string(context.interner()).into()) Ok(js_string!(code.to_interned_string(context.interner())).into())
} }
fn set_trace_flag_in_function_object(object: &JsObject, value: bool) -> JsResult<()> { fn set_trace_flag_in_function_object(object: &JsObject, value: bool) -> JsResult<()> {
@ -176,9 +179,21 @@ fn traceable(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsV
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context) ObjectInitializer::new(context)
.function(NativeFunction::from_fn_ptr(flowgraph), "flowgraph", 1) .function(
.function(NativeFunction::from_fn_ptr(bytecode), "bytecode", 1) NativeFunction::from_fn_ptr(flowgraph),
.function(NativeFunction::from_fn_ptr(trace), "trace", 1) js_string!("flowgraph"),
.function(NativeFunction::from_fn_ptr(traceable), "traceable", 2) 1,
)
.function(
NativeFunction::from_fn_ptr(bytecode),
js_string!("bytecode"),
1,
)
.function(NativeFunction::from_fn_ptr(trace), js_string!("trace"), 1)
.function(
NativeFunction::from_fn_ptr(traceable),
js_string!("traceable"),
2,
)
.build() .build()
} }

10
boa_cli/src/debug/gc.rs

@ -1,4 +1,6 @@
use boa_engine::{object::ObjectInitializer, Context, JsObject, JsResult, JsValue, NativeFunction}; use boa_engine::{
js_string, object::ObjectInitializer, Context, JsObject, JsResult, JsValue, NativeFunction,
};
/// Trigger garbage collection. /// Trigger garbage collection.
fn collect(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { fn collect(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
@ -8,6 +10,10 @@ fn collect(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue>
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context) ObjectInitializer::new(context)
.function(NativeFunction::from_fn_ptr(collect), "collect", 0) .function(
NativeFunction::from_fn_ptr(collect),
js_string!("collect"),
0,
)
.build() .build()
} }

5
boa_cli/src/debug/limits.rs

@ -1,4 +1,5 @@
use boa_engine::{ use boa_engine::{
js_string,
object::{FunctionObjectBuilder, ObjectInitializer}, object::{FunctionObjectBuilder, ObjectInitializer},
property::Attribute, property::Attribute,
Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction,
@ -55,13 +56,13 @@ pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
.build(); .build();
ObjectInitializer::new(context) ObjectInitializer::new(context)
.accessor( .accessor(
"loop", js_string!("loop"),
Some(get_loop), Some(get_loop),
Some(set_loop), Some(set_loop),
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
) )
.accessor( .accessor(
"recursion", js_string!("recursion"),
Some(get_recursion), Some(get_recursion),
Some(set_recursion), Some(set_recursion),
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,

18
boa_cli/src/debug/mod.rs

@ -1,7 +1,7 @@
// Allow lint so it, doesn't warn about `JsResult<>` unneeded return on functions. // Allow lint so it, doesn't warn about `JsResult<>` unneeded return on functions.
#![allow(clippy::unnecessary_wraps)] #![allow(clippy::unnecessary_wraps)]
use boa_engine::{object::ObjectInitializer, property::Attribute, Context, JsObject}; use boa_engine::{js_string, object::ObjectInitializer, property::Attribute, Context, JsObject};
mod function; mod function;
mod gc; mod gc;
@ -22,37 +22,37 @@ fn create_boa_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context) ObjectInitializer::new(context)
.property( .property(
"function", js_string!("function"),
function_module, function_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"object", js_string!("object"),
object_module, object_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"shape", js_string!("shape"),
shape_module, shape_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"optimizer", js_string!("optimizer"),
optimizer_module, optimizer_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"gc", js_string!("gc"),
gc_module, gc_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"realm", js_string!("realm"),
realm_module, realm_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"limits", js_string!("limits"),
limits_module, limits_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
@ -63,7 +63,7 @@ pub(crate) fn init_boa_debug_object(context: &mut Context<'_>) {
let boa_object = create_boa_object(context); let boa_object = create_boa_object(context);
context context
.register_global_property( .register_global_property(
"$boa", js_string!("$boa"),
boa_object, boa_object,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )

7
boa_cli/src/debug/object.rs

@ -1,5 +1,6 @@
use boa_engine::{ use boa_engine::{
object::ObjectInitializer, Context, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, js_string, object::ObjectInitializer, Context, JsNativeError, JsObject, JsResult, JsValue,
NativeFunction,
}; };
/// Returns objects pointer in memory. /// Returns objects pointer in memory.
@ -17,11 +18,11 @@ fn id(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
}; };
let ptr: *const _ = object.as_ref(); let ptr: *const _ = object.as_ref();
Ok(format!("0x{:X}", ptr as usize).into()) Ok(js_string!(format!("0x{:X}", ptr as usize)).into())
} }
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context) ObjectInitializer::new(context)
.function(NativeFunction::from_fn_ptr(id), "id", 1) .function(NativeFunction::from_fn_ptr(id), js_string!("id"), 1)
.build() .build()
} }

5
boa_cli/src/debug/optimizer.rs

@ -1,4 +1,5 @@
use boa_engine::{ use boa_engine::{
js_string,
object::{FunctionObjectBuilder, ObjectInitializer}, object::{FunctionObjectBuilder, ObjectInitializer},
optimizer::OptimizerOptions, optimizer::OptimizerOptions,
property::Attribute, property::Attribute,
@ -71,13 +72,13 @@ pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
.build(); .build();
ObjectInitializer::new(context) ObjectInitializer::new(context)
.accessor( .accessor(
"constantFolding", js_string!("constantFolding"),
Some(get_constant_folding), Some(get_constant_folding),
Some(set_constant_folding), Some(set_constant_folding),
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
) )
.accessor( .accessor(
"statistics", js_string!("statistics"),
Some(get_statistics), Some(get_statistics),
Some(set_statistics), Some(set_statistics),
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,

6
boa_cli/src/debug/realm.rs

@ -1,4 +1,6 @@
use boa_engine::{object::ObjectInitializer, Context, JsObject, JsResult, JsValue, NativeFunction}; use boa_engine::{
js_string, object::ObjectInitializer, Context, JsObject, JsResult, JsValue, NativeFunction,
};
/// Creates a new ECMAScript Realm and returns the global object of the realm. /// Creates a new ECMAScript Realm and returns the global object of the realm.
fn create(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { fn create(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
@ -9,6 +11,6 @@ fn create(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue>
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context) ObjectInitializer::new(context)
.function(NativeFunction::from_fn_ptr(create), "create", 0) .function(NativeFunction::from_fn_ptr(create), js_string!("create"), 0)
.build() .build()
} }

8
boa_cli/src/debug/shape.rs

@ -23,7 +23,7 @@ fn id(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
let object = get_object(args, 0)?; let object = get_object(args, 0)?;
let object = object.borrow(); let object = object.borrow();
let shape = object.shape(); let shape = object.shape();
Ok(format!("0x{:X}", shape.to_addr_usize()).into()) Ok(js_string!(format!("0x{:X}", shape.to_addr_usize())).into())
} }
/// Returns object's shape type. /// Returns object's shape type.
@ -62,8 +62,8 @@ fn same(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue>
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context) ObjectInitializer::new(context)
.function(NativeFunction::from_fn_ptr(id), "id", 1) .function(NativeFunction::from_fn_ptr(id), js_string!("id"), 1)
.function(NativeFunction::from_fn_ptr(r#type), "type", 1) .function(NativeFunction::from_fn_ptr(r#type), js_string!("type"), 1)
.function(NativeFunction::from_fn_ptr(same), "same", 2) .function(NativeFunction::from_fn_ptr(same), js_string!("same"), 2)
.build() .build()
} }

17
boa_cli/src/main.rs

@ -70,6 +70,7 @@ use boa_engine::{
builtins::promise::PromiseState, builtins::promise::PromiseState,
context::ContextBuilder, context::ContextBuilder,
job::{FutureJob, JobQueue, NativeJob}, job::{FutureJob, JobQueue, NativeJob},
js_string,
module::{Module, ModuleLoader, SimpleModuleLoader}, module::{Module, ModuleLoader, SimpleModuleLoader},
optimizer::OptimizerOptions, optimizer::OptimizerOptions,
property::Attribute, property::Attribute,
@ -87,13 +88,22 @@ use std::{
println, println,
}; };
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] #[cfg(all(
target_arch = "x86_64",
target_os = "linux",
target_env = "gnu",
not(feature = "dhat")
))]
#[cfg_attr( #[cfg_attr(
all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"), all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
global_allocator global_allocator
)] )]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(feature = "dhat")]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
/// CLI configuration for Boa. /// CLI configuration for Boa.
static CLI_HISTORY: &str = ".boa_history"; static CLI_HISTORY: &str = ".boa_history";
@ -366,6 +376,9 @@ fn evaluate_files(
} }
fn main() -> Result<(), io::Error> { fn main() -> Result<(), io::Error> {
#[cfg(feature = "dhat")]
let _profiler = dhat::Profiler::new_heap();
let args = Opt::parse(); let args = Opt::parse();
let queue: &dyn JobQueue = &Jobs::default(); let queue: &dyn JobQueue = &Jobs::default();
@ -480,7 +493,7 @@ fn main() -> Result<(), io::Error> {
fn add_runtime(context: &mut Context<'_>) { fn add_runtime(context: &mut Context<'_>) {
let console = Console::init(context); let console = Console::init(context);
context context
.register_global_property(Console::NAME, console, Attribute::all()) .register_global_property(js_string!(Console::NAME), console, Attribute::all())
.expect("the console object shouldn't exist"); .expect("the console object shouldn't exist");
} }

1
boa_engine/Cargo.toml

@ -80,6 +80,7 @@ pollster.workspace = true
thin-vec.workspace = true thin-vec.workspace = true
itertools = { version = "0.11.0", default-features = false } itertools = { version = "0.11.0", default-features = false }
icu_normalizer = "~1.3.0" icu_normalizer = "~1.3.0"
paste = "1.0"
# intl deps # intl deps
boa_icu_provider = {workspace = true, features = ["std"], optional = true } boa_icu_provider = {workspace = true, features = ["std"], optional = true }

7
boa_engine/src/builtins/array/array_iterator.rs

@ -11,6 +11,7 @@ use crate::{
}, },
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
@ -37,7 +38,7 @@ pub struct ArrayIterator {
impl IntrinsicObject for ArrayIterator { impl IntrinsicObject for ArrayIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("ArrayIterator", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -47,10 +48,10 @@ impl IntrinsicObject for ArrayIterator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 0) .static_method(Self::next, js_string!("next"), 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Array Iterator", js_string!("Array Iterator"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.build(); .build();

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

@ -23,9 +23,10 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR},
property::{Attribute, PropertyDescriptor, PropertyNameKind}, property::{Attribute, PropertyDescriptor, PropertyNameKind},
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult, JsString,
}; };
use std::cmp::{max, min, Ordering}; use std::cmp::{max, min, Ordering};
@ -49,13 +50,13 @@ pub(crate) struct Array;
impl IntrinsicObject for Array { impl IntrinsicObject for Array {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let symbol_iterator = JsSymbol::iterator(); let symbol_iterator = JsSymbol::iterator();
let symbol_unscopables = JsSymbol::unscopables(); let symbol_unscopables = JsSymbol::unscopables();
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let values_function = BuiltInBuilder::callable_with_object( let values_function = BuiltInBuilder::callable_with_object(
@ -63,7 +64,7 @@ impl IntrinsicObject for Array {
realm.intrinsics().objects().array_prototype_values().into(), realm.intrinsics().objects().array_prototype_values().into(),
Self::values, Self::values,
) )
.name("values") .name(js_string!("values"))
.build(); .build();
let to_string_function = BuiltInBuilder::callable_with_object( let to_string_function = BuiltInBuilder::callable_with_object(
@ -75,7 +76,7 @@ impl IntrinsicObject for Array {
.into(), .into(),
Self::to_string, Self::to_string,
) )
.name("toString") .name(js_string!("toString"))
.build(); .build();
let unscopables_object = Self::unscopables_object(); let unscopables_object = Self::unscopables_object();
@ -107,47 +108,47 @@ impl IntrinsicObject for Array {
unscopables_object, unscopables_object,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::at, "at", 1) .method(Self::at, js_string!("at"), 1)
.method(Self::concat, "concat", 1) .method(Self::concat, js_string!("concat"), 1)
.method(Self::push, "push", 1) .method(Self::push, js_string!("push"), 1)
.method(Self::index_of, "indexOf", 1) .method(Self::index_of, js_string!("indexOf"), 1)
.method(Self::last_index_of, "lastIndexOf", 1) .method(Self::last_index_of, js_string!("lastIndexOf"), 1)
.method(Self::includes_value, "includes", 1) .method(Self::includes_value, js_string!("includes"), 1)
.method(Self::map, "map", 1) .method(Self::map, js_string!("map"), 1)
.method(Self::fill, "fill", 1) .method(Self::fill, js_string!("fill"), 1)
.method(Self::for_each, "forEach", 1) .method(Self::for_each, js_string!("forEach"), 1)
.method(Self::filter, "filter", 1) .method(Self::filter, js_string!("filter"), 1)
.method(Self::pop, "pop", 0) .method(Self::pop, js_string!("pop"), 0)
.method(Self::join, "join", 1) .method(Self::join, js_string!("join"), 1)
.property( .property(
utf16!("toString"), utf16!("toString"),
to_string_function, to_string_function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::reverse, "reverse", 0) .method(Self::reverse, js_string!("reverse"), 0)
.method(Self::shift, "shift", 0) .method(Self::shift, js_string!("shift"), 0)
.method(Self::unshift, "unshift", 1) .method(Self::unshift, js_string!("unshift"), 1)
.method(Self::every, "every", 1) .method(Self::every, js_string!("every"), 1)
.method(Self::find, "find", 1) .method(Self::find, js_string!("find"), 1)
.method(Self::find_index, "findIndex", 1) .method(Self::find_index, js_string!("findIndex"), 1)
.method(Self::find_last, "findLast", 1) .method(Self::find_last, js_string!("findLast"), 1)
.method(Self::find_last_index, "findLastIndex", 1) .method(Self::find_last_index, js_string!("findLastIndex"), 1)
.method(Self::flat, "flat", 0) .method(Self::flat, js_string!("flat"), 0)
.method(Self::flat_map, "flatMap", 1) .method(Self::flat_map, js_string!("flatMap"), 1)
.method(Self::slice, "slice", 2) .method(Self::slice, js_string!("slice"), 2)
.method(Self::some, "some", 1) .method(Self::some, js_string!("some"), 1)
.method(Self::sort, "sort", 1) .method(Self::sort, js_string!("sort"), 1)
.method(Self::splice, "splice", 2) .method(Self::splice, js_string!("splice"), 2)
.method(Self::to_locale_string, "toLocaleString", 0) .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
.method(Self::reduce, "reduce", 1) .method(Self::reduce, js_string!("reduce"), 1)
.method(Self::reduce_right, "reduceRight", 1) .method(Self::reduce_right, js_string!("reduceRight"), 1)
.method(Self::keys, "keys", 0) .method(Self::keys, js_string!("keys"), 0)
.method(Self::entries, "entries", 0) .method(Self::entries, js_string!("entries"), 0)
.method(Self::copy_within, "copyWithin", 2) .method(Self::copy_within, js_string!("copyWithin"), 2)
// Static Methods // Static Methods
.static_method(Self::from, "from", 1) .static_method(Self::from, js_string!("from"), 1)
.static_method(Self::is_array, "isArray", 1) .static_method(Self::is_array, js_string!("isArray"), 1)
.static_method(Self::of, "of", 0) .static_method(Self::of, js_string!("of"), 0)
.build(); .build();
} }
@ -157,7 +158,7 @@ impl IntrinsicObject for Array {
} }
impl BuiltInObject for Array { impl BuiltInObject for Array {
const NAME: &'static str = "Array"; const NAME: JsString = StaticJsStrings::ARRAY;
} }
impl BuiltInConstructor for Array { impl BuiltInConstructor for Array {

22
boa_engine/src/builtins/array/tests.rs

@ -1,5 +1,7 @@
use super::Array; use super::Array;
use crate::{builtins::Number, run_test_actions, Context, JsNativeErrorKind, JsValue, TestAction}; use crate::{
builtins::Number, js_string, run_test_actions, Context, JsNativeErrorKind, JsValue, TestAction,
};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -36,7 +38,7 @@ fn of() {
TestAction::assert("arrayEquals(Array.of(), [])"), TestAction::assert("arrayEquals(Array.of(), [])"),
TestAction::run("let a = Array.of.call(Date, 'a', undefined, 3);"), TestAction::run("let a = Array.of.call(Date, 'a', undefined, 3);"),
TestAction::assert("a instanceof Date"), TestAction::assert("a instanceof Date"),
TestAction::assert_eq("a[0]", "a"), TestAction::assert_eq("a[0]", js_string!("a")),
TestAction::assert_eq("a[1]", JsValue::undefined()), TestAction::assert_eq("a[1]", JsValue::undefined()),
TestAction::assert_eq("a[2]", 3), TestAction::assert_eq("a[2]", 3),
TestAction::assert_eq("a.length", 3), TestAction::assert_eq("a.length", 3),
@ -72,18 +74,18 @@ fn copy_within() {
#[test] #[test]
fn join() { fn join() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("[].join('.')", ""), TestAction::assert_eq("[].join('.')", js_string!()),
TestAction::assert_eq("['a'].join('.')", "a"), TestAction::assert_eq("['a'].join('.')", js_string!("a")),
TestAction::assert_eq("['a', 'b', 'c'].join('.')", "a.b.c"), TestAction::assert_eq("['a', 'b', 'c'].join('.')", js_string!("a.b.c")),
]); ]);
} }
#[test] #[test]
fn to_string() { fn to_string() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("[].toString()", ""), TestAction::assert_eq("[].toString()", js_string!()),
TestAction::assert_eq("['a'].toString()", "a"), TestAction::assert_eq("['a'].toString()", js_string!("a")),
TestAction::assert_eq("['a', 'b', 'c'].toString()", "a,b,c"), TestAction::assert_eq("['a', 'b', 'c'].toString()", js_string!("a,b,c")),
]); ]);
} }
@ -113,7 +115,7 @@ fn every() {
fn find() { fn find() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"['a', 'b', 'c'].find(e => e == 'a')", "['a', 'b', 'c'].find(e => e == 'a')",
"a", js_string!("a"),
)]); )]);
} }
@ -347,7 +349,7 @@ fn fill_obj_ref() {
let a = new Array(3).fill(obj); let a = new Array(3).fill(obj);
obj.hi = 'hi' obj.hi = 'hi'
"#}), "#}),
TestAction::assert_eq("a[2].hi", "hi"), TestAction::assert_eq("a[2].hi", js_string!("hi")),
]); ]);
} }

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

@ -14,13 +14,14 @@ use crate::{
builtins::{typed_array::TypedArrayKind, BuiltInObject}, builtins::{typed_array::TypedArrayKind, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, Numeric}, value::{IntegerOrInfinity, Numeric},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -49,21 +50,21 @@ impl ArrayBuffer {
impl IntrinsicObject for ArrayBuffer { impl IntrinsicObject for ArrayBuffer {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length) let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)
.name("get byteLength") .name(js_string!("get byteLength"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.accessor( .accessor(
utf16!("byteLength"), js_string!("byteLength"),
Some(get_byte_length), Some(get_byte_length),
None, None,
flag_attributes, flag_attributes,
@ -74,8 +75,8 @@ impl IntrinsicObject for ArrayBuffer {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.static_method(Self::is_view, "isView", 1) .static_method(Self::is_view, js_string!("isView"), 1)
.method(Self::slice, "slice", 2) .method(Self::slice, js_string!("slice"), 2)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -90,7 +91,7 @@ impl IntrinsicObject for ArrayBuffer {
} }
impl BuiltInObject for ArrayBuffer { impl BuiltInObject for ArrayBuffer {
const NAME: &'static str = "ArrayBuffer"; const NAME: JsString = StaticJsStrings::ARRAY_BUFFER;
} }
impl BuiltInConstructor for ArrayBuffer { impl BuiltInConstructor for ArrayBuffer {

7
boa_engine/src/builtins/async_function/mod.rs

@ -12,8 +12,9 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsResult, JsValue, Context, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -25,7 +26,7 @@ pub struct AsyncFunction;
impl IntrinsicObject for AsyncFunction { impl IntrinsicObject for AsyncFunction {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().function().constructor()) .prototype(realm.intrinsics().constructors().function().constructor())
@ -46,7 +47,7 @@ impl IntrinsicObject for AsyncFunction {
} }
impl BuiltInObject for AsyncFunction { impl BuiltInObject for AsyncFunction {
const NAME: &'static str = "AsyncFunction"; const NAME: JsString = StaticJsStrings::ASYNC_FUNCTION;
} }
impl BuiltInConstructor for AsyncFunction { impl BuiltInConstructor for AsyncFunction {

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

@ -12,14 +12,16 @@ use crate::{
}, },
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR}, object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
vm::{CompletionRecord, GeneratorResumeKind}, vm::{CompletionRecord, GeneratorResumeKind},
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult, JsString,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -68,7 +70,7 @@ pub struct AsyncGenerator {
impl IntrinsicObject for AsyncGenerator { impl IntrinsicObject for AsyncGenerator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -78,9 +80,9 @@ impl IntrinsicObject for AsyncGenerator {
.iterator_prototypes() .iterator_prototypes()
.async_iterator(), .async_iterator(),
) )
.static_method(Self::next, "next", 1) .static_method(Self::next, js_string!("next"), 1)
.static_method(Self::r#return, "return", 1) .static_method(Self::r#return, js_string!("return"), 1)
.static_method(Self::throw, "throw", 1) .static_method(Self::throw, js_string!("throw"), 1)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -104,7 +106,7 @@ impl IntrinsicObject for AsyncGenerator {
} }
impl AsyncGenerator { impl AsyncGenerator {
const NAME: &'static str = "AsyncGenerator"; const NAME: JsString = StaticJsStrings::ASYNC_GENERATOR;
/// `AsyncGenerator.prototype.next ( value )` /// `AsyncGenerator.prototype.next ( value )`
/// ///

7
boa_engine/src/builtins/async_generator_function/mod.rs

@ -11,9 +11,10 @@ use crate::{
object::{JsObject, PROTOTYPE}, object::{JsObject, PROTOTYPE},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsResult, Context, JsResult, JsString,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -25,7 +26,7 @@ pub struct AsyncGeneratorFunction;
impl IntrinsicObject for AsyncGeneratorFunction { impl IntrinsicObject for AsyncGeneratorFunction {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.inherits(Some( .inherits(Some(
@ -51,7 +52,7 @@ impl IntrinsicObject for AsyncGeneratorFunction {
} }
impl BuiltInObject for AsyncGeneratorFunction { impl BuiltInObject for AsyncGeneratorFunction {
const NAME: &'static str = "AsyncGeneratorFunction"; const NAME: JsString = StaticJsStrings::ASYNC_GENERATOR_FUNCTION;
} }
impl BuiltInConstructor for AsyncGeneratorFunction { impl BuiltInConstructor for AsyncGeneratorFunction {

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

@ -16,12 +16,14 @@ use crate::{
builtins::BuiltInObject, builtins::BuiltInObject,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, PreferredType}, value::{IntegerOrInfinity, PreferredType},
Context, JsArgs, JsBigInt, JsResult, JsValue, Context, JsArgs, JsBigInt, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
use num_bigint::ToBigInt; use num_bigint::ToBigInt;
@ -37,13 +39,13 @@ pub struct BigInt;
impl IntrinsicObject for BigInt { impl IntrinsicObject for BigInt {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, js_string!("valueOf"), 0)
.static_method(Self::as_int_n, "asIntN", 2) .static_method(Self::as_int_n, js_string!("asIntN"), 2)
.static_method(Self::as_uint_n, "asUintN", 2) .static_method(Self::as_uint_n, js_string!("asUintN"), 2)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -58,7 +60,7 @@ impl IntrinsicObject for BigInt {
} }
impl BuiltInObject for BigInt { impl BuiltInObject for BigInt {
const NAME: &'static str = "BigInt"; const NAME: JsString = StaticJsStrings::BIG_INT;
} }
impl BuiltInConstructor for BigInt { impl BuiltInConstructor for BigInt {
@ -179,7 +181,7 @@ impl BigInt {
let radix_mv = if radix.is_undefined() { let radix_mv = if radix.is_undefined() {
// 5. If radixMV = 10, return ! ToString(x). // 5. If radixMV = 10, return ! ToString(x).
// Note: early return optimization. // Note: early return optimization.
return Ok(x.to_string().into()); return Ok(js_string!(x.to_string()).into());
// 3. Else, let radixMV be ? ToIntegerOrInfinity(radix). // 3. Else, let radixMV be ? ToIntegerOrInfinity(radix).
} else { } else {
radix.to_integer_or_infinity(context)? radix.to_integer_or_infinity(context)?
@ -197,14 +199,14 @@ impl BigInt {
// 5. If radixMV = 10, return ! ToString(x). // 5. If radixMV = 10, return ! ToString(x).
if radix_mv == 10 { if radix_mv == 10 {
return Ok(x.to_string().into()); return Ok(js_string!(x.to_string()).into());
} }
// 1. Let x be ? thisBigIntValue(this value). // 1. Let x be ? thisBigIntValue(this value).
// 6. Return the String representation of this Number value using the radix specified by radixMV. // 6. Return the String representation of this Number value using the radix specified by radixMV.
// Letters a-z are used for digits with values 10 through 35. // Letters a-z are used for digits with values 10 through 35.
// The precise algorithm is implementation-defined, however the algorithm should be a generalization of that specified in 6.1.6.2.23. // The precise algorithm is implementation-defined, however the algorithm should be a generalization of that specified in 6.1.6.2.23.
Ok(JsValue::new(x.to_string_radix(radix_mv as u32))) Ok(JsValue::new(js_string!(x.to_string_radix(radix_mv as u32))))
} }
/// `BigInt.prototype.valueOf()` /// `BigInt.prototype.valueOf()`

10
boa_engine/src/builtins/bigint/tests.rs

@ -1,4 +1,4 @@
use crate::{run_test_actions, JsBigInt, JsNativeErrorKind, TestAction}; use crate::{js_string, run_test_actions, JsBigInt, JsNativeErrorKind, TestAction};
#[test] #[test]
fn equality() { fn equality() {
@ -147,10 +147,10 @@ fn operations() {
#[test] #[test]
fn to_string() { fn to_string() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("1000n.toString()", "1000"), TestAction::assert_eq("1000n.toString()", js_string!("1000")),
TestAction::assert_eq("1000n.toString(2)", "1111101000"), TestAction::assert_eq("1000n.toString(2)", js_string!("1111101000")),
TestAction::assert_eq("255n.toString(16)", "ff"), TestAction::assert_eq("255n.toString(16)", js_string!("ff")),
TestAction::assert_eq("1000n.toString(36)", "rs"), TestAction::assert_eq("1000n.toString(36)", js_string!("rs")),
]); ]);
} }

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

@ -16,9 +16,11 @@ use crate::{
builtins::BuiltInObject, builtins::BuiltInObject,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm, realm::Realm,
Context, JsResult, JsValue, string::common::StaticJsStrings,
Context, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -30,11 +32,11 @@ pub(crate) struct Boolean;
impl IntrinsicObject for Boolean { impl IntrinsicObject for Boolean {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, js_string!("valueOf"), 0)
.build(); .build();
} }
@ -44,7 +46,7 @@ impl IntrinsicObject for Boolean {
} }
impl BuiltInObject for Boolean { impl BuiltInObject for Boolean {
const NAME: &'static str = "Boolean"; const NAME: JsString = StaticJsStrings::BOOLEAN;
} }
impl BuiltInConstructor for Boolean { impl BuiltInConstructor for Boolean {
@ -111,7 +113,7 @@ impl Boolean {
_: &mut Context<'_>, _: &mut Context<'_>,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let boolean = Self::this_boolean_value(this)?; let boolean = Self::this_boolean_value(this)?;
Ok(JsValue::new(boolean.to_string())) Ok(JsValue::new(js_string!(boolean.to_string())))
} }
/// The valueOf() method returns the primitive value of a `Boolean` object. /// The valueOf() method returns the primitive value of a `Boolean` object.

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

@ -11,13 +11,14 @@ use crate::{
builtins::{array_buffer::SharedMemoryOrder, typed_array::TypedArrayKind, BuiltInObject}, builtins::{array_buffer::SharedMemoryOrder, typed_array::TypedArrayKind, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, Context, JsArgs, JsResult, JsString,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -36,51 +37,56 @@ impl IntrinsicObject for DataView {
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;
let get_buffer = BuiltInBuilder::callable(realm, Self::get_buffer) let get_buffer = BuiltInBuilder::callable(realm, Self::get_buffer)
.name("get buffer") .name(js_string!("get buffer"))
.build(); .build();
let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length) let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)
.name("get byteLength") .name(js_string!("get byteLength"))
.build(); .build();
let get_byte_offset = BuiltInBuilder::callable(realm, Self::get_byte_offset) let get_byte_offset = BuiltInBuilder::callable(realm, Self::get_byte_offset)
.name("get byteOffset") .name(js_string!("get byteOffset"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes)
.accessor( .accessor(
utf16!("byteLength"), js_string!("buffer"),
Some(get_buffer),
None,
flag_attributes,
)
.accessor(
js_string!("byteLength"),
Some(get_byte_length), Some(get_byte_length),
None, None,
flag_attributes, flag_attributes,
) )
.accessor( .accessor(
utf16!("byteOffset"), js_string!("byteOffset"),
Some(get_byte_offset), Some(get_byte_offset),
None, None,
flag_attributes, flag_attributes,
) )
.method(Self::get_big_int64, "getBigInt64", 1) .method(Self::get_big_int64, js_string!("getBigInt64"), 1)
.method(Self::get_big_uint64, "getBigUint64", 1) .method(Self::get_big_uint64, js_string!("getBigUint64"), 1)
.method(Self::get_float32, "getFloat32", 1) .method(Self::get_float32, js_string!("getFloat32"), 1)
.method(Self::get_float64, "getFloat64", 1) .method(Self::get_float64, js_string!("getFloat64"), 1)
.method(Self::get_int8, "getInt8", 1) .method(Self::get_int8, js_string!("getInt8"), 1)
.method(Self::get_int16, "getInt16", 1) .method(Self::get_int16, js_string!("getInt16"), 1)
.method(Self::get_int32, "getInt32", 1) .method(Self::get_int32, js_string!("getInt32"), 1)
.method(Self::get_uint8, "getUint8", 1) .method(Self::get_uint8, js_string!("getUint8"), 1)
.method(Self::get_uint16, "getUint16", 1) .method(Self::get_uint16, js_string!("getUint16"), 1)
.method(Self::get_uint32, "getUint32", 1) .method(Self::get_uint32, js_string!("getUint32"), 1)
.method(Self::set_big_int64, "setBigInt64", 2) .method(Self::set_big_int64, js_string!("setBigInt64"), 2)
.method(Self::set_big_uint64, "setBigUint64", 2) .method(Self::set_big_uint64, js_string!("setBigUint64"), 2)
.method(Self::set_float32, "setFloat32", 2) .method(Self::set_float32, js_string!("setFloat32"), 2)
.method(Self::set_float64, "setFloat64", 2) .method(Self::set_float64, js_string!("setFloat64"), 2)
.method(Self::set_int8, "setInt8", 2) .method(Self::set_int8, js_string!("setInt8"), 2)
.method(Self::set_int16, "setInt16", 2) .method(Self::set_int16, js_string!("setInt16"), 2)
.method(Self::set_int32, "setInt32", 2) .method(Self::set_int32, js_string!("setInt32"), 2)
.method(Self::set_uint8, "setUint8", 2) .method(Self::set_uint8, js_string!("setUint8"), 2)
.method(Self::set_uint16, "setUint16", 2) .method(Self::set_uint16, js_string!("setUint16"), 2)
.method(Self::set_uint32, "setUint32", 2) .method(Self::set_uint32, js_string!("setUint32"), 2)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -95,7 +101,7 @@ impl IntrinsicObject for DataView {
} }
impl BuiltInObject for DataView { impl BuiltInObject for DataView {
const NAME: &'static str = "DataView"; const NAME: JsString = StaticJsStrings::DATA_VIEW;
} }
impl BuiltInConstructor for DataView { impl BuiltInConstructor for DataView {

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

@ -24,10 +24,10 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrNan, JsValue, PreferredType}, value::{IntegerOrNan, JsValue, PreferredType},
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult, JsString,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
use chrono::prelude::*; use chrono::prelude::*;
@ -94,73 +94,109 @@ impl Date {
impl IntrinsicObject for Date { impl IntrinsicObject for Date {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let to_utc_string = BuiltInBuilder::callable(realm, Self::to_utc_string) let to_utc_string = BuiltInBuilder::callable(realm, Self::to_utc_string)
.name("toUTCString") .name(js_string!("toUTCString"))
.length(0) .length(0)
.build(); .build();
let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive) let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive)
.name("[Symbol.toPrimitive]") .name(js_string!("[Symbol.toPrimitive]"))
.length(1) .length(1)
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::now, "now", 0) .static_method(Self::now, js_string!("now"), 0)
.static_method(Self::parse, "parse", 1) .static_method(Self::parse, js_string!("parse"), 1)
.static_method(Self::utc, "UTC", 7) .static_method(Self::utc, js_string!("UTC"), 7)
.method(Self::get_date::<true>, "getDate", 0) .method(Self::get_date::<true>, js_string!("getDate"), 0)
.method(Self::get_day::<true>, "getDay", 0) .method(Self::get_day::<true>, js_string!("getDay"), 0)
.method(Self::get_full_year::<true>, "getFullYear", 0) .method(Self::get_full_year::<true>, js_string!("getFullYear"), 0)
.method(Self::get_hours::<true>, "getHours", 0) .method(Self::get_hours::<true>, js_string!("getHours"), 0)
.method(Self::get_milliseconds::<true>, "getMilliseconds", 0) .method(
.method(Self::get_minutes::<true>, "getMinutes", 0) Self::get_milliseconds::<true>,
.method(Self::get_month::<true>, "getMonth", 0) js_string!("getMilliseconds"),
.method(Self::get_seconds::<true>, "getSeconds", 0) 0,
.method(Self::get_time, "getTime", 0) )
.method(Self::get_timezone_offset, "getTimezoneOffset", 0) .method(Self::get_minutes::<true>, js_string!("getMinutes"), 0)
.method(Self::get_date::<false>, "getUTCDate", 0) .method(Self::get_month::<true>, js_string!("getMonth"), 0)
.method(Self::get_day::<false>, "getUTCDay", 0) .method(Self::get_seconds::<true>, js_string!("getSeconds"), 0)
.method(Self::get_full_year::<false>, "getUTCFullYear", 0) .method(Self::get_time, js_string!("getTime"), 0)
.method(Self::get_hours::<false>, "getUTCHours", 0) .method(
.method(Self::get_milliseconds::<false>, "getUTCMilliseconds", 0) Self::get_timezone_offset,
.method(Self::get_minutes::<false>, "getUTCMinutes", 0) js_string!("getTimezoneOffset"),
.method(Self::get_month::<false>, "getUTCMonth", 0) 0,
.method(Self::get_seconds::<false>, "getUTCSeconds", 0) )
.method(Self::get_year, "getYear", 0) .method(Self::get_date::<false>, js_string!("getUTCDate"), 0)
.method(Self::set_date::<true>, "setDate", 1) .method(Self::get_day::<false>, js_string!("getUTCDay"), 0)
.method(Self::set_full_year::<true>, "setFullYear", 3) .method(
.method(Self::set_hours::<true>, "setHours", 4) Self::get_full_year::<false>,
.method(Self::set_milliseconds::<true>, "setMilliseconds", 1) js_string!("getUTCFullYear"),
.method(Self::set_minutes::<true>, "setMinutes", 3) 0,
.method(Self::set_month::<true>, "setMonth", 2) )
.method(Self::set_seconds::<true>, "setSeconds", 2) .method(Self::get_hours::<false>, js_string!("getUTCHours"), 0)
.method(Self::set_time, "setTime", 1) .method(
.method(Self::set_date::<false>, "setUTCDate", 1) Self::get_milliseconds::<false>,
.method(Self::set_full_year::<false>, "setUTCFullYear", 3) js_string!("getUTCMilliseconds"),
.method(Self::set_hours::<false>, "setUTCHours", 4) 0,
.method(Self::set_milliseconds::<false>, "setUTCMilliseconds", 1) )
.method(Self::set_minutes::<false>, "setUTCMinutes", 3) .method(Self::get_minutes::<false>, js_string!("getUTCMinutes"), 0)
.method(Self::set_month::<false>, "setUTCMonth", 2) .method(Self::get_month::<false>, js_string!("getUTCMonth"), 0)
.method(Self::set_seconds::<false>, "setUTCSeconds", 2) .method(Self::get_seconds::<false>, js_string!("getUTCSeconds"), 0)
.method(Self::set_year, "setYear", 1) .method(Self::get_year, js_string!("getYear"), 0)
.method(Self::to_date_string, "toDateString", 0) .method(Self::set_date::<true>, js_string!("setDate"), 1)
.method(Self::to_iso_string, "toISOString", 0) .method(Self::set_full_year::<true>, js_string!("setFullYear"), 3)
.method(Self::to_json, "toJSON", 1) .method(Self::set_hours::<true>, js_string!("setHours"), 4)
.method(Self::to_locale_date_string, "toLocaleDateString", 0) .method(
.method(Self::to_locale_string, "toLocaleString", 0) Self::set_milliseconds::<true>,
.method(Self::to_locale_time_string, "toLocaleTimeString", 0) js_string!("setMilliseconds"),
.method(Self::to_string, "toString", 0) 1,
.method(Self::to_time_string, "toTimeString", 0) )
.method(Self::value_of, "valueOf", 0) .method(Self::set_minutes::<true>, js_string!("setMinutes"), 3)
.method(Self::set_month::<true>, js_string!("setMonth"), 2)
.method(Self::set_seconds::<true>, js_string!("setSeconds"), 2)
.method(Self::set_time, js_string!("setTime"), 1)
.method(Self::set_date::<false>, js_string!("setUTCDate"), 1)
.method(
Self::set_full_year::<false>,
js_string!("setUTCFullYear"),
3,
)
.method(Self::set_hours::<false>, js_string!("setUTCHours"), 4)
.method(
Self::set_milliseconds::<false>,
js_string!("setUTCMilliseconds"),
1,
)
.method(Self::set_minutes::<false>, js_string!("setUTCMinutes"), 3)
.method(Self::set_month::<false>, js_string!("setUTCMonth"), 2)
.method(Self::set_seconds::<false>, js_string!("setUTCSeconds"), 2)
.method(Self::set_year, js_string!("setYear"), 1)
.method(Self::to_date_string, js_string!("toDateString"), 0)
.method(Self::to_iso_string, js_string!("toISOString"), 0)
.method(Self::to_json, js_string!("toJSON"), 1)
.method(
Self::to_locale_date_string,
js_string!("toLocaleDateString"),
0,
)
.method(Self::to_locale_string, js_string!("toLocaleString"), 0)
.method(
Self::to_locale_time_string,
js_string!("toLocaleTimeString"),
0,
)
.method(Self::to_string, js_string!("toString"), 0)
.method(Self::to_time_string, js_string!("toTimeString"), 0)
.method(Self::value_of, js_string!("valueOf"), 0)
.property( .property(
"toGMTString", js_string!("toGMTString"),
to_utc_string.clone(), to_utc_string.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"toUTCString", js_string!("toUTCString"),
to_utc_string, to_utc_string,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
@ -178,7 +214,7 @@ impl IntrinsicObject for Date {
} }
impl BuiltInObject for Date { impl BuiltInObject for Date {
const NAME: &'static str = "Date"; const NAME: JsString = StaticJsStrings::DATE;
} }
impl BuiltInConstructor for Date { impl BuiltInConstructor for Date {
@ -205,13 +241,11 @@ impl BuiltInConstructor for Date {
if new_target.is_undefined() { if new_target.is_undefined() {
// a. Let now be the time value (UTC) identifying the current time. // a. Let now be the time value (UTC) identifying the current time.
// b. Return ToDateString(now). // b. Return ToDateString(now).
return Ok(JsValue::new( return Ok(JsValue::new(js_string!(context
context .host_hooks()
.host_hooks() .local_from_utc(context.host_hooks().utc_now())
.local_from_utc(context.host_hooks().utc_now()) .format("%a %b %d %Y %H:%M:%S GMT%:z")
.format("%a %b %d %Y %H:%M:%S GMT%:z") .to_string())));
.to_string(),
));
} }
// 2. Let numberOfArgs be the number of elements in values. // 2. Let numberOfArgs be the number of elements in values.
let dv = match args { let dv = match args {
@ -1253,12 +1287,12 @@ impl Date {
// 4. Let t be LocalTime(tv). // 4. Let t be LocalTime(tv).
// 5. Return DateString(t). // 5. Return DateString(t).
Ok(context Ok(js_string!(context
.host_hooks() .host_hooks()
.local_from_utc(tv) .local_from_utc(tv)
.format("%a %b %d %Y") .format("%a %b %d %Y")
.to_string() .to_string())
.into()) .into())
} }
/// [`Date.prototype.toISOString()`][spec]. /// [`Date.prototype.toISOString()`][spec].
@ -1280,11 +1314,11 @@ impl Date {
let t = this_time_value(this)? let t = this_time_value(this)?
.and_then(NaiveDateTime::from_timestamp_millis) .and_then(NaiveDateTime::from_timestamp_millis)
.ok_or_else(|| JsNativeError::range().with_message("Invalid time value"))?; .ok_or_else(|| JsNativeError::range().with_message("Invalid time value"))?;
Ok(Utc Ok(js_string!(Utc
.from_utc_datetime(&t) .from_utc_datetime(&t)
.format("%Y-%m-%dT%H:%M:%S.%3fZ") .format("%Y-%m-%dT%H:%M:%S.%3fZ")
.to_string() .to_string())
.into()) .into())
} }
/// [`Date.prototype.toJSON()`][spec]. /// [`Date.prototype.toJSON()`][spec].
@ -1334,7 +1368,9 @@ impl Date {
_args: &[JsValue], _args: &[JsValue],
_context: &mut Context<'_>, _context: &mut Context<'_>,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
Err(JsError::from_opaque(JsValue::new("Function Unimplemented"))) Err(JsError::from_opaque(JsValue::new(js_string!(
"Function Unimplemented"
))))
} }
/// [`Date.prototype.toLocaleString()`][spec]. /// [`Date.prototype.toLocaleString()`][spec].
@ -1351,9 +1387,9 @@ impl Date {
_: &[JsValue], _: &[JsValue],
_context: &mut Context<'_>, _context: &mut Context<'_>,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
Err(JsError::from_opaque(JsValue::new( Err(JsError::from_opaque(JsValue::new(js_string!(
"Function Unimplemented]", "Function Unimplemented]"
))) ))))
} }
/// [`Date.prototype.toLocaleTimeString()`][spec]. /// [`Date.prototype.toLocaleTimeString()`][spec].
@ -1371,9 +1407,9 @@ impl Date {
_args: &[JsValue], _args: &[JsValue],
_context: &mut Context<'_>, _context: &mut Context<'_>,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
Err(JsError::from_opaque(JsValue::new( Err(JsError::from_opaque(JsValue::new(js_string!(
"Function Unimplemented]", "Function Unimplemented]"
))) ))))
} }
/// [`Date.prototype.toString()`][spec]. /// [`Date.prototype.toString()`][spec].
@ -1395,12 +1431,12 @@ impl Date {
let Some(tv) = this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis) else { let Some(tv) = this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis) else {
return Ok(js_string!("Invalid Date").into()); return Ok(js_string!("Invalid Date").into());
}; };
Ok(context Ok(js_string!(context
.host_hooks() .host_hooks()
.local_from_utc(tv) .local_from_utc(tv)
.format("%a %b %d %Y %H:%M:%S GMT%z") .format("%a %b %d %Y %H:%M:%S GMT%z")
.to_string() .to_string())
.into()) .into())
} }
/// [`Date.prototype.toTimeString()`][spec]. /// [`Date.prototype.toTimeString()`][spec].
@ -1427,12 +1463,12 @@ impl Date {
// 4. Let t be LocalTime(tv). // 4. Let t be LocalTime(tv).
// 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv). // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv).
Ok(context Ok(js_string!(context
.host_hooks() .host_hooks()
.local_from_utc(tv) .local_from_utc(tv)
.format("%H:%M:%S GMT%z") .format("%H:%M:%S GMT%z")
.to_string() .to_string())
.into()) .into())
} }
/// [`Date.prototype.toUTCString()`][spec]. /// [`Date.prototype.toUTCString()`][spec].
@ -1466,7 +1502,7 @@ impl Date {
// code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), yearSign, paddedYear, the code // code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), yearSign, paddedYear, the code
// unit 0x0020 (SPACE), and TimeString(tv) // unit 0x0020 (SPACE), and TimeString(tv)
let utc_string = t.format("%a, %d %b %Y %H:%M:%S GMT").to_string(); let utc_string = t.format("%a, %d %b %Y %H:%M:%S GMT").to_string();
Ok(JsValue::new(utc_string)) Ok(JsValue::new(js_string!(utc_string)))
} }
/// [`Date.prototype.valueOf()`][spec]. /// [`Date.prototype.valueOf()`][spec].

22
boa_engine/src/builtins/date/tests.rs

@ -1,4 +1,4 @@
use crate::{run_test_actions, JsNativeErrorKind, TestAction}; use crate::{js_string, run_test_actions, JsNativeErrorKind, TestAction};
use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone}; use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone};
use indoc::indoc; use indoc::indoc;
@ -762,7 +762,7 @@ fn date_proto_set_utc_seconds() {
fn date_proto_to_date_string() { fn date_proto_to_date_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(2020, 6, 8, 9, 16, 15, 779).toDateString()", "new Date(2020, 6, 8, 9, 16, 15, 779).toDateString()",
"Wed Jul 08 2020", js_string!("Wed Jul 08 2020"),
)]); )]);
} }
@ -770,7 +770,7 @@ fn date_proto_to_date_string() {
fn date_proto_to_gmt_string() { fn date_proto_to_gmt_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toGMTString()", "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toGMTString()",
"Wed, 08 Jul 2020 09:16:15 GMT", js_string!("Wed, 08 Jul 2020 09:16:15 GMT"),
)]); )]);
} }
@ -778,7 +778,7 @@ fn date_proto_to_gmt_string() {
fn date_proto_to_iso_string() { fn date_proto_to_iso_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toISOString()", "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toISOString()",
"2020-07-08T09:16:15.779Z", js_string!("2020-07-08T09:16:15.779Z"),
)]); )]);
} }
@ -786,7 +786,7 @@ fn date_proto_to_iso_string() {
fn date_proto_to_json() { fn date_proto_to_json() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toJSON()", "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toJSON()",
"2020-07-08T09:16:15.779Z", js_string!("2020-07-08T09:16:15.779Z"),
)]); )]);
} }
@ -794,7 +794,7 @@ fn date_proto_to_json() {
fn date_proto_to_string() { fn date_proto_to_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(2020, 6, 8, 9, 16, 15, 779).toString()", "new Date(2020, 6, 8, 9, 16, 15, 779).toString()",
Local js_string!(Local
.from_local_datetime(&NaiveDateTime::new( .from_local_datetime(&NaiveDateTime::new(
NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(), NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(),
NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap(), NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap(),
@ -802,7 +802,7 @@ fn date_proto_to_string() {
.earliest() .earliest()
.unwrap() .unwrap()
.format("Wed Jul 08 2020 09:16:15 GMT%z") .format("Wed Jul 08 2020 09:16:15 GMT%z")
.to_string(), .to_string()),
)]); )]);
} }
@ -810,7 +810,7 @@ fn date_proto_to_string() {
fn date_proto_to_time_string() { fn date_proto_to_time_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(2020, 6, 8, 9, 16, 15, 779).toTimeString()", "new Date(2020, 6, 8, 9, 16, 15, 779).toTimeString()",
Local js_string!(Local
.from_local_datetime(&NaiveDateTime::new( .from_local_datetime(&NaiveDateTime::new(
NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(), NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(),
NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap(), NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap(),
@ -818,7 +818,7 @@ fn date_proto_to_time_string() {
.earliest() .earliest()
.unwrap() .unwrap()
.format("09:16:15 GMT%z") .format("09:16:15 GMT%z")
.to_string(), .to_string()),
)]); )]);
} }
@ -826,7 +826,7 @@ fn date_proto_to_time_string() {
fn date_proto_to_utc_string() { fn date_proto_to_utc_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toUTCString()", "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toUTCString()",
"Wed, 08 Jul 2020 09:16:15 GMT", js_string!("Wed, 08 Jul 2020 09:16:15 GMT"),
)]); )]);
} }
@ -850,6 +850,6 @@ fn date_neg() {
fn date_json() { fn date_json() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"JSON.stringify({ date: new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)) })", "JSON.stringify({ date: new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)) })",
r#"{"date":"2020-07-08T09:16:15.779Z"}"#, js_string!(r#"{"date":"2020-07-08T09:16:15.779Z"}"#),
)]); )]);
} }

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

@ -13,11 +13,12 @@ use crate::{
IntrinsicObject, IntrinsicObject,
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptorBuilder}, property::{Attribute, PropertyDescriptorBuilder},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -28,14 +29,14 @@ pub(crate) struct AggregateError;
impl IntrinsicObject for AggregateError { impl IntrinsicObject for AggregateError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -45,7 +46,7 @@ impl IntrinsicObject for AggregateError {
} }
impl BuiltInObject for AggregateError { impl BuiltInObject for AggregateError {
const NAME: &'static str = "AggregateError"; const NAME: JsString = StaticJsStrings::AGGREGATE_ERROR;
} }
impl BuiltInConstructor for AggregateError { impl BuiltInConstructor for AggregateError {

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

@ -14,11 +14,12 @@
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -30,14 +31,14 @@ pub(crate) struct EvalError;
impl IntrinsicObject for EvalError { impl IntrinsicObject for EvalError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -47,7 +48,7 @@ impl IntrinsicObject for EvalError {
} }
impl BuiltInObject for EvalError { impl BuiltInObject for EvalError {
const NAME: &'static str = "EvalError"; const NAME: JsString = StaticJsStrings::EVAL_ERROR;
} }
impl BuiltInConstructor for EvalError { impl BuiltInConstructor for EvalError {

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

@ -18,8 +18,8 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -129,13 +129,13 @@ pub(crate) struct Error;
impl IntrinsicObject for Error { impl IntrinsicObject for Error {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.build(); .build();
} }
@ -145,7 +145,7 @@ impl IntrinsicObject for Error {
} }
impl BuiltInObject for Error { impl BuiltInObject for Error {
const NAME: &'static str = "Error"; const NAME: JsString = StaticJsStrings::ERROR;
} }
impl BuiltInConstructor for Error { impl BuiltInConstructor for Error {

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

@ -12,11 +12,12 @@
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -28,14 +29,14 @@ pub(crate) struct RangeError;
impl IntrinsicObject for RangeError { impl IntrinsicObject for RangeError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -45,7 +46,7 @@ impl IntrinsicObject for RangeError {
} }
impl BuiltInObject for RangeError { impl BuiltInObject for RangeError {
const NAME: &'static str = "RangeError"; const NAME: JsString = StaticJsStrings::RANGE_ERROR;
} }
impl BuiltInConstructor for RangeError { impl BuiltInConstructor for RangeError {

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

@ -12,11 +12,12 @@
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -27,14 +28,14 @@ pub(crate) struct ReferenceError;
impl IntrinsicObject for ReferenceError { impl IntrinsicObject for ReferenceError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(js_string!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(js_string!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -44,7 +45,7 @@ impl IntrinsicObject for ReferenceError {
} }
impl BuiltInObject for ReferenceError { impl BuiltInObject for ReferenceError {
const NAME: &'static str = "ReferenceError"; const NAME: JsString = StaticJsStrings::REFERENCE_ERROR;
} }
impl BuiltInConstructor for ReferenceError { impl BuiltInConstructor for ReferenceError {

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

@ -14,11 +14,12 @@
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -30,14 +31,14 @@ pub(crate) struct SyntaxError;
impl IntrinsicObject for SyntaxError { impl IntrinsicObject for SyntaxError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -47,7 +48,7 @@ impl IntrinsicObject for SyntaxError {
} }
impl BuiltInObject for SyntaxError { impl BuiltInObject for SyntaxError {
const NAME: &'static str = "SyntaxError"; const NAME: JsString = StaticJsStrings::SYNTAX_ERROR;
} }
impl BuiltInConstructor for SyntaxError { impl BuiltInConstructor for SyntaxError {

66
boa_engine/src/builtins/error/tests.rs

@ -1,24 +1,42 @@
use crate::{run_test_actions, TestAction}; use crate::{js_string, run_test_actions, TestAction};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
fn error_to_string() { fn error_to_string() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("(new Error('1')).toString()", "Error: 1"), TestAction::assert_eq("(new Error('1')).toString()", js_string!("Error: 1")),
TestAction::assert_eq("(new RangeError('2')).toString()", "RangeError: 2"), TestAction::assert_eq(
TestAction::assert_eq("(new ReferenceError('3')).toString()", "ReferenceError: 3"), "(new RangeError('2')).toString()",
TestAction::assert_eq("(new SyntaxError('4')).toString()", "SyntaxError: 4"), js_string!("RangeError: 2"),
TestAction::assert_eq("(new TypeError('5')).toString()", "TypeError: 5"), ),
TestAction::assert_eq("(new EvalError('6')).toString()", "EvalError: 6"), TestAction::assert_eq(
TestAction::assert_eq("(new URIError('7')).toString()", "URIError: 7"), "(new ReferenceError('3')).toString()",
js_string!("ReferenceError: 3"),
),
TestAction::assert_eq(
"(new SyntaxError('4')).toString()",
js_string!("SyntaxError: 4"),
),
TestAction::assert_eq(
"(new TypeError('5')).toString()",
js_string!("TypeError: 5"),
),
TestAction::assert_eq(
"(new EvalError('6')).toString()",
js_string!("EvalError: 6"),
),
TestAction::assert_eq("(new URIError('7')).toString()", js_string!("URIError: 7")),
// no message // no message
TestAction::assert_eq("(new Error()).toString()", "Error"), TestAction::assert_eq("(new Error()).toString()", js_string!("Error")),
TestAction::assert_eq("(new RangeError()).toString()", "RangeError"), TestAction::assert_eq("(new RangeError()).toString()", js_string!("RangeError")),
TestAction::assert_eq("(new ReferenceError()).toString()", "ReferenceError"), TestAction::assert_eq(
TestAction::assert_eq("(new SyntaxError()).toString()", "SyntaxError"), "(new ReferenceError()).toString()",
TestAction::assert_eq("(new TypeError()).toString()", "TypeError"), js_string!("ReferenceError"),
TestAction::assert_eq("(new EvalError()).toString()", "EvalError"), ),
TestAction::assert_eq("(new URIError()).toString()", "URIError"), TestAction::assert_eq("(new SyntaxError()).toString()", js_string!("SyntaxError")),
TestAction::assert_eq("(new TypeError()).toString()", js_string!("TypeError")),
TestAction::assert_eq("(new EvalError()).toString()", js_string!("EvalError")),
TestAction::assert_eq("(new URIError()).toString()", js_string!("URIError")),
// no name // no name
TestAction::assert_eq( TestAction::assert_eq(
indoc! {r#" indoc! {r#"
@ -26,7 +44,7 @@ fn error_to_string() {
message.name = ''; message.name = '';
message.toString() message.toString()
"#}, "#},
"message", js_string!("message"),
), ),
]); ]);
} }
@ -34,14 +52,14 @@ fn error_to_string() {
#[test] #[test]
fn error_names() { fn error_names() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("Error.name", "Error"), TestAction::assert_eq("Error.name", js_string!("Error")),
TestAction::assert_eq("EvalError.name", "EvalError"), TestAction::assert_eq("EvalError.name", js_string!("EvalError")),
TestAction::assert_eq("RangeError.name", "RangeError"), TestAction::assert_eq("RangeError.name", js_string!("RangeError")),
TestAction::assert_eq("ReferenceError.name", "ReferenceError"), TestAction::assert_eq("ReferenceError.name", js_string!("ReferenceError")),
TestAction::assert_eq("SyntaxError.name", "SyntaxError"), TestAction::assert_eq("SyntaxError.name", js_string!("SyntaxError")),
TestAction::assert_eq("URIError.name", "URIError"), TestAction::assert_eq("URIError.name", js_string!("URIError")),
TestAction::assert_eq("TypeError.name", "TypeError"), TestAction::assert_eq("TypeError.name", js_string!("TypeError")),
TestAction::assert_eq("AggregateError.name", "AggregateError"), TestAction::assert_eq("AggregateError.name", js_string!("AggregateError")),
]); ]);
} }

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

@ -22,11 +22,12 @@ use crate::{
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, NativeFunction, Context, JsArgs, JsResult, JsString, JsValue, NativeFunction,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -38,14 +39,14 @@ pub(crate) struct TypeError;
impl IntrinsicObject for TypeError { impl IntrinsicObject for TypeError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -55,7 +56,7 @@ impl IntrinsicObject for TypeError {
} }
impl BuiltInObject for TypeError { impl BuiltInObject for TypeError {
const NAME: &'static str = "TypeError"; const NAME: JsString = StaticJsStrings::TYPE_ERROR;
} }
impl BuiltInConstructor for TypeError { impl BuiltInConstructor for TypeError {
@ -129,7 +130,7 @@ impl IntrinsicObject for ThrowTypeError {
let obj = BuiltInBuilder::with_intrinsic::<Self>(realm) let obj = BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(realm.intrinsics().constructors().function().prototype()) .prototype(realm.intrinsics().constructors().function().prototype())
.static_property(utf16!("length"), 0, Attribute::empty()) .static_property(utf16!("length"), 0, Attribute::empty())
.static_property(utf16!("name"), "", Attribute::empty()) .static_property(utf16!("name"), js_string!(), Attribute::empty())
.build(); .build();
let mut obj = obj.borrow_mut(); let mut obj = obj.borrow_mut();

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

@ -13,11 +13,12 @@
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -29,14 +30,14 @@ pub(crate) struct UriError;
impl IntrinsicObject for UriError { impl IntrinsicObject for UriError {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor()) .prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype())) .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(utf16!("name"), Self::NAME, attribute) .property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute) .property(utf16!("message"), js_string!(), attribute)
.build(); .build();
} }
@ -46,7 +47,7 @@ impl IntrinsicObject for UriError {
} }
impl BuiltInObject for UriError { impl BuiltInObject for UriError {
const NAME: &'static str = "URIError"; const NAME: JsString = StaticJsStrings::URI_ERROR;
} }
impl BuiltInConstructor for UriError { impl BuiltInConstructor for UriError {

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

@ -11,8 +11,8 @@
//! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object //! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object
use crate::{ use crate::{
context::intrinsics::Intrinsics, js_string, realm::Realm, Context, JsArgs, JsObject, JsResult, context::intrinsics::Intrinsics, js_string, realm::Realm, string::common::StaticJsStrings,
JsValue, Context, JsArgs, JsObject, JsResult, JsString, JsValue,
}; };
use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject}; use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};
@ -34,7 +34,7 @@ impl IntrinsicObject for Escape {
} }
impl BuiltInObject for Escape { impl BuiltInObject for Escape {
const NAME: &'static str = "escape"; const NAME: JsString = StaticJsStrings::ESCAPE;
} }
/// Builtin JavaScript `escape ( string )` function. /// Builtin JavaScript `escape ( string )` function.
@ -106,7 +106,7 @@ impl IntrinsicObject for Unescape {
} }
impl BuiltInObject for Unescape { impl BuiltInObject for Unescape {
const NAME: &'static str = "unescape"; const NAME: JsString = StaticJsStrings::UNESCAPE;
} }
/// Builtin JavaScript `unescape ( string )` function. /// Builtin JavaScript `unescape ( string )` function.

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

@ -17,6 +17,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
vm::{CallFrame, Opcode}, vm::{CallFrame, Opcode},
Context, JsArgs, JsResult, JsString, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
@ -33,7 +34,7 @@ pub(crate) struct Eval;
impl IntrinsicObject for Eval { impl IntrinsicObject for Eval {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, Self::eval) BuiltInBuilder::callable_with_intrinsic::<Self>(realm, Self::eval)
.name(Self::NAME) .name(Self::NAME)
@ -47,7 +48,7 @@ impl IntrinsicObject for Eval {
} }
impl BuiltInObject for Eval { impl BuiltInObject for Eval {
const NAME: &'static str = "eval"; const NAME: JsString = StaticJsStrings::EVAL;
} }
impl Eval { impl Eval {

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

@ -23,7 +23,7 @@ use crate::{
object::{JsFunction, PrivateElement, PrivateName}, object::{JsFunction, PrivateElement, PrivateName},
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
vm::{ActiveRunnable, CodeBlock}, vm::{ActiveRunnable, CodeBlock},
@ -461,20 +461,20 @@ pub struct BuiltInFunctionObject;
impl IntrinsicObject for BuiltInFunctionObject { impl IntrinsicObject for BuiltInFunctionObject {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("function", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let has_instance = BuiltInBuilder::callable(realm, Self::has_instance) let has_instance = BuiltInBuilder::callable(realm, Self::has_instance)
.name("[Symbol.hasInstance]") .name(js_string!("[Symbol.hasInstance]"))
.length(1) .length(1)
.build(); .build();
let throw_type_error = realm.intrinsics().objects().throw_type_error(); let throw_type_error = realm.intrinsics().objects().throw_type_error();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.method(Self::apply, "apply", 2) .method(Self::apply, js_string!("apply"), 2)
.method(Self::bind, "bind", 1) .method(Self::bind, js_string!("bind"), 1)
.method(Self::call, "call", 1) .method(Self::call, js_string!("call"), 1)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.property(JsSymbol::has_instance(), has_instance, Attribute::default()) .property(JsSymbol::has_instance(), has_instance, Attribute::default())
.accessor( .accessor(
utf16!("caller"), utf16!("caller"),
@ -493,7 +493,7 @@ impl IntrinsicObject for BuiltInFunctionObject {
let prototype = realm.intrinsics().constructors().function().prototype(); let prototype = realm.intrinsics().constructors().function().prototype();
BuiltInBuilder::callable_with_object(realm, prototype.clone(), Self::prototype) BuiltInBuilder::callable_with_object(realm, prototype.clone(), Self::prototype)
.name("") .name(js_string!())
.length(0) .length(0)
.build(); .build();
@ -506,7 +506,7 @@ impl IntrinsicObject for BuiltInFunctionObject {
} }
impl BuiltInObject for BuiltInFunctionObject { impl BuiltInObject for BuiltInFunctionObject {
const NAME: &'static str = "Function"; const NAME: JsString = StaticJsStrings::FUNCTION;
} }
impl BuiltInConstructor for BuiltInFunctionObject { impl BuiltInConstructor for BuiltInFunctionObject {

14
boa_engine/src/builtins/function/tests.rs

@ -50,7 +50,7 @@ fn self_mutating_function_when_constructing() {
#[test] #[test]
fn function_prototype() { fn function_prototype() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("Function.prototype.name", ""), TestAction::assert_eq("Function.prototype.name", js_string!()),
TestAction::assert_eq("Function.prototype.length", 0), TestAction::assert_eq("Function.prototype.length", 0),
TestAction::assert_eq("Function.prototype()", JsValue::undefined()), TestAction::assert_eq("Function.prototype()", JsValue::undefined()),
TestAction::assert_eq( TestAction::assert_eq(
@ -69,7 +69,7 @@ fn function_prototype() {
fn function_prototype_call() { fn function_prototype_call() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"Object.prototype.toString.call(new Error())", "Object.prototype.toString.call(new Error())",
"[object Error]", js_string!("[object Error]"),
)]); )]);
} }
@ -134,9 +134,9 @@ fn closure_capture_clone() {
let object = JsObject::with_object_proto(ctx.intrinsics()); let object = JsObject::with_object_proto(ctx.intrinsics());
object object
.define_property_or_throw( .define_property_or_throw(
"key", js_string!("key"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.value(" world!") .value(js_string!(" world!"))
.writable(false) .writable(false)
.enumerable(false) .enumerable(false)
.configurable(false), .configurable(false),
@ -153,7 +153,7 @@ fn closure_capture_clone() {
let hw = js_string!( let hw = js_string!(
string, string,
&object &object
.__get_own_property__(&"key".into(), context)? .__get_own_property__(&js_string!("key").into(), context)?
.and_then(|prop| prop.value().cloned()) .and_then(|prop| prop.value().cloned())
.and_then(|val| val.as_string().cloned()) .and_then(|val| val.as_string().cloned())
.ok_or_else( .ok_or_else(
@ -168,10 +168,10 @@ fn closure_capture_clone() {
.name("closure") .name("closure")
.build(); .build();
ctx.register_global_property("closure", func, Attribute::default()) ctx.register_global_property(js_string!("closure"), func, Attribute::default())
.unwrap(); .unwrap();
}), }),
TestAction::assert_eq("closure()", "Hello world!"), TestAction::assert_eq("closure()", js_string!("Hello world!")),
]); ]);
} }

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

@ -14,13 +14,15 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
environments::EnvironmentStack, environments::EnvironmentStack,
error::JsNativeError, error::JsNativeError,
js_string,
object::{JsObject, CONSTRUCTOR}, object::{JsObject, CONSTRUCTOR},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
vm::{CallFrame, CompletionRecord, GeneratorResumeKind}, vm::{CallFrame, CompletionRecord, GeneratorResumeKind},
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult, JsString,
}; };
use boa_gc::{custom_trace, Finalize, Trace}; use boa_gc::{custom_trace, Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -136,7 +138,7 @@ pub struct Generator {
impl IntrinsicObject for Generator { impl IntrinsicObject for Generator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -146,9 +148,9 @@ impl IntrinsicObject for Generator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 1) .static_method(Self::next, js_string!("next"), 1)
.static_method(Self::r#return, "return", 1) .static_method(Self::r#return, js_string!("return"), 1)
.static_method(Self::throw, "throw", 1) .static_method(Self::throw, js_string!("throw"), 1)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -172,7 +174,7 @@ impl IntrinsicObject for Generator {
} }
impl Generator { impl Generator {
const NAME: &'static str = "Generator"; const NAME: JsString = StaticJsStrings::GENERATOR;
/// `Generator.prototype.next ( value )` /// `Generator.prototype.next ( value )`
/// ///

7
boa_engine/src/builtins/generator_function/mod.rs

@ -16,9 +16,10 @@ use crate::{
object::PROTOTYPE, object::PROTOTYPE,
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsResult, Context, JsResult, JsString,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -30,7 +31,7 @@ pub struct GeneratorFunction;
impl IntrinsicObject for GeneratorFunction { impl IntrinsicObject for GeneratorFunction {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.inherits(Some( .inherits(Some(
@ -56,7 +57,7 @@ impl IntrinsicObject for GeneratorFunction {
} }
impl BuiltInObject for GeneratorFunction { impl BuiltInObject for GeneratorFunction {
const NAME: &'static str = "GeneratorFunction"; const NAME: JsString = StaticJsStrings::GENERATOR_FUNCTION;
} }
impl BuiltInConstructor for GeneratorFunction { impl BuiltInConstructor for GeneratorFunction {

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

@ -19,6 +19,7 @@ use crate::{
intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
BoaProvider, BoaProvider,
}, },
js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{ object::{
internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction, internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction,
@ -26,9 +27,9 @@ use crate::{
}, },
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
use super::{ use super::{
@ -149,26 +150,30 @@ impl Service for Collator {
impl IntrinsicObject for Collator { impl IntrinsicObject for Collator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let compare = BuiltInBuilder::callable(realm, Self::compare) let compare = BuiltInBuilder::callable(realm, Self::compare)
.name("get compare") .name(js_string!("get compare"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .static_method(
Self::supported_locales_of,
js_string!("supportedLocalesOf"),
1,
)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Intl.Collator", js_string!("Intl.Collator"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("compare"), js_string!("compare"),
Some(compare), Some(compare),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::resolved_options, "resolvedOptions", 0) .method(Self::resolved_options, js_string!("resolvedOptions"), 0)
.build(); .build();
} }
@ -178,7 +183,7 @@ impl IntrinsicObject for Collator {
} }
impl BuiltInObject for Collator { impl BuiltInObject for Collator {
const NAME: &'static str = "Collator"; const NAME: JsString = StaticJsStrings::COLLATOR;
} }
impl BuiltInConstructor for Collator { impl BuiltInConstructor for Collator {
@ -510,14 +515,18 @@ impl Collator {
// i. Perform ! CreateDataPropertyOrThrow(options, p, v). // i. Perform ! CreateDataPropertyOrThrow(options, p, v).
// 5. Return options. // 5. Return options.
options options
.create_data_property_or_throw(utf16!("locale"), collator.locale.to_string(), context) .create_data_property_or_throw(
utf16!("locale"),
js_string!(collator.locale.to_string()),
context,
)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("usage"), utf16!("usage"),
match collator.usage { match collator.usage {
Usage::Search => "search", Usage::Search => js_string!("search"),
Usage::Sort => "sort", Usage::Sort => js_string!("sort"),
}, },
context, context,
) )
@ -526,25 +535,25 @@ impl Collator {
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("sensitivity"), utf16!("sensitivity"),
match collator.sensitivity { match collator.sensitivity {
Sensitivity::Base => "base", Sensitivity::Base => js_string!("base"),
Sensitivity::Accent => "accent", Sensitivity::Accent => js_string!("accent"),
Sensitivity::Case => "case", Sensitivity::Case => js_string!("case"),
Sensitivity::Variant => "variant", Sensitivity::Variant => js_string!("variant"),
}, },
context, context,
) )
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("ignorePunctuation"), js_string!("ignorePunctuation"),
collator.ignore_punctuation, collator.ignore_punctuation,
context, context,
) )
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("collation"), js_string!("collation"),
collator.collation.to_string(), js_string!(collator.collation.to_string()),
context, context,
) )
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
@ -554,11 +563,11 @@ impl Collator {
if let Some(kf) = collator.case_first { if let Some(kf) = collator.case_first {
options options
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("caseFirst"), js_string!("caseFirst"),
match kf { match kf {
CaseFirst::Off => "false", CaseFirst::Off => js_string!("false"),
CaseFirst::LowerFirst => "lower", CaseFirst::LowerFirst => js_string!("lower"),
CaseFirst::UpperFirst => "upper", CaseFirst::UpperFirst => js_string!("upper"),
_ => unreachable!(), _ => unreachable!(),
}, },
context, context,

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

@ -16,7 +16,7 @@ use crate::{
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
@ -64,7 +64,7 @@ pub struct DateTimeFormat {
impl IntrinsicObject for DateTimeFormat { impl IntrinsicObject for DateTimeFormat {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm).build(); BuiltInBuilder::from_standard_constructor::<Self>(realm).build();
} }
@ -75,7 +75,7 @@ impl IntrinsicObject for DateTimeFormat {
} }
impl BuiltInObject for DateTimeFormat { impl BuiltInObject for DateTimeFormat {
const NAME: &'static str = "DateTimeFormat"; const NAME: JsString = StaticJsStrings::DATE_TIME_FORMAT;
} }
impl BuiltInConstructor for DateTimeFormat { impl BuiltInConstructor for DateTimeFormat {
@ -272,7 +272,7 @@ pub(crate) fn to_date_time_options(
// a. For each property name prop of « "year", "month", "day" », do // a. For each property name prop of « "year", "month", "day" », do
for property in [utf16!("year"), utf16!("month"), utf16!("day")] { for property in [utf16!("year"), utf16!("month"), utf16!("day")] {
// i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
options.create_data_property_or_throw(property, "numeric", context)?; options.create_data_property_or_throw(property, js_string!("numeric"), context)?;
} }
} }
@ -281,7 +281,7 @@ pub(crate) fn to_date_time_options(
// a. For each property name prop of « "hour", "minute", "second" », do // a. For each property name prop of « "hour", "minute", "second" », do
for property in [utf16!("hour"), utf16!("minute"), utf16!("second")] { for property in [utf16!("hour"), utf16!("minute"), utf16!("second")] {
// i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
options.create_data_property_or_throw(property, "numeric", context)?; options.create_data_property_or_throw(property, js_string!("numeric"), context)?;
} }
} }

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

@ -11,10 +11,11 @@ use crate::{
Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
@ -44,18 +45,22 @@ impl Service for ListFormat {
impl IntrinsicObject for ListFormat { impl IntrinsicObject for ListFormat {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .static_method(
Self::supported_locales_of,
js_string!("supportedLocalesOf"),
1,
)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Intl.ListFormat", js_string!("Intl.ListFormat"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::format, "format", 1) .method(Self::format, js_string!("format"), 1)
.method(Self::format_to_parts, "formatToParts", 1) .method(Self::format_to_parts, js_string!("formatToParts"), 1)
.method(Self::resolved_options, "resolvedOptions", 0) .method(Self::resolved_options, js_string!("resolvedOptions"), 0)
.build(); .build();
} }
@ -65,7 +70,7 @@ impl IntrinsicObject for ListFormat {
} }
impl BuiltInObject for ListFormat { impl BuiltInObject for ListFormat {
const NAME: &'static str = "ListFormat"; const NAME: JsString = StaticJsStrings::LIST_FORMAT;
} }
impl BuiltInConstructor for ListFormat { impl BuiltInConstructor for ListFormat {
@ -229,11 +234,12 @@ impl ListFormat {
// TODO: support for UTF-16 unpaired surrogates formatting // TODO: support for UTF-16 unpaired surrogates formatting
let strings = string_list_from_iterable(args.get_or_undefined(0), context)?; let strings = string_list_from_iterable(args.get_or_undefined(0), context)?;
// 4. Return ! FormatList(lf, stringList). let formatted = lf
Ok(lf
.native .native
.format_to_string(strings.into_iter().map(|s| s.to_std_string_escaped())) .format_to_string(strings.into_iter().map(|s| s.to_std_string_escaped()));
.into())
// 4. Return ! FormatList(lf, stringList).
Ok(js_string!(formatted).into())
} }
/// [`Intl.ListFormat.prototype.formatToParts ( list )`][spec]. /// [`Intl.ListFormat.prototype.formatToParts ( list )`][spec].
@ -375,11 +381,11 @@ impl ListFormat {
.create(ObjectData::ordinary(), vec![]); .create(ObjectData::ordinary(), vec![]);
// b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
o.create_data_property_or_throw(utf16!("type"), part.typ(), context) o.create_data_property_or_throw(utf16!("type"), js_string!(part.typ()), context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
// c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
o.create_data_property_or_throw(utf16!("value"), part.value(), context) o.create_data_property_or_throw(utf16!("value"), js_string!(part.value()), context)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
// d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O). // d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O).
@ -433,26 +439,30 @@ impl ListFormat {
// c. Assert: v is not undefined. // c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v). // d. Perform ! CreateDataPropertyOrThrow(options, p, v).
options options
.create_data_property_or_throw(utf16!("locale"), lf.locale.to_string(), context) .create_data_property_or_throw(
utf16!("locale"),
js_string!(lf.locale.to_string()),
context,
)
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("type"), js_string!("type"),
match lf.typ { match lf.typ {
ListFormatType::Conjunction => "conjunction", ListFormatType::Conjunction => js_string!("conjunction"),
ListFormatType::Disjunction => "disjunction", ListFormatType::Disjunction => js_string!("disjunction"),
ListFormatType::Unit => "unit", ListFormatType::Unit => js_string!("unit"),
}, },
context, context,
) )
.expect("operation must not fail per the spec"); .expect("operation must not fail per the spec");
options options
.create_data_property_or_throw( .create_data_property_or_throw(
utf16!("style"), js_string!("style"),
match lf.style { match lf.style {
ListLength::Wide => "long", ListLength::Wide => js_string!("long"),
ListLength::Short => "short", ListLength::Short => js_string!("short"),
ListLength::Narrow => "narrow", ListLength::Narrow => js_string!("narrow"),
_ => unreachable!(), _ => unreachable!(),
}, },
context, context,

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

@ -1,4 +1,8 @@
use crate::{builtins::options::get_option, realm::Realm, string::utf16}; use crate::{
builtins::options::get_option,
realm::Realm,
string::{common::StaticJsStrings, utf16},
};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use icu_collator::CaseFirst; use icu_collator::CaseFirst;
use icu_datetime::options::preferences::HourCycle; use icu_datetime::options::preferences::HourCycle;
@ -33,113 +37,113 @@ pub(crate) struct Locale;
impl IntrinsicObject for Locale { impl IntrinsicObject for Locale {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let base_name = BuiltInBuilder::callable(realm, Self::base_name) let base_name = BuiltInBuilder::callable(realm, Self::base_name)
.name("get baseName") .name(js_string!("get baseName"))
.build(); .build();
let calendar = BuiltInBuilder::callable(realm, Self::calendar) let calendar = BuiltInBuilder::callable(realm, Self::calendar)
.name("get calendar") .name(js_string!("get calendar"))
.build(); .build();
let case_first = BuiltInBuilder::callable(realm, Self::case_first) let case_first = BuiltInBuilder::callable(realm, Self::case_first)
.name("get caseFirst") .name(js_string!("get caseFirst"))
.build(); .build();
let collation = BuiltInBuilder::callable(realm, Self::collation) let collation = BuiltInBuilder::callable(realm, Self::collation)
.name("get collation") .name(js_string!("get collation"))
.build(); .build();
let hour_cycle = BuiltInBuilder::callable(realm, Self::hour_cycle) let hour_cycle = BuiltInBuilder::callable(realm, Self::hour_cycle)
.name("get hourCycle") .name(js_string!("get hourCycle"))
.build(); .build();
let numeric = BuiltInBuilder::callable(realm, Self::numeric) let numeric = BuiltInBuilder::callable(realm, Self::numeric)
.name("get numeric") .name(js_string!("get numeric"))
.build(); .build();
let numbering_system = BuiltInBuilder::callable(realm, Self::numbering_system) let numbering_system = BuiltInBuilder::callable(realm, Self::numbering_system)
.name("get numberingSystem") .name(js_string!("get numberingSystem"))
.build(); .build();
let language = BuiltInBuilder::callable(realm, Self::language) let language = BuiltInBuilder::callable(realm, Self::language)
.name("get language") .name(js_string!("get language"))
.build(); .build();
let script = BuiltInBuilder::callable(realm, Self::script) let script = BuiltInBuilder::callable(realm, Self::script)
.name("get script") .name(js_string!("get script"))
.build(); .build();
let region = BuiltInBuilder::callable(realm, Self::region) let region = BuiltInBuilder::callable(realm, Self::region)
.name("get region") .name(js_string!("get region"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Intl.Locale", js_string!("Intl.Locale"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::maximize, "maximize", 0) .method(Self::maximize, js_string!("maximize"), 0)
.method(Self::minimize, "minimize", 0) .method(Self::minimize, js_string!("minimize"), 0)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.accessor( .accessor(
utf16!("baseName"), js_string!("baseName"),
Some(base_name), Some(base_name),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("calendar"), js_string!("calendar"),
Some(calendar), Some(calendar),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("caseFirst"), js_string!("caseFirst"),
Some(case_first), Some(case_first),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("collation"), js_string!("collation"),
Some(collation), Some(collation),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("hourCycle"), js_string!("hourCycle"),
Some(hour_cycle), Some(hour_cycle),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("numeric"), js_string!("numeric"),
Some(numeric), Some(numeric),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("numberingSystem"), js_string!("numberingSystem"),
Some(numbering_system), Some(numbering_system),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("language"), js_string!("language"),
Some(language), Some(language),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("script"), js_string!("script"),
Some(script), Some(script),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.accessor( .accessor(
utf16!("region"), js_string!("region"),
Some(region), Some(region),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
@ -153,7 +157,7 @@ impl IntrinsicObject for Locale {
} }
impl BuiltInObject for Locale { impl BuiltInObject for Locale {
const NAME: &'static str = "Locale"; const NAME: JsString = StaticJsStrings::LOCALE;
} }
impl BuiltInConstructor for Locale { impl BuiltInConstructor for Locale {

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

@ -8,6 +8,7 @@ use crate::{
Array, Array,
}, },
context::{icu::Icu, BoaProvider}, context::{icu::Icu, BoaProvider},
js_string,
object::JsObject, object::JsObject,
string::utf16, string::utf16,
Context, JsNativeError, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
@ -563,7 +564,9 @@ where
// 5. Return CreateArrayFromList(supportedLocales). // 5. Return CreateArrayFromList(supportedLocales).
Ok(Array::create_array_from_list( Ok(Array::create_array_from_list(
elements.into_iter().map(|loc| loc.to_string().into()), elements
.into_iter()
.map(|loc| js_string!(loc.to_string()).into()),
context, context,
)) ))
} }

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

@ -16,11 +16,13 @@
use crate::{ use crate::{
builtins::{Array, BuiltInBuilder, BuiltInObject, IntrinsicObject}, builtins::{Array, BuiltInBuilder, BuiltInObject, IntrinsicObject},
context::{intrinsics::Intrinsics, BoaProvider}, context::{intrinsics::Intrinsics, BoaProvider},
js_string,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -47,7 +49,7 @@ pub(crate) struct Intl;
impl IntrinsicObject for Intl { impl IntrinsicObject for Intl {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_property( .static_property(
@ -97,7 +99,11 @@ impl IntrinsicObject for Intl {
.constructor(), .constructor(),
DateTimeFormat::ATTRIBUTE, DateTimeFormat::ATTRIBUTE,
) )
.static_method(Self::get_canonical_locales, "getCanonicalLocales", 1) .static_method(
Self::get_canonical_locales,
js_string!("getCanonicalLocales"),
1,
)
.build(); .build();
} }
@ -107,7 +113,7 @@ impl IntrinsicObject for Intl {
} }
impl BuiltInObject for Intl { impl BuiltInObject for Intl {
const NAME: &'static str = "Intl"; const NAME: JsString = StaticJsStrings::INTL;
} }
impl Intl { impl Intl {
@ -133,7 +139,7 @@ impl Intl {
// 2. Return CreateArrayFromList(ll). // 2. Return CreateArrayFromList(ll).
Ok(JsValue::Object(Array::create_array_from_list( Ok(JsValue::Object(Array::create_array_from_list(
ll.into_iter().map(|loc| loc.to_string().into()), ll.into_iter().map(|loc| js_string!(loc.to_string()).into()),
context, context,
))) )))
} }

33
boa_engine/src/builtins/intl/plural_rules/mod.rs

@ -19,6 +19,7 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, ObjectData, ObjectInitializer}, object::{internal_methods::get_prototype_from_constructor, ObjectData, ObjectInitializer},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
}; };
@ -48,17 +49,21 @@ impl Service for PluralRules {
impl IntrinsicObject for PluralRules { impl IntrinsicObject for PluralRules {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .static_method(
Self::supported_locales_of,
js_string!("supportedLocalesOf"),
1,
)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Intl.PluralRules", js_string!("Intl.PluralRules"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::resolved_options, "resolvedOptions", 0) .method(Self::resolved_options, js_string!("resolvedOptions"), 0)
.method(Self::select, "select", 1) .method(Self::select, js_string!("select"), 1)
.build(); .build();
} }
@ -68,7 +73,7 @@ impl IntrinsicObject for PluralRules {
} }
impl BuiltInObject for PluralRules { impl BuiltInObject for PluralRules {
const NAME: &'static str = "PluralRules"; const NAME: JsString = StaticJsStrings::PLURAL_RULES;
} }
impl BuiltInConstructor for PluralRules { impl BuiltInConstructor for PluralRules {
@ -263,15 +268,15 @@ impl PluralRules {
options options
.property( .property(
js_string!("locale"), js_string!("locale"),
plural_rules.locale.to_string(), js_string!(plural_rules.locale.to_string()),
Attribute::all(), Attribute::all(),
) )
.property( .property(
js_string!("type"), js_string!("type"),
match plural_rules.rule_type { match plural_rules.rule_type {
PluralRuleType::Cardinal => "cardinal", PluralRuleType::Cardinal => js_string!("cardinal"),
PluralRuleType::Ordinal => "ordinal", PluralRuleType::Ordinal => js_string!("ordinal"),
_ => "unknown", _ => js_string!("unknown"),
}, },
Attribute::all(), Attribute::all(),
) )
@ -318,7 +323,7 @@ impl PluralRules {
options options
.property( .property(
js_string!("roundingMode"), js_string!("roundingMode"),
plural_rules.format_options.rounding_mode.to_string(), js_string!(plural_rules.format_options.rounding_mode.to_string()),
Attribute::all(), Attribute::all(),
) )
.property( .property(
@ -328,10 +333,10 @@ impl PluralRules {
) )
.property( .property(
js_string!("trailingZeroDisplay"), js_string!("trailingZeroDisplay"),
plural_rules js_string!(plural_rules
.format_options .format_options
.trailing_zero_display .trailing_zero_display
.to_string(), .to_string()),
Attribute::all(), Attribute::all(),
); );
@ -360,7 +365,7 @@ impl PluralRules {
// a. Perform ! CreateDataPropertyOrThrow(options, "roundingPriority", "auto"). // a. Perform ! CreateDataPropertyOrThrow(options, "roundingPriority", "auto").
options.property( options.property(
js_string!("roundingPriority"), js_string!("roundingPriority"),
plural_rules.format_options.rounding_priority.to_string(), js_string!(plural_rules.format_options.rounding_priority.to_string()),
Attribute::all(), Attribute::all(),
); );

21
boa_engine/src/builtins/intl/segmenter/mod.rs

@ -19,6 +19,7 @@ use crate::{
}, },
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
Context, JsArgs, JsNativeError, JsResult, JsString, JsSymbol, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsSymbol, JsValue,
}; };
@ -77,17 +78,21 @@ impl Service for Segmenter {
impl IntrinsicObject for Segmenter { impl IntrinsicObject for Segmenter {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::supported_locales_of, "supportedLocalesOf", 1) .static_method(
Self::supported_locales_of,
js_string!("supportedLocalesOf"),
1,
)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Intl.Segmenter", js_string!("Intl.Segmenter"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::resolved_options, "resolvedOptions", 0) .method(Self::resolved_options, js_string!("resolvedOptions"), 0)
.method(Self::segment, "segment", 1) .method(Self::segment, js_string!("segment"), 1)
.build(); .build();
} }
@ -97,7 +102,7 @@ impl IntrinsicObject for Segmenter {
} }
impl BuiltInObject for Segmenter { impl BuiltInObject for Segmenter {
const NAME: &'static str = "Segmenter"; const NAME: JsString = StaticJsStrings::SEGMENTER;
} }
impl BuiltInConstructor for Segmenter { impl BuiltInConstructor for Segmenter {
@ -247,12 +252,12 @@ impl Segmenter {
let options = ObjectInitializer::new(context) let options = ObjectInitializer::new(context)
.property( .property(
js_string!("locale"), js_string!("locale"),
segmenter.locale.to_string(), js_string!(segmenter.locale.to_string()),
Attribute::all(), Attribute::all(),
) )
.property( .property(
js_string!("granularity"), js_string!("granularity"),
segmenter.native.granularity().to_string(), js_string!(segmenter.native.granularity().to_string()),
Attribute::all(), Attribute::all(),
) )
.build(); .build();

2
boa_engine/src/builtins/intl/segmenter/segments.rs

@ -24,7 +24,7 @@ impl IntrinsicObject for Segments {
let _timer = Profiler::global().start_event("%SegmentsPrototype%", "init"); let _timer = Profiler::global().start_event("%SegmentsPrototype%", "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method(Self::containing, "containing", 1) .static_method(Self::containing, js_string!("containing"), 1)
.static_method( .static_method(
Self::iterator, Self::iterator,
(JsSymbol::iterator(), js_string!("[Symbol.iterator]")), (JsSymbol::iterator(), js_string!("[Symbol.iterator]")),

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

@ -5,6 +5,7 @@ use crate::{
BuiltInBuilder, IntrinsicObject, Promise, BuiltInBuilder, IntrinsicObject, Promise,
}, },
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject, ObjectData}, object::{FunctionObjectBuilder, JsObject, ObjectData},
realm::Realm, realm::Realm,
@ -28,7 +29,7 @@ pub struct AsyncFromSyncIterator {
impl IntrinsicObject for AsyncFromSyncIterator { impl IntrinsicObject for AsyncFromSyncIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("AsyncFromSyncIteratorPrototype", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -38,9 +39,9 @@ impl IntrinsicObject for AsyncFromSyncIterator {
.iterator_prototypes() .iterator_prototypes()
.async_iterator(), .async_iterator(),
) )
.static_method(Self::next, "next", 1) .static_method(Self::next, js_string!("next"), 1)
.static_method(Self::r#return, "return", 1) .static_method(Self::r#return, js_string!("return"), 1)
.static_method(Self::throw, "throw", 1) .static_method(Self::throw, js_string!("throw"), 1)
.build(); .build();
} }

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

@ -26,7 +26,7 @@ use crate::{
object::JsObject, object::JsObject,
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::{utf16, CodePoint}, string::{common::StaticJsStrings, utf16, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
vm::CallFrame, vm::CallFrame,
@ -48,14 +48,14 @@ pub(crate) struct Json;
impl IntrinsicObject for Json { impl IntrinsicObject for Json {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let to_string_tag = JsSymbol::to_string_tag(); let to_string_tag = JsSymbol::to_string_tag();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method(Self::parse, "parse", 2) .static_method(Self::parse, js_string!("parse"), 2)
.static_method(Self::stringify, "stringify", 3) .static_method(Self::stringify, js_string!("stringify"), 3)
.static_property(to_string_tag, Self::NAME, attribute) .static_property(to_string_tag, Self::NAME, attribute)
.build(); .build();
} }
@ -66,7 +66,7 @@ impl IntrinsicObject for Json {
} }
impl BuiltInObject for Json { impl BuiltInObject for Json {
const NAME: &'static str = "JSON"; const NAME: JsString = StaticJsStrings::JSON;
} }
impl Json { impl Json {

66
boa_engine/src/builtins/json/tests.rs

@ -1,12 +1,15 @@
use indoc::indoc; use indoc::indoc;
use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction};
#[test] #[test]
fn json_sanity() { fn json_sanity() {
run_test_actions([ run_test_actions([
TestAction::assert_eq(r#"JSON.parse('{"aaa":"bbb"}').aaa"#, "bbb"), TestAction::assert_eq(r#"JSON.parse('{"aaa":"bbb"}').aaa"#, js_string!("bbb")),
TestAction::assert_eq(r#"JSON.stringify({aaa: 'bbb'})"#, r#"{"aaa":"bbb"}"#), TestAction::assert_eq(
r#"JSON.stringify({aaa: 'bbb'})"#,
js_string!(r#"{"aaa":"bbb"}"#),
),
]); ]);
} }
@ -14,7 +17,7 @@ fn json_sanity() {
fn json_stringify_remove_undefined_values_from_objects() { fn json_stringify_remove_undefined_values_from_objects() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({ aaa: undefined, bbb: 'ccc' })"#, r#"JSON.stringify({ aaa: undefined, bbb: 'ccc' })"#,
r#"{"bbb":"ccc"}"#, js_string!(r#"{"bbb":"ccc"}"#),
)]); )]);
} }
@ -22,7 +25,7 @@ fn json_stringify_remove_undefined_values_from_objects() {
fn json_stringify_remove_function_values_from_objects() { fn json_stringify_remove_function_values_from_objects() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({ aaa: () => {}, bbb: 'ccc' })"#, r#"JSON.stringify({ aaa: () => {}, bbb: 'ccc' })"#,
r#"{"bbb":"ccc"}"#, js_string!(r#"{"bbb":"ccc"}"#),
)]); )]);
} }
@ -30,7 +33,7 @@ fn json_stringify_remove_function_values_from_objects() {
fn json_stringify_remove_symbols_from_objects() { fn json_stringify_remove_symbols_from_objects() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({ aaa: Symbol(), bbb: 'ccc' })"#, r#"JSON.stringify({ aaa: Symbol(), bbb: 'ccc' })"#,
r#"{"bbb":"ccc"}"#, js_string!(r#"{"bbb":"ccc"}"#),
)]); )]);
} }
@ -38,7 +41,7 @@ fn json_stringify_remove_symbols_from_objects() {
fn json_stringify_replacer_array_strings() { fn json_stringify_replacer_array_strings() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({aaa: 'bbb', bbb: 'ccc', ccc: 'ddd'}, ['aaa', 'bbb'])"#, r#"JSON.stringify({aaa: 'bbb', bbb: 'ccc', ccc: 'ddd'}, ['aaa', 'bbb'])"#,
r#"{"aaa":"bbb","bbb":"ccc"}"#, js_string!(r#"{"aaa":"bbb","bbb":"ccc"}"#),
)]); )]);
} }
@ -46,7 +49,7 @@ fn json_stringify_replacer_array_strings() {
fn json_stringify_replacer_array_numbers() { fn json_stringify_replacer_array_numbers() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({ 0: 'aaa', 1: 'bbb', 2: 'ccc'}, [1, 2])"#, r#"JSON.stringify({ 0: 'aaa', 1: 'bbb', 2: 'ccc'}, [1, 2])"#,
r#"{"1":"bbb","2":"ccc"}"#, js_string!(r#"{"1":"bbb","2":"ccc"}"#),
)]); )]);
} }
@ -62,7 +65,7 @@ fn json_stringify_replacer_function() {
return value; return value;
}) })
"#}, "#},
r#"{"bbb":2}"#, js_string!(r#"{"bbb":2}"#),
)]); )]);
} }
@ -70,7 +73,7 @@ fn json_stringify_replacer_function() {
fn json_stringify_arrays() { fn json_stringify_arrays() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"JSON.stringify(['a', 'b'])", "JSON.stringify(['a', 'b'])",
r#"["a","b"]"#, js_string!(r#"["a","b"]"#),
)]); )]);
} }
@ -78,7 +81,7 @@ fn json_stringify_arrays() {
fn json_stringify_object_array() { fn json_stringify_object_array() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"JSON.stringify([{a: 'b'}, {b: 'c'}])", "JSON.stringify([{a: 'b'}, {b: 'c'}])",
r#"[{"a":"b"},{"b":"c"}]"#, js_string!(r#"[{"a":"b"},{"b":"c"}]"#),
)]); )]);
} }
@ -86,7 +89,7 @@ fn json_stringify_object_array() {
fn json_stringify_array_converts_undefined_to_null() { fn json_stringify_array_converts_undefined_to_null() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"JSON.stringify([undefined])", "JSON.stringify([undefined])",
"[null]", js_string!("[null]"),
)]); )]);
} }
@ -94,7 +97,7 @@ fn json_stringify_array_converts_undefined_to_null() {
fn json_stringify_array_converts_function_to_null() { fn json_stringify_array_converts_function_to_null() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"JSON.stringify([() => {}])", "JSON.stringify([() => {}])",
"[null]", js_string!("[null]"),
)]); )]);
} }
@ -102,7 +105,7 @@ fn json_stringify_array_converts_function_to_null() {
fn json_stringify_array_converts_symbol_to_null() { fn json_stringify_array_converts_symbol_to_null() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"JSON.stringify([Symbol()])", "JSON.stringify([Symbol()])",
"[null]", js_string!("[null]"),
)]); )]);
} }
#[test] #[test]
@ -147,19 +150,22 @@ fn json_stringify_no_args() {
#[test] #[test]
fn json_stringify_fractional_numbers() { fn json_stringify_fractional_numbers() {
run_test_actions([TestAction::assert_eq("JSON.stringify(1.2)", "1.2")]); run_test_actions([TestAction::assert_eq(
"JSON.stringify(1.2)",
js_string!("1.2"),
)]);
} }
#[test] #[test]
fn json_stringify_pretty_print() { fn json_stringify_pretty_print() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, 4)"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, 4)"#,
indoc! {r#" js_string!(indoc! {r#"
{ {
"a": "b", "a": "b",
"b": "c" "b": "c"
}"# }"#
}, }),
)]); )]);
} }
@ -167,12 +173,12 @@ fn json_stringify_pretty_print() {
fn json_stringify_pretty_print_four_spaces() { fn json_stringify_pretty_print_four_spaces() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, 4.3)"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, 4.3)"#,
indoc! {r#" js_string!(indoc! {r#"
{ {
"a": "b", "a": "b",
"b": "c" "b": "c"
}"# }"#
}, }),
)]); )]);
} }
@ -180,12 +186,12 @@ fn json_stringify_pretty_print_four_spaces() {
fn json_stringify_pretty_print_twenty_spaces() { fn json_stringify_pretty_print_twenty_spaces() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, 20)"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, 20)"#,
indoc! {r#" js_string!(indoc! {r#"
{ {
"a": "b", "a": "b",
"b": "c" "b": "c"
}"# }"#
}, }),
)]); )]);
} }
@ -193,12 +199,12 @@ fn json_stringify_pretty_print_twenty_spaces() {
fn json_stringify_pretty_print_with_number_object() { fn json_stringify_pretty_print_with_number_object() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, new Number(10))"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, new Number(10))"#,
indoc! {r#" js_string!(indoc! {r#"
{ {
"a": "b", "a": "b",
"b": "c" "b": "c"
}"# }"#
}, }),
)]); )]);
} }
@ -206,7 +212,7 @@ fn json_stringify_pretty_print_with_number_object() {
fn json_stringify_pretty_print_bad_space_argument() { fn json_stringify_pretty_print_bad_space_argument() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, [])"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, [])"#,
r#"{"a":"b","b":"c"}"#, js_string!(r#"{"a":"b","b":"c"}"#),
)]); )]);
} }
@ -214,12 +220,12 @@ fn json_stringify_pretty_print_bad_space_argument() {
fn json_stringify_pretty_print_with_too_long_string() { fn json_stringify_pretty_print_with_too_long_string() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, "abcdefghijklmn")"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, "abcdefghijklmn")"#,
indoc! {r#" js_string!(indoc! {r#"
{ {
abcdefghij"a": "b", abcdefghij"a": "b",
abcdefghij"b": "c" abcdefghij"b": "c"
}"# }"#
}, }),
)]); )]);
} }
@ -227,12 +233,12 @@ fn json_stringify_pretty_print_with_too_long_string() {
fn json_stringify_pretty_print_with_string_object() { fn json_stringify_pretty_print_with_string_object() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
r#"JSON.stringify({a: "b", b: "c"}, undefined, new String("abcd"))"#, r#"JSON.stringify({a: "b", b: "c"}, undefined, new String("abcd"))"#,
indoc! {r#" js_string!(indoc! {r#"
{ {
abcd"a": "b", abcd"a": "b",
abcd"b": "c" abcd"b": "c"
}"# }"#
}, }),
)]); )]);
} }
@ -272,8 +278,8 @@ fn json_parse_object_with_reviver() {
var jsonObj = JSON.parse(jsonString, dataReviver); var jsonObj = JSON.parse(jsonString, dataReviver);
"#}), "#}),
TestAction::assert_eq("jsonObj.firstname", "boa"), TestAction::assert_eq("jsonObj.firstname", js_string!("boa")),
TestAction::assert_eq("jsonObj.lastname", "interpreter"), TestAction::assert_eq("jsonObj.lastname", js_string!("interpreter")),
]); ]);
} }

7
boa_engine/src/builtins/map/map_iterator.rs

@ -12,6 +12,7 @@ use crate::{
}, },
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
@ -38,7 +39,7 @@ pub struct MapIterator {
impl IntrinsicObject for MapIterator { impl IntrinsicObject for MapIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("MapIterator", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -48,10 +49,10 @@ impl IntrinsicObject for MapIterator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 0) .static_method(Self::next, js_string!("next"), 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Map Iterator", js_string!("Map Iterator"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.build(); .build();

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

@ -14,12 +14,13 @@ use crate::{
builtins::BuiltInObject, builtins::BuiltInObject,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
use num_traits::Zero; use num_traits::Zero;
@ -39,18 +40,18 @@ pub(crate) struct Map;
impl IntrinsicObject for Map { impl IntrinsicObject for Map {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let get_size = BuiltInBuilder::callable(realm, Self::get_size) let get_size = BuiltInBuilder::callable(realm, Self::get_size)
.name("get size") .name(js_string!("get size"))
.build(); .build();
let entries_function = BuiltInBuilder::callable(realm, Self::entries) let entries_function = BuiltInBuilder::callable(realm, Self::entries)
.name("entries") .name(js_string!("entries"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
@ -75,16 +76,16 @@ impl IntrinsicObject for Map {
Self::NAME, Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::clear, "clear", 0) .method(Self::clear, js_string!("clear"), 0)
.method(Self::delete, "delete", 1) .method(Self::delete, js_string!("delete"), 1)
.method(Self::for_each, "forEach", 1) .method(Self::for_each, js_string!("forEach"), 1)
.method(Self::get, "get", 1) .method(Self::get, js_string!("get"), 1)
.method(Self::has, "has", 1) .method(Self::has, js_string!("has"), 1)
.method(Self::keys, "keys", 0) .method(Self::keys, js_string!("keys"), 0)
.method(Self::set, "set", 2) .method(Self::set, js_string!("set"), 2)
.method(Self::values, "values", 0) .method(Self::values, js_string!("values"), 0)
.accessor( .accessor(
utf16!("size"), js_string!("size"),
Some(get_size), Some(get_size),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
@ -98,7 +99,7 @@ impl IntrinsicObject for Map {
} }
impl BuiltInObject for Map { impl BuiltInObject for Map {
const NAME: &'static str = "Map"; const NAME: JsString = StaticJsStrings::MAP;
} }
impl BuiltInConstructor for Map { impl BuiltInConstructor for Map {

18
boa_engine/src/builtins/map/tests.rs

@ -1,4 +1,4 @@
use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -74,7 +74,7 @@ fn merge() {
let merged2 = new Map([...second, ...third]); let merged2 = new Map([...second, ...third]);
"#}), "#}),
TestAction::assert_eq("merged1.size", 3), TestAction::assert_eq("merged1.size", 3),
TestAction::assert_eq("merged1.get('2')", "second two"), TestAction::assert_eq("merged1.get('2')", js_string!("second two")),
TestAction::assert_eq("merged2.size", 4), TestAction::assert_eq("merged2.size", 4),
]); ]);
} }
@ -85,8 +85,8 @@ fn get() {
TestAction::run(indoc! {r#" TestAction::run(indoc! {r#"
let map = new Map([["1", "one"], ["2", "two"]]); let map = new Map([["1", "one"], ["2", "two"]]);
"#}), "#}),
TestAction::assert_eq("map.get('1')", "one"), TestAction::assert_eq("map.get('1')", js_string!("one")),
TestAction::assert_eq("map.get('2')", "two"), TestAction::assert_eq("map.get('2')", js_string!("two")),
TestAction::assert_eq("map.get('3')", JsValue::undefined()), TestAction::assert_eq("map.get('3')", JsValue::undefined()),
TestAction::assert_eq("map.get()", JsValue::undefined()), TestAction::assert_eq("map.get()", JsValue::undefined()),
]); ]);
@ -98,7 +98,7 @@ fn set() {
TestAction::run("let map = new Map();"), TestAction::run("let map = new Map();"),
TestAction::assert("map.set(); map.has(undefined)"), TestAction::assert("map.set(); map.has(undefined)"),
TestAction::assert_eq("map.get()", JsValue::undefined()), TestAction::assert_eq("map.get()", JsValue::undefined()),
TestAction::assert_eq("map.set('1', 'one'); map.get('1')", "one"), TestAction::assert_eq("map.set('1', 'one'); map.get('1')", js_string!("one")),
TestAction::assert("map.set('2'); map.has('2')"), TestAction::assert("map.set('2'); map.has('2')"),
TestAction::assert_eq("map.get('2')", JsValue::undefined()), TestAction::assert_eq("map.get('2')", JsValue::undefined()),
]); ]);
@ -148,7 +148,7 @@ fn keys() {
let item2 = keysIterator.next(); let item2 = keysIterator.next();
let item3 = keysIterator.next(); let item3 = keysIterator.next();
"#}), "#}),
TestAction::assert_eq("item1.value", "0"), TestAction::assert_eq("item1.value", js_string!("0")),
TestAction::assert_eq("item2.value", 1), TestAction::assert_eq("item2.value", 1),
TestAction::assert("item3.done"), TestAction::assert("item3.done"),
]); ]);
@ -187,8 +187,8 @@ fn values() {
let item2 = valuesIterator.next(); let item2 = valuesIterator.next();
let item3 = valuesIterator.next(); let item3 = valuesIterator.next();
"#}), "#}),
TestAction::assert_eq("item1.value", "foo"), TestAction::assert_eq("item1.value", js_string!("foo")),
TestAction::assert_eq("item2.value", "bar"), TestAction::assert_eq("item2.value", js_string!("bar")),
TestAction::assert("item3.done"), TestAction::assert("item3.done"),
]); ]);
} }
@ -201,7 +201,7 @@ fn modify_key() {
let map = new Map([[obj, "one"]]); let map = new Map([[obj, "one"]]);
obj.field = "Value"; obj.field = "Value";
"#}), "#}),
TestAction::assert_eq("map.get(obj)", "one"), TestAction::assert_eq("map.get(obj)", js_string!("one")),
]); ]);
} }

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

@ -12,9 +12,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{ use crate::{
builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject, builtins::BuiltInObject, context::intrinsics::Intrinsics, js_string, object::JsObject,
property::Attribute, realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, property::Attribute, realm::Realm, string::common::StaticJsStrings, symbol::JsSymbol, Context,
JsValue, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -29,57 +29,57 @@ pub(crate) struct Math;
impl IntrinsicObject for Math { impl IntrinsicObject for Math {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_property(utf16!("E"), std::f64::consts::E, attribute) .static_property(js_string!("E"), std::f64::consts::E, attribute)
.static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute) .static_property(js_string!("LN10"), std::f64::consts::LN_10, attribute)
.static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute) .static_property(js_string!("LN2"), std::f64::consts::LN_2, attribute)
.static_property(utf16!("LOG10E"), std::f64::consts::LOG10_E, attribute) .static_property(js_string!("LOG10E"), std::f64::consts::LOG10_E, attribute)
.static_property(utf16!("LOG2E"), std::f64::consts::LOG2_E, attribute) .static_property(js_string!("LOG2E"), std::f64::consts::LOG2_E, attribute)
.static_property(utf16!("PI"), std::f64::consts::PI, attribute) .static_property(js_string!("PI"), std::f64::consts::PI, attribute)
.static_property( .static_property(
utf16!("SQRT1_2"), js_string!("SQRT1_2"),
std::f64::consts::FRAC_1_SQRT_2, std::f64::consts::FRAC_1_SQRT_2,
attribute, attribute,
) )
.static_property(utf16!("SQRT2"), std::f64::consts::SQRT_2, attribute) .static_property(js_string!("SQRT2"), std::f64::consts::SQRT_2, attribute)
.static_method(Self::abs, "abs", 1) .static_method(Self::abs, js_string!("abs"), 1)
.static_method(Self::acos, "acos", 1) .static_method(Self::acos, js_string!("acos"), 1)
.static_method(Self::acosh, "acosh", 1) .static_method(Self::acosh, js_string!("acosh"), 1)
.static_method(Self::asin, "asin", 1) .static_method(Self::asin, js_string!("asin"), 1)
.static_method(Self::asinh, "asinh", 1) .static_method(Self::asinh, js_string!("asinh"), 1)
.static_method(Self::atan, "atan", 1) .static_method(Self::atan, js_string!("atan"), 1)
.static_method(Self::atanh, "atanh", 1) .static_method(Self::atanh, js_string!("atanh"), 1)
.static_method(Self::atan2, "atan2", 2) .static_method(Self::atan2, js_string!("atan2"), 2)
.static_method(Self::cbrt, "cbrt", 1) .static_method(Self::cbrt, js_string!("cbrt"), 1)
.static_method(Self::ceil, "ceil", 1) .static_method(Self::ceil, js_string!("ceil"), 1)
.static_method(Self::clz32, "clz32", 1) .static_method(Self::clz32, js_string!("clz32"), 1)
.static_method(Self::cos, "cos", 1) .static_method(Self::cos, js_string!("cos"), 1)
.static_method(Self::cosh, "cosh", 1) .static_method(Self::cosh, js_string!("cosh"), 1)
.static_method(Self::exp, "exp", 1) .static_method(Self::exp, js_string!("exp"), 1)
.static_method(Self::expm1, "expm1", 1) .static_method(Self::expm1, js_string!("expm1"), 1)
.static_method(Self::floor, "floor", 1) .static_method(Self::floor, js_string!("floor"), 1)
.static_method(Self::fround, "fround", 1) .static_method(Self::fround, js_string!("fround"), 1)
.static_method(Self::hypot, "hypot", 2) .static_method(Self::hypot, js_string!("hypot"), 2)
.static_method(Self::imul, "imul", 2) .static_method(Self::imul, js_string!("imul"), 2)
.static_method(Self::log, "log", 1) .static_method(Self::log, js_string!("log"), 1)
.static_method(Self::log1p, "log1p", 1) .static_method(Self::log1p, js_string!("log1p"), 1)
.static_method(Self::log10, "log10", 1) .static_method(Self::log10, js_string!("log10"), 1)
.static_method(Self::log2, "log2", 1) .static_method(Self::log2, js_string!("log2"), 1)
.static_method(Self::max, "max", 2) .static_method(Self::max, js_string!("max"), 2)
.static_method(Self::min, "min", 2) .static_method(Self::min, js_string!("min"), 2)
.static_method(Self::pow, "pow", 2) .static_method(Self::pow, js_string!("pow"), 2)
.static_method(Self::random, "random", 0) .static_method(Self::random, js_string!("random"), 0)
.static_method(Self::round, "round", 1) .static_method(Self::round, js_string!("round"), 1)
.static_method(Self::sign, "sign", 1) .static_method(Self::sign, js_string!("sign"), 1)
.static_method(Self::sin, "sin", 1) .static_method(Self::sin, js_string!("sin"), 1)
.static_method(Self::sinh, "sinh", 1) .static_method(Self::sinh, js_string!("sinh"), 1)
.static_method(Self::sqrt, "sqrt", 1) .static_method(Self::sqrt, js_string!("sqrt"), 1)
.static_method(Self::tan, "tan", 1) .static_method(Self::tan, js_string!("tan"), 1)
.static_method(Self::tanh, "tanh", 1) .static_method(Self::tanh, js_string!("tanh"), 1)
.static_method(Self::trunc, "trunc", 1) .static_method(Self::trunc, js_string!("trunc"), 1)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
@ -94,7 +94,7 @@ impl IntrinsicObject for Math {
} }
impl BuiltInObject for Math { impl BuiltInObject for Math {
const NAME: &'static str = "Math"; const NAME: JsString = StaticJsStrings::MATH;
} }
impl Math { impl Math {

22
boa_engine/src/builtins/mod.rs

@ -137,7 +137,7 @@ pub(crate) trait BuiltInObject: IntrinsicObject {
/// E.g. If you want access the properties of a `Complex` built-in with the name `Cplx` you must /// E.g. If you want access the properties of a `Complex` built-in with the name `Cplx` you must
/// assign `"Cplx"` to this constant, making any property inside it accessible from ECMAScript /// assign `"Cplx"` to this constant, making any property inside it accessible from ECMAScript
/// as `Cplx.prop` /// as `Cplx.prop`
const NAME: &'static str; const NAME: JsString;
/// Property attribute flags of the built-in. Check [`Attribute`] for more information. /// Property attribute flags of the built-in. Check [`Attribute`] for more information.
const ATTRIBUTE: Attribute = Attribute::WRITABLE const ATTRIBUTE: Attribute = Attribute::WRITABLE
@ -649,8 +649,8 @@ impl BuiltInConstructorWithPrototype<'_> {
/// Specify the name of the constructor function. /// Specify the name of the constructor function.
/// ///
/// Default is `""` /// Default is `""`
fn name<N: Into<JsString>>(mut self, name: N) -> Self { fn name(mut self, name: JsString) -> Self {
self.name = name.into(); self.name = name;
self self
} }
@ -826,8 +826,8 @@ impl BuiltInConstructorWithPrototype<'_> {
let length = self.length; let length = self.length;
let name = self.name.clone(); let name = self.name.clone();
let prototype = self.prototype.clone(); let prototype = self.prototype.clone();
self = self.static_property("length", length, Attribute::CONFIGURABLE); self = self.static_property(js_string!("length"), length, Attribute::CONFIGURABLE);
self = self.static_property("name", name, Attribute::CONFIGURABLE); self = self.static_property(js_string!("name"), name, Attribute::CONFIGURABLE);
self = self.static_property(PROTOTYPE, prototype, Attribute::empty()); self = self.static_property(PROTOTYPE, prototype, Attribute::empty());
let attributes = self.attributes; let attributes = self.attributes;
@ -877,8 +877,8 @@ impl BuiltInConstructorWithPrototype<'_> {
let length = self.length; let length = self.length;
let name = self.name.clone(); let name = self.name.clone();
self = self.static_property("length", length, Attribute::CONFIGURABLE); self = self.static_property(js_string!("length"), length, Attribute::CONFIGURABLE);
self = self.static_property("name", name, Attribute::CONFIGURABLE); self = self.static_property(js_string!("name"), name, Attribute::CONFIGURABLE);
let mut object = self.object.borrow_mut(); let mut object = self.object.borrow_mut();
*object.kind_mut() = ObjectKind::Function(function); *object.kind_mut() = ObjectKind::Function(function);
@ -916,8 +916,8 @@ impl BuiltInCallable<'_> {
/// Specify the name of the constructor function. /// Specify the name of the constructor function.
/// ///
/// Default is `""` /// Default is `""`
fn name<N: Into<JsString>>(mut self, name: N) -> Self { fn name(mut self, name: JsString) -> Self {
self.name = name.into(); self.name = name;
self self
} }
@ -1074,8 +1074,8 @@ impl<FnTyp> BuiltInBuilder<'_, Callable<FnTyp>> {
/// Specify the name of the constructor function. /// Specify the name of the constructor function.
/// ///
/// Default is `""` /// Default is `""`
fn name<N: Into<JsString>>(mut self, name: N) -> Self { fn name(mut self, name: JsString) -> Self {
self.kind.name = name.into(); self.kind.name = name;
self self
} }
} }

12
boa_engine/src/builtins/number/globals.rs

@ -5,8 +5,8 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
object::JsObject, object::JsObject,
realm::Realm, realm::Realm,
string::Utf16Trim, string::{common::StaticJsStrings, Utf16Trim},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use num_traits::Num; use num_traits::Num;
@ -50,7 +50,7 @@ impl IntrinsicObject for IsFinite {
} }
impl BuiltInObject for IsFinite { impl BuiltInObject for IsFinite {
const NAME: &'static str = "isFinite"; const NAME: JsString = StaticJsStrings::IS_FINITE;
} }
/// Builtin javascript 'isNaN(number)' function. /// Builtin javascript 'isNaN(number)' function.
@ -96,7 +96,7 @@ impl IntrinsicObject for IsNaN {
} }
impl BuiltInObject for IsNaN { impl BuiltInObject for IsNaN {
const NAME: &'static str = "isNaN"; const NAME: JsString = StaticJsStrings::IS_NAN;
} }
/// Builtin javascript 'parseInt(str, radix)' function. /// Builtin javascript 'parseInt(str, radix)' function.
@ -237,7 +237,7 @@ impl IntrinsicObject for ParseInt {
} }
impl BuiltInObject for ParseInt { impl BuiltInObject for ParseInt {
const NAME: &'static str = "parseInt"; const NAME: JsString = StaticJsStrings::PARSE_INT;
} }
/// Builtin javascript 'parseFloat(str)' function. /// Builtin javascript 'parseFloat(str)' function.
@ -310,5 +310,5 @@ impl IntrinsicObject for ParseFloat {
} }
impl BuiltInObject for ParseFloat { impl BuiltInObject for ParseFloat {
const NAME: &'static str = "parseFloat"; const NAME: JsString = StaticJsStrings::PARSE_FLOAT;
} }

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

@ -17,12 +17,13 @@ use crate::{
builtins::BuiltInObject, builtins::BuiltInObject,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::common::StaticJsStrings,
value::{AbstractRelation, IntegerOrInfinity, JsValue}, value::{AbstractRelation, IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult, JsString,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
use num_traits::float::FloatCore; use num_traits::float::FloatCore;
@ -47,47 +48,51 @@ pub(crate) struct Number;
impl IntrinsicObject for Number { impl IntrinsicObject for Number {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_property(utf16!("EPSILON"), f64::EPSILON, attribute) .static_property(js_string!("EPSILON"), f64::EPSILON, attribute)
.static_property( .static_property(
utf16!("MAX_SAFE_INTEGER"), js_string!("MAX_SAFE_INTEGER"),
Self::MAX_SAFE_INTEGER, Self::MAX_SAFE_INTEGER,
attribute, attribute,
) )
.static_property( .static_property(
utf16!("MIN_SAFE_INTEGER"), js_string!("MIN_SAFE_INTEGER"),
Self::MIN_SAFE_INTEGER, Self::MIN_SAFE_INTEGER,
attribute, attribute,
) )
.static_property(utf16!("MAX_VALUE"), Self::MAX_VALUE, attribute) .static_property(js_string!("MAX_VALUE"), Self::MAX_VALUE, attribute)
.static_property(utf16!("MIN_VALUE"), Self::MIN_VALUE, attribute) .static_property(js_string!("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( .static_property(
utf16!("parseInt"), js_string!("NEGATIVE_INFINITY"),
f64::NEG_INFINITY,
attribute,
)
.static_property(js_string!("POSITIVE_INFINITY"), f64::INFINITY, attribute)
.static_property(js_string!("NaN"), f64::NAN, attribute)
.static_property(
js_string!("parseInt"),
realm.intrinsics().objects().parse_int(), realm.intrinsics().objects().parse_int(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.static_property( .static_property(
utf16!("parseFloat"), js_string!("parseFloat"),
realm.intrinsics().objects().parse_float(), realm.intrinsics().objects().parse_float(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.static_method(Self::number_is_finite, "isFinite", 1) .static_method(Self::number_is_finite, js_string!("isFinite"), 1)
.static_method(Self::number_is_nan, "isNaN", 1) .static_method(Self::number_is_nan, js_string!("isNaN"), 1)
.static_method(Self::is_safe_integer, "isSafeInteger", 1) .static_method(Self::is_safe_integer, js_string!("isSafeInteger"), 1)
.static_method(Self::number_is_integer, "isInteger", 1) .static_method(Self::number_is_integer, js_string!("isInteger"), 1)
.method(Self::to_exponential, "toExponential", 1) .method(Self::to_exponential, js_string!("toExponential"), 1)
.method(Self::to_fixed, "toFixed", 1) .method(Self::to_fixed, js_string!("toFixed"), 1)
.method(Self::to_locale_string, "toLocaleString", 0) .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
.method(Self::to_precision, "toPrecision", 1) .method(Self::to_precision, js_string!("toPrecision"), 1)
.method(Self::to_string, "toString", 1) .method(Self::to_string, js_string!("toString"), 1)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, js_string!("valueOf"), 0)
.build(); .build();
} }
@ -97,7 +102,7 @@ impl IntrinsicObject for Number {
} }
impl BuiltInObject for Number { impl BuiltInObject for Number {
const NAME: &'static str = "Number"; const NAME: JsString = StaticJsStrings::NUMBER;
} }
impl BuiltInConstructor for Number { impl BuiltInConstructor for Number {
@ -222,7 +227,7 @@ impl Number {
}; };
// 4. If x is not finite, return ! Number::toString(x). // 4. If x is not finite, return ! Number::toString(x).
if !this_num.is_finite() { if !this_num.is_finite() {
return Ok(JsValue::new(Self::to_native_string(this_num))); return Ok(JsValue::new(Self::to_js_string(this_num)));
} }
// Get rid of the '-' sign for -0.0 // Get rid of the '-' sign for -0.0
let this_num = if this_num == 0. { 0. } else { this_num }; let this_num = if this_num == 0. { 0. } else { this_num };
@ -276,7 +281,7 @@ impl Number {
// 6. If x is not finite, return ! Number::toString(x). // 6. If x is not finite, return ! Number::toString(x).
if !this_num.is_finite() { if !this_num.is_finite() {
Ok(JsValue::new(Self::to_native_string(this_num))) Ok(JsValue::new(Self::to_js_string(this_num)))
// 10. If x ≥ 10^21, then let m be ! ToString(𝔽(x)). // 10. If x ≥ 10^21, then let m be ! ToString(𝔽(x)).
} else if this_num >= 1.0e21 { } else if this_num >= 1.0e21 {
Ok(JsValue::new(f64_to_exponential(this_num))) Ok(JsValue::new(f64_to_exponential(this_num)))
@ -284,7 +289,7 @@ impl Number {
// Get rid of the '-' sign for -0.0 because of 9. If x < 0, then set s to "-". // Get rid of the '-' sign for -0.0 because of 9. If x < 0, then set s to "-".
let this_num = if this_num == 0_f64 { 0_f64 } else { this_num }; let this_num = if this_num == 0_f64 { 0_f64 } else { this_num };
let this_fixed_num = format!("{this_num:.precision$}"); let this_fixed_num = format!("{this_num:.precision$}");
Ok(JsValue::new(this_fixed_num)) Ok(JsValue::new(js_string!(this_fixed_num)))
} }
} }
@ -309,7 +314,7 @@ impl Number {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let this_num = Self::this_number_value(this)?; let this_num = Self::this_number_value(this)?;
let this_str_num = this_num.to_string(); let this_str_num = this_num.to_string();
Ok(JsValue::new(this_str_num)) Ok(JsValue::new(js_string!(this_str_num)))
} }
/// `flt_str_to_exp` - used in `to_precision` /// `flt_str_to_exp` - used in `to_precision`
@ -494,14 +499,14 @@ impl Number {
// iv, v // iv, v
suffix.push_str(&exponent.to_string()); suffix.push_str(&exponent.to_string());
return Ok(JsValue::new(prefix + &suffix)); return Ok(JsValue::new(js_string!(prefix + &suffix)));
} }
} }
// 11 // 11
let e_inc = exponent + 1; let e_inc = exponent + 1;
if e_inc == precision_i32 { if e_inc == precision_i32 {
return Ok(JsValue::new(prefix + &suffix)); return Ok(JsValue::new(js_string!(prefix + &suffix)));
} }
// 12 // 12
@ -515,7 +520,7 @@ impl Number {
} }
// 14 // 14
Ok(JsValue::new(prefix + &suffix)) Ok(JsValue::new(js_string!(prefix + &suffix)))
} }
// https://golang.org/src/math/nextafter.go // https://golang.org/src/math/nextafter.go
@ -535,7 +540,7 @@ impl Number {
// https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/numbers/conversions.cc#1230 // https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/numbers/conversions.cc#1230
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_native_string_radix(mut value: f64, radix: u8) -> String { pub(crate) fn to_js_string_radix(mut value: f64, radix: u8) -> JsString {
assert!(radix >= 2); assert!(radix >= 2);
assert!(radix <= 36); assert!(radix <= 36);
assert!(value.is_finite()); assert!(value.is_finite());
@ -639,13 +644,15 @@ impl Number {
let integer_cursor = int_iter.next().expect("integer buffer exhausted").0 + 1; let integer_cursor = int_iter.next().expect("integer buffer exhausted").0 + 1;
let fraction_cursor = fraction_cursor + BUF_SIZE / 2; let fraction_cursor = fraction_cursor + BUF_SIZE / 2;
String::from_utf8_lossy(&buffer[integer_cursor..fraction_cursor]).into() js_string!(&*String::from_utf8_lossy(
&buffer[integer_cursor..fraction_cursor]
))
} }
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_native_string(x: f64) -> String { pub(crate) fn to_js_string(x: f64) -> JsString {
let mut buffer = ryu_js::Buffer::new(); let mut buffer = ryu_js::Buffer::new();
buffer.format(x).to_string() js_string!(buffer.format(x).to_string())
} }
/// `Number.prototype.toString( [radix] )` /// `Number.prototype.toString( [radix] )`
@ -686,17 +693,17 @@ impl Number {
// 5. If radixNumber = 10, return ! ToString(x). // 5. If radixNumber = 10, return ! ToString(x).
if radix_number == 10 { if radix_number == 10 {
return Ok(JsValue::new(Self::to_native_string(x))); return Ok(JsValue::new(Self::to_js_string(x)));
} }
if x == -0. { if x == -0. {
return Ok(JsValue::new("0")); return Ok(JsValue::new(js_string!("0")));
} else if x.is_nan() { } else if x.is_nan() {
return Ok(JsValue::new("NaN")); return Ok(JsValue::new(js_string!("NaN")));
} else if x.is_infinite() && x.is_sign_positive() { } else if x.is_infinite() && x.is_sign_positive() {
return Ok(JsValue::new("Infinity")); return Ok(JsValue::new(js_string!("Infinity")));
} else if x.is_infinite() && x.is_sign_negative() { } else if x.is_infinite() && x.is_sign_negative() {
return Ok(JsValue::new("-Infinity")); return Ok(JsValue::new(js_string!("-Infinity")));
} }
// This is a Optimization from the v8 source code to print values that can fit in a single character // This is a Optimization from the v8 source code to print values that can fit in a single character
@ -708,7 +715,7 @@ impl Number {
// } // }
// 6. Return the String representation of this Number value using the radix specified by radixNumber. // 6. Return the String representation of this Number value using the radix specified by radixNumber.
Ok(JsValue::new(Self::to_native_string_radix(x, radix_number))) Ok(JsValue::new(Self::to_js_string_radix(x, radix_number)))
} }
/// `Number.prototype.toString()` /// `Number.prototype.toString()`
@ -920,22 +927,22 @@ impl Number {
} }
/// Helper function that formats a float as a ES6-style exponential number string. /// Helper function that formats a float as a ES6-style exponential number string.
fn f64_to_exponential(n: f64) -> String { fn f64_to_exponential(n: f64) -> JsString {
match n.abs() { js_string!(match n.abs() {
x if x >= 1.0 || x == 0.0 => format!("{n:e}").replace('e', "e+"), x if x >= 1.0 || x == 0.0 => format!("{n:e}").replace('e', "e+"),
_ => format!("{n:e}"), _ => format!("{n:e}"),
} })
} }
/// Helper function that formats a float as a ES6-style exponential number string with a given precision. /// Helper function that formats a float as a ES6-style exponential number string with a given precision.
// We can't use the same approach as in `f64_to_exponential` // We can't use the same approach as in `f64_to_exponential`
// because in cases like (0.999).toExponential(0) the result will be 1e0. // because in cases like (0.999).toExponential(0) the result will be 1e0.
// Instead we get the index of 'e', and if the next character is not '-' we insert the plus sign // Instead we get the index of 'e', and if the next character is not '-' we insert the plus sign
fn f64_to_exponential_with_precision(n: f64, prec: usize) -> String { fn f64_to_exponential_with_precision(n: f64, prec: usize) -> JsString {
let mut res = format!("{n:.prec$e}"); let mut res = format!("{n:.prec$e}");
let idx = res.find('e').expect("'e' not found in exponential string"); let idx = res.find('e').expect("'e' not found in exponential string");
if res.as_bytes()[idx + 1] != b'-' { if res.as_bytes()[idx + 1] != b'-' {
res.insert(idx + 1, '+'); res.insert(idx + 1, '+');
} }
res js_string!(res)
} }

239
boa_engine/src/builtins/number/tests.rs

@ -1,10 +1,11 @@
use crate::{ use crate::{
builtins::Number, run_test_actions, value::AbstractRelation, JsNativeErrorKind, TestAction, builtins::Number, js_string, run_test_actions, value::AbstractRelation, JsNativeErrorKind,
TestAction,
}; };
#[test] #[test]
fn integer_number_primitive_to_number_object() { fn integer_number_primitive_to_number_object() {
run_test_actions([TestAction::assert_eq("(100).toString()", "100")]); run_test_actions([TestAction::assert_eq("(100).toString()", js_string!("100"))]);
} }
#[test] #[test]
@ -24,23 +25,29 @@ fn call_number() {
#[test] #[test]
fn to_exponential() { fn to_exponential() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("Number().toExponential()", "0e+0"), TestAction::assert_eq("Number().toExponential()", js_string!("0e+0")),
TestAction::assert_eq("Number(5).toExponential()", "5e+0"), TestAction::assert_eq("Number(5).toExponential()", js_string!("5e+0")),
TestAction::assert_eq("Number(1.234).toExponential()", "1.234e+0"), TestAction::assert_eq("Number(1.234).toExponential()", js_string!("1.234e+0")),
TestAction::assert_eq("Number(1234).toExponential()", "1.234e+3"), TestAction::assert_eq("Number(1234).toExponential()", js_string!("1.234e+3")),
TestAction::assert_eq("Number('I am also not a number').toExponential()", "NaN"), TestAction::assert_eq(
TestAction::assert_eq("Number('1.23e+2').toExponential()", "1.23e+2"), "Number('I am also not a number').toExponential()",
js_string!("NaN"),
),
TestAction::assert_eq("Number('1.23e+2').toExponential()", js_string!("1.23e+2")),
]); ]);
} }
#[test] #[test]
fn to_fixed() { fn to_fixed() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("Number().toFixed()", "0"), TestAction::assert_eq("Number().toFixed()", js_string!("0")),
TestAction::assert_eq("Number('3.456e+4').toFixed()", "34560"), TestAction::assert_eq("Number('3.456e+4').toFixed()", js_string!("34560")),
TestAction::assert_eq("Number('3.456e-4').toFixed()", "0"), TestAction::assert_eq("Number('3.456e-4').toFixed()", js_string!("0")),
TestAction::assert_eq("Number(5).toFixed()", "5"), TestAction::assert_eq("Number(5).toFixed()", js_string!("5")),
TestAction::assert_eq("Number('I am also not a number').toFixed()", "NaN"), TestAction::assert_eq(
"Number('I am also not a number').toFixed()",
js_string!("NaN"),
),
]); ]);
} }
@ -49,10 +56,10 @@ fn to_locale_string() {
// TODO: We don't actually do any locale checking here // TODO: We don't actually do any locale checking here
// To honor the spec we should print numbers according to user locale. // To honor the spec we should print numbers according to user locale.
run_test_actions([ run_test_actions([
TestAction::assert_eq("Number().toLocaleString()", "0"), TestAction::assert_eq("Number().toLocaleString()", js_string!("0")),
TestAction::assert_eq("Number(5).toLocaleString()", "5"), TestAction::assert_eq("Number(5).toLocaleString()", js_string!("5")),
TestAction::assert_eq("Number('345600').toLocaleString()", "345600"), TestAction::assert_eq("Number('345600').toLocaleString()", js_string!("345600")),
TestAction::assert_eq("Number(-25).toLocaleString()", "-25"), TestAction::assert_eq("Number(-25).toLocaleString()", js_string!("-25")),
]); ]);
} }
@ -60,21 +67,21 @@ fn to_locale_string() {
fn to_precision() { fn to_precision() {
const ERROR: &str = "precision must be an integer at least 1 and no greater than 100"; const ERROR: &str = "precision must be an integer at least 1 and no greater than 100";
run_test_actions([ run_test_actions([
TestAction::assert_eq("(1/0).toPrecision(3)", "Infinity"), TestAction::assert_eq("(1/0).toPrecision(3)", js_string!("Infinity")),
TestAction::assert_eq("Number().toPrecision()", "0"), TestAction::assert_eq("Number().toPrecision()", js_string!("0")),
TestAction::assert_eq("Number().toPrecision(undefined)", "0"), TestAction::assert_eq("Number().toPrecision(undefined)", js_string!("0")),
TestAction::assert_eq("(123456789).toPrecision(1)", "1e+8"), TestAction::assert_eq("(123456789).toPrecision(1)", js_string!("1e+8")),
TestAction::assert_eq("(123456789).toPrecision(4)", "1.235e+8"), TestAction::assert_eq("(123456789).toPrecision(4)", js_string!("1.235e+8")),
TestAction::assert_eq("(123456789).toPrecision(9)", "123456789"), TestAction::assert_eq("(123456789).toPrecision(9)", js_string!("123456789")),
TestAction::assert_eq("(-123456789).toPrecision(4)", "-1.235e+8"), TestAction::assert_eq("(-123456789).toPrecision(4)", js_string!("-1.235e+8")),
TestAction::assert_eq( TestAction::assert_eq(
"(123456789).toPrecision(50)", "(123456789).toPrecision(50)",
"123456789.00000000000000000000000000000000000000000", js_string!("123456789.00000000000000000000000000000000000000000"),
), ),
TestAction::assert_eq("(0.1).toPrecision(4)", "0.1000"), TestAction::assert_eq("(0.1).toPrecision(4)", js_string!("0.1000")),
TestAction::assert_eq( TestAction::assert_eq(
"(1/3).toPrecision(60)", "(1/3).toPrecision(60)",
"0.333333333333333314829616256247390992939472198486328125000000", js_string!("0.333333333333333314829616256247390992939472198486328125000000"),
), ),
TestAction::assert_native_error("(1).toPrecision(101)", JsNativeErrorKind::Range, ERROR), TestAction::assert_native_error("(1).toPrecision(101)", JsNativeErrorKind::Range, ERROR),
TestAction::assert_native_error("(1).toPrecision(0)", JsNativeErrorKind::Range, ERROR), TestAction::assert_native_error("(1).toPrecision(0)", JsNativeErrorKind::Range, ERROR),
@ -86,90 +93,132 @@ fn to_precision() {
#[test] #[test]
fn to_string() { fn to_string() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("Number(NaN).toString()", "NaN"), TestAction::assert_eq("Number(NaN).toString()", js_string!("NaN")),
TestAction::assert_eq("Number(1/0).toString()", "Infinity"), TestAction::assert_eq("Number(1/0).toString()", js_string!("Infinity")),
TestAction::assert_eq("Number(-1/0).toString()", "-Infinity"), TestAction::assert_eq("Number(-1/0).toString()", js_string!("-Infinity")),
TestAction::assert_eq("Number(0).toString()", "0"), TestAction::assert_eq("Number(0).toString()", js_string!("0")),
TestAction::assert_eq("Number(9).toString()", "9"), TestAction::assert_eq("Number(9).toString()", js_string!("9")),
TestAction::assert_eq("Number(90).toString()", "90"), TestAction::assert_eq("Number(90).toString()", js_string!("90")),
TestAction::assert_eq("Number(90.12).toString()", "90.12"), TestAction::assert_eq("Number(90.12).toString()", js_string!("90.12")),
TestAction::assert_eq("Number(0.1).toString()", "0.1"), TestAction::assert_eq("Number(0.1).toString()", js_string!("0.1")),
TestAction::assert_eq("Number(0.01).toString()", "0.01"), TestAction::assert_eq("Number(0.01).toString()", js_string!("0.01")),
TestAction::assert_eq("Number(0.0123).toString()", "0.0123"), TestAction::assert_eq("Number(0.0123).toString()", js_string!("0.0123")),
TestAction::assert_eq("Number(0.00001).toString()", "0.00001"), TestAction::assert_eq("Number(0.00001).toString()", js_string!("0.00001")),
TestAction::assert_eq("Number(0.000001).toString()", "0.000001"), TestAction::assert_eq("Number(0.000001).toString()", js_string!("0.000001")),
TestAction::assert_eq("Number(NaN).toString(16)", "NaN"), TestAction::assert_eq("Number(NaN).toString(16)", js_string!("NaN")),
TestAction::assert_eq("Number(1/0).toString(16)", "Infinity"), TestAction::assert_eq("Number(1/0).toString(16)", js_string!("Infinity")),
TestAction::assert_eq("Number(-1/0).toString(16)", "-Infinity"), TestAction::assert_eq("Number(-1/0).toString(16)", js_string!("-Infinity")),
TestAction::assert_eq("Number(0).toString(16)", "0"), TestAction::assert_eq("Number(0).toString(16)", js_string!("0")),
TestAction::assert_eq("Number(9).toString(16)", "9"), TestAction::assert_eq("Number(9).toString(16)", js_string!("9")),
TestAction::assert_eq("Number(90).toString(16)", "5a"), TestAction::assert_eq("Number(90).toString(16)", js_string!("5a")),
TestAction::assert_eq("Number(90.12).toString(16)", "5a.1eb851eb852"), TestAction::assert_eq("Number(90.12).toString(16)", js_string!("5a.1eb851eb852")),
TestAction::assert_eq("Number(0.1).toString(16)", "0.1999999999999a"), TestAction::assert_eq("Number(0.1).toString(16)", js_string!("0.1999999999999a")),
TestAction::assert_eq("Number(0.01).toString(16)", "0.028f5c28f5c28f6"), TestAction::assert_eq("Number(0.01).toString(16)", js_string!("0.028f5c28f5c28f6")),
TestAction::assert_eq("Number(0.0123).toString(16)", "0.032617c1bda511a"), TestAction::assert_eq(
"Number(0.0123).toString(16)",
js_string!("0.032617c1bda511a"),
),
TestAction::assert_eq( TestAction::assert_eq(
"Number(111111111111111111111).toString(16)", "Number(111111111111111111111).toString(16)",
"605f9f6dd18bc8000", js_string!("605f9f6dd18bc8000"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(1111111111111111111111).toString(16)", "Number(1111111111111111111111).toString(16)",
"3c3bc3a4a2f75c0000", js_string!("3c3bc3a4a2f75c0000"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(11111111111111111111111).toString(16)", "Number(11111111111111111111111).toString(16)",
"25a55a46e5da9a00000", js_string!("25a55a46e5da9a00000"),
),
TestAction::assert_eq(
"Number(0.00001).toString(16)",
js_string!("0.0000a7c5ac471b4788"),
),
TestAction::assert_eq(
"Number(0.000001).toString(16)",
js_string!("0.000010c6f7a0b5ed8d"),
),
TestAction::assert_eq(
"Number(0.0000001).toString(16)",
js_string!("0.000001ad7f29abcaf48"),
),
TestAction::assert_eq(
"Number(0.00000012).toString(16)",
js_string!("0.000002036565348d256"),
),
TestAction::assert_eq(
"Number(0.000000123).toString(16)",
js_string!("0.0000021047ee22aa466"),
),
TestAction::assert_eq(
"Number(0.00000001).toString(16)",
js_string!("0.0000002af31dc4611874"),
),
TestAction::assert_eq(
"Number(0.000000012).toString(16)",
js_string!("0.000000338a23b87483be"),
), ),
TestAction::assert_eq("Number(0.00001).toString(16)", "0.0000a7c5ac471b4788"),
TestAction::assert_eq("Number(0.000001).toString(16)", "0.000010c6f7a0b5ed8d"),
TestAction::assert_eq("Number(0.0000001).toString(16)", "0.000001ad7f29abcaf48"),
TestAction::assert_eq("Number(0.00000012).toString(16)", "0.000002036565348d256"),
TestAction::assert_eq("Number(0.000000123).toString(16)", "0.0000021047ee22aa466"),
TestAction::assert_eq("Number(0.00000001).toString(16)", "0.0000002af31dc4611874"),
TestAction::assert_eq("Number(0.000000012).toString(16)", "0.000000338a23b87483be"),
TestAction::assert_eq( TestAction::assert_eq(
"Number(0.0000000123).toString(16)", "Number(0.0000000123).toString(16)",
"0.00000034d3fe36aaa0a2", js_string!("0.00000034d3fe36aaa0a2"),
), ),
TestAction::assert_eq("Number(-0).toString(16)", "0"), TestAction::assert_eq("Number(-0).toString(16)", js_string!("0")),
TestAction::assert_eq("Number(-9).toString(16)", "-9"), TestAction::assert_eq("Number(-9).toString(16)", js_string!("-9")),
// //
TestAction::assert_eq("Number(-90).toString(16)", "-5a"), TestAction::assert_eq("Number(-90).toString(16)", js_string!("-5a")),
TestAction::assert_eq("Number(-90.12).toString(16)", "-5a.1eb851eb852"), TestAction::assert_eq("Number(-90.12).toString(16)", js_string!("-5a.1eb851eb852")),
TestAction::assert_eq("Number(-0.1).toString(16)", "-0.1999999999999a"), TestAction::assert_eq("Number(-0.1).toString(16)", js_string!("-0.1999999999999a")),
TestAction::assert_eq("Number(-0.01).toString(16)", "-0.028f5c28f5c28f6"), TestAction::assert_eq(
TestAction::assert_eq("Number(-0.0123).toString(16)", "-0.032617c1bda511a"), "Number(-0.01).toString(16)",
js_string!("-0.028f5c28f5c28f6"),
),
TestAction::assert_eq(
"Number(-0.0123).toString(16)",
js_string!("-0.032617c1bda511a"),
),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-111111111111111111111).toString(16)", "Number(-111111111111111111111).toString(16)",
"-605f9f6dd18bc8000", js_string!("-605f9f6dd18bc8000"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-1111111111111111111111).toString(16)", "Number(-1111111111111111111111).toString(16)",
"-3c3bc3a4a2f75c0000", js_string!("-3c3bc3a4a2f75c0000"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-11111111111111111111111).toString(16)", "Number(-11111111111111111111111).toString(16)",
"-25a55a46e5da9a00000", js_string!("-25a55a46e5da9a00000"),
),
TestAction::assert_eq(
"Number(-0.00001).toString(16)",
js_string!("-0.0000a7c5ac471b4788"),
),
TestAction::assert_eq(
"Number(-0.000001).toString(16)",
js_string!("-0.000010c6f7a0b5ed8d"),
),
TestAction::assert_eq(
"Number(-0.0000001).toString(16)",
js_string!("-0.000001ad7f29abcaf48"),
),
TestAction::assert_eq(
"Number(-0.00000012).toString(16)",
js_string!("-0.000002036565348d256"),
), ),
TestAction::assert_eq("Number(-0.00001).toString(16)", "-0.0000a7c5ac471b4788"),
TestAction::assert_eq("Number(-0.000001).toString(16)", "-0.000010c6f7a0b5ed8d"),
TestAction::assert_eq("Number(-0.0000001).toString(16)", "-0.000001ad7f29abcaf48"),
TestAction::assert_eq("Number(-0.00000012).toString(16)", "-0.000002036565348d256"),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-0.000000123).toString(16)", "Number(-0.000000123).toString(16)",
"-0.0000021047ee22aa466", js_string!("-0.0000021047ee22aa466"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-0.00000001).toString(16)", "Number(-0.00000001).toString(16)",
"-0.0000002af31dc4611874", js_string!("-0.0000002af31dc4611874"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-0.000000012).toString(16)", "Number(-0.000000012).toString(16)",
"-0.000000338a23b87483be", js_string!("-0.000000338a23b87483be"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"Number(-0.0000000123).toString(16)", "Number(-0.0000000123).toString(16)",
"-0.00000034d3fe36aaa0a2", js_string!("-0.00000034d3fe36aaa0a2"),
), ),
]); ]);
} }
@ -177,26 +226,26 @@ fn to_string() {
#[test] #[test]
fn num_to_string_exponential() { fn num_to_string_exponential() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("(0).toString()", "0"), TestAction::assert_eq("(0).toString()", js_string!("0")),
TestAction::assert_eq("(-0).toString()", "0"), TestAction::assert_eq("(-0).toString()", js_string!("0")),
TestAction::assert_eq( TestAction::assert_eq(
"(111111111111111111111).toString()", "(111111111111111111111).toString()",
"111111111111111110000", js_string!("111111111111111110000"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"(1111111111111111111111).toString()", "(1111111111111111111111).toString()",
"1.1111111111111111e+21", js_string!("1.1111111111111111e+21"),
), ),
TestAction::assert_eq( TestAction::assert_eq(
"(11111111111111111111111).toString()", "(11111111111111111111111).toString()",
"1.1111111111111111e+22", js_string!("1.1111111111111111e+22"),
), ),
TestAction::assert_eq("(0.0000001).toString()", "1e-7"), TestAction::assert_eq("(0.0000001).toString()", js_string!("1e-7")),
TestAction::assert_eq("(0.00000012).toString()", "1.2e-7"), TestAction::assert_eq("(0.00000012).toString()", js_string!("1.2e-7")),
TestAction::assert_eq("(0.000000123).toString()", "1.23e-7"), TestAction::assert_eq("(0.000000123).toString()", js_string!("1.23e-7")),
TestAction::assert_eq("(0.00000001).toString()", "1e-8"), TestAction::assert_eq("(0.00000001).toString()", js_string!("1e-8")),
TestAction::assert_eq("(0.000000012).toString()", "1.2e-8"), TestAction::assert_eq("(0.000000012).toString()", js_string!("1.2e-8")),
TestAction::assert_eq("(0.0000000123).toString()", "1.23e-8"), TestAction::assert_eq("(0.0000000123).toString()", js_string!("1.23e-8")),
]); ]);
} }
@ -475,7 +524,13 @@ fn number_is_safe_integer() {
#[test] #[test]
fn issue_2717() { fn issue_2717() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("(0.1600057092765239).toString(36)", "0.5rd85dm1ixq"), TestAction::assert_eq(
TestAction::assert_eq("(0.23046743672210102).toString(36)", "0.8aoosla2phj"), "(0.1600057092765239).toString(36)",
js_string!("0.5rd85dm1ixq"),
),
TestAction::assert_eq(
"(0.23046743672210102).toString(36)",
js_string!("0.8aoosla2phj"),
),
]); ]);
} }

5
boa_engine/src/builtins/object/for_in_iterator.rs

@ -12,6 +12,7 @@ use crate::{
builtins::{iterable::create_iter_result_object, BuiltInBuilder, IntrinsicObject}, builtins::{iterable::create_iter_result_object, BuiltInBuilder, IntrinsicObject},
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::PropertyKey, property::PropertyKey,
realm::Realm, realm::Realm,
@ -39,7 +40,7 @@ pub struct ForInIterator {
impl IntrinsicObject for ForInIterator { impl IntrinsicObject for ForInIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("ForInIterator", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -49,7 +50,7 @@ impl IntrinsicObject for ForInIterator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 0) .static_method(Self::next, js_string!("next"), 0)
.build(); .build();
} }

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

@ -28,7 +28,7 @@ use crate::{
}, },
property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
@ -46,14 +46,14 @@ pub struct Object;
impl IntrinsicObject for Object { impl IntrinsicObject for Object {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let legacy_proto_getter = BuiltInBuilder::callable(realm, Self::legacy_proto_getter) let legacy_proto_getter = BuiltInBuilder::callable(realm, Self::legacy_proto_getter)
.name("get __proto__") .name(js_string!("get __proto__"))
.build(); .build();
let legacy_setter_proto = BuiltInBuilder::callable(realm, Self::legacy_proto_setter) let legacy_setter_proto = BuiltInBuilder::callable(realm, Self::legacy_proto_setter)
.name("set __proto__") .name(js_string!("set __proto__"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
@ -64,46 +64,74 @@ impl IntrinsicObject for Object {
Some(legacy_setter_proto), Some(legacy_setter_proto),
Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::has_own_property, "hasOwnProperty", 1) .method(Self::has_own_property, js_string!("hasOwnProperty"), 1)
.method(Self::property_is_enumerable, "propertyIsEnumerable", 1) .method(
.method(Self::to_string, "toString", 0) Self::property_is_enumerable,
.method(Self::to_locale_string, "toLocaleString", 0) js_string!("propertyIsEnumerable"),
.method(Self::value_of, "valueOf", 0) 1,
.method(Self::is_prototype_of, "isPrototypeOf", 1) )
.method(Self::legacy_define_getter, "__defineGetter__", 2) .method(Self::to_string, js_string!("toString"), 0)
.method(Self::legacy_define_setter, "__defineSetter__", 2) .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
.method(Self::legacy_lookup_getter, "__lookupGetter__", 1) .method(Self::value_of, js_string!("valueOf"), 0)
.method(Self::legacy_lookup_setter, "__lookupSetter__", 1) .method(Self::is_prototype_of, js_string!("isPrototypeOf"), 1)
.static_method(Self::create, "create", 2) .method(
.static_method(Self::set_prototype_of, "setPrototypeOf", 2) Self::legacy_define_getter,
.static_method(Self::get_prototype_of, "getPrototypeOf", 1) js_string!("__defineGetter__"),
.static_method(Self::define_property, "defineProperty", 3) 2,
.static_method(Self::define_properties, "defineProperties", 2) )
.static_method(Self::assign, "assign", 2) .method(
.static_method(Self::is, "is", 2) Self::legacy_define_setter,
.static_method(Self::keys, "keys", 1) js_string!("__defineSetter__"),
.static_method(Self::values, "values", 1) 2,
.static_method(Self::entries, "entries", 1) )
.static_method(Self::seal, "seal", 1) .method(
.static_method(Self::is_sealed, "isSealed", 1) Self::legacy_lookup_getter,
.static_method(Self::freeze, "freeze", 1) js_string!("__lookupGetter__"),
.static_method(Self::is_frozen, "isFrozen", 1) 1,
.static_method(Self::prevent_extensions, "preventExtensions", 1) )
.static_method(Self::is_extensible, "isExtensible", 1) .method(
Self::legacy_lookup_setter,
js_string!("__lookupSetter__"),
1,
)
.static_method(Self::create, js_string!("create"), 2)
.static_method(Self::set_prototype_of, js_string!("setPrototypeOf"), 2)
.static_method(Self::get_prototype_of, js_string!("getPrototypeOf"), 1)
.static_method(Self::define_property, js_string!("defineProperty"), 3)
.static_method(Self::define_properties, js_string!("defineProperties"), 2)
.static_method(Self::assign, js_string!("assign"), 2)
.static_method(Self::is, js_string!("is"), 2)
.static_method(Self::keys, js_string!("keys"), 1)
.static_method(Self::values, js_string!("values"), 1)
.static_method(Self::entries, js_string!("entries"), 1)
.static_method(Self::seal, js_string!("seal"), 1)
.static_method(Self::is_sealed, js_string!("isSealed"), 1)
.static_method(Self::freeze, js_string!("freeze"), 1)
.static_method(Self::is_frozen, js_string!("isFrozen"), 1)
.static_method(Self::prevent_extensions, js_string!("preventExtensions"), 1)
.static_method(Self::is_extensible, js_string!("isExtensible"), 1)
.static_method( .static_method(
Self::get_own_property_descriptor, Self::get_own_property_descriptor,
"getOwnPropertyDescriptor", js_string!("getOwnPropertyDescriptor"),
2, 2,
) )
.static_method( .static_method(
Self::get_own_property_descriptors, Self::get_own_property_descriptors,
"getOwnPropertyDescriptors", js_string!("getOwnPropertyDescriptors"),
1,
)
.static_method(
Self::get_own_property_names,
js_string!("getOwnPropertyNames"),
1, 1,
) )
.static_method(Self::get_own_property_names, "getOwnPropertyNames", 1) .static_method(
.static_method(Self::get_own_property_symbols, "getOwnPropertySymbols", 1) Self::get_own_property_symbols,
.static_method(Self::has_own, "hasOwn", 2) js_string!("getOwnPropertySymbols"),
.static_method(Self::from_entries, "fromEntries", 1) 1,
)
.static_method(Self::has_own, js_string!("hasOwn"), 2)
.static_method(Self::from_entries, js_string!("fromEntries"), 1)
.build(); .build();
} }
@ -113,7 +141,7 @@ impl IntrinsicObject for Object {
} }
impl BuiltInObject for Object { impl BuiltInObject for Object {
const NAME: &'static str = "Object"; const NAME: JsString = StaticJsStrings::OBJECT;
} }
impl BuiltInConstructor for Object { impl BuiltInConstructor for Object {
@ -791,11 +819,11 @@ impl Object {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. If the this value is undefined, return "[object Undefined]". // 1. If the this value is undefined, return "[object Undefined]".
if this.is_undefined() { if this.is_undefined() {
return Ok("[object Undefined]".into()); return Ok(js_string!("[object Undefined]").into());
} }
// 2. If the this value is null, return "[object Null]". // 2. If the this value is null, return "[object Null]".
if this.is_null() { if this.is_null() {
return Ok("[object Null]".into()); return Ok(js_string!("[object Null]").into());
} }
// 3. Let O be ! ToObject(this value). // 3. Let O be ! ToObject(this value).
let o = this.to_object(context).expect("toObject cannot fail here"); let o = this.to_object(context).expect("toObject cannot fail here");
@ -1391,7 +1419,9 @@ fn get_own_property_keys(
match (r#type, &next_key) { match (r#type, &next_key) {
(PropertyKeyType::String, PropertyKey::String(_)) (PropertyKeyType::String, PropertyKey::String(_))
| (PropertyKeyType::Symbol, PropertyKey::Symbol(_)) => Some(next_key.into()), | (PropertyKeyType::Symbol, PropertyKey::Symbol(_)) => Some(next_key.into()),
(PropertyKeyType::String, PropertyKey::Index(index)) => Some(index.to_string().into()), (PropertyKeyType::String, PropertyKey::Index(index)) => {
Some(js_string!(index.to_string()).into())
}
_ => None, _ => None,
} }
}); });

29
boa_engine/src/builtins/object/tests.rs

@ -1,4 +1,4 @@
use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -137,18 +137,21 @@ fn object_to_string() {
"#}), "#}),
TestAction::assert_eq( TestAction::assert_eq(
"Object.prototype.toString.call(undefined)", "Object.prototype.toString.call(undefined)",
"[object Undefined]", js_string!("[object Undefined]"),
), ),
TestAction::assert_eq("Object.prototype.toString.call(null)", "[object Null]"), TestAction::assert_eq(
TestAction::assert_eq("[].toString()", "[object Array]"), "Object.prototype.toString.call(null)",
TestAction::assert_eq("(() => {}).toString()", "[object Function]"), js_string!("[object Null]"),
TestAction::assert_eq("(new Error('')).toString()", "[object Error]"), ),
TestAction::assert_eq("Boolean().toString()", "[object Boolean]"), TestAction::assert_eq("[].toString()", js_string!("[object Array]")),
TestAction::assert_eq("Number(42).toString()", "[object Number]"), TestAction::assert_eq("(() => {}).toString()", js_string!("[object Function]")),
TestAction::assert_eq("String('boa').toString()", "[object String]"), TestAction::assert_eq("(new Error('')).toString()", js_string!("[object Error]")),
TestAction::assert_eq("(new Date()).toString()", "[object Date]"), TestAction::assert_eq("Boolean().toString()", js_string!("[object Boolean]")),
TestAction::assert_eq("/boa/.toString()", "[object RegExp]"), TestAction::assert_eq("Number(42).toString()", js_string!("[object Number]")),
TestAction::assert_eq("({}).toString()", "[object Object]"), TestAction::assert_eq("String('boa').toString()", js_string!("[object String]")),
TestAction::assert_eq("(new Date()).toString()", js_string!("[object Date]")),
TestAction::assert_eq("/boa/.toString()", js_string!("[object RegExp]")),
TestAction::assert_eq("({}).toString()", js_string!("[object Object]")),
]); ]);
} }
@ -160,7 +163,7 @@ fn define_symbol_property() {
let sym = Symbol("key"); let sym = Symbol("key");
Object.defineProperty(obj, sym, { value: "val" }); Object.defineProperty(obj, sym, { value: "val" });
"#}), "#}),
TestAction::assert_eq("obj[sym]", "val"), TestAction::assert_eq("obj[sym]", js_string!("val")),
]); ]);
} }

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

@ -9,6 +9,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
job::{JobCallback, NativeJob}, job::{JobCallback, NativeJob},
js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{ object::{
internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction, internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction,
@ -16,10 +17,10 @@ use crate::{
}, },
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult, JsString,
}; };
use boa_gc::{custom_trace, Finalize, Gc, GcRefCell, Trace}; use boa_gc::{custom_trace, Finalize, Gc, GcRefCell, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -332,28 +333,28 @@ impl PromiseCapability {
impl IntrinsicObject for Promise { impl IntrinsicObject for Promise {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm) let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::all, "all", 1) .static_method(Self::all, js_string!("all"), 1)
.static_method(Self::all_settled, "allSettled", 1) .static_method(Self::all_settled, js_string!("allSettled"), 1)
.static_method(Self::any, "any", 1) .static_method(Self::any, js_string!("any"), 1)
.static_method(Self::race, "race", 1) .static_method(Self::race, js_string!("race"), 1)
.static_method(Self::reject, "reject", 1) .static_method(Self::reject, js_string!("reject"), 1)
.static_method(Self::resolve, "resolve", 1) .static_method(Self::resolve, js_string!("resolve"), 1)
.static_accessor( .static_accessor(
JsSymbol::species(), JsSymbol::species(),
Some(get_species), Some(get_species),
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::then, "then", 2) .method(Self::then, js_string!("then"), 2)
.method(Self::catch, "catch", 1) .method(Self::catch, js_string!("catch"), 1)
.method(Self::finally, "finally", 1) .method(Self::finally, js_string!("finally"), 1)
// <https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag> // <https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag>
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
@ -374,7 +375,7 @@ impl IntrinsicObject for Promise {
} }
impl BuiltInObject for Promise { impl BuiltInObject for Promise {
const NAME: &'static str = "Promise"; const NAME: JsString = StaticJsStrings::PROMISE;
} }
impl BuiltInConstructor for Promise { impl BuiltInConstructor for Promise {
@ -901,8 +902,12 @@ impl Promise {
let obj = JsObject::with_object_proto(context.intrinsics()); let obj = JsObject::with_object_proto(context.intrinsics());
// 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled"). // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled").
obj.create_data_property_or_throw(utf16!("status"), "fulfilled", context) obj.create_data_property_or_throw(
.expect("cannot fail per spec"); utf16!("status"),
js_string!("fulfilled"),
context,
)
.expect("cannot fail per spec");
// 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x).
obj.create_data_property_or_throw( obj.create_data_property_or_throw(
@ -987,8 +992,12 @@ impl Promise {
let obj = JsObject::with_object_proto(context.intrinsics()); let obj = JsObject::with_object_proto(context.intrinsics());
// 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected"). // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected").
obj.create_data_property_or_throw(utf16!("status"), "rejected", context) obj.create_data_property_or_throw(
.expect("cannot fail per spec"); utf16!("status"),
js_string!("rejected"),
context,
)
.expect("cannot fail per spec");
// 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x).
obj.create_data_property_or_throw( obj.create_data_property_or_throw(

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

@ -14,11 +14,12 @@ use crate::{
builtins::BuiltInObject, builtins::BuiltInObject,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData}, object::{FunctionObjectBuilder, JsFunction, JsObject, ObjectData},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, GcRefCell, Trace}; use boa_gc::{Finalize, GcRefCell, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -33,10 +34,10 @@ pub struct Proxy {
impl IntrinsicObject for Proxy { impl IntrinsicObject for Proxy {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::revocable, "revocable", 2) .static_method(Self::revocable, js_string!("revocable"), 2)
.build_without_prototype(); .build_without_prototype();
} }
@ -46,7 +47,7 @@ impl IntrinsicObject for Proxy {
} }
impl BuiltInObject for Proxy { impl BuiltInObject for Proxy {
const NAME: &'static str = "Proxy"; const NAME: JsString = StaticJsStrings::PROXY;
} }
impl BuiltInConstructor for Proxy { impl BuiltInConstructor for Proxy {

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

@ -15,11 +15,13 @@ use crate::{
builtins::{self, BuiltInObject}, builtins::{self, BuiltInObject},
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,28 +34,28 @@ pub(crate) struct Reflect;
impl IntrinsicObject for Reflect { impl IntrinsicObject for Reflect {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let to_string_tag = JsSymbol::to_string_tag(); let to_string_tag = JsSymbol::to_string_tag();
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_method(Self::apply, "apply", 3) .static_method(Self::apply, js_string!("apply"), 3)
.static_method(Self::construct, "construct", 2) .static_method(Self::construct, js_string!("construct"), 2)
.static_method(Self::define_property, "defineProperty", 3) .static_method(Self::define_property, js_string!("defineProperty"), 3)
.static_method(Self::delete_property, "deleteProperty", 2) .static_method(Self::delete_property, js_string!("deleteProperty"), 2)
.static_method(Self::get, "get", 2) .static_method(Self::get, js_string!("get"), 2)
.static_method( .static_method(
Self::get_own_property_descriptor, Self::get_own_property_descriptor,
"getOwnPropertyDescriptor", js_string!("getOwnPropertyDescriptor"),
2, 2,
) )
.static_method(Self::get_prototype_of, "getPrototypeOf", 1) .static_method(Self::get_prototype_of, js_string!("getPrototypeOf"), 1)
.static_method(Self::has, "has", 2) .static_method(Self::has, js_string!("has"), 2)
.static_method(Self::is_extensible, "isExtensible", 1) .static_method(Self::is_extensible, js_string!("isExtensible"), 1)
.static_method(Self::own_keys, "ownKeys", 1) .static_method(Self::own_keys, js_string!("ownKeys"), 1)
.static_method(Self::prevent_extensions, "preventExtensions", 1) .static_method(Self::prevent_extensions, js_string!("preventExtensions"), 1)
.static_method(Self::set, "set", 3) .static_method(Self::set, js_string!("set"), 3)
.static_method(Self::set_prototype_of, "setPrototypeOf", 2) .static_method(Self::set_prototype_of, js_string!("setPrototypeOf"), 2)
.static_property( .static_property(
to_string_tag, to_string_tag,
Self::NAME, Self::NAME,
@ -68,7 +70,7 @@ impl IntrinsicObject for Reflect {
} }
impl BuiltInObject for Reflect { impl BuiltInObject for Reflect {
const NAME: &'static str = "Reflect"; const NAME: JsString = StaticJsStrings::REFLECT;
} }
impl Reflect { impl Reflect {

9
boa_engine/src/builtins/reflect/tests.rs

@ -1,4 +1,4 @@
use crate::{run_test_actions, JsValue, TestAction}; use crate::{js_string, run_test_actions, JsValue, TestAction};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -68,7 +68,10 @@ fn get_prototype_of() {
function F() { this.p = 42 }; function F() { this.p = 42 };
let f = new F(); let f = new F();
"#}), "#}),
TestAction::assert_eq("Reflect.getPrototypeOf(f).constructor.name", "F"), TestAction::assert_eq(
"Reflect.getPrototypeOf(f).constructor.name",
js_string!("F"),
),
]); ]);
} }
@ -131,6 +134,6 @@ fn set_prototype_of() {
let obj = {} let obj = {}
Reflect.setPrototypeOf(obj, F); Reflect.setPrototypeOf(obj, F);
"#}), "#}),
TestAction::assert_eq("Reflect.getPrototypeOf(obj).name", "F"), TestAction::assert_eq("Reflect.getPrototypeOf(obj).name", js_string!("F")),
]); ]);
} }

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

@ -20,7 +20,7 @@ use crate::{
}, },
property::{Attribute, PropertyDescriptorBuilder}, property::{Attribute, PropertyDescriptorBuilder},
realm::Realm, realm::Realm,
string::{utf16, CodePoint}, string::{common::StaticJsStrings, utf16, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
@ -49,40 +49,40 @@ pub struct RegExp {
impl IntrinsicObject for RegExp { impl IntrinsicObject for RegExp {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;
let get_has_indices = BuiltInBuilder::callable(realm, Self::get_has_indices) let get_has_indices = BuiltInBuilder::callable(realm, Self::get_has_indices)
.name("get hasIndices") .name(js_string!("get hasIndices"))
.build(); .build();
let get_global = BuiltInBuilder::callable(realm, Self::get_global) let get_global = BuiltInBuilder::callable(realm, Self::get_global)
.name("get global") .name(js_string!("get global"))
.build(); .build();
let get_ignore_case = BuiltInBuilder::callable(realm, Self::get_ignore_case) let get_ignore_case = BuiltInBuilder::callable(realm, Self::get_ignore_case)
.name("get ignoreCase") .name(js_string!("get ignoreCase"))
.build(); .build();
let get_multiline = BuiltInBuilder::callable(realm, Self::get_multiline) let get_multiline = BuiltInBuilder::callable(realm, Self::get_multiline)
.name("get multiline") .name(js_string!("get multiline"))
.build(); .build();
let get_dot_all = BuiltInBuilder::callable(realm, Self::get_dot_all) let get_dot_all = BuiltInBuilder::callable(realm, Self::get_dot_all)
.name("get dotAll") .name(js_string!("get dotAll"))
.build(); .build();
let get_unicode = BuiltInBuilder::callable(realm, Self::get_unicode) let get_unicode = BuiltInBuilder::callable(realm, Self::get_unicode)
.name("get unicode") .name(js_string!("get unicode"))
.build(); .build();
let get_sticky = BuiltInBuilder::callable(realm, Self::get_sticky) let get_sticky = BuiltInBuilder::callable(realm, Self::get_sticky)
.name("get sticky") .name(js_string!("get sticky"))
.build(); .build();
let get_flags = BuiltInBuilder::callable(realm, Self::get_flags) let get_flags = BuiltInBuilder::callable(realm, Self::get_flags)
.name("get flags") .name(js_string!("get flags"))
.build(); .build();
let get_source = BuiltInBuilder::callable(realm, Self::get_source) let get_source = BuiltInBuilder::callable(realm, Self::get_source)
.name("get source") .name(js_string!("get source"))
.build(); .build();
let regexp = BuiltInBuilder::from_standard_constructor::<Self>(realm) let regexp = BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( .static_accessor(
@ -91,46 +91,87 @@ impl IntrinsicObject for RegExp {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property(utf16!("lastIndex"), 0, Attribute::all()) .property(js_string!("lastIndex"), 0, Attribute::all())
.method(Self::test, "test", 1) .method(Self::test, js_string!("test"), 1)
.method(Self::exec, "exec", 1) .method(Self::exec, js_string!("exec"), 1)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.method(Self::r#match, (JsSymbol::r#match(), "[Symbol.match]"), 1) .method(
Self::r#match,
(JsSymbol::r#match(), js_string!("[Symbol.match]")),
1,
)
.method( .method(
Self::match_all, Self::match_all,
(JsSymbol::match_all(), "[Symbol.matchAll]"), (JsSymbol::match_all(), js_string!("[Symbol.matchAll]")),
1,
)
.method(
Self::replace,
(JsSymbol::replace(), js_string!("[Symbol.replace]")),
2,
)
.method(
Self::search,
(JsSymbol::search(), js_string!("[Symbol.search]")),
1, 1,
) )
.method(Self::replace, (JsSymbol::replace(), "[Symbol.replace]"), 2) .method(
.method(Self::search, (JsSymbol::search(), "[Symbol.search]"), 1) Self::split,
.method(Self::split, (JsSymbol::split(), "[Symbol.split]"), 2) (JsSymbol::split(), js_string!("[Symbol.split]")),
2,
)
.accessor( .accessor(
utf16!("hasIndices"), js_string!("hasIndices"),
Some(get_has_indices), Some(get_has_indices),
None, None,
flag_attributes, flag_attributes,
) )
.accessor(utf16!("global"), Some(get_global), None, flag_attributes)
.accessor( .accessor(
utf16!("ignoreCase"), js_string!("global"),
Some(get_global),
None,
flag_attributes,
)
.accessor(
js_string!("ignoreCase"),
Some(get_ignore_case), Some(get_ignore_case),
None, None,
flag_attributes, flag_attributes,
) )
.accessor( .accessor(
utf16!("multiline"), js_string!("multiline"),
Some(get_multiline), Some(get_multiline),
None, None,
flag_attributes, flag_attributes,
) )
.accessor(utf16!("dotAll"), Some(get_dot_all), None, flag_attributes) .accessor(
.accessor(utf16!("unicode"), Some(get_unicode), None, flag_attributes) js_string!("dotAll"),
.accessor(utf16!("sticky"), Some(get_sticky), None, flag_attributes) Some(get_dot_all),
.accessor(utf16!("flags"), Some(get_flags), None, flag_attributes) None,
.accessor(utf16!("source"), Some(get_source), None, flag_attributes); flag_attributes,
)
.accessor(
js_string!("unicode"),
Some(get_unicode),
None,
flag_attributes,
)
.accessor(
js_string!("sticky"),
Some(get_sticky),
None,
flag_attributes,
)
.accessor(js_string!("flags"), Some(get_flags), None, flag_attributes)
.accessor(
js_string!("source"),
Some(get_source),
None,
flag_attributes,
);
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
let regexp = regexp.method(Self::compile, "compile", 2); let regexp = regexp.method(Self::compile, js_string!("compile"), 2);
regexp.build(); regexp.build();
} }
@ -141,7 +182,7 @@ impl IntrinsicObject for RegExp {
} }
impl BuiltInObject for RegExp { impl BuiltInObject for RegExp {
const NAME: &'static str = "RegExp"; const NAME: JsString = StaticJsStrings::REG_EXP;
} }
impl BuiltInConstructor for RegExp { impl BuiltInConstructor for RegExp {
@ -210,12 +251,12 @@ impl BuiltInConstructor for RegExp {
(p, f) (p, f)
} else if let Some(pattern) = pattern_is_regexp { } else if let Some(pattern) = pattern_is_regexp {
// a. Let P be ? Get(pattern, "source"). // a. Let P be ? Get(pattern, "source").
let p = pattern.get("source", context)?; let p = pattern.get(js_string!("source"), context)?;
// b. If flags is undefined, then // b. If flags is undefined, then
let f = if flags.is_undefined() { let f = if flags.is_undefined() {
// i. Let F be ? Get(pattern, "flags"). // i. Let F be ? Get(pattern, "flags").
pattern.get("flags", context)? pattern.get(js_string!("flags"), context)?
// c. Else, // c. Else,
} else { } else {
// i. Let F be flags. // i. Let F be flags.
@ -641,7 +682,7 @@ impl RegExp {
} }
// 18. Return result. // 18. Return result.
return Ok(result.into()); return Ok(js_string!(result).into());
} }
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -684,7 +725,7 @@ impl RegExp {
this, this,
&JsValue::new(context.intrinsics().constructors().regexp().prototype()), &JsValue::new(context.intrinsics().constructors().regexp().prototype()),
) { ) {
Ok(JsValue::new("(?:)")) Ok(JsValue::new(js_string!("(?:)")))
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
.with_message("RegExp.prototype.source method called on incompatible value") .with_message("RegExp.prototype.source method called on incompatible value")
@ -1064,12 +1105,13 @@ impl RegExp {
// ii. Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue). // ii. Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue).
// iii. Append s to groupNames. // iii. Append s to groupNames.
for (name, range) in named_groups { for (name, range) in named_groups {
let name = js_string!(name);
if let Some(range) = range { if let Some(range) = range {
// TODO: Full UTF-16 regex support // TODO: Full UTF-16 regex support
let value = js_string!(&lossy_input[range.clone()]); let value = js_string!(&lossy_input[range.clone()]);
groups groups
.create_data_property_or_throw(name, value, context) .create_data_property_or_throw(name.clone(), value, context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups ) // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups )
@ -1079,7 +1121,7 @@ impl RegExp {
// d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), matchIndexPair). // d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), matchIndexPair).
group_names group_names
.create_data_property_or_throw( .create_data_property_or_throw(
name, name.clone(),
Array::create_array_from_list( Array::create_array_from_list(
[range.start.into(), range.end.into()], [range.start.into(), range.end.into()],
context, context,
@ -1089,7 +1131,7 @@ impl RegExp {
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
} else { } else {
groups groups
.create_data_property_or_throw(name, JsValue::undefined(), context) .create_data_property_or_throw(name.clone(), JsValue::undefined(), context)
.expect("this CreateDataPropertyOrThrow call must not fail"); .expect("this CreateDataPropertyOrThrow call must not fail");
// 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups ) // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups )

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

@ -14,6 +14,7 @@ use crate::{
builtins::{iterable::create_iter_result_object, regexp, BuiltInBuilder, IntrinsicObject}, builtins::{iterable::create_iter_result_object, regexp, BuiltInBuilder, IntrinsicObject},
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
@ -42,7 +43,7 @@ pub struct RegExpStringIterator {
impl IntrinsicObject for RegExpStringIterator { impl IntrinsicObject for RegExpStringIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("RegExpStringIterator", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -52,10 +53,10 @@ impl IntrinsicObject for RegExpStringIterator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 0) .static_method(Self::next, js_string!("next"), 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"RegExp String Iterator", js_string!("RegExp String Iterator"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.build(); .build();

26
boa_engine/src/builtins/regexp/tests.rs

@ -1,4 +1,6 @@
use crate::{object::JsObject, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; use crate::{
js_string, object::JsObject, run_test_actions, JsNativeErrorKind, JsValue, TestAction,
};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -33,7 +35,7 @@ fn species() {
// return-value // return-value
TestAction::assert("Object.is(accessor.call(thisVal), thisVal)"), TestAction::assert("Object.is(accessor.call(thisVal), thisVal)"),
// symbol-species-name // symbol-species-name
TestAction::assert_eq("name.value", "get [Symbol.species]"), TestAction::assert_eq("name.value", js_string!("get [Symbol.species]")),
TestAction::assert("!name.enumerable"), TestAction::assert("!name.enumerable"),
TestAction::assert("!name.writable"), TestAction::assert("!name.writable"),
TestAction::assert("name.configurable"), TestAction::assert("name.configurable"),
@ -61,7 +63,7 @@ fn flags() {
TestAction::assert("!re_gi.dotAll"), TestAction::assert("!re_gi.dotAll"),
TestAction::assert("!re_gi.unicode"), TestAction::assert("!re_gi.unicode"),
TestAction::assert("!re_gi.sticky"), TestAction::assert("!re_gi.sticky"),
TestAction::assert_eq("re_gi.flags", "gi"), TestAction::assert_eq("re_gi.flags", js_string!("gi")),
// //
TestAction::assert("!re_sm.global"), TestAction::assert("!re_sm.global"),
TestAction::assert("!re_sm.ignoreCase"), TestAction::assert("!re_sm.ignoreCase"),
@ -69,7 +71,7 @@ fn flags() {
TestAction::assert("re_sm.dotAll"), TestAction::assert("re_sm.dotAll"),
TestAction::assert("!re_sm.unicode"), TestAction::assert("!re_sm.unicode"),
TestAction::assert("!re_sm.sticky"), TestAction::assert("!re_sm.sticky"),
TestAction::assert_eq("re_sm.flags", "ms"), TestAction::assert_eq("re_sm.flags", js_string!("ms")),
// //
TestAction::assert("!re_u.global"), TestAction::assert("!re_u.global"),
TestAction::assert("!re_u.ignoreCase"), TestAction::assert("!re_u.ignoreCase"),
@ -77,7 +79,7 @@ fn flags() {
TestAction::assert("!re_u.dotAll"), TestAction::assert("!re_u.dotAll"),
TestAction::assert("re_u.unicode"), TestAction::assert("re_u.unicode"),
TestAction::assert("!re_u.sticky"), TestAction::assert("!re_u.sticky"),
TestAction::assert_eq("re_u.flags", "u"), TestAction::assert_eq("re_u.flags", js_string!("u")),
]); ]);
} }
@ -110,7 +112,7 @@ fn exec() {
TestAction::assert_eq("result.index", 4), TestAction::assert_eq("result.index", 4),
TestAction::assert_eq( TestAction::assert_eq(
"result.input", "result.input",
"The Quick Brown Fox Jumps Over The Lazy Dog", js_string!("The Quick Brown Fox Jumps Over The Lazy Dog"),
), ),
]); ]);
} }
@ -134,11 +136,11 @@ fn no_panic_on_parse_fail() {
#[test] #[test]
fn to_string() { fn to_string() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("(new RegExp('a+b+c')).toString()", "/a+b+c/"), TestAction::assert_eq("(new RegExp('a+b+c')).toString()", js_string!("/a+b+c/")),
TestAction::assert_eq("(new RegExp('bar', 'g')).toString()", "/bar/g"), TestAction::assert_eq("(new RegExp('bar', 'g')).toString()", js_string!("/bar/g")),
TestAction::assert_eq(r"(new RegExp('\\n', 'g')).toString()", r"/\n/g"), TestAction::assert_eq(r"(new RegExp('\\n', 'g')).toString()", js_string!(r"/\n/g")),
TestAction::assert_eq(r"/\n/g.toString()", r"/\n/g"), TestAction::assert_eq(r"/\n/g.toString()", js_string!(r"/\n/g")),
TestAction::assert_eq(r"/,\;/.toString()", r"/,\;/"), TestAction::assert_eq(r"/,\;/.toString()", js_string!(r"/,\;/")),
]); ]);
} }
#[test] #[test]
@ -160,7 +162,7 @@ fn search() {
TestAction::assert("!length.writable"), TestAction::assert("!length.writable"),
TestAction::assert("length.configurable"), TestAction::assert("length.configurable"),
// name // name
TestAction::assert_eq("name.value", "[Symbol.search]"), TestAction::assert_eq("name.value", js_string!("[Symbol.search]")),
TestAction::assert("!name.enumerable"), TestAction::assert("!name.enumerable"),
TestAction::assert("!name.writable"), TestAction::assert("!name.writable"),
TestAction::assert("name.configurable"), TestAction::assert("name.configurable"),

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

@ -22,12 +22,13 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
use num_traits::Zero; use num_traits::Zero;
@ -42,18 +43,18 @@ impl IntrinsicObject for Set {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
} }
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let size_getter = BuiltInBuilder::callable(realm, Self::size_getter) let size_getter = BuiltInBuilder::callable(realm, Self::size_getter)
.name("get size") .name(js_string!("get size"))
.build(); .build();
let values_function = BuiltInBuilder::callable(realm, Self::values) let values_function = BuiltInBuilder::callable(realm, Self::values)
.name("values") .name(js_string!("values"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
@ -63,12 +64,12 @@ impl IntrinsicObject for Set {
None, None,
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::add, "add", 1) .method(Self::add, js_string!("add"), 1)
.method(Self::clear, "clear", 0) .method(Self::clear, js_string!("clear"), 0)
.method(Self::delete, "delete", 1) .method(Self::delete, js_string!("delete"), 1)
.method(Self::entries, "entries", 0) .method(Self::entries, js_string!("entries"), 0)
.method(Self::for_each, "forEach", 1) .method(Self::for_each, js_string!("forEach"), 1)
.method(Self::has, "has", 1) .method(Self::has, js_string!("has"), 1)
.property( .property(
utf16!("keys"), utf16!("keys"),
values_function.clone(), values_function.clone(),
@ -100,7 +101,7 @@ impl IntrinsicObject for Set {
} }
impl BuiltInObject for Set { impl BuiltInObject for Set {
const NAME: &'static str = "Set"; const NAME: JsString = StaticJsStrings::SET;
} }
impl BuiltInConstructor for Set { impl BuiltInConstructor for Set {

7
boa_engine/src/builtins/set/set_iterator.rs

@ -12,6 +12,7 @@ use crate::{
}, },
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::{JsObject, ObjectData}, object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
@ -38,7 +39,7 @@ pub struct SetIterator {
impl IntrinsicObject for SetIterator { impl IntrinsicObject for SetIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("SetIterator", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -48,10 +49,10 @@ impl IntrinsicObject for SetIterator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 0) .static_method(Self::next, js_string!("next"), 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"Set Iterator", js_string!("Set Iterator"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.build(); .build();

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

@ -17,7 +17,7 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor}, property::{Attribute, PropertyDescriptor},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
string::{CodePoint, Utf16Trim}, string::{CodePoint, Utf16Trim},
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
@ -76,18 +76,18 @@ pub(crate) struct String;
impl IntrinsicObject for String { impl IntrinsicObject for String {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let symbol_iterator = JsSymbol::iterator(); let symbol_iterator = JsSymbol::iterator();
let trim_start = BuiltInBuilder::callable(realm, Self::trim_start) let trim_start = BuiltInBuilder::callable(realm, Self::trim_start)
.length(0) .length(0)
.name("trimStart") .name(js_string!("trimStart"))
.build(); .build();
let trim_end = BuiltInBuilder::callable(realm, Self::trim_end) let trim_end = BuiltInBuilder::callable(realm, Self::trim_end)
.length(0) .length(0)
.name("trimEnd") .name(js_string!("trimEnd"))
.build(); .build();
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
@ -98,81 +98,93 @@ impl IntrinsicObject for String {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm) let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(utf16!("length"), 0, attribute) .property(js_string!("length"), 0, attribute)
.property( .property(
utf16!("trimStart"), js_string!("trimStart"),
trim_start, trim_start,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
utf16!("trimEnd"), js_string!("trimEnd"),
trim_end, trim_end,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.static_method(Self::raw, "raw", 1) .static_method(Self::raw, js_string!("raw"), 1)
.static_method(Self::from_char_code, "fromCharCode", 1) .static_method(Self::from_char_code, js_string!("fromCharCode"), 1)
.static_method(Self::from_code_point, "fromCodePoint", 1) .static_method(Self::from_code_point, js_string!("fromCodePoint"), 1)
.method(Self::char_at, "charAt", 1) .method(Self::char_at, js_string!("charAt"), 1)
.method(Self::char_code_at, "charCodeAt", 1) .method(Self::char_code_at, js_string!("charCodeAt"), 1)
.method(Self::code_point_at, "codePointAt", 1) .method(Self::code_point_at, js_string!("codePointAt"), 1)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.method(Self::concat, "concat", 1) .method(Self::concat, js_string!("concat"), 1)
.method(Self::repeat, "repeat", 1) .method(Self::repeat, js_string!("repeat"), 1)
.method(Self::slice, "slice", 2) .method(Self::slice, js_string!("slice"), 2)
.method(Self::starts_with, "startsWith", 1) .method(Self::starts_with, js_string!("startsWith"), 1)
.method(Self::ends_with, "endsWith", 1) .method(Self::ends_with, js_string!("endsWith"), 1)
.method(Self::includes, "includes", 1) .method(Self::includes, js_string!("includes"), 1)
.method(Self::index_of, "indexOf", 1) .method(Self::index_of, js_string!("indexOf"), 1)
.method(Self::is_well_formed, "isWellFormed", 0) .method(Self::is_well_formed, js_string!("isWellFormed"), 0)
.method(Self::last_index_of, "lastIndexOf", 1) .method(Self::last_index_of, js_string!("lastIndexOf"), 1)
.method(Self::locale_compare, "localeCompare", 1) .method(Self::locale_compare, js_string!("localeCompare"), 1)
.method(Self::r#match, "match", 1) .method(Self::r#match, js_string!("match"), 1)
.method(Self::normalize, "normalize", 0) .method(Self::normalize, js_string!("normalize"), 0)
.method(Self::pad_end, "padEnd", 1) .method(Self::pad_end, js_string!("padEnd"), 1)
.method(Self::pad_start, "padStart", 1) .method(Self::pad_start, js_string!("padStart"), 1)
.method(Self::trim, "trim", 0) .method(Self::trim, js_string!("trim"), 0)
.method(Self::to_case::<false>, "toLowerCase", 0) .method(Self::to_case::<false>, js_string!("toLowerCase"), 0)
.method(Self::to_case::<true>, "toUpperCase", 0) .method(Self::to_case::<true>, js_string!("toUpperCase"), 0)
.method(Self::to_well_formed, "toWellFormed", 0) .method(Self::to_well_formed, js_string!("toWellFormed"), 0)
.method(Self::to_locale_case::<false>, "toLocaleLowerCase", 0) .method(
.method(Self::to_locale_case::<true>, "toLocaleUpperCase", 0) Self::to_locale_case::<false>,
.method(Self::substring, "substring", 2) js_string!("toLocaleLowerCase"),
.method(Self::split, "split", 2) 0,
.method(Self::value_of, "valueOf", 0) )
.method(Self::match_all, "matchAll", 1) .method(
.method(Self::replace, "replace", 2) Self::to_locale_case::<true>,
.method(Self::replace_all, "replaceAll", 2) js_string!("toLocaleUpperCase"),
.method(Self::iterator, (symbol_iterator, "[Symbol.iterator]"), 0) 0,
.method(Self::search, "search", 1) )
.method(Self::at, "at", 1); .method(Self::substring, js_string!("substring"), 2)
.method(Self::split, js_string!("split"), 2)
.method(Self::value_of, js_string!("valueOf"), 0)
.method(Self::match_all, js_string!("matchAll"), 1)
.method(Self::replace, js_string!("replace"), 2)
.method(Self::replace_all, js_string!("replaceAll"), 2)
.method(
Self::iterator,
(symbol_iterator, js_string!("[Symbol.iterator]")),
0,
)
.method(Self::search, js_string!("search"), 1)
.method(Self::at, js_string!("at"), 1);
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
let builder = { let builder = {
builder builder
.property( .property(
utf16!("trimLeft"), js_string!("trimLeft"),
trim_left, trim_left,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
utf16!("trimRight"), js_string!("trimRight"),
trim_right, trim_right,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::substr, "substr", 2) .method(Self::substr, js_string!("substr"), 2)
.method(Self::anchor, "anchor", 1) .method(Self::anchor, js_string!("anchor"), 1)
.method(Self::big, "big", 0) .method(Self::big, js_string!("big"), 0)
.method(Self::blink, "blink", 0) .method(Self::blink, js_string!("blink"), 0)
.method(Self::bold, "bold", 0) .method(Self::bold, js_string!("bold"), 0)
.method(Self::fixed, "fixed", 0) .method(Self::fixed, js_string!("fixed"), 0)
.method(Self::fontcolor, "fontcolor", 1) .method(Self::fontcolor, js_string!("fontcolor"), 1)
.method(Self::fontsize, "fontsize", 1) .method(Self::fontsize, js_string!("fontsize"), 1)
.method(Self::italics, "italics", 0) .method(Self::italics, js_string!("italics"), 0)
.method(Self::link, "link", 1) .method(Self::link, js_string!("link"), 1)
.method(Self::small, "small", 0) .method(Self::small, js_string!("small"), 0)
.method(Self::strike, "strike", 0) .method(Self::strike, js_string!("strike"), 0)
.method(Self::sub, "sub", 0) .method(Self::sub, js_string!("sub"), 0)
.method(Self::sup, "sup", 0) .method(Self::sup, js_string!("sup"), 0)
}; };
builder.build(); builder.build();
@ -184,7 +196,7 @@ impl IntrinsicObject for String {
} }
impl BuiltInObject for String { impl BuiltInObject for String {
const NAME: &'static str = "String"; const NAME: JsString = StaticJsStrings::STRING;
} }
impl BuiltInConstructor for String { impl BuiltInConstructor for String {
@ -1835,7 +1847,7 @@ impl String {
.collect::<std::string::String>(); .collect::<std::string::String>();
// 7. Return result. // 7. Return result.
Ok(result.into()) Ok(js_string!(result).into())
} }
/// `String.prototype.substring( indexStart[, indexEnd] )` /// `String.prototype.substring( indexStart[, indexEnd] )`

6
boa_engine/src/builtins/string/string_iterator.rs

@ -33,7 +33,7 @@ pub struct StringIterator {
impl IntrinsicObject for StringIterator { impl IntrinsicObject for StringIterator {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("StringIterator", "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm) BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype( .prototype(
@ -43,10 +43,10 @@ impl IntrinsicObject for StringIterator {
.iterator_prototypes() .iterator_prototypes()
.iterator(), .iterator(),
) )
.static_method(Self::next, "next", 0) .static_method(Self::next, js_string!("next"), 0)
.static_property( .static_property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"String Iterator", js_string!("String Iterator"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.build(); .build();

131
boa_engine/src/builtins/string/tests.rs

@ -56,9 +56,12 @@ fn concat() {
"#}), "#}),
TestAction::assert_eq( TestAction::assert_eq(
"hello.concat(world, nice)", "hello.concat(world, nice)",
"Hello, world! Have a nice day.", js_string!("Hello, world! Have a nice day."),
),
TestAction::assert_eq(
"hello + world + nice",
js_string!("Hello, world! Have a nice day."),
), ),
TestAction::assert_eq("hello + world + nice", "Hello, world! Have a nice day."),
]); ]);
} }
@ -69,7 +72,10 @@ fn generic_concat() {
Number.prototype.concat = String.prototype.concat; Number.prototype.concat = String.prototype.concat;
let number = new Number(100); let number = new Number(100);
"#}), "#}),
TestAction::assert_eq("number.concat(' - 50', ' = 50')", "100 - 50 = 50"), TestAction::assert_eq(
"number.concat(' - 50', ' = 50')",
js_string!("100 - 50 = 50"),
),
]); ]);
} }
@ -90,12 +96,12 @@ fn repeat() {
var en = new String('english'); var en = new String('english');
var zh = new String(''); var zh = new String('');
"#}), "#}),
TestAction::assert_eq("empty.repeat(0)", ""), TestAction::assert_eq("empty.repeat(0)", js_string!()),
TestAction::assert_eq("empty.repeat(1)", ""), TestAction::assert_eq("empty.repeat(1)", js_string!()),
TestAction::assert_eq("en.repeat(0)", ""), TestAction::assert_eq("en.repeat(0)", js_string!()),
TestAction::assert_eq("zh.repeat(0)", ""), TestAction::assert_eq("zh.repeat(0)", js_string!()),
TestAction::assert_eq("en.repeat(1)", "english"), TestAction::assert_eq("en.repeat(1)", js_string!("english")),
TestAction::assert_eq("zh.repeat(2)", "中文中文"), TestAction::assert_eq("zh.repeat(2)", js_string!("中文中文")),
]); ]);
} }
@ -133,10 +139,10 @@ fn repeat_throws_when_count_overflows_max_length() {
fn repeat_generic() { fn repeat_generic() {
run_test_actions([ run_test_actions([
TestAction::run("Number.prototype.repeat = String.prototype.repeat;"), TestAction::run("Number.prototype.repeat = String.prototype.repeat;"),
TestAction::assert_eq("(0).repeat(0)", ""), TestAction::assert_eq("(0).repeat(0)", js_string!()),
TestAction::assert_eq("(1).repeat(1)", "1"), TestAction::assert_eq("(1).repeat(1)", js_string!("1")),
TestAction::assert_eq("(1).repeat(5)", "11111"), TestAction::assert_eq("(1).repeat(5)", js_string!("11111")),
TestAction::assert_eq("(12).repeat(3)", "121212"), TestAction::assert_eq("(12).repeat(3)", js_string!("121212")),
]); ]);
} }
@ -146,7 +152,7 @@ fn replace() {
indoc! {r#" indoc! {r#"
"abc".replace("a", "2") "abc".replace("a", "2")
"#}, "#},
"2bc", js_string!("2bc"),
)]); )]);
} }
@ -156,7 +162,7 @@ fn replace_no_match() {
indoc! {r#" indoc! {r#"
"abc".replace(/d/, "$&$&") "abc".replace(/d/, "$&$&")
"#}, "#},
"abc", js_string!("abc"),
)]); )]);
} }
@ -166,7 +172,7 @@ fn replace_with_capture_groups() {
indoc! {r#" indoc! {r#"
"John Smith".replace(/(\w+)\s(\w+)/, '$2, $1') "John Smith".replace(/(\w+)\s(\w+)/, '$2, $1')
"#}, "#},
"Smith, John", js_string!("Smith, John"),
)]); )]);
} }
@ -177,7 +183,7 @@ fn replace_with_tenth_capture_group() {
var re = /(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/; var re = /(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/;
"0123456789".replace(re, '$10') "0123456789".replace(re, '$10')
"#}, "#},
"9", js_string!("9"),
)]); )]);
} }
@ -193,11 +199,11 @@ fn replace_substitutions() {
var end = a.replace(re, " $' "); var end = a.replace(re, " $' ");
var no_sub = a.replace(re, " $_ "); var no_sub = a.replace(re, " $_ ");
"#}), "#}),
TestAction::assert_eq("a.replace(re, \" $$ \")", "one $ three"), TestAction::assert_eq("a.replace(re, \" $$ \")", js_string!("one $ three")),
TestAction::assert_eq("a.replace(re, \"$&$&\")", "one two two three"), TestAction::assert_eq("a.replace(re, \"$&$&\")", js_string!("one two two three")),
TestAction::assert_eq("a.replace(re, \" $` \")", "one one three"), TestAction::assert_eq("a.replace(re, \" $` \")", js_string!("one one three")),
TestAction::assert_eq("a.replace(re, \" $' \")", "one three three"), TestAction::assert_eq("a.replace(re, \" $' \")", js_string!("one three three")),
TestAction::assert_eq("a.replace(re, \" $_ \")", "one $_ three"), TestAction::assert_eq("a.replace(re, \" $_ \")", js_string!("one $_ three")),
]); ]);
} }
@ -216,11 +222,11 @@ fn replace_with_function() {
"#}), "#}),
TestAction::assert_eq( TestAction::assert_eq(
"\"ecmascript is cool\".replace(/c(o)(o)(l)/, replacer)", "\"ecmascript is cool\".replace(/c(o)(o)(l)/, replacer)",
"ecmascript is awesome!", js_string!("ecmascript is awesome!"),
), ),
TestAction::assert_eq("p1", "o"), TestAction::assert_eq("p1", js_string!("o")),
TestAction::assert_eq("p2", "o"), TestAction::assert_eq("p2", js_string!("o")),
TestAction::assert_eq("p3", "l"), TestAction::assert_eq("p3", js_string!("l")),
TestAction::assert_eq("length", 14), TestAction::assert_eq("length", 14),
]); ]);
} }
@ -323,7 +329,7 @@ fn match_all_one() {
) )
"#}), "#}),
TestAction::assert_eq("m1.value.index", 0), TestAction::assert_eq("m1.value.index", 0),
TestAction::assert_eq("m1.value.input", "test1test2"), TestAction::assert_eq("m1.value.input", js_string!("test1test2")),
TestAction::assert_eq("m1.value.groups", JsValue::undefined()), TestAction::assert_eq("m1.value.groups", JsValue::undefined()),
TestAction::assert(indoc! {r#" TestAction::assert(indoc! {r#"
arrayEquals( arrayEquals(
@ -332,7 +338,7 @@ fn match_all_one() {
) )
"#}), "#}),
TestAction::assert_eq("m2.value.index", 5), TestAction::assert_eq("m2.value.index", 5),
TestAction::assert_eq("m2.value.input", "test1test2"), TestAction::assert_eq("m2.value.input", js_string!("test1test2")),
TestAction::assert_eq("m2.value.groups", JsValue::undefined()), TestAction::assert_eq("m2.value.groups", JsValue::undefined()),
TestAction::assert_eq("m3.value", JsValue::undefined()), TestAction::assert_eq("m3.value", JsValue::undefined()),
]); ]);
@ -360,7 +366,7 @@ fn match_all_two() {
) )
"#}), "#}),
TestAction::assert_eq("m1.value.index", 6), TestAction::assert_eq("m1.value.index", 6),
TestAction::assert_eq("m1.value.input", "table football, foosball"), TestAction::assert_eq("m1.value.input", js_string!("table football, foosball")),
TestAction::assert_eq("m1.value.groups", JsValue::undefined()), TestAction::assert_eq("m1.value.groups", JsValue::undefined()),
TestAction::assert(indoc! {r#" TestAction::assert(indoc! {r#"
arrayEquals( arrayEquals(
@ -369,7 +375,7 @@ fn match_all_two() {
) )
"#}), "#}),
TestAction::assert_eq("m2.value.index", 16), TestAction::assert_eq("m2.value.index", 16),
TestAction::assert_eq("m2.value.input", "table football, foosball"), TestAction::assert_eq("m2.value.input", js_string!("table football, foosball")),
TestAction::assert_eq("m2.value.groups", JsValue::undefined()), TestAction::assert_eq("m2.value.groups", JsValue::undefined()),
TestAction::assert_eq("m3.value", JsValue::undefined()), TestAction::assert_eq("m3.value", JsValue::undefined()),
]); ]);
@ -395,7 +401,7 @@ fn test_match() {
TestAction::assert_eq("result1.index", 4), TestAction::assert_eq("result1.index", 4),
TestAction::assert_eq( TestAction::assert_eq(
"result1.input", "result1.input",
"The Quick Brown Fox Jumps Over The Lazy Dog", js_string!("The Quick Brown Fox Jumps Over The Lazy Dog"),
), ),
TestAction::assert(indoc! {r#" TestAction::assert(indoc! {r#"
arrayEquals( arrayEquals(
@ -412,7 +418,7 @@ fn test_match() {
TestAction::assert_eq("result3.index", 0), TestAction::assert_eq("result3.index", 0),
TestAction::assert_eq( TestAction::assert_eq(
"result3.input", "result3.input",
"The Quick Brown Fox Jumps Over The Lazy Dog", js_string!("The Quick Brown Fox Jumps Over The Lazy Dog"),
), ),
TestAction::assert(indoc! {r#" TestAction::assert(indoc! {r#"
arrayEquals( arrayEquals(
@ -426,30 +432,30 @@ fn test_match() {
#[test] #[test]
fn trim() { fn trim() {
run_test_actions([ run_test_actions([
TestAction::assert_eq(r"'Hello'.trim()", "Hello"), TestAction::assert_eq(r"'Hello'.trim()", js_string!("Hello")),
TestAction::assert_eq(r"' \nHello'.trim()", "Hello"), TestAction::assert_eq(r"' \nHello'.trim()", js_string!("Hello")),
TestAction::assert_eq(r"'Hello \n\r'.trim()", "Hello"), TestAction::assert_eq(r"'Hello \n\r'.trim()", js_string!("Hello")),
TestAction::assert_eq(r"' Hello '.trim()", "Hello"), TestAction::assert_eq(r"' Hello '.trim()", js_string!("Hello")),
]); ]);
} }
#[test] #[test]
fn trim_start() { fn trim_start() {
run_test_actions([ run_test_actions([
TestAction::assert_eq(r"'Hello'.trimStart()", "Hello"), TestAction::assert_eq(r"'Hello'.trimStart()", js_string!("Hello")),
TestAction::assert_eq(r"' \nHello'.trimStart()", "Hello"), TestAction::assert_eq(r"' \nHello'.trimStart()", js_string!("Hello")),
TestAction::assert_eq(r"'Hello \n\r'.trimStart()", "Hello \n\r"), TestAction::assert_eq(r"'Hello \n\r'.trimStart()", js_string!("Hello \n\r")),
TestAction::assert_eq(r"' Hello '.trimStart()", "Hello "), TestAction::assert_eq(r"' Hello '.trimStart()", js_string!("Hello ")),
]); ]);
} }
#[test] #[test]
fn trim_end() { fn trim_end() {
run_test_actions([ run_test_actions([
TestAction::assert_eq(r"'Hello'.trimEnd()", "Hello"), TestAction::assert_eq(r"'Hello'.trimEnd()", js_string!("Hello")),
TestAction::assert_eq(r"' \nHello'.trimEnd()", " \nHello"), TestAction::assert_eq(r"' \nHello'.trimEnd()", js_string!(" \nHello")),
TestAction::assert_eq(r"'Hello \n\r'.trimEnd()", "Hello"), TestAction::assert_eq(r"'Hello \n\r'.trimEnd()", js_string!("Hello")),
TestAction::assert_eq(r"' Hello '.trimEnd()", " Hello"), TestAction::assert_eq(r"' Hello '.trimEnd()", js_string!(" Hello")),
]); ]);
} }
@ -572,7 +578,7 @@ fn split_with_symbol_split_method() {
sep_a[Symbol.split] = function(s, limit) { return s + limit.toString(); }; sep_a[Symbol.split] = function(s, limit) { return s + limit.toString(); };
'hello'.split(sep_a, 10) 'hello'.split(sep_a, 10)
"#}, "#},
"hello10", js_string!("hello10"),
), ),
TestAction::assert(indoc! {r#" TestAction::assert(indoc! {r#"
let sep_b = {}; let sep_b = {};
@ -745,11 +751,11 @@ fn last_index_non_integer_position_argument() {
#[test] #[test]
fn char_at() { fn char_at() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("'abc'.charAt(-1)", ""), TestAction::assert_eq("'abc'.charAt(-1)", js_string!()),
TestAction::assert_eq("'abc'.charAt(1)", "b"), TestAction::assert_eq("'abc'.charAt(1)", js_string!("b")),
TestAction::assert_eq("'abc'.charAt(9)", ""), TestAction::assert_eq("'abc'.charAt(9)", js_string!()),
TestAction::assert_eq("'abc'.charAt()", "a"), TestAction::assert_eq("'abc'.charAt()", js_string!("a")),
TestAction::assert_eq("'abc'.charAt(null)", "a"), TestAction::assert_eq("'abc'.charAt(null)", js_string!("a")),
TestAction::assert_eq(r"'\uDBFF'.charAt(0)", js_string!(&[0xDBFFu16])), TestAction::assert_eq(r"'\uDBFF'.charAt(0)", js_string!(&[0xDBFFu16])),
]); ]);
} }
@ -788,11 +794,11 @@ fn code_point_at() {
#[test] #[test]
fn slice() { fn slice() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("'abc'.slice()", "abc"), TestAction::assert_eq("'abc'.slice()", js_string!("abc")),
TestAction::assert_eq("'abc'.slice(1)", "bc"), TestAction::assert_eq("'abc'.slice(1)", js_string!("bc")),
TestAction::assert_eq("'abc'.slice(-1)", "c"), TestAction::assert_eq("'abc'.slice(-1)", js_string!("c")),
TestAction::assert_eq("'abc'.slice(0, 9)", "abc"), TestAction::assert_eq("'abc'.slice(0, 9)", js_string!("abc")),
TestAction::assert_eq("'abc'.slice(9, 10)", ""), TestAction::assert_eq("'abc'.slice(9, 10)", js_string!()),
]); ]);
} }
@ -838,8 +844,8 @@ fn unicode_iter() {
fn string_get_property() { fn string_get_property() {
run_test_actions([ run_test_actions([
TestAction::assert_eq("'abc'[-1]", JsValue::undefined()), TestAction::assert_eq("'abc'[-1]", JsValue::undefined()),
TestAction::assert_eq("'abc'[1]", "b"), TestAction::assert_eq("'abc'[1]", js_string!("b")),
TestAction::assert_eq("'abc'[2]", "c"), TestAction::assert_eq("'abc'[2]", js_string!("c")),
TestAction::assert_eq("'abc'[3]", JsValue::undefined()), TestAction::assert_eq("'abc'[3]", JsValue::undefined()),
TestAction::assert_eq("'abc'['foo']", JsValue::undefined()), TestAction::assert_eq("'abc'['foo']", JsValue::undefined()),
TestAction::assert_eq("'😀'[0]", js_string!(&[0xD83D])), TestAction::assert_eq("'😀'[0]", js_string!(&[0xD83D])),
@ -860,9 +866,9 @@ fn search() {
fn from_code_point() { fn from_code_point() {
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint // Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
run_test_actions([ run_test_actions([
TestAction::assert_eq("String.fromCodePoint(42)", "*"), TestAction::assert_eq("String.fromCodePoint(42)", js_string!("*")),
TestAction::assert_eq("String.fromCodePoint(65, 90)", "AZ"), TestAction::assert_eq("String.fromCodePoint(65, 90)", js_string!("AZ")),
TestAction::assert_eq("String.fromCodePoint(0x404)", "Є"), TestAction::assert_eq("String.fromCodePoint(0x404)", js_string!("Є")),
TestAction::assert_eq( TestAction::assert_eq(
"String.fromCodePoint(0x2f804)", "String.fromCodePoint(0x2f804)",
js_string!(&[0xD87E, 0xDC04]), js_string!(&[0xD87E, 0xDC04]),
@ -876,7 +882,10 @@ fn from_code_point() {
"String.fromCharCode(0xD800, 0xD8FF)", "String.fromCharCode(0xD800, 0xD8FF)",
js_string!(&[0xD800, 0xD8FF]), js_string!(&[0xD800, 0xD8FF]),
), ),
TestAction::assert_eq("String.fromCodePoint(9731, 9733, 9842, 0x4F60)", "☃★♲你"), TestAction::assert_eq(
"String.fromCodePoint(9731, 9733, 9842, 0x4F60)",
js_string!("☃★♲你"),
),
TestAction::assert_native_error( TestAction::assert_native_error(
"String.fromCodePoint('_')", "String.fromCodePoint('_')",
JsNativeErrorKind::Range, JsNativeErrorKind::Range,

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

@ -28,7 +28,7 @@ use crate::{
object::JsObject, object::JsObject,
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
@ -93,7 +93,7 @@ pub struct Symbol;
impl IntrinsicObject for Symbol { impl IntrinsicObject for Symbol {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let symbol_async_iterator = JsSymbol::async_iterator(); let symbol_async_iterator = JsSymbol::async_iterator();
let symbol_has_instance = JsSymbol::has_instance(); let symbol_has_instance = JsSymbol::has_instance();
@ -112,46 +112,50 @@ impl IntrinsicObject for Symbol {
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive) let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive)
.name("[Symbol.toPrimitive]") .name(js_string!("[Symbol.toPrimitive]"))
.length(1) .length(1)
.build(); .build();
let get_description = BuiltInBuilder::callable(realm, Self::get_description) let get_description = BuiltInBuilder::callable(realm, Self::get_description)
.name("get description") .name(js_string!("get description"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_method(Self::for_, "for", 1) .static_method(Self::for_, js_string!("for"), 1)
.static_method(Self::key_for, "keyFor", 1) .static_method(Self::key_for, js_string!("keyFor"), 1)
.static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute)
.static_property(utf16!("hasInstance"), symbol_has_instance, attribute)
.static_property( .static_property(
utf16!("isConcatSpreadable"), js_string!("asyncIterator"),
symbol_async_iterator,
attribute,
)
.static_property(js_string!("hasInstance"), symbol_has_instance, attribute)
.static_property(
js_string!("isConcatSpreadable"),
symbol_is_concat_spreadable, symbol_is_concat_spreadable,
attribute, attribute,
) )
.static_property(utf16!("iterator"), symbol_iterator, attribute) .static_property(js_string!("iterator"), symbol_iterator, attribute)
.static_property(utf16!("match"), symbol_match, attribute) .static_property(js_string!("match"), symbol_match, attribute)
.static_property(utf16!("matchAll"), symbol_match_all, attribute) .static_property(js_string!("matchAll"), symbol_match_all, attribute)
.static_property(utf16!("replace"), symbol_replace, attribute) .static_property(js_string!("replace"), symbol_replace, attribute)
.static_property(utf16!("search"), symbol_search, attribute) .static_property(js_string!("search"), symbol_search, attribute)
.static_property(utf16!("species"), symbol_species, attribute) .static_property(js_string!("species"), symbol_species, attribute)
.static_property(utf16!("split"), symbol_split, attribute) .static_property(js_string!("split"), symbol_split, attribute)
.static_property( .static_property(
utf16!("toPrimitive"), js_string!("toPrimitive"),
symbol_to_primitive.clone(), symbol_to_primitive.clone(),
attribute, attribute,
) )
.static_property( .static_property(
utf16!("toStringTag"), js_string!("toStringTag"),
symbol_to_string_tag.clone(), symbol_to_string_tag.clone(),
attribute, attribute,
) )
.static_property(utf16!("unscopables"), symbol_unscopables, attribute) .static_property(js_string!("unscopables"), symbol_unscopables, attribute)
.method(Self::to_string, "toString", 0) .method(Self::to_string, js_string!("toString"), 0)
.method(Self::value_of, "valueOf", 0) .method(Self::value_of, js_string!("valueOf"), 0)
.accessor( .accessor(
utf16!("description"), js_string!("description"),
Some(get_description), Some(get_description),
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
@ -175,7 +179,7 @@ impl IntrinsicObject for Symbol {
} }
impl BuiltInObject for Symbol { impl BuiltInObject for Symbol {
const NAME: &'static str = "Symbol"; const NAME: JsString = StaticJsStrings::SYMBOL;
} }
impl BuiltInConstructor for Symbol { impl BuiltInConstructor for Symbol {

4
boa_engine/src/builtins/symbol/tests.rs

@ -1,4 +1,4 @@
use crate::{run_test_actions, JsValue, TestAction}; use crate::{js_string, run_test_actions, JsValue, TestAction};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -12,7 +12,7 @@ fn call_symbol_and_check_return_type() {
fn print_symbol_expect_description() { fn print_symbol_expect_description() {
run_test_actions([TestAction::assert_eq( run_test_actions([TestAction::assert_eq(
"String(Symbol('Hello'))", "String(Symbol('Hello'))",
"Symbol(Hello)", js_string!("Symbol(Hello)"),
)]); )]);
} }

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

@ -29,22 +29,25 @@ use crate::{
}, },
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult, JsString,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
use num_traits::Zero; use num_traits::Zero;
use paste::paste;
use std::cmp::Ordering; use std::cmp::Ordering;
pub mod integer_indexed_object; pub mod integer_indexed_object;
macro_rules! typed_array { macro_rules! typed_array {
($ty:ident, $variant:ident, $name:literal, $global_object_name:ident) => { ($ty:ident, $variant:ident, $name:literal, $js_name:expr, $global_object_name:ident) => {
#[doc = concat!("JavaScript `", $name, "` built-in implementation.")] paste! {
#[derive(Debug, Clone, Copy)] #[doc = "JavaScript `" $name "` built-in implementation."]
pub struct $ty; #[derive(Debug, Clone, Copy)]
pub struct $ty;
}
impl IntrinsicObject for $ty { impl IntrinsicObject for $ty {
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {
@ -52,10 +55,10 @@ macro_rules! typed_array {
} }
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_species = BuiltInBuilder::callable(realm, TypedArray::get_species) let get_species = BuiltInBuilder::callable(realm, TypedArray::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
@ -76,12 +79,12 @@ macro_rules! typed_array {
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.property( .property(
utf16!("BYTES_PER_ELEMENT"), js_string!("BYTES_PER_ELEMENT"),
TypedArrayKind::$variant.element_size(), TypedArrayKind::$variant.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
.static_property( .static_property(
utf16!("BYTES_PER_ELEMENT"), js_string!("BYTES_PER_ELEMENT"),
TypedArrayKind::$variant.element_size(), TypedArrayKind::$variant.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
) )
@ -90,7 +93,7 @@ macro_rules! typed_array {
} }
impl BuiltInObject for $ty { impl BuiltInObject for $ty {
const NAME: &'static str = $name; const NAME: JsString = $js_name;
const ATTRIBUTE: Attribute = Attribute::WRITABLE const ATTRIBUTE: Attribute = Attribute::WRITABLE
.union(Attribute::NON_ENUMERABLE) .union(Attribute::NON_ENUMERABLE)
@ -244,31 +247,31 @@ pub(crate) struct TypedArray;
impl IntrinsicObject for TypedArray { impl IntrinsicObject for TypedArray {
fn init(realm: &Realm) { fn init(realm: &Realm) {
let get_species = BuiltInBuilder::callable(realm, Self::get_species) let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]") .name(js_string!("get [Symbol.species]"))
.build(); .build();
let get_buffer = BuiltInBuilder::callable(realm, Self::buffer) let get_buffer = BuiltInBuilder::callable(realm, Self::buffer)
.name("get buffer") .name(js_string!("get buffer"))
.build(); .build();
let get_byte_length = BuiltInBuilder::callable(realm, Self::byte_length) let get_byte_length = BuiltInBuilder::callable(realm, Self::byte_length)
.name("get byteLength") .name(js_string!("get byteLength"))
.build(); .build();
let get_byte_offset = BuiltInBuilder::callable(realm, Self::byte_offset) let get_byte_offset = BuiltInBuilder::callable(realm, Self::byte_offset)
.name("get byteOffset") .name(js_string!("get byteOffset"))
.build(); .build();
let get_length = BuiltInBuilder::callable(realm, Self::length) let get_length = BuiltInBuilder::callable(realm, Self::length)
.name("get length") .name(js_string!("get length"))
.build(); .build();
let get_to_string_tag = BuiltInBuilder::callable(realm, Self::to_string_tag) let get_to_string_tag = BuiltInBuilder::callable(realm, Self::to_string_tag)
.name("get [Symbol.toStringTag]") .name(js_string!("get [Symbol.toStringTag]"))
.build(); .build();
let values_function = BuiltInBuilder::callable(realm, Self::values) let values_function = BuiltInBuilder::callable(realm, Self::values)
.name("values") .name(js_string!("values"))
.length(0) .length(0)
.build(); .build();
@ -314,44 +317,44 @@ impl IntrinsicObject for TypedArray {
None, None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
) )
.static_method(Self::from, "from", 1) .static_method(Self::from, js_string!("from"), 1)
.static_method(Self::of, "of", 0) .static_method(Self::of, js_string!("of"), 0)
.method(Self::at, "at", 1) .method(Self::at, js_string!("at"), 1)
.method(Self::copy_within, "copyWithin", 2) .method(Self::copy_within, js_string!("copyWithin"), 2)
.method(Self::entries, "entries", 0) .method(Self::entries, js_string!("entries"), 0)
.method(Self::every, "every", 1) .method(Self::every, js_string!("every"), 1)
.method(Self::fill, "fill", 1) .method(Self::fill, js_string!("fill"), 1)
.method(Self::filter, "filter", 1) .method(Self::filter, js_string!("filter"), 1)
.method(Self::find, "find", 1) .method(Self::find, js_string!("find"), 1)
.method(Self::find_index, "findIndex", 1) .method(Self::find_index, js_string!("findIndex"), 1)
.method(Self::find_last, "findLast", 1) .method(Self::find_last, js_string!("findLast"), 1)
.method(Self::find_last_index, "findLastIndex", 1) .method(Self::find_last_index, js_string!("findLastIndex"), 1)
.method(Self::foreach, "forEach", 1) .method(Self::foreach, js_string!("forEach"), 1)
.method(Self::includes, "includes", 1) .method(Self::includes, js_string!("includes"), 1)
.method(Self::index_of, "indexOf", 1) .method(Self::index_of, js_string!("indexOf"), 1)
.method(Self::join, "join", 1) .method(Self::join, js_string!("join"), 1)
.method(Self::keys, "keys", 0) .method(Self::keys, js_string!("keys"), 0)
.method(Self::last_index_of, "lastIndexOf", 1) .method(Self::last_index_of, js_string!("lastIndexOf"), 1)
.method(Self::map, "map", 1) .method(Self::map, js_string!("map"), 1)
.method(Self::reduce, "reduce", 1) .method(Self::reduce, js_string!("reduce"), 1)
.method(Self::reduceright, "reduceRight", 1) .method(Self::reduceright, js_string!("reduceRight"), 1)
.method(Self::reverse, "reverse", 0) .method(Self::reverse, js_string!("reverse"), 0)
.method(Self::set, "set", 1) .method(Self::set, js_string!("set"), 1)
.method(Self::slice, "slice", 2) .method(Self::slice, js_string!("slice"), 2)
.method(Self::some, "some", 1) .method(Self::some, js_string!("some"), 1)
.method(Self::sort, "sort", 1) .method(Self::sort, js_string!("sort"), 1)
.method(Self::subarray, "subarray", 2) .method(Self::subarray, js_string!("subarray"), 2)
.method(Self::to_locale_string, "toLocaleString", 0) .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
// 23.2.3.29 %TypedArray%.prototype.toString ( ) // 23.2.3.29 %TypedArray%.prototype.toString ( )
// The initial value of the %TypedArray%.prototype.toString data property is the same // The initial value of the %TypedArray%.prototype.toString data property is the same
// built-in function object as the Array.prototype.toString method defined in 23.1.3.30. // built-in function object as the Array.prototype.toString method defined in 23.1.3.30.
.property( .property(
utf16!("toString"), js_string!("toString"),
realm.intrinsics().objects().array_prototype_to_string(), realm.intrinsics().objects().array_prototype_to_string(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.property( .property(
"values", js_string!("values"),
values_function, values_function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
@ -364,7 +367,7 @@ impl IntrinsicObject for TypedArray {
} }
impl BuiltInObject for TypedArray { impl BuiltInObject for TypedArray {
const NAME: &'static str = "TypedArray"; const NAME: JsString = StaticJsStrings::TYPED_ARRAY;
} }
impl BuiltInConstructor for TypedArray { impl BuiltInConstructor for TypedArray {
@ -3676,19 +3679,19 @@ impl TypedArrayKind {
} }
/// Gets the name of this typed array name. /// Gets the name of this typed array name.
pub(crate) const fn name(&self) -> &str { pub(crate) const fn name(self) -> JsString {
match self { match self {
Self::Int8 => "Int8Array", Self::Int8 => StaticJsStrings::INT8_ARRAY,
Self::Uint8 => "Uint8Array", Self::Uint8 => StaticJsStrings::UINT8_ARRAY,
Self::Uint8Clamped => "Uint8ClampedArray", Self::Uint8Clamped => StaticJsStrings::UINT8_CLAMPED_ARRAY,
Self::Int16 => "Int16Array", Self::Int16 => StaticJsStrings::INT16_ARRAY,
Self::Uint16 => "Uint16Array", Self::Uint16 => StaticJsStrings::UINT16_ARRAY,
Self::Int32 => "Int32Array", Self::Int32 => StaticJsStrings::INT32_ARRAY,
Self::Uint32 => "Uint32Array", Self::Uint32 => StaticJsStrings::UINT32_ARRAY,
Self::BigInt64 => "BigInt64Array", Self::BigInt64 => StaticJsStrings::BIG_INT64_ARRAY,
Self::BigUint64 => "BigUint64Array", Self::BigUint64 => StaticJsStrings::BIG_UINT64_ARRAY,
Self::Float32 => "Float32Array", Self::Float32 => StaticJsStrings::FLOAT32_ARRAY,
Self::Float64 => "Float64Array", Self::Float64 => StaticJsStrings::FLOAT64_ARRAY,
} }
} }
@ -3697,29 +3700,80 @@ impl TypedArrayKind {
} }
} }
typed_array!(Int8Array, Int8, "Int8Array", typed_int8_array); typed_array!(
typed_array!(Uint8Array, Uint8, "Uint8Array", typed_uint8_array); Int8Array,
Int8,
"Int8Array",
StaticJsStrings::INT8_ARRAY,
typed_int8_array
);
typed_array!(
Uint8Array,
Uint8,
"UInt8Array",
StaticJsStrings::UINT8_ARRAY,
typed_uint8_array
);
typed_array!( typed_array!(
Uint8ClampedArray, Uint8ClampedArray,
Uint8Clamped, Uint8Clamped,
"Uint8ClampedArray", "UInt8ClampedArray",
StaticJsStrings::UINT8_CLAMPED_ARRAY,
typed_uint8clamped_array typed_uint8clamped_array
); );
typed_array!(Int16Array, Int16, "Int16Array", typed_int16_array); typed_array!(
typed_array!(Uint16Array, Uint16, "Uint16Array", typed_uint16_array); Int16Array,
typed_array!(Int32Array, Int32, "Int32Array", typed_int32_array); Int16,
typed_array!(Uint32Array, Uint32, "Uint32Array", typed_uint32_array); "Int16Array",
StaticJsStrings::INT16_ARRAY,
typed_int16_array
);
typed_array!(
Uint16Array,
Uint16,
"UInt16Array",
StaticJsStrings::UINT16_ARRAY,
typed_uint16_array
);
typed_array!(
Int32Array,
Int32,
"Int32Array",
StaticJsStrings::INT32_ARRAY,
typed_int32_array
);
typed_array!(
Uint32Array,
Uint32,
"UInt32Array",
StaticJsStrings::UINT32_ARRAY,
typed_uint32_array
);
typed_array!( typed_array!(
BigInt64Array, BigInt64Array,
BigInt64, BigInt64,
"BigInt64Array", "BigInt64Array",
StaticJsStrings::BIG_INT64_ARRAY,
typed_bigint64_array typed_bigint64_array
); );
typed_array!( typed_array!(
BigUint64Array, BigUint64Array,
BigUint64, BigUint64,
"BigUint64Array", "BigUint64Array",
StaticJsStrings::BIG_UINT64_ARRAY,
typed_biguint64_array typed_biguint64_array
); );
typed_array!(Float32Array, Float32, "Float32Array", typed_float32_array); typed_array!(
typed_array!(Float64Array, Float64, "Float64Array", typed_float64_array); Float32Array,
Float32,
"Float32Array",
StaticJsStrings::FLOAT32_ARRAY,
typed_float32_array
);
typed_array!(
Float64Array,
Float64,
"Float64Array",
StaticJsStrings::FLOAT64_ARRAY,
typed_float64_array
);

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

@ -25,7 +25,7 @@ use crate::{
js_string, js_string,
object::{JsFunction, JsObject}, object::{JsFunction, JsObject},
realm::Realm, realm::Realm,
string::CodePoint, string::{common::StaticJsStrings, CodePoint},
Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
@ -93,7 +93,7 @@ impl IntrinsicObject for DecodeUri {
} }
impl BuiltInObject for DecodeUri { impl BuiltInObject for DecodeUri {
const NAME: &'static str = "decodeURI"; const NAME: JsString = StaticJsStrings::DECODE_URI;
} }
pub(crate) struct DecodeUriComponent; pub(crate) struct DecodeUriComponent;
@ -115,7 +115,7 @@ impl IntrinsicObject for DecodeUriComponent {
} }
impl BuiltInObject for DecodeUriComponent { impl BuiltInObject for DecodeUriComponent {
const NAME: &'static str = "decodeURIComponent"; const NAME: JsString = StaticJsStrings::DECODE_URI_COMPONENT;
} }
pub(crate) struct EncodeUri; pub(crate) struct EncodeUri;
@ -133,7 +133,7 @@ impl IntrinsicObject for EncodeUri {
} }
impl BuiltInObject for EncodeUri { impl BuiltInObject for EncodeUri {
const NAME: &'static str = "encodeURI"; const NAME: JsString = StaticJsStrings::ENCODE_URI;
} }
pub(crate) struct EncodeUriComponent; pub(crate) struct EncodeUriComponent;
@ -154,7 +154,7 @@ impl IntrinsicObject for EncodeUriComponent {
} }
impl BuiltInObject for EncodeUriComponent { impl BuiltInObject for EncodeUriComponent {
const NAME: &'static str = "encodeURIComponent"; const NAME: JsString = StaticJsStrings::ENCODE_URI_COMPONENT;
} }
/// Builtin JavaScript `decodeURI ( encodedURI )` function. /// Builtin JavaScript `decodeURI ( encodedURI )` function.

12
boa_engine/src/builtins/weak/weak_ref.rs

@ -4,11 +4,13 @@ use boa_profiler::Profiler;
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
/// Boa's implementation of ECMAScript's `WeakRef` builtin object. /// Boa's implementation of ECMAScript's `WeakRef` builtin object.
@ -30,20 +32,20 @@ impl IntrinsicObject for WeakRef {
} }
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
"WeakRef", js_string!("WeakRef"),
Attribute::CONFIGURABLE, Attribute::CONFIGURABLE,
) )
.method(Self::deref, "deref", 0) .method(Self::deref, js_string!("deref"), 0)
.build(); .build();
} }
} }
impl BuiltInObject for WeakRef { impl BuiltInObject for WeakRef {
const NAME: &'static str = "WeakRef"; const NAME: JsString = StaticJsStrings::WEAK_REF;
const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE); const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);
} }

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

@ -13,12 +13,13 @@ use crate::{
IntrinsicObject, IntrinsicObject,
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,23 +33,23 @@ impl IntrinsicObject for WeakMap {
} }
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::delete, "delete", 1) .method(Self::delete, js_string!("delete"), 1)
.method(Self::get, "get", 1) .method(Self::get, js_string!("get"), 1)
.method(Self::has, "has", 1) .method(Self::has, js_string!("has"), 1)
.method(Self::set, "set", 2) .method(Self::set, js_string!("set"), 2)
.build(); .build();
} }
} }
impl BuiltInObject for WeakMap { impl BuiltInObject for WeakMap {
const NAME: &'static str = "WeakMap"; const NAME: JsString = StaticJsStrings::WEAK_MAP;
const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE); const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);
} }

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

@ -10,12 +10,13 @@
use crate::{ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace, WeakMap}; use boa_gc::{Finalize, Trace, WeakMap};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -29,22 +30,22 @@ impl IntrinsicObject for WeakSet {
} }
fn init(realm: &Realm) { fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init"); let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property( .property(
JsSymbol::to_string_tag(), JsSymbol::to_string_tag(),
Self::NAME, Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
) )
.method(Self::add, "add", 1) .method(Self::add, js_string!("add"), 1)
.method(Self::delete, "delete", 1) .method(Self::delete, js_string!("delete"), 1)
.method(Self::has, "has", 1) .method(Self::has, js_string!("has"), 1)
.build(); .build();
} }
} }
impl BuiltInObject for WeakSet { impl BuiltInObject for WeakSet {
const NAME: &'static str = "WeakSet"; const NAME: JsString = StaticJsStrings::WEAK_SET;
const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE); const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);
} }

9
boa_engine/src/class.rs

@ -67,6 +67,7 @@
use crate::{ use crate::{
error::JsNativeError, error::JsNativeError,
js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{ConstructorBuilder, JsFunction, JsObject, NativeObject, ObjectData, PROTOTYPE}, object::{ConstructorBuilder, JsFunction, JsObject, NativeObject, ObjectData, PROTOTYPE},
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
@ -121,7 +122,7 @@ impl<T: Class> ClassConstructor for T {
.into()); .into());
} }
let class = context.global_object().get(T::NAME, context)?; let class = context.global_object().get(js_string!(T::NAME), context)?;
let JsValue::Object(ref class_constructor) = class else { let JsValue::Object(ref class_constructor) = class else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message(format!( .with_message(format!(
@ -190,7 +191,8 @@ impl<'ctx, 'host> ClassBuilder<'ctx, 'host> {
where where
N: AsRef<str>, N: AsRef<str>,
{ {
self.builder.method(function, name.as_ref(), length); self.builder
.method(function, js_string!(name.as_ref()), length);
self self
} }
@ -206,7 +208,8 @@ impl<'ctx, 'host> ClassBuilder<'ctx, 'host> {
where where
N: AsRef<str>, N: AsRef<str>,
{ {
self.builder.static_method(function, name.as_ref(), length); self.builder
.static_method(function, js_string!(name.as_ref()), length);
self self
} }

2
boa_engine/src/context/intrinsics.rs

@ -191,7 +191,7 @@ impl Default for StandardConstructors {
)), )),
string: StandardConstructor::with_prototype(JsObject::from_proto_and_data( string: StandardConstructor::with_prototype(JsObject::from_proto_and_data(
None, None,
ObjectData::string("".into()), ObjectData::string(js_string!()),
)), )),
regexp: StandardConstructor::default(), regexp: StandardConstructor::default(),
symbol: StandardConstructor::default(), symbol: StandardConstructor::default(),

35
boa_engine/src/context/mod.rs

@ -20,6 +20,7 @@ use crate::{
builtins, builtins,
class::{Class, ClassBuilder}, class::{Class, ClassBuilder},
job::{JobQueue, NativeJob, SimpleJobQueue}, job::{JobQueue, NativeJob, SimpleJobQueue},
js_string,
module::{IdleModuleLoader, ModuleLoader, SimpleModuleLoader}, module::{IdleModuleLoader, ModuleLoader, SimpleModuleLoader},
native_function::NativeFunction, native_function::NativeFunction,
object::{shape::RootShape, FunctionObjectBuilder, JsObject}, object::{shape::RootShape, FunctionObjectBuilder, JsObject},
@ -28,7 +29,7 @@ use crate::{
realm::Realm, realm::Realm,
script::Script, script::Script,
vm::{ActiveRunnable, CallFrame, Vm}, vm::{ActiveRunnable, CallFrame, Vm},
JsResult, JsValue, Source, JsResult, JsString, JsValue, Source,
}; };
use boa_ast::{expression::Identifier, StatementList}; use boa_ast::{expression::Identifier, StatementList};
use boa_interner::Interner; use boa_interner::Interner;
@ -48,6 +49,7 @@ use crate::vm::RuntimeLimits;
/// ///
/// ```rust /// ```rust
/// use boa_engine::{ /// use boa_engine::{
/// js_string,
/// object::ObjectInitializer, /// object::ObjectInitializer,
/// property::{Attribute, PropertyDescriptor}, /// property::{Attribute, PropertyDescriptor},
/// Context, Source, /// Context, Source,
@ -69,9 +71,9 @@ use crate::vm::RuntimeLimits;
/// ///
/// // Create an object that can be used in eval calls. /// // Create an object that can be used in eval calls.
/// let arg = ObjectInitializer::new(&mut context) /// let arg = ObjectInitializer::new(&mut context)
/// .property("x", 12, Attribute::READONLY) /// .property(js_string!("x"), 12, Attribute::READONLY)
/// .build(); /// .build();
/// context.register_global_property("arg", arg, Attribute::all()); /// context.register_global_property(js_string!("arg"), arg, Attribute::all());
/// ///
/// let value = context.eval(Source::from_bytes("test(arg)")).unwrap(); /// let value = context.eval(Source::from_bytes("test(arg)")).unwrap();
/// ///
@ -196,6 +198,7 @@ impl<'host> Context<'host> {
/// # Example /// # Example
/// ``` /// ```
/// use boa_engine::{ /// use boa_engine::{
/// js_string,
/// object::ObjectInitializer, /// object::ObjectInitializer,
/// property::{Attribute, PropertyDescriptor}, /// property::{Attribute, PropertyDescriptor},
/// Context, /// Context,
@ -204,15 +207,23 @@ impl<'host> Context<'host> {
/// let mut context = Context::default(); /// let mut context = Context::default();
/// ///
/// context /// context
/// .register_global_property("myPrimitiveProperty", 10, Attribute::all()) /// .register_global_property(
/// js_string!("myPrimitiveProperty"),
/// 10,
/// Attribute::all(),
/// )
/// .expect("property shouldn't exist"); /// .expect("property shouldn't exist");
/// ///
/// let object = ObjectInitializer::new(&mut context) /// let object = ObjectInitializer::new(&mut context)
/// .property("x", 0, Attribute::all()) /// .property(js_string!("x"), 0, Attribute::all())
/// .property("y", 1, Attribute::all()) /// .property(js_string!("y"), 1, Attribute::all())
/// .build(); /// .build();
/// context /// context
/// .register_global_property("myObjectProperty", object, Attribute::all()) /// .register_global_property(
/// js_string!("myObjectProperty"),
/// object,
/// Attribute::all(),
/// )
/// .expect("property shouldn't exist"); /// .expect("property shouldn't exist");
/// ``` /// ```
pub fn register_global_property<K, V>( pub fn register_global_property<K, V>(
@ -251,12 +262,12 @@ impl<'host> Context<'host> {
/// can use the [`FunctionObjectBuilder`] API. /// can use the [`FunctionObjectBuilder`] API.
pub fn register_global_callable( pub fn register_global_callable(
&mut self, &mut self,
name: &str, name: JsString,
length: usize, length: usize,
body: NativeFunction, body: NativeFunction,
) -> JsResult<()> { ) -> JsResult<()> {
let function = FunctionObjectBuilder::new(&self.realm, body) let function = FunctionObjectBuilder::new(&self.realm, body)
.name(name) .name(name.clone())
.length(length) .length(length)
.constructor(true) .constructor(true)
.build(); .build();
@ -284,12 +295,12 @@ impl<'host> Context<'host> {
/// `constructable`. Usage of the function as a constructor will produce a `TypeError`. /// `constructable`. Usage of the function as a constructor will produce a `TypeError`.
pub fn register_global_builtin_callable( pub fn register_global_builtin_callable(
&mut self, &mut self,
name: &str, name: JsString,
length: usize, length: usize,
body: NativeFunction, body: NativeFunction,
) -> JsResult<()> { ) -> JsResult<()> {
let function = FunctionObjectBuilder::new(&self.realm, body) let function = FunctionObjectBuilder::new(&self.realm, body)
.name(name) .name(name.clone())
.length(length) .length(length)
.constructor(false) .constructor(false)
.build(); .build();
@ -336,7 +347,7 @@ impl<'host> Context<'host> {
.configurable(T::ATTRIBUTES.configurable()); .configurable(T::ATTRIBUTES.configurable());
self.global_object() self.global_object()
.define_property_or_throw(T::NAME, property, self)?; .define_property_or_throw(js_string!(T::NAME), property, self)?;
Ok(()) Ok(())
} }

40
boa_engine/src/error.rs

@ -2,6 +2,7 @@
use crate::{ use crate::{
builtins::{error::ErrorKind, Array}, builtins::{error::ErrorKind, Array},
js_string,
object::JsObject, object::JsObject,
object::ObjectData, object::ObjectData,
property::PropertyDescriptor, property::PropertyDescriptor,
@ -28,11 +29,11 @@ use thiserror::Error;
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue}; /// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue, js_string};
/// let cause = JsError::from_opaque("error!".into()); /// let cause = JsError::from_opaque(js_string!("error!").into());
/// ///
/// assert!(cause.as_opaque().is_some()); /// assert!(cause.as_opaque().is_some());
/// assert_eq!(cause.as_opaque().unwrap(), &JsValue::from("error!")); /// assert_eq!(cause.as_opaque().unwrap(), &JsValue::from(js_string!("error!")));
/// ///
/// let native_error: JsError = JsNativeError::typ() /// let native_error: JsError = JsNativeError::typ()
/// .with_message("invalid type!") /// .with_message("invalid type!")
@ -228,21 +229,23 @@ impl JsError {
.as_error() .as_error()
.ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?; .ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?;
let try_get_property = |key, context: &mut Context<'_>| { let try_get_property = |key: JsString, name, context: &mut Context<'_>| {
obj.has_property(key, context) obj.has_property(key.clone(), context)
.map_err(|e| TryNativeError::InaccessibleProperty { .map_err(|e| TryNativeError::InaccessibleProperty {
property: key, property: name,
source: e, source: e,
})? })?
.then(|| obj.get(key, context)) .then(|| obj.get(key, context))
.transpose() .transpose()
.map_err(|e| TryNativeError::InaccessibleProperty { .map_err(|e| TryNativeError::InaccessibleProperty {
property: key, property: name,
source: e, source: e,
}) })
}; };
let message = if let Some(msg) = try_get_property("message", context)? { let message = if let Some(msg) =
try_get_property(js_string!("message"), "message", context)?
{
msg.as_string() msg.as_string()
.map(JsString::to_std_string) .map(JsString::to_std_string)
.transpose() .transpose()
@ -253,7 +256,7 @@ impl JsError {
Box::default() Box::default()
}; };
let cause = try_get_property("cause", context)?; let cause = try_get_property(js_string!("cause"), "cause", context)?;
let kind = match error { let kind = match error {
ErrorKind::Error => JsNativeErrorKind::Error, ErrorKind::Error => JsNativeErrorKind::Error,
@ -297,7 +300,7 @@ impl JsError {
} }
}; };
let realm = try_get_property("constructor", context)? let realm = try_get_property(js_string!("constructor"), "constructor", context)?
.as_ref() .as_ref()
.and_then(JsValue::as_constructor) .and_then(JsValue::as_constructor)
.ok_or(TryNativeError::InvalidConstructor)? .ok_or(TryNativeError::InvalidConstructor)?
@ -769,14 +772,17 @@ impl JsNativeError {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use boa_engine::{Context, JsError, JsNativeError}; /// # use boa_engine::{Context, JsError, JsNativeError, js_string};
/// let context = &mut Context::default(); /// let context = &mut Context::default();
/// ///
/// let error = JsNativeError::error().with_message("error!"); /// let error = JsNativeError::error().with_message("error!");
/// let error_obj = error.to_opaque(context); /// let error_obj = error.to_opaque(context);
/// ///
/// assert!(error_obj.borrow().is_error()); /// assert!(error_obj.borrow().is_error());
/// assert_eq!(error_obj.get("message", context).unwrap(), "error!".into()) /// assert_eq!(
/// error_obj.get(js_string!("message"), context).unwrap(),
/// js_string!("error!").into()
/// )
/// ``` /// ```
/// ///
/// # Panics /// # Panics
@ -828,11 +834,15 @@ impl JsNativeError {
ObjectData::error(tag), ObjectData::error(tag),
); );
o.create_non_enumerable_data_property_or_throw(utf16!("message"), &**message, context); o.create_non_enumerable_data_property_or_throw(
js_string!("message"),
js_string!(&**message),
context,
);
if let Some(cause) = cause { if let Some(cause) = cause {
o.create_non_enumerable_data_property_or_throw( o.create_non_enumerable_data_property_or_throw(
utf16!("cause"), js_string!("cause"),
cause.to_opaque(context), cause.to_opaque(context),
context, context,
); );
@ -845,7 +855,7 @@ impl JsNativeError {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let errors = Array::create_array_from_list(errors, context); let errors = Array::create_array_from_list(errors, context);
o.define_property_or_throw( o.define_property_or_throw(
utf16!("errors"), js_string!("errors"),
PropertyDescriptor::builder() PropertyDescriptor::builder()
.configurable(true) .configurable(true)
.enumerable(false) .enumerable(false)

6
boa_engine/src/object/builtins/jsdate.rs

@ -18,7 +18,9 @@ use crate::{
/// Create a `JsDate` object and set date to December 4 1995 /// Create a `JsDate` object and set date to December 4 1995
/// ///
/// ``` /// ```
/// use boa_engine::{object::builtins::JsDate, Context, JsResult, JsValue}; /// use boa_engine::{
/// js_string, object::builtins::JsDate, Context, JsResult, JsValue,
/// };
/// ///
/// fn main() -> JsResult<()> { /// fn main() -> JsResult<()> {
/// // JS mutable Context /// // JS mutable Context
@ -30,7 +32,7 @@ use crate::{
/// ///
/// assert_eq!( /// assert_eq!(
/// date.to_date_string(context)?, /// date.to_date_string(context)?,
/// JsValue::from("Mon Dec 04 1995") /// JsValue::from(js_string!("Mon Dec 04 1995"))
/// ); /// );
/// ///
/// Ok(()) /// Ok(())

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

@ -20,7 +20,7 @@ use std::ops::Deref;
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// // Create default `Context` /// // Create default `Context`
@ -30,8 +30,8 @@ use std::ops::Deref;
/// let map = JsMap::new(context); /// let map = JsMap::new(context);
/// ///
/// // Set key-value pairs for the `JsMap`. /// // Set key-value pairs for the `JsMap`.
/// map.set("Key-1", "Value-1", context)?; /// map.set(js_string!("Key-1"), js_string!("Value-1"), context)?;
/// map.set("Key-2", 10, context)?; /// map.set(js_string!("Key-2"), 10, context)?;
/// ///
/// assert_eq!(map.get_size(context)?, 2.into()); /// assert_eq!(map.get_size(context)?, 2.into());
/// # Ok(()) /// # Ok(())
@ -42,7 +42,7 @@ use std::ops::Deref;
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::{JsArray, JsMap}, /// # object::builtins::{JsArray, JsMap},
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// // Create a default `Context` /// // Create a default `Context`
@ -52,7 +52,10 @@ use std::ops::Deref;
/// let js_array = JsArray::new(context); /// let js_array = JsArray::new(context);
/// ///
/// // Create a `[key, value]` pair of JsValues /// // Create a `[key, value]` pair of JsValues
/// let vec_one: Vec<JsValue> = vec![JsValue::new("first-key"), JsValue::new("first-value")]; /// let vec_one: Vec<JsValue> = vec![
/// js_string!("first-key").into(),
/// js_string!("first-value").into()
/// ];
/// ///
/// // We create an push our `[key, value]` pair onto our array as a `JsArray` /// // We create an push our `[key, value]` pair onto our array as a `JsArray`
/// js_array.push(JsArray::from_iter(vec_one, context), context)?; /// js_array.push(JsArray::from_iter(vec_one, context), context)?;
@ -61,8 +64,8 @@ use std::ops::Deref;
/// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context)?; /// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context)?;
/// ///
/// assert_eq!( /// assert_eq!(
/// js_iterable_map.get("first-key", context)?, /// js_iterable_map.get(js_string!("first-key"), context)?,
/// "first-value".into() /// js_string!("first-value").into()
/// ); /// );
/// ///
/// # Ok(()) /// # Ok(())
@ -99,7 +102,7 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::{JsArray, JsMap}, /// # object::builtins::{JsArray, JsMap},
/// # Context, JsResult, JsValue, /// # Context, JsResult, JsValue, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # // Create a default `Context` /// # // Create a default `Context`
@ -108,7 +111,10 @@ impl JsMap {
/// let js_array = JsArray::new(context); /// let js_array = JsArray::new(context);
/// ///
/// // Create a `[key, value]` pair of JsValues and add it to the `JsArray` as a `JsArray` /// // Create a `[key, value]` pair of JsValues and add it to the `JsArray` as a `JsArray`
/// let vec_one: Vec<JsValue> = vec![JsValue::new("first-key"), JsValue::new("first-value")]; /// let vec_one: Vec<JsValue> = vec![
/// js_string!("first-key").into(),
/// js_string!("first-value").into()
/// ];
/// js_array.push(JsArray::from_iter(vec_one, context), context)?; /// js_array.push(JsArray::from_iter(vec_one, context), context)?;
/// ///
/// // Create a `JsMap` from the `JsArray` using it's iterable property. /// // Create a `JsMap` from the `JsArray` using it's iterable property.
@ -217,16 +223,19 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let js_map = JsMap::new(context); /// let js_map = JsMap::new(context);
/// ///
/// js_map.set("foo", "bar", context)?; /// js_map.set(js_string!("foo"), js_string!("bar"), context)?;
/// js_map.set(2, 4, context)?; /// js_map.set(2, 4, context)?;
/// ///
/// assert_eq!(js_map.get("foo", context)?, "bar".into()); /// assert_eq!(
/// js_map.get(js_string!("foo"), context)?,
/// js_string!("bar").into()
/// );
/// assert_eq!(js_map.get(2, context)?, 4.into()); /// assert_eq!(js_map.get(2, context)?, 4.into());
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -250,13 +259,13 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let js_map = JsMap::new(context); /// let js_map = JsMap::new(context);
/// ///
/// js_map.set("foo", "bar", context)?; /// js_map.set(js_string!("foo"), js_string!("bar"), context)?;
/// ///
/// let map_size = js_map.get_size(context)?; /// let map_size = js_map.get_size(context)?;
/// ///
@ -276,18 +285,21 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let js_map = JsMap::new(context); /// let js_map = JsMap::new(context);
/// js_map.set("foo", "bar", context)?; /// js_map.set(js_string!("foo"), js_string!("bar"), context)?;
/// js_map.set("hello", "world", context)?; /// js_map.set(js_string!("hello"), js_string!("world"), context)?;
/// ///
/// js_map.delete("foo", context)?; /// js_map.delete(js_string!("foo"), context)?;
/// ///
/// assert_eq!(js_map.get_size(context)?, 1.into()); /// assert_eq!(js_map.get_size(context)?, 1.into());
/// assert_eq!(js_map.get("foo", context)?, JsValue::undefined()); /// assert_eq!(
/// js_map.get(js_string!("foo"), context)?,
/// JsValue::undefined()
/// );
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -305,16 +317,16 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let js_map = JsMap::new(context); /// let js_map = JsMap::new(context);
/// js_map.set("foo", "bar", context)?; /// js_map.set(js_string!("foo"), js_string!("bar"), context)?;
/// ///
/// let retrieved_value = js_map.get("foo", context)?; /// let retrieved_value = js_map.get(js_string!("foo"), context)?;
/// ///
/// assert_eq!(retrieved_value, "bar".into()); /// assert_eq!(retrieved_value, js_string!("bar").into());
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -332,13 +344,13 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let js_map = JsMap::new(context); /// let js_map = JsMap::new(context);
/// js_map.set("foo", "bar", context)?; /// js_map.set(js_string!("foo"), js_string!("bar"), context)?;
/// js_map.set("hello", "world", context)?; /// js_map.set(js_string!("hello"), js_string!("world"), context)?;
/// ///
/// js_map.clear(context)?; /// js_map.clear(context)?;
/// ///
@ -358,14 +370,14 @@ impl JsMap {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsMap, /// # object::builtins::JsMap,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let js_map = JsMap::new(context); /// let js_map = JsMap::new(context);
/// js_map.set("foo", "bar", context)?; /// js_map.set(js_string!("foo"), js_string!("bar"), context)?;
/// ///
/// let has_key = js_map.has("foo", context)?; /// let has_key = js_map.has(js_string!("foo"), context)?;
/// ///
/// assert_eq!(has_key, true.into()); /// assert_eq!(has_key, true.into());
/// # Ok(()) /// # Ok(())

49
boa_engine/src/object/builtins/jspromise.rs

@ -35,7 +35,11 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace};
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// let context = &mut Context::default(); /// let context = &mut Context::default();
/// ///
/// context.register_global_property("finally", false, Attribute::all()); /// context.register_global_property(
/// js_string!("finally"),
/// false,
/// Attribute::all(),
/// );
/// ///
/// let promise = JsPromise::new( /// let promise = JsPromise::new(
/// |resolvers, context| { /// |resolvers, context| {
@ -82,7 +86,7 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace};
/// context.realm(), /// context.realm(),
/// NativeFunction::from_fn_ptr(|_, _, context| { /// NativeFunction::from_fn_ptr(|_, _, context| {
/// context.global_object().clone().set( /// context.global_object().clone().set(
/// "finally", /// js_string!("finally"),
/// JsValue::from(true), /// JsValue::from(true),
/// true, /// true,
/// context, /// context,
@ -102,7 +106,10 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace};
/// ); /// );
/// ///
/// assert_eq!( /// assert_eq!(
/// context.global_object().clone().get("finally", context)?, /// context
/// .global_object()
/// .clone()
/// .get(js_string!("finally"), context)?,
/// JsValue::from(true) /// JsValue::from(true)
/// ); /// );
/// ///
@ -617,12 +624,16 @@ impl JsPromise {
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::{builtins::JsPromise, FunctionObjectBuilder}, /// # object::{builtins::JsPromise, FunctionObjectBuilder},
/// # property::Attribute, /// # property::Attribute,
/// # Context, JsNativeError, JsValue, NativeFunction, /// # Context, JsNativeError, JsValue, NativeFunction, js_string
/// # }; /// # };
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// let context = &mut Context::default(); /// let context = &mut Context::default();
/// ///
/// context.register_global_property("finally", false, Attribute::all()); /// context.register_global_property(
/// js_string!("finally"),
/// false,
/// Attribute::all(),
/// );
/// ///
/// let promise = JsPromise::new( /// let promise = JsPromise::new(
/// |resolvers, context| { /// |resolvers, context| {
@ -642,7 +653,7 @@ impl JsPromise {
/// context.realm(), /// context.realm(),
/// NativeFunction::from_fn_ptr(|_, _, context| { /// NativeFunction::from_fn_ptr(|_, _, context| {
/// context.global_object().clone().set( /// context.global_object().clone().set(
/// "finally", /// js_string!("finally"),
/// JsValue::from(true), /// JsValue::from(true),
/// true, /// true,
/// context, /// context,
@ -657,7 +668,10 @@ impl JsPromise {
/// context.run_jobs(); /// context.run_jobs();
/// ///
/// assert_eq!( /// assert_eq!(
/// context.global_object().clone().get("finally", context)?, /// context
/// .global_object()
/// .clone()
/// .get(js_string!("finally"), context)?,
/// JsValue::from(true) /// JsValue::from(true)
/// ); /// );
/// ///
@ -788,19 +802,28 @@ impl JsPromise {
/// let array = JsArray::from_object(array)?; /// let array = JsArray::from_object(array)?;
/// ///
/// let a = array.at(0, context)?.as_object().unwrap().clone(); /// let a = array.at(0, context)?.as_object().unwrap().clone();
/// assert_eq!(a.get("status", context)?, js_string!("fulfilled").into()); /// assert_eq!(
/// assert_eq!(a.get("value", context)?, 1.into()); /// a.get(js_string!("status"), context)?,
/// js_string!("fulfilled").into()
/// );
/// assert_eq!(a.get(js_string!("value"), context)?, 1.into());
/// ///
/// let b = array.at(1, context)?.as_object().unwrap().clone(); /// let b = array.at(1, context)?.as_object().unwrap().clone();
/// assert_eq!(b.get("status", context)?, js_string!("rejected").into());
/// assert_eq!( /// assert_eq!(
/// b.get("reason", context)?.to_string(context)?, /// b.get(js_string!("status"), context)?,
/// js_string!("rejected").into()
/// );
/// assert_eq!(
/// b.get(js_string!("reason"), context)?.to_string(context)?,
/// js_string!("TypeError") /// js_string!("TypeError")
/// ); /// );
/// ///
/// let c = array.at(2, context)?.as_object().unwrap().clone(); /// let c = array.at(2, context)?.as_object().unwrap().clone();
/// assert_eq!(c.get("status", context)?, js_string!("fulfilled").into()); /// assert_eq!(
/// assert_eq!(c.get("value", context)?, 3.into()); /// c.get(js_string!("status"), context)?,
/// js_string!("fulfilled").into()
/// );
/// assert_eq!(c.get(js_string!("value"), context)?, 3.into());
/// ///
/// # Ok(()) /// # Ok(())
/// # } /// # }

30
boa_engine/src/object/builtins/jsregexp.rs

@ -18,16 +18,16 @@ use std::ops::Deref;
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsRegExp, /// # object::builtins::JsRegExp,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult,js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// // Initialize the `Context` /// // Initialize the `Context`
/// let context = &mut Context::default(); /// let context = &mut Context::default();
/// ///
/// // Create a new RegExp with pattern and flags /// // Create a new RegExp with pattern and flags
/// let regexp = JsRegExp::new("foo", "gi", context)?; /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
/// ///
/// let test_result = regexp.test("football", context)?; /// let test_result = regexp.test(js_string!("football"), context)?;
/// assert!(test_result); /// assert!(test_result);
/// ///
/// let to_string = regexp.to_string(context)?; /// let to_string = regexp.to_string(context)?;
@ -45,14 +45,14 @@ impl JsRegExp {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsRegExp, /// # object::builtins::JsRegExp,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// // Initialize the `Context` /// // Initialize the `Context`
/// let context = &mut Context::default(); /// let context = &mut Context::default();
/// ///
/// // Create a new RegExp with pattern and flags /// // Create a new RegExp with pattern and flags
/// let regexp = JsRegExp::new("foo", "gi", context)?; /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -141,11 +141,11 @@ impl JsRegExp {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsRegExp, /// # object::builtins::JsRegExp,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let regexp = JsRegExp::new("foo", "gi", context)?; /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
/// ///
/// let flags = regexp.flags(context)?; /// let flags = regexp.flags(context)?;
/// assert_eq!(flags, String::from("gi")); /// assert_eq!(flags, String::from("gi"));
@ -166,11 +166,11 @@ impl JsRegExp {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsRegExp, /// # object::builtins::JsRegExp,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let regexp = JsRegExp::new("foo", "gi", context)?; /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
/// ///
/// let src = regexp.source(context)?; /// let src = regexp.source(context)?;
/// assert_eq!(src, String::from("foo")); /// assert_eq!(src, String::from("foo"));
@ -191,13 +191,13 @@ impl JsRegExp {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsRegExp, /// # object::builtins::JsRegExp,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let regexp = JsRegExp::new("foo", "gi", context)?; /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
/// ///
/// let test_result = regexp.test("football", context)?; /// let test_result = regexp.test(js_string!("football"), context)?;
/// assert!(test_result); /// assert!(test_result);
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -233,14 +233,14 @@ impl JsRegExp {
/// ``` /// ```
/// # use boa_engine::{ /// # use boa_engine::{
/// # object::builtins::JsRegExp, /// # object::builtins::JsRegExp,
/// # Context, JsValue, JsResult, /// # Context, JsValue, JsResult, js_string
/// # }; /// # };
/// # fn main() -> JsResult<()> { /// # fn main() -> JsResult<()> {
/// # let context = &mut Context::default(); /// # let context = &mut Context::default();
/// let regexp = JsRegExp::new("foo", "gi", context)?; /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
/// ///
/// let to_string = regexp.to_string(context)?; /// let to_string = regexp.to_string(context)?;
/// assert_eq!(to_string, String::from("/foo/gi")); /// assert_eq!(to_string, "/foo/gi");
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```

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

@ -45,7 +45,7 @@ fn canonical_numeric_index_string(argument: &JsString) -> Option<f64> {
let n = argument.to_number(); let n = argument.to_number();
// 3. If ! ToString(n) is argument, return n. // 3. If ! ToString(n) is argument, return n.
if &JsString::from(Number::to_native_string(n)) == argument { if &Number::to_js_string(n) == argument {
return Some(n); return Some(n);
} }

3
boa_engine/src/object/jsobject.rs

@ -10,6 +10,7 @@ use super::{
use crate::{ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string,
object::{ObjectData, ObjectKind}, object::{ObjectData, ObjectKind},
property::{PropertyDescriptor, PropertyKey}, property::{PropertyDescriptor, PropertyKey},
string::utf16, string::utf16,
@ -245,7 +246,7 @@ impl JsObject {
// we're in a recursive object, bail // we're in a recursive object, bail
return Ok(match hint { return Ok(match hint {
PreferredType::Number => JsValue::new(0), PreferredType::Number => JsValue::new(0),
PreferredType::String => JsValue::new(""), PreferredType::String => JsValue::new(js_string!()),
PreferredType::Default => unreachable!("checked type hint in step 2"), PreferredType::Default => unreachable!("checked type hint in step 2"),
}); });
} }

31
boa_engine/src/object/mod.rs

@ -2013,30 +2013,6 @@ pub struct FunctionBinding {
pub(crate) name: JsString, pub(crate) name: JsString,
} }
impl From<&str> for FunctionBinding {
#[inline]
fn from(name: &str) -> Self {
let name: JsString = name.into();
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<String> for FunctionBinding {
#[inline]
fn from(name: String) -> Self {
let name: JsString = name.into();
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<JsString> for FunctionBinding { impl From<JsString> for FunctionBinding {
#[inline] #[inline]
fn from(name: JsString) -> Self { fn from(name: JsString) -> Self {
@ -2146,15 +2122,16 @@ impl<'realm> FunctionObjectBuilder<'realm> {
/// # JsValue, /// # JsValue,
/// # NativeFunction, /// # NativeFunction,
/// # object::ObjectInitializer, /// # object::ObjectInitializer,
/// # property::Attribute /// # property::Attribute,
/// # js_string,
/// # }; /// # };
/// let mut context = Context::default(); /// let mut context = Context::default();
/// let object = ObjectInitializer::new(&mut context) /// let object = ObjectInitializer::new(&mut context)
/// .property("hello", "world", Attribute::all()) /// .property(js_string!("hello"), js_string!("world"), Attribute::all())
/// .property(1, 1, Attribute::all()) /// .property(1, 1, Attribute::all())
/// .function( /// .function(
/// NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())), /// NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())),
/// "func", /// js_string!("func"),
/// 0, /// 0,
/// ) /// )
/// .build(); /// .build();

2
boa_engine/src/optimizer/pass/constant_folding.rs

@ -82,7 +82,7 @@ impl ConstantFolding {
}, },
), ),
(literal, UnaryOp::TypeOf) => Ok(JsValue::new( (literal, UnaryOp::TypeOf) => Ok(JsValue::new(
literal_to_js_value(literal, context).type_of(), literal_to_js_value(literal, context).js_type_of(),
)), )),
(_, UnaryOp::Delete) => { (_, UnaryOp::Delete) => {
return PassAction::Replace(Expression::Literal(Literal::Bool(true))) return PassAction::Replace(Expression::Literal(Literal::Bool(true)))

24
boa_engine/src/property/mod.rs

@ -678,28 +678,6 @@ impl From<JsString> for PropertyKey {
} }
} }
impl From<&str> for PropertyKey {
#[inline]
fn from(string: &str) -> Self {
parse_u32_index(string.bytes()).map_or_else(|| Self::String(string.into()), Self::Index)
}
}
impl From<String> for PropertyKey {
#[inline]
fn from(string: String) -> Self {
parse_u32_index(string.bytes()).map_or_else(|| Self::String(string.into()), Self::Index)
}
}
impl From<Box<str>> for PropertyKey {
#[inline]
fn from(string: Box<str>) -> Self {
parse_u32_index(string.bytes())
.map_or_else(|| Self::String(string.as_ref().into()), Self::Index)
}
}
impl From<JsSymbol> for PropertyKey { impl From<JsSymbol> for PropertyKey {
#[inline] #[inline]
fn from(symbol: JsSymbol) -> Self { fn from(symbol: JsSymbol) -> Self {
@ -737,7 +715,7 @@ impl From<PropertyKey> for JsValue {
match property_key { match property_key {
PropertyKey::String(ref string) => string.clone().into(), PropertyKey::String(ref string) => string.clone().into(),
PropertyKey::Symbol(ref symbol) => symbol.clone().into(), PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
PropertyKey::Index(index) => index.to_string().into(), PropertyKey::Index(index) => js_string!(index.to_string()).into(),
} }
} }
} }

289
boa_engine/src/string/common.rs

@ -4,17 +4,20 @@ use crate::tagged::Tagged;
use super::JsString; use super::JsString;
use boa_macros::utf16; use boa_macros::utf16;
use paste::paste;
use rustc_hash::{FxHashMap, FxHasher}; use rustc_hash::{FxHashMap, FxHasher};
macro_rules! well_known_statics { macro_rules! well_known_statics {
( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => {
$( $(
$(#[$attr])* pub(crate) const fn $name() -> JsString { paste!{
JsString { #[doc = "Gets the static `JsString` for `\"" $string "\"`."]
#[allow(unused)]
pub(crate) const $name: JsString = JsString {
ptr: Tagged::from_tag( ptr: Tagged::from_tag(
Self::find_index(utf16!($string)), Self::find_index(utf16!($string)),
), ),
} };
} }
)+ )+
}; };
@ -74,35 +77,89 @@ impl StaticJsStrings {
RAW_STATICS.get(index).copied() RAW_STATICS.get(index).copied()
} }
// Some consts are only used on certain features, which triggers the unused lint.
well_known_statics! { well_known_statics! {
/// Gets the empty string (`""`) `JsString`. (EMPTY_STRING, ""),
(empty_string, ""), // Symbols
/// Gets the static `JsString` for `"Symbol.asyncIterator"`. (SYMBOL_ASYNC_ITERATOR, "Symbol.asyncIterator"),
(symbol_async_iterator, "Symbol.asyncIterator"), (SYMBOL_HAS_INSTANCE, "Symbol.hasInstance"),
/// Gets the static `JsString` for `"Symbol.hasInstance"`. (SYMBOL_IS_CONCAT_SPREADABLE, "Symbol.isConcatSpreadable"),
(symbol_has_instance, "Symbol.hasInstance"), (SYMBOL_ITERATOR, "Symbol.iterator"),
/// Gets the static `JsString` for `"Symbol.isConcatSpreadable"`. (SYMBOL_MATCH, "Symbol.match"),
(symbol_is_concat_spreadable, "Symbol.isConcatSpreadable"), (SYMBOL_MATCH_ALL, "Symbol.matchAll"),
/// Gets the static `JsString` for `"Symbol.iterator"`. (SYMBOL_REPLACE, "Symbol.replace"),
(symbol_iterator, "Symbol.iterator"), (SYMBOL_SEARCH, "Symbol.search"),
/// Gets the static `JsString` for `"Symbol.match"`. (SYMBOL_SPECIES, "Symbol.species"),
(symbol_match, "Symbol.match"), (SYMBOL_SPLIT, "Symbol.split"),
/// Gets the static `JsString` for `"Symbol.matchAll"`. (SYMBOL_TO_PRIMITIVE, "Symbol.toPrimitive"),
(symbol_match_all, "Symbol.matchAll"), (SYMBOL_TO_STRING_TAG, "Symbol.toStringTag"),
/// Gets the static `JsString` for `"Symbol.replace"`. (SYMBOL_UNSCOPABLES, "Symbol.unscopables"),
(symbol_replace, "Symbol.replace"), // Builtins
/// Gets the static `JsString` for `"Symbol.search"`. (ARRAY, "Array"),
(symbol_search, "Symbol.search"), (ARRAY_BUFFER, "ArrayBuffer"),
/// Gets the static `JsString` for `"Symbol.species"`. (ASYNC_FUNCTION, "AsyncFunction"),
(symbol_species, "Symbol.species"), (ASYNC_GENERATOR, "AsyncGenerator"),
/// Gets the static `JsString` for `"Symbol.split"`. (ASYNC_GENERATOR_FUNCTION, "AsyncGeneratorFunction"),
(symbol_split, "Symbol.split"), (BIG_INT, "BigInt"),
/// Gets the static `JsString` for `"Symbol.toPrimitive"`. (BOOLEAN, "Boolean"),
(symbol_to_primitive, "Symbol.toPrimitive"), (DATA_VIEW, "DataView"),
/// Gets the static `JsString` for `"Symbol.toStringTag"`. (DATE, "Date"),
(symbol_to_string_tag, "Symbol.toStringTag"), (ERROR, "Error"),
/// Gets the static `JsString` for `"Symbol.unscopables"`. (AGGREGATE_ERROR, "AggregateError"),
(symbol_unscopables, "Symbol.unscopables"), (EVAL_ERROR, "EvalError"),
(RANGE_ERROR, "RangeError"),
(REFERENCE_ERROR, "ReferenceError"),
(SYNTAX_ERROR, "SyntaxError"),
(TYPE_ERROR, "TypeError"),
(URI_ERROR, "URIError"),
(ESCAPE, "escape"),
(UNESCAPE, "unescape"),
(EVAL, "eval"),
(FUNCTION, "Function"),
(GENERATOR, "Generator"),
(GENERATOR_FUNCTION, "GeneratorFunction"),
(INTL, "Intl"),
(COLLATOR, "Collator"),
(LIST_FORMAT, "ListFormat"),
(LOCALE, "Locale"),
(PLURAL_RULES, "PluralRules"),
(SEGMENTER, "Segmenter"),
(DATE_TIME_FORMAT, "DateTimeFormat"),
(JSON, "JSON"),
(MAP, "Map"),
(MATH, "Math"),
(NUMBER, "Number"),
(IS_FINITE, "isFinite"),
(IS_NAN, "isNaN"),
(PARSE_INT, "parseInt"),
(PARSE_FLOAT, "parseFloat"),
(OBJECT, "Object"),
(PROMISE, "Promise"),
(PROXY, "Proxy"),
(REFLECT, "Reflect"),
(REG_EXP, "RegExp"),
(SET, "Set"),
(STRING, "String"),
(SYMBOL, "Symbol"),
(TYPED_ARRAY, "TypedArray"),
(INT8_ARRAY, "Int8Array"),
(UINT8_ARRAY, "Uint8Array"),
(UINT8_CLAMPED_ARRAY, "Uint8ClampedArray"),
(INT16_ARRAY, "Int16Array"),
(UINT16_ARRAY, "Uint16Array"),
(INT32_ARRAY, "Int32Array"),
(UINT32_ARRAY, "Uint32Array"),
(BIG_INT64_ARRAY, "BigInt64Array"),
(BIG_UINT64_ARRAY, "BigUint64Array"),
(FLOAT32_ARRAY, "Float32Array"),
(FLOAT64_ARRAY, "Float64Array"),
(ENCODE_URI, "encodeURI"),
(ENCODE_URI_COMPONENT, "encodeURIComponent"),
(DECODE_URI, "decodeURI"),
(DECODE_URI_COMPONENT, "decodeURIComponent"),
(WEAK_REF, "WeakRef"),
(WEAK_MAP, "WeakMap"),
(WEAK_SET, "WeakSet"),
} }
} }
@ -136,11 +193,101 @@ thread_local! {
} }
/// Array of raw static strings that aren't reference counted. /// Array of raw static strings that aren't reference counted.
///
/// The macro `static_strings` automatically sorts the array of strings, making it faster
/// for searches by using `binary_search`.
const RAW_STATICS: &[&[u16]] = &[ const RAW_STATICS: &[&[u16]] = &[
utf16!(""), utf16!(""),
// Well known symbols
utf16!("Symbol.asyncIterator"),
utf16!("[Symbol.asyncIterator]"),
utf16!("Symbol.hasInstance"),
utf16!("[Symbol.hasInstance]"),
utf16!("Symbol.isConcatSpreadable"),
utf16!("[Symbol.isConcatSpreadable]"),
utf16!("Symbol.iterator"),
utf16!("[Symbol.iterator]"),
utf16!("Symbol.match"),
utf16!("[Symbol.match]"),
utf16!("Symbol.matchAll"),
utf16!("[Symbol.matchAll]"),
utf16!("Symbol.replace"),
utf16!("[Symbol.replace]"),
utf16!("Symbol.search"),
utf16!("[Symbol.search]"),
utf16!("Symbol.species"),
utf16!("[Symbol.species]"),
utf16!("Symbol.split"),
utf16!("[Symbol.split]"),
utf16!("Symbol.toPrimitive"),
utf16!("[Symbol.toPrimitive]"),
utf16!("Symbol.toStringTag"),
utf16!("[Symbol.toStringTag]"),
utf16!("Symbol.unscopables"),
utf16!("[Symbol.unscopables]"),
// Well known builtins
utf16!("Array"),
utf16!("ArrayBuffer"),
utf16!("AsyncFunction"),
utf16!("AsyncGenerator"),
utf16!("AsyncGeneratorFunction"),
utf16!("BigInt"),
utf16!("Boolean"),
utf16!("DataView"),
utf16!("Date"),
utf16!("Error"),
utf16!("AggregateError"),
utf16!("EvalError"),
utf16!("RangeError"),
utf16!("ReferenceError"),
utf16!("SyntaxError"),
utf16!("TypeError"),
utf16!("URIError"),
utf16!("escape"),
utf16!("unescape"),
utf16!("eval"),
utf16!("Function"),
utf16!("Generator"),
utf16!("GeneratorFunction"),
utf16!("Intl"),
utf16!("Collator"),
utf16!("ListFormat"),
utf16!("Locale"),
utf16!("PluralRules"),
utf16!("Segmenter"),
utf16!("DateTimeFormat"),
utf16!("JSON"),
utf16!("Map"),
utf16!("Math"),
utf16!("Number"),
utf16!("isFinite"),
utf16!("isNaN"),
utf16!("parseInt"),
utf16!("parseFloat"),
utf16!("Object"),
utf16!("Promise"),
utf16!("Proxy"),
utf16!("Reflect"),
utf16!("RegExp"),
utf16!("Set"),
utf16!("String"),
utf16!("Symbol"),
utf16!("TypedArray"),
utf16!("Int8Array"),
utf16!("Uint8Array"),
utf16!("Uint8ClampedArray"),
utf16!("Int16Array"),
utf16!("Uint16Array"),
utf16!("Int32Array"),
utf16!("Uint32Array"),
utf16!("BigInt64Array"),
utf16!("BigUint64Array"),
utf16!("Float32Array"),
utf16!("Float64Array"),
utf16!("encodeURI"),
utf16!("encodeURIComponent"),
utf16!("decodeURI"),
utf16!("decodeURIComponent"),
utf16!("WeakRef"),
utf16!("WeakMap"),
utf16!("WeakSet"),
// Misc // Misc
utf16!(","), utf16!(","),
utf16!(":"), utf16!(":"),
@ -197,14 +344,10 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("entries"), utf16!("entries"),
utf16!("fromEntries"), utf16!("fromEntries"),
// Function object // Function object
utf16!("Function"),
utf16!("apply"), utf16!("apply"),
utf16!("bind"), utf16!("bind"),
utf16!("call"), utf16!("call"),
// Generator object
utf16!("Generator"),
// Array object // Array object
utf16!("Array"),
utf16!("at"), utf16!("at"),
utf16!("from"), utf16!("from"),
utf16!("isArray"), utf16!("isArray"),
@ -237,7 +380,6 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("push"), utf16!("push"),
utf16!("pop"), utf16!("pop"),
// String object // String object
utf16!("String"),
utf16!("charAt"), utf16!("charAt"),
utf16!("charCodeAt"), utf16!("charCodeAt"),
utf16!("codePointAt"), utf16!("codePointAt"),
@ -267,13 +409,8 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("trimEnd"), utf16!("trimEnd"),
utf16!("trimStart"), utf16!("trimStart"),
// Number object // Number object
utf16!("Number"),
utf16!("Infinity"), utf16!("Infinity"),
utf16!("NaN"), utf16!("NaN"),
utf16!("parseInt"),
utf16!("parseFloat"),
utf16!("isFinite"),
utf16!("isNaN"),
utf16!("EPSILON"), utf16!("EPSILON"),
utf16!("MAX_SAFE_INTEGER"), utf16!("MAX_SAFE_INTEGER"),
utf16!("MIN_SAFE_INTEGER"), utf16!("MIN_SAFE_INTEGER"),
@ -284,14 +421,10 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("toExponential"), utf16!("toExponential"),
utf16!("toFixed"), utf16!("toFixed"),
utf16!("toPrecision"), utf16!("toPrecision"),
// Boolean object
utf16!("Boolean"),
// BigInt object // BigInt object
utf16!("BigInt"),
utf16!("asIntN"), utf16!("asIntN"),
utf16!("asUintN"), utf16!("asUintN"),
// RegExp object // RegExp object
utf16!("RegExp"),
utf16!("exec"), utf16!("exec"),
utf16!("test"), utf16!("test"),
utf16!("flags"), utf16!("flags"),
@ -314,7 +447,6 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("get flags"), utf16!("get flags"),
utf16!("get source"), utf16!("get source"),
// Symbol object // Symbol object
utf16!("Symbol"),
utf16!("for"), utf16!("for"),
utf16!("keyFor"), utf16!("keyFor"),
utf16!("description"), utf16!("description"),
@ -327,32 +459,18 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("toPrimitive"), utf16!("toPrimitive"),
utf16!("get description"), utf16!("get description"),
// Map object // Map object
utf16!("Map"),
utf16!("clear"), utf16!("clear"),
utf16!("delete"), utf16!("delete"),
utf16!("has"), utf16!("has"),
utf16!("size"), utf16!("size"),
// Set object // Set object
utf16!("Set"),
utf16!("add"), utf16!("add"),
// Reflect object // Reflect object
utf16!("Reflect"),
// Proxy object // Proxy object
utf16!("Proxy"),
utf16!("revocable"), utf16!("revocable"),
// Error objects // Error objects
utf16!("Error"),
utf16!("AggregateError"),
utf16!("TypeError"),
utf16!("RangeError"),
utf16!("SyntaxError"),
utf16!("ReferenceError"),
utf16!("EvalError"),
utf16!("ThrowTypeError"),
utf16!("URIError"),
utf16!("message"), utf16!("message"),
// Date object // Date object
utf16!("Date"),
utf16!("toJSON"), utf16!("toJSON"),
utf16!("getDate"), utf16!("getDate"),
utf16!("getDay"), utf16!("getDay"),
@ -394,7 +512,6 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("now"), utf16!("now"),
utf16!("UTC"), utf16!("UTC"),
// JSON object // JSON object
utf16!("JSON"),
utf16!("parse"), utf16!("parse"),
utf16!("stringify"), utf16!("stringify"),
// Iterator object // Iterator object
@ -404,7 +521,6 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("Map Iterator"), utf16!("Map Iterator"),
utf16!("For In Iterator"), utf16!("For In Iterator"),
// Math object // Math object
utf16!("Math"),
utf16!("LN10"), utf16!("LN10"),
utf16!("LN2"), utf16!("LN2"),
utf16!("LOG10E"), utf16!("LOG10E"),
@ -447,22 +563,7 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("tan"), utf16!("tan"),
utf16!("tanh"), utf16!("tanh"),
utf16!("trunc"), utf16!("trunc"),
// Intl object
utf16!("Intl"),
utf16!("DateTimeFormat"),
// TypedArray object // TypedArray object
utf16!("TypedArray"),
utf16!("ArrayBuffer"),
utf16!("Int8Array"),
utf16!("Uint8Array"),
utf16!("Int16Array"),
utf16!("Uint16Array"),
utf16!("Int32Array"),
utf16!("Uint32Array"),
utf16!("BigInt64Array"),
utf16!("BigUint64Array"),
utf16!("Float32Array"),
utf16!("Float64Array"),
utf16!("buffer"), utf16!("buffer"),
utf16!("byteLength"), utf16!("byteLength"),
utf16!("byteOffset"), utf16!("byteOffset"),
@ -474,7 +575,6 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("get size"), utf16!("get size"),
utf16!("get length"), utf16!("get length"),
// DataView object // DataView object
utf16!("DataView"),
utf16!("getBigInt64"), utf16!("getBigInt64"),
utf16!("getBigUint64"), utf16!("getBigUint64"),
utf16!("getFloat32"), utf16!("getFloat32"),
@ -569,31 +669,4 @@ const RAW_STATICS: &[&[u16]] = &[
utf16!("Z"), utf16!("Z"),
utf16!("_"), utf16!("_"),
utf16!("$"), utf16!("$"),
// Well known symbols
utf16!("Symbol.asyncIterator"),
utf16!("[Symbol.asyncIterator]"),
utf16!("Symbol.hasInstance"),
utf16!("[Symbol.hasInstance]"),
utf16!("Symbol.isConcatSpreadable"),
utf16!("[Symbol.isConcatSpreadable]"),
utf16!("Symbol.iterator"),
utf16!("[Symbol.iterator]"),
utf16!("Symbol.match"),
utf16!("[Symbol.match]"),
utf16!("Symbol.matchAll"),
utf16!("[Symbol.matchAll]"),
utf16!("Symbol.replace"),
utf16!("[Symbol.replace]"),
utf16!("Symbol.search"),
utf16!("[Symbol.search]"),
utf16!("Symbol.species"),
utf16!("[Symbol.species]"),
utf16!("Symbol.split"),
utf16!("[Symbol.split]"),
utf16!("Symbol.toPrimitive"),
utf16!("[Symbol.toPrimitive]"),
utf16!("Symbol.toStringTag"),
utf16!("[Symbol.toStringTag]"),
utf16!("Symbol.unscopables"),
utf16!("[Symbol.unscopables]"),
]; ];

2
boa_engine/src/string/mod.rs

@ -651,7 +651,7 @@ impl Clone for JsString {
impl Default for JsString { impl Default for JsString {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {
StaticJsStrings::empty_string() StaticJsStrings::EMPTY_STRING
} }
} }

26
boa_engine/src/symbol.rs

@ -80,19 +80,19 @@ enum WellKnown {
impl WellKnown { impl WellKnown {
const fn description(self) -> JsString { const fn description(self) -> JsString {
match self { match self {
Self::AsyncIterator => StaticJsStrings::symbol_async_iterator(), Self::AsyncIterator => StaticJsStrings::SYMBOL_ASYNC_ITERATOR,
Self::HasInstance => StaticJsStrings::symbol_has_instance(), Self::HasInstance => StaticJsStrings::SYMBOL_HAS_INSTANCE,
Self::IsConcatSpreadable => StaticJsStrings::symbol_is_concat_spreadable(), Self::IsConcatSpreadable => StaticJsStrings::SYMBOL_IS_CONCAT_SPREADABLE,
Self::Iterator => StaticJsStrings::symbol_iterator(), Self::Iterator => StaticJsStrings::SYMBOL_ITERATOR,
Self::Match => StaticJsStrings::symbol_match(), Self::Match => StaticJsStrings::SYMBOL_MATCH,
Self::MatchAll => StaticJsStrings::symbol_match_all(), Self::MatchAll => StaticJsStrings::SYMBOL_MATCH_ALL,
Self::Replace => StaticJsStrings::symbol_replace(), Self::Replace => StaticJsStrings::SYMBOL_REPLACE,
Self::Search => StaticJsStrings::symbol_search(), Self::Search => StaticJsStrings::SYMBOL_SEARCH,
Self::Species => StaticJsStrings::symbol_species(), Self::Species => StaticJsStrings::SYMBOL_SPECIES,
Self::Split => StaticJsStrings::symbol_split(), Self::Split => StaticJsStrings::SYMBOL_SPLIT,
Self::ToPrimitive => StaticJsStrings::symbol_to_primitive(), Self::ToPrimitive => StaticJsStrings::SYMBOL_TO_PRIMITIVE,
Self::ToStringTag => StaticJsStrings::symbol_to_string_tag(), Self::ToStringTag => StaticJsStrings::SYMBOL_TO_STRING_TAG,
Self::Unscopables => StaticJsStrings::symbol_unscopables(), Self::Unscopables => StaticJsStrings::SYMBOL_UNSCOPABLES,
} }
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save