Browse Source

Implement static shapes

optimization/static-shapes
Haled Odat 2 years ago
parent
commit
85b2861b3e
  1. 22
      Cargo.lock
  2. 2
      Cargo.toml
  3. 25
      boa_builtins/Cargo.toml
  4. 1
      boa_builtins/README.md
  5. 1485
      boa_builtins/build.rs
  6. 181
      boa_builtins/src/lib.rs
  7. 6
      boa_cli/src/debug/shape.rs
  8. 3
      boa_engine/Cargo.toml
  9. 118
      boa_engine/src/builtins/array/mod.rs
  10. 37
      boa_engine/src/builtins/array_buffer/mod.rs
  11. 24
      boa_engine/src/builtins/bigint/mod.rs
  12. 12
      boa_engine/src/builtins/boolean/mod.rs
  13. 75
      boa_engine/src/builtins/dataview/mod.rs
  14. 122
      boa_engine/src/builtins/date/mod.rs
  15. 21
      boa_engine/src/builtins/error/aggregate.rs
  16. 20
      boa_engine/src/builtins/error/eval.rs
  17. 16
      boa_engine/src/builtins/error/mod.rs
  18. 20
      boa_engine/src/builtins/error/range.rs
  19. 20
      boa_engine/src/builtins/error/reference.rs
  20. 20
      boa_engine/src/builtins/error/syntax.rs
  21. 19
      boa_engine/src/builtins/error/type.rs
  22. 20
      boa_engine/src/builtins/error/uri.rs
  23. 4
      boa_engine/src/builtins/escape/mod.rs
  24. 2
      boa_engine/src/builtins/eval/mod.rs
  25. 93
      boa_engine/src/builtins/function/mod.rs
  26. 19
      boa_engine/src/builtins/json/mod.rs
  27. 59
      boa_engine/src/builtins/map/mod.rs
  28. 109
      boa_engine/src/builtins/math/mod.rs
  29. 371
      boa_engine/src/builtins/mod.rs
  30. 8
      boa_engine/src/builtins/number/globals.rs
  31. 68
      boa_engine/src/builtins/number/mod.rs
  32. 91
      boa_engine/src/builtins/object/mod.rs
  33. 43
      boa_engine/src/builtins/promise/mod.rs
  34. 47
      boa_engine/src/builtins/reflect/mod.rs
  35. 75
      boa_engine/src/builtins/regexp/mod.rs
  36. 61
      boa_engine/src/builtins/set/mod.rs
  37. 133
      boa_engine/src/builtins/string/mod.rs
  38. 77
      boa_engine/src/builtins/symbol/mod.rs
  39. 164
      boa_engine/src/builtins/typed_array/mod.rs
  40. 16
      boa_engine/src/builtins/uri/mod.rs
  41. 18
      boa_engine/src/builtins/weak/weak_ref.rs
  42. 23
      boa_engine/src/builtins/weak_map/mod.rs
  43. 21
      boa_engine/src/builtins/weak_set/mod.rs
  44. 130
      boa_engine/src/context/intrinsics.rs
  45. 13
      boa_engine/src/object/builtins/jsfunction.rs
  46. 25
      boa_engine/src/object/jsobject.rs
  47. 29
      boa_engine/src/object/mod.rs
  48. 38
      boa_engine/src/object/property_map.rs
  49. 36
      boa_engine/src/object/shape/mod.rs
  50. 2
      boa_engine/src/object/shape/slot.rs
  51. 149
      boa_engine/src/object/shape/static_shape.rs
  52. 465
      boa_engine/src/string/common.rs
  53. 22
      boa_engine/src/string/mod.rs
  54. 2
      boa_engine/src/symbol.rs
  55. 8
      boa_engine/src/vm/opcode/set/property.rs

22
Cargo.lock generated

@ -377,6 +377,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "boa_builtins"
version = "0.16.0"
dependencies = [
"bitflags 2.3.1",
"boa_macros",
"phf",
"phf_codegen",
"phf_shared",
]
[[package]] [[package]]
name = "boa_cli" name = "boa_cli"
version = "0.16.0" version = "0.16.0"
@ -403,6 +414,7 @@ version = "0.16.0"
dependencies = [ dependencies = [
"bitflags 2.3.1", "bitflags 2.3.1",
"boa_ast", "boa_ast",
"boa_builtins",
"boa_gc", "boa_gc",
"boa_icu_provider", "boa_icu_provider",
"boa_interner", "boa_interner",
@ -3018,6 +3030,16 @@ dependencies = [
"phf_shared", "phf_shared",
] ]
[[package]]
name = "phf_codegen"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]] [[package]]
name = "phf_generator" name = "phf_generator"
version = "0.11.1" version = "0.11.1"

2
Cargo.toml

@ -1,6 +1,7 @@
[workspace] [workspace]
members = [ members = [
"boa_ast", "boa_ast",
"boa_builtins",
"boa_cli", "boa_cli",
"boa_engine", "boa_engine",
"boa_examples", "boa_examples",
@ -28,6 +29,7 @@ description = "Boa is a Javascript lexer, parser and compiler written in Rust. C
[workspace.dependencies] [workspace.dependencies]
boa_ast = { version = "0.16.0", path = "boa_ast" } boa_ast = { version = "0.16.0", path = "boa_ast" }
boa_builtins = { version = "0.16.0", path = "boa_builtins" }
boa_engine = { version = "0.16.0", path = "boa_engine" } boa_engine = { version = "0.16.0", path = "boa_engine" }
boa_gc = { version = "0.16.0", path = "boa_gc" } boa_gc = { version = "0.16.0", path = "boa_gc" }
boa_icu_provider = { version = "0.16.0", path = "boa_icu_provider" } boa_icu_provider = { version = "0.16.0", path = "boa_icu_provider" }

25
boa_builtins/Cargo.toml

@ -0,0 +1,25 @@
[package]
name = "boa_builtins"
description = "Builtins of the Boa JavaScript engine."
publish = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
build = "build.rs"
[features]
annex-b = []
[dependencies]
bitflags = "2.1.0"
phf = "^0.11.1"
phf_shared = "^0.11.1"
[build-dependencies]
boa_macros.workspace = true
phf_codegen = "^0.11.1"
phf_shared = "^0.11.1"
bitflags = "2.1.0"

1
boa_builtins/README.md

@ -0,0 +1 @@
# TOOD

1485
boa_builtins/build.rs

File diff suppressed because it is too large Load Diff

181
boa_builtins/src/lib.rs

@ -0,0 +1,181 @@
use std::{
fmt::{self, Debug},
hash::{Hash, Hasher},
};
use bitflags::bitflags;
use phf::PhfHash;
use phf_shared::PhfBorrow;
bitflags! {
/// This struct constains the property flags as described in the ECMAScript specification.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Attribute: u8 {
/// The `Writable` attribute decides whether the value associated with the property can be changed or not, from its initial value.
const WRITABLE = 0b0000_0001;
/// If the property can be enumerated by a `for-in` loop.
const ENUMERABLE = 0b0000_0010;
/// If the property descriptor can be changed later.
const CONFIGURABLE = 0b0000_0100;
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct StaticString {
index: u16,
}
impl StaticString {
#[inline]
pub fn index(self) -> u16 {
self.index
}
}
#[derive(Clone, Copy)]
pub struct EncodedStaticPropertyKey(u16);
impl EncodedStaticPropertyKey {
#[inline]
pub fn decode(&self) -> StaticPropertyKey {
let value = self.0 >> 1;
if self.0 & 1 == 0 {
StaticPropertyKey::String(value)
} else {
StaticPropertyKey::Symbol(value as u8)
}
}
}
const fn string(index: u16) -> EncodedStaticPropertyKey {
debug_assert!(index < 2u16.pow(15));
EncodedStaticPropertyKey(index << 1)
}
const fn symbol(index: u8) -> EncodedStaticPropertyKey {
EncodedStaticPropertyKey(((index as u16) << 1) | 1)
}
impl Debug for EncodedStaticPropertyKey {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.decode().fmt(f)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum StaticPropertyKey {
String(u16),
Symbol(u8),
}
impl StaticPropertyKey {
#[inline]
pub fn encode(self) -> EncodedStaticPropertyKey {
match self {
StaticPropertyKey::String(x) => string(x),
StaticPropertyKey::Symbol(x) => symbol(x),
}
}
}
impl Debug for StaticPropertyKey {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
StaticPropertyKey::String(index) => {
let string = RAW_STATICS[index as usize];
let string = String::from_utf16_lossy(string);
write!(f, "String(\"{string}\")")
}
StaticPropertyKey::Symbol(symbol) => {
write!(f, "Symbol({symbol})")
}
}
}
}
impl Eq for EncodedStaticPropertyKey {}
impl PartialEq for EncodedStaticPropertyKey {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Hash for EncodedStaticPropertyKey {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl PhfHash for EncodedStaticPropertyKey {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}
impl PhfBorrow<EncodedStaticPropertyKey> for EncodedStaticPropertyKey {
#[inline]
fn borrow(&self) -> &EncodedStaticPropertyKey {
self
}
}
pub type Slot = (u8, Attribute);
#[derive(Debug)]
pub struct StaticShape {
pub property_table: phf::OrderedMap<EncodedStaticPropertyKey, Slot>,
pub storage_len: usize,
/// \[\[Prototype\]\]
pub prototype: Option<&'static StaticShape>,
}
impl StaticShape {
#[inline]
pub fn get(&self, key: StaticPropertyKey) -> Option<Slot> {
// SAFETY: only used to extend the lifetime, so we are able to call get.
self.property_table
.get(&key.encode())
.map(|(index, attributes)| (*index, *attributes))
}
#[inline]
pub fn is_empty(&self) -> bool {
self.property_table.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.property_table.len()
}
#[inline]
pub fn get_string_key_expect(&self, index: usize) -> StaticString {
match self
.property_table
.index(index)
.expect("there should be a key at the given index")
.0
.decode()
{
StaticPropertyKey::String(index) => StaticString { index },
StaticPropertyKey::Symbol(s) => {
panic!("The key should be a string at position {index}, but symbol {s}")
}
}
}
}
include!(concat!(env!("OUT_DIR"), "/static_shapes_codegen.rs"));
// static NUMBER_BUITIN_OBJECT_STATIC_SHAPE_REF: &StaticShape = &NUMBER_BUITIN_OBJECT_STATIC_SHAPE;

6
boa_cli/src/debug/shape.rs

@ -31,8 +31,12 @@ fn r#type(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValu
Ok(if shape.is_shared() { Ok(if shape.is_shared() {
js_string!("shared") js_string!("shared")
} else { } else if shape.is_unique() {
js_string!("unique") js_string!("unique")
} else if shape.is_static() {
js_string!("static")
} else {
unreachable!("shapes can only be shared, unique, or static")
} }
.into()) .into())
} }

3
boa_engine/Cargo.toml

@ -43,10 +43,11 @@ flowgraph = []
trace = [] trace = []
# Enable Boa's additional ECMAScript features for web browsers. # Enable Boa's additional ECMAScript features for web browsers.
annex-b = ["boa_parser/annex-b"] annex-b = ["boa_parser/annex-b", "boa_builtins/annex-b"]
[dependencies] [dependencies]
boa_interner.workspace = true boa_interner.workspace = true
boa_builtins.workspace = true
boa_gc = { workspace = true, features = [ "thinvec" ] } boa_gc = { workspace = true, features = [ "thinvec" ] }
boa_profiler.workspace = true boa_profiler.workspace = true
boa_macros.workspace = true boa_macros.workspace = true

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

@ -21,7 +21,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
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::{PropertyDescriptor, PropertyNameKind},
realm::Realm, realm::Realm,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
@ -44,9 +44,6 @@ 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(Self::NAME, "init");
let symbol_iterator = JsSymbol::iterator();
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("get [Symbol.species]")
.build(); .build();
@ -61,71 +58,54 @@ impl IntrinsicObject for Array {
let unscopables_object = Self::unscopables_object(); let unscopables_object = Self::unscopables_object();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_accessor( realm,
JsSymbol::species(), &boa_builtins::ARRAY_CONSTRUCTOR_STATIC_SHAPE,
Some(get_species), &boa_builtins::ARRAY_PROTOTYPE_STATIC_SHAPE,
None, )
Attribute::CONFIGURABLE, .property(0)
) .property(values_function.clone())
.property( .property(values_function)
utf16!("length"), .property(unscopables_object)
0, .method(Self::at, 1)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, .method(Self::concat, 1)
) .method(Self::push, 1)
.property( .method(Self::index_of, 1)
utf16!("values"), .method(Self::last_index_of, 1)
values_function.clone(), .method(Self::includes_value, 1)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::map, 1)
) .method(Self::fill, 1)
.property( .method(Self::for_each, 1)
symbol_iterator, .method(Self::filter, 1)
values_function, .method(Self::pop, 0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::join, 1)
) .method(Self::to_string, 0)
.property( .method(Self::reverse, 0)
symbol_unscopables, .method(Self::shift, 0)
unscopables_object, .method(Self::unshift, 1)
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::every, 1)
) .method(Self::find, 1)
.method(Self::at, "at", 1) .method(Self::find_index, 1)
.method(Self::concat, "concat", 1) .method(Self::find_last, 1)
.method(Self::push, "push", 1) .method(Self::find_last_index, 1)
.method(Self::index_of, "indexOf", 1) .method(Self::flat, 0)
.method(Self::last_index_of, "lastIndexOf", 1) .method(Self::flat_map, 1)
.method(Self::includes_value, "includes", 1) .method(Self::slice, 2)
.method(Self::map, "map", 1) .method(Self::some, 1)
.method(Self::fill, "fill", 1) .method(Self::sort, 1)
.method(Self::for_each, "forEach", 1) .method(Self::splice, 2)
.method(Self::filter, "filter", 1) .method(Self::to_locale_string, 0)
.method(Self::pop, "pop", 0) .method(Self::reduce, 1)
.method(Self::join, "join", 1) .method(Self::reduce_right, 1)
.method(Self::to_string, "toString", 0) .method(Self::keys, 0)
.method(Self::reverse, "reverse", 0) .method(Self::entries, 0)
.method(Self::shift, "shift", 0) .method(Self::copy_within, 2)
.method(Self::unshift, "unshift", 1) // Static Methods
.method(Self::every, "every", 1) .static_accessor(Some(get_species), None)
.method(Self::find, "find", 1) .static_method(Self::from, 1)
.method(Self::find_index, "findIndex", 1) .static_method(Self::is_array, 1)
.method(Self::find_last, "findLast", 1) .static_method(Self::of, 0)
.method(Self::find_last_index, "findLastIndex", 1) .build();
.method(Self::flat, "flat", 0)
.method(Self::flat_map, "flatMap", 1)
.method(Self::slice, "slice", 2)
.method(Self::some, "some", 1)
.method(Self::sort, "sort", 1)
.method(Self::splice, "splice", 2)
.method(Self::to_locale_string, "toLocaleString", 0)
.method(Self::reduce, "reduce", 1)
.method(Self::reduce_right, "reduceRight", 1)
.method(Self::keys, "keys", 0)
.method(Self::entries, "entries", 0)
.method(Self::copy_within, "copyWithin", 2)
// Static Methods
.static_method(Self::from, "from", 1)
.static_method(Self::is_array, "isArray", 1)
.static_method(Self::of, "of", 0)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -15,10 +15,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16,
symbol::JsSymbol,
value::{IntegerOrInfinity, Numeric}, value::{IntegerOrInfinity, Numeric},
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
@ -51,8 +48,6 @@ 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(Self::NAME, "init");
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("get [Symbol.species]")
.build(); .build();
@ -61,27 +56,17 @@ impl IntrinsicObject for ArrayBuffer {
.name("get byteLength") .name("get byteLength")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.accessor( realm,
utf16!("byteLength"), &boa_builtins::ARRAY_BUFFER_CONSTRUCTOR_STATIC_SHAPE,
Some(get_byte_length), &boa_builtins::ARRAY_BUFFER_PROTOTYPE_STATIC_SHAPE,
None, )
flag_attributes, .static_accessor(Some(get_species), None)
) .static_method(Self::is_view, 1)
.static_accessor( .accessor(Some(get_byte_length), None)
JsSymbol::species(), .method(Self::slice, 2)
Some(get_species), .property(Self::NAME)
None, .build();
Attribute::CONFIGURABLE,
)
.static_method(Self::is_view, "isView", 1)
.method(Self::slice, "slice", 2)
.property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -17,9 +17,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
property::Attribute,
realm::Realm, realm::Realm,
symbol::JsSymbol,
value::{IntegerOrInfinity, PreferredType}, value::{IntegerOrInfinity, PreferredType},
Context, JsArgs, JsBigInt, JsResult, JsValue, Context, JsArgs, JsBigInt, JsResult, JsValue,
}; };
@ -39,17 +37,17 @@ 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(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.method(Self::to_string, "toString", 0) realm,
.method(Self::value_of, "valueOf", 0) &boa_builtins::BIGINT_CONSTRUCTOR_STATIC_SHAPE,
.static_method(Self::as_int_n, "asIntN", 2) &boa_builtins::BIGINT_PROTOTYPE_STATIC_SHAPE,
.static_method(Self::as_uint_n, "asUintN", 2) )
.property( .method(Self::to_string, 0)
JsSymbol::to_string_tag(), .method(Self::value_of, 0)
Self::NAME, .static_method(Self::as_int_n, 2)
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .static_method(Self::as_uint_n, 2)
) .property(Self::NAME)
.build(); .build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -32,10 +32,14 @@ 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(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.method(Self::to_string, "toString", 0) realm,
.method(Self::value_of, "valueOf", 0) &boa_builtins::BOOLEAN_CONSTRUCTOR_STATIC_SHAPE,
.build(); &boa_builtins::BOOLEAN_PROTOTYPE_STATIC_SHAPE,
)
.method(Self::to_string, 0)
.method(Self::value_of, 0)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -12,10 +12,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16,
symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
}; };
@ -33,8 +30,6 @@ pub struct DataView {
impl IntrinsicObject for DataView { impl IntrinsicObject for DataView {
fn init(realm: &Realm) { fn init(realm: &Realm) {
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("get buffer")
.build(); .build();
@ -47,46 +42,36 @@ impl IntrinsicObject for DataView {
.name("get byteOffset") .name("get byteOffset")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes) realm,
.accessor( &boa_builtins::DATA_VIEW_CONSTRUCTOR_STATIC_SHAPE,
utf16!("byteLength"), &boa_builtins::DATA_VIEW_PROTOTYPE_STATIC_SHAPE,
Some(get_byte_length), )
None, .accessor(Some(get_buffer), None)
flag_attributes, .accessor(Some(get_byte_length), None)
) .accessor(Some(get_byte_offset), None)
.accessor( .method(Self::get_big_int64, 1)
utf16!("byteOffset"), .method(Self::get_big_uint64, 1)
Some(get_byte_offset), .method(Self::get_float32, 1)
None, .method(Self::get_float64, 1)
flag_attributes, .method(Self::get_int8, 1)
) .method(Self::get_int16, 1)
.method(Self::get_big_int64, "getBigInt64", 1) .method(Self::get_int32, 1)
.method(Self::get_big_uint64, "getBigUint64", 1) .method(Self::get_uint8, 1)
.method(Self::get_float32, "getFloat32", 1) .method(Self::get_uint16, 1)
.method(Self::get_float64, "getFloat64", 1) .method(Self::get_uint32, 1)
.method(Self::get_int8, "getInt8", 1) .method(Self::set_big_int64, 2)
.method(Self::get_int16, "getInt16", 1) .method(Self::set_big_uint64, 2)
.method(Self::get_int32, "getInt32", 1) .method(Self::set_float32, 2)
.method(Self::get_uint8, "getUint8", 1) .method(Self::set_float64, 2)
.method(Self::get_uint16, "getUint16", 1) .method(Self::set_int8, 2)
.method(Self::get_uint32, "getUint32", 1) .method(Self::set_int16, 2)
.method(Self::set_big_int64, "setBigInt64", 2) .method(Self::set_int32, 2)
.method(Self::set_big_uint64, "setBigUint64", 2) .method(Self::set_uint8, 2)
.method(Self::set_float32, "setFloat32", 2) .method(Self::set_uint16, 2)
.method(Self::set_float64, "setFloat64", 2) .method(Self::set_uint32, 2)
.method(Self::set_int8, "setInt8", 2) .property(Self::NAME)
.method(Self::set_int16, "setInt16", 2) .build();
.method(Self::set_int32, "setInt32", 2)
.method(Self::set_uint8, "setUint8", 2)
.method(Self::set_uint16, "setUint16", 2)
.method(Self::set_uint32, "setUint32", 2)
.property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -22,10 +22,8 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
value::{IntegerOrNan, JsValue, PreferredType}, value::{IntegerOrNan, JsValue, PreferredType},
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult,
}; };
@ -106,70 +104,62 @@ impl IntrinsicObject for Date {
.length(1) .length(1)
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_method(Self::now, "now", 0) realm,
.static_method(Self::parse, "parse", 1) &boa_builtins::DATE_CONSTRUCTOR_STATIC_SHAPE,
.static_method(Self::utc, "UTC", 7) &boa_builtins::DATE_PROTOTYPE_STATIC_SHAPE,
.method(Self::get_date::<true>, "getDate", 0) )
.method(Self::get_day::<true>, "getDay", 0) .static_method(Self::now, 0)
.method(Self::get_full_year::<true>, "getFullYear", 0) .static_method(Self::parse, 1)
.method(Self::get_hours::<true>, "getHours", 0) .static_method(Self::utc, 7)
.method(Self::get_milliseconds::<true>, "getMilliseconds", 0) .method(Self::get_date::<true>, 0)
.method(Self::get_minutes::<true>, "getMinutes", 0) .method(Self::get_day::<true>, 0)
.method(Self::get_month::<true>, "getMonth", 0) .method(Self::get_full_year::<true>, 0)
.method(Self::get_seconds::<true>, "getSeconds", 0) .method(Self::get_hours::<true>, 0)
.method(Self::get_time, "getTime", 0) .method(Self::get_milliseconds::<true>, 0)
.method(Self::get_timezone_offset, "getTimezoneOffset", 0) .method(Self::get_minutes::<true>, 0)
.method(Self::get_date::<false>, "getUTCDate", 0) .method(Self::get_month::<true>, 0)
.method(Self::get_day::<false>, "getUTCDay", 0) .method(Self::get_seconds::<true>, 0)
.method(Self::get_full_year::<false>, "getUTCFullYear", 0) .method(Self::get_time, 0)
.method(Self::get_hours::<false>, "getUTCHours", 0) .method(Self::get_timezone_offset, 0)
.method(Self::get_milliseconds::<false>, "getUTCMilliseconds", 0) .method(Self::get_date::<false>, 0)
.method(Self::get_minutes::<false>, "getUTCMinutes", 0) .method(Self::get_day::<false>, 0)
.method(Self::get_month::<false>, "getUTCMonth", 0) .method(Self::get_full_year::<false>, 0)
.method(Self::get_seconds::<false>, "getUTCSeconds", 0) .method(Self::get_hours::<false>, 0)
.method(Self::get_year, "getYear", 0) .method(Self::get_milliseconds::<false>, 0)
.method(Self::set_date::<true>, "setDate", 1) .method(Self::get_minutes::<false>, 0)
.method(Self::set_full_year::<true>, "setFullYear", 3) .method(Self::get_month::<false>, 0)
.method(Self::set_hours::<true>, "setHours", 4) .method(Self::get_seconds::<false>, 0)
.method(Self::set_milliseconds::<true>, "setMilliseconds", 1) .method(Self::get_year, 0)
.method(Self::set_minutes::<true>, "setMinutes", 3) .method(Self::set_date::<true>, 1)
.method(Self::set_month::<true>, "setMonth", 2) .method(Self::set_full_year::<true>, 3)
.method(Self::set_seconds::<true>, "setSeconds", 2) .method(Self::set_hours::<true>, 4)
.method(Self::set_time, "setTime", 1) .method(Self::set_milliseconds::<true>, 1)
.method(Self::set_date::<false>, "setUTCDate", 1) .method(Self::set_minutes::<true>, 3)
.method(Self::set_full_year::<false>, "setUTCFullYear", 3) .method(Self::set_month::<true>, 2)
.method(Self::set_hours::<false>, "setUTCHours", 4) .method(Self::set_seconds::<true>, 2)
.method(Self::set_milliseconds::<false>, "setUTCMilliseconds", 1) .method(Self::set_time, 1)
.method(Self::set_minutes::<false>, "setUTCMinutes", 3) .method(Self::set_date::<false>, 1)
.method(Self::set_month::<false>, "setUTCMonth", 2) .method(Self::set_full_year::<false>, 3)
.method(Self::set_seconds::<false>, "setUTCSeconds", 2) .method(Self::set_hours::<false>, 4)
.method(Self::set_year, "setYear", 1) .method(Self::set_milliseconds::<false>, 1)
.method(Self::to_date_string, "toDateString", 0) .method(Self::set_minutes::<false>, 3)
.method(Self::to_iso_string, "toISOString", 0) .method(Self::set_month::<false>, 2)
.method(Self::to_json, "toJSON", 1) .method(Self::set_seconds::<false>, 2)
.method(Self::to_locale_date_string, "toLocaleDateString", 0) .method(Self::set_year, 1)
.method(Self::to_locale_string, "toLocaleString", 0) .method(Self::to_date_string, 0)
.method(Self::to_locale_time_string, "toLocaleTimeString", 0) .method(Self::to_iso_string, 0)
.method(Self::to_string, "toString", 0) .method(Self::to_json, 1)
.method(Self::to_time_string, "toTimeString", 0) .method(Self::to_locale_date_string, 0)
.method(Self::value_of, "valueOf", 0) .method(Self::to_locale_string, 0)
.property( .method(Self::to_locale_time_string, 0)
"toGMTString", .method(Self::to_string, 0)
to_utc_string.clone(), .method(Self::to_time_string, 0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::value_of, 0)
) .property(to_utc_string.clone())
.property( .property(to_utc_string)
"toUTCString", .property(to_primitive)
to_utc_string, .build();
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
JsSymbol::to_primitive(),
to_primitive,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -14,10 +14,10 @@ use crate::{
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptorBuilder}, property::PropertyDescriptorBuilder,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -30,13 +30,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -15,10 +15,9 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,13 +31,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -16,7 +16,6 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
@ -131,12 +130,15 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.property(utf16!("name"), Self::NAME, attribute) &boa_builtins::ERROR_CONSTRUCTOR_STATIC_SHAPE,
.property(utf16!("message"), "", attribute) &boa_builtins::ERROR_PROTOTYPE_STATIC_SHAPE,
.method(Self::to_string, "toString", 0) )
.build(); .property(Self::NAME)
.property(js_string!(""))
.method(Self::to_string, 0)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -13,10 +13,9 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -30,13 +29,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -13,10 +13,9 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -29,13 +28,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -15,10 +15,9 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -32,13 +31,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -26,7 +26,7 @@ use crate::{
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, NativeFunction, Context, JsArgs, JsResult, JsString, JsValue, NativeFunction,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -40,13 +40,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -14,10 +14,9 @@ use crate::{
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -31,13 +30,16 @@ 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(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.prototype(realm.intrinsics().constructors().error().constructor()) &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE,
.inherits(Some(realm.intrinsics().constructors().error().prototype())) &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE,
.property(utf16!("name"), Self::NAME, attribute) )
.property(utf16!("message"), "", attribute) .prototype(realm.intrinsics().constructors().error().constructor())
.build(); .inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(Self::NAME)
.property(JsString::default())
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -23,7 +23,7 @@ pub(crate) struct Escape;
impl IntrinsicObject for Escape { impl IntrinsicObject for Escape {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, escape) BuiltInBuilder::callable_intrinsic::<Self>(realm, escape)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();
@ -95,7 +95,7 @@ pub(crate) struct Unescape;
impl IntrinsicObject for Unescape { impl IntrinsicObject for Unescape {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, unescape) BuiltInBuilder::callable_intrinsic::<Self>(realm, unescape)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();

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

@ -35,7 +35,7 @@ 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(Self::NAME, "init");
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, Self::eval) BuiltInBuilder::callable_intrinsic::<Self>(realm, Self::eval)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();

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

@ -19,12 +19,14 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, object::{
object::{JsFunction, PrivateElement, PrivateName}, internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData, ObjectKind,
property::{Attribute, PropertyDescriptor, PropertyKey}, PrivateName,
},
object::{JsFunction, PrivateElement},
property::{PropertyDescriptor, PropertyKey},
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
vm::{ActiveRunnable, CodeBlock}, vm::{ActiveRunnable, CodeBlock},
Context, JsArgs, JsResult, JsString, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
@ -468,33 +470,65 @@ impl IntrinsicObject for BuiltInFunctionObject {
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, "apply", 2)
.method(Self::bind, "bind", 1) // .method(Self::bind, "bind", 1)
.method(Self::call, "call", 1) // .method(Self::call, "call", 1)
.method(Self::to_string, "toString", 0) // .method(Self::to_string, "toString", 0)
.property(JsSymbol::has_instance(), has_instance, Attribute::default()) // .property(JsSymbol::has_instance(), has_instance, Attribute::default())
.accessor( // .accessor(
utf16!("caller"), // utf16!("caller"),
Some(throw_type_error.clone()), // Some(throw_type_error.clone()),
Some(throw_type_error.clone()), // Some(throw_type_error.clone()),
Attribute::CONFIGURABLE, // Attribute::CONFIGURABLE,
) // )
.accessor( // .accessor(
utf16!("arguments"), // utf16!("arguments"),
Some(throw_type_error.clone()), // Some(throw_type_error.clone()),
Some(throw_type_error), // Some(throw_type_error),
Attribute::CONFIGURABLE, // Attribute::CONFIGURABLE,
) // )
.build(); // .build();
// BuiltInBuilder::callable_with_object(realm, prototype.clone(), Self::prototype)
// .name("")
// .length(0)
// .build();
// prototype.set_prototype(Some(realm.intrinsics().constructors().object().prototype()));
BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
realm,
&boa_builtins::FUNCTION_CONSTRUCTOR_STATIC_SHAPE,
&boa_builtins::FUNCTION_PROTOTYPE_STATIC_SHAPE,
)
.property(0)
.property("")
.method(Self::apply, 2)
.method(Self::bind, 1)
.method(Self::call, 1)
.method(Self::to_string, 0)
.property(has_instance)
.accessor(
Some(throw_type_error.clone()),
Some(throw_type_error.clone()),
)
.accessor(Some(throw_type_error.clone()), Some(throw_type_error))
.build();
let prototype = realm.intrinsics().constructors().function().prototype(); let prototype = realm.intrinsics().constructors().function().prototype();
{
BuiltInBuilder::callable_with_object(realm, prototype.clone(), Self::prototype) let mut prototype = prototype.borrow_mut();
.name("") let function = Function::new(
.length(0) FunctionKind::Native {
.build(); function: NativeFunction::from_fn_ptr(Self::prototype),
constructor: (true).then_some(ConstructorKind::Base),
},
realm.clone(),
);
*prototype.kind_mut() = ObjectKind::Function(function);
}
// TODO: improve this, because it's converting the static shape into a unique one, on initialization.
prototype.set_prototype(Some(realm.intrinsics().constructors().object().prototype())); prototype.set_prototype(Some(realm.intrinsics().constructors().object().prototype()));
} }
@ -1013,6 +1047,7 @@ impl BuiltInFunctionObject {
Ok(JsValue::ordinary_has_instance(this, args.get_or_undefined(0), context)?.into()) Ok(JsValue::ordinary_has_instance(this, args.get_or_undefined(0), context)?.into())
} }
#[allow(dead_code)]
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
fn prototype(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> { fn prototype(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
Ok(JsValue::undefined()) Ok(JsValue::undefined())

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

@ -24,10 +24,9 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::JsObject, object::JsObject,
property::{Attribute, PropertyNameKind}, property::PropertyNameKind,
realm::Realm, realm::Realm,
string::{utf16, CodePoint}, string::{utf16, CodePoint},
symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
vm::CallFrame, vm::CallFrame,
Context, JsArgs, JsResult, JsString, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
@ -50,14 +49,14 @@ 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(Self::NAME, "init");
let to_string_tag = JsSymbol::to_string_tag(); BuiltInBuilder::with_intrinsic_static_shape::<Self>(
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; realm,
&boa_builtins::JSON_OBJECT_STATIC_SHAPE,
BuiltInBuilder::with_intrinsic::<Self>(realm) )
.static_method(Self::parse, "parse", 2) .static_method(Self::parse, 2)
.static_method(Self::stringify, "stringify", 3) .static_method(Self::stringify, 3)
.static_property(to_string_tag, Self::NAME, attribute) .static_property(Self::NAME)
.build(); .build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -15,10 +15,9 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::PropertyNameKind,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -53,43 +52,25 @@ impl IntrinsicObject for Map {
.name("entries") .name("entries")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_accessor( realm,
JsSymbol::species(), &boa_builtins::MAP_CONSTRUCTOR_STATIC_SHAPE,
Some(get_species), &boa_builtins::MAP_PROTOTYPE_STATIC_SHAPE,
None, )
Attribute::CONFIGURABLE, .static_accessor(Some(get_species), None)
) .property(entries_function.clone())
.property( .property(entries_function)
utf16!("entries"), .property(Self::NAME)
entries_function.clone(), .method(Self::clear, 0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::delete, 1)
) .method(Self::for_each, 1)
.property( .method(Self::get, 1)
JsSymbol::iterator(), .method(Self::has, 1)
entries_function, .method(Self::keys, 0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::set, 2)
) .method(Self::values, 0)
.property( .accessor(Some(get_size), None)
JsSymbol::to_string_tag(), .build();
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.method(Self::clear, "clear", 0)
.method(Self::delete, "delete", 1)
.method(Self::for_each, "forEach", 1)
.method(Self::get, "get", 1)
.method(Self::has, "has", 1)
.method(Self::keys, "keys", 0)
.method(Self::set, "set", 2)
.method(Self::values, "values", 0)
.accessor(
utf16!("size"),
Some(get_size),
None,
Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -12,9 +12,8 @@
//! [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, object::JsObject, realm::Realm,
property::Attribute, realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, Context, JsArgs, JsResult, JsValue,
JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -31,61 +30,55 @@ 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(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; BuiltInBuilder::with_intrinsic_static_shape::<Self>(
BuiltInBuilder::with_intrinsic::<Self>(realm) realm,
.static_property(utf16!("E"), std::f64::consts::E, attribute) &boa_builtins::MATH_OBJECT_STATIC_SHAPE,
.static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute) )
.static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute) .static_property(std::f64::consts::E)
.static_property(utf16!("LOG10E"), std::f64::consts::LOG10_E, attribute) .static_property(std::f64::consts::LN_10)
.static_property(utf16!("LOG2E"), std::f64::consts::LOG2_E, attribute) .static_property(std::f64::consts::LN_2)
.static_property(utf16!("PI"), std::f64::consts::PI, attribute) .static_property(std::f64::consts::LOG10_E)
.static_property( .static_property(std::f64::consts::LOG2_E)
utf16!("SQRT1_2"), .static_property(std::f64::consts::PI)
std::f64::consts::FRAC_1_SQRT_2, .static_property(std::f64::consts::FRAC_1_SQRT_2)
attribute, .static_property(std::f64::consts::SQRT_2)
) .static_method(Self::abs, 1)
.static_property(utf16!("SQRT2"), std::f64::consts::SQRT_2, attribute) .static_method(Self::acos, 1)
.static_method(Self::abs, "abs", 1) .static_method(Self::acosh, 1)
.static_method(Self::acos, "acos", 1) .static_method(Self::asin, 1)
.static_method(Self::acosh, "acosh", 1) .static_method(Self::asinh, 1)
.static_method(Self::asin, "asin", 1) .static_method(Self::atan, 1)
.static_method(Self::asinh, "asinh", 1) .static_method(Self::atanh, 1)
.static_method(Self::atan, "atan", 1) .static_method(Self::atan2, 2)
.static_method(Self::atanh, "atanh", 1) .static_method(Self::cbrt, 1)
.static_method(Self::atan2, "atan2", 2) .static_method(Self::ceil, 1)
.static_method(Self::cbrt, "cbrt", 1) .static_method(Self::clz32, 1)
.static_method(Self::ceil, "ceil", 1) .static_method(Self::cos, 1)
.static_method(Self::clz32, "clz32", 1) .static_method(Self::cosh, 1)
.static_method(Self::cos, "cos", 1) .static_method(Self::exp, 1)
.static_method(Self::cosh, "cosh", 1) .static_method(Self::expm1, 1)
.static_method(Self::exp, "exp", 1) .static_method(Self::floor, 1)
.static_method(Self::expm1, "expm1", 1) .static_method(Self::fround, 1)
.static_method(Self::floor, "floor", 1) .static_method(Self::hypot, 2)
.static_method(Self::fround, "fround", 1) .static_method(Self::imul, 2)
.static_method(Self::hypot, "hypot", 2) .static_method(Self::log, 1)
.static_method(Self::imul, "imul", 2) .static_method(Self::log1p, 1)
.static_method(Self::log, "log", 1) .static_method(Self::log10, 1)
.static_method(Self::log1p, "log1p", 1) .static_method(Self::log2, 1)
.static_method(Self::log10, "log10", 1) .static_method(Self::max, 2)
.static_method(Self::log2, "log2", 1) .static_method(Self::min, 2)
.static_method(Self::max, "max", 2) .static_method(Self::pow, 2)
.static_method(Self::min, "min", 2) .static_method(Self::random, 0)
.static_method(Self::pow, "pow", 2) .static_method(Self::round, 1)
.static_method(Self::random, "random", 0) .static_method(Self::sign, 1)
.static_method(Self::round, "round", 1) .static_method(Self::sin, 1)
.static_method(Self::sign, "sign", 1) .static_method(Self::sinh, 1)
.static_method(Self::sin, "sin", 1) .static_method(Self::sqrt, 1)
.static_method(Self::sinh, "sinh", 1) .static_method(Self::tan, 1)
.static_method(Self::sqrt, "sqrt", 1) .static_method(Self::tanh, 1)
.static_method(Self::tan, "tan", 1) .static_method(Self::trunc, 1)
.static_method(Self::tanh, "tanh", 1) .static_property(Self::NAME)
.static_method(Self::trunc, "trunc", 1) .build();
.static_property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

371
boa_engine/src/builtins/mod.rs

@ -39,6 +39,8 @@ pub mod escape;
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
pub mod intl; pub mod intl;
use boa_builtins::StaticShape as RawStaticShape;
pub(crate) use self::{ pub(crate) use self::{
array::Array, array::Array,
async_function::AsyncFunction, async_function::AsyncFunction,
@ -95,7 +97,9 @@ use crate::{
js_string, js_string,
native_function::{NativeFunction, NativeFunctionPointer}, native_function::{NativeFunction, NativeFunctionPointer},
object::{ object::{
shape::{property_table::PropertyTableInner, slot::SlotAttributes}, shape::{
property_table::PropertyTableInner, slot::SlotAttributes, static_shape::StaticShape,
},
FunctionBinding, JsFunction, JsObject, JsPrototype, Object, ObjectData, ObjectKind, FunctionBinding, JsFunction, JsObject, JsPrototype, Object, ObjectData, ObjectKind,
CONSTRUCTOR, PROTOTYPE, CONSTRUCTOR, PROTOTYPE,
}, },
@ -587,19 +591,318 @@ struct BuiltInBuilder<'ctx, Kind> {
prototype: JsObject, prototype: JsObject,
} }
impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { struct BuiltInBuilderCallableIntrinsic<'ctx> {
// fn new(realm: &'ctx Realm) -> BuiltInBuilder<'ctx, OrdinaryObject> { realm: &'ctx Realm,
// BuiltInBuilder { function: NativeFunctionPointer,
// realm, object: JsObject,
// object: BuiltInObjectInitializer::Unique { name: JsString,
// object: Object::default(), length: usize,
// data: ObjectData::ordinary(), }
// },
// kind: OrdinaryObject, impl BuiltInBuilderCallableIntrinsic<'_> {
// prototype: realm.intrinsics().constructors().object().prototype(), fn name<N: Into<JsString>>(mut self, name: N) -> Self {
// } self.name = name.into();
// } self
}
/// Specify how many arguments the constructor function takes.
///
/// Default is `0`.
const fn length(mut self, length: usize) -> Self {
self.length = length;
self
}
fn build(self) {
let function = function::Function::new(
function::FunctionKind::Native {
function: NativeFunction::from_fn_ptr(self.function),
constructor: (true).then_some(function::ConstructorKind::Base),
},
self.realm.clone(),
);
let mut object = self.object.borrow_mut();
object.properties_mut().shape = self
.realm
.intrinsics()
.templates()
.function()
.shape()
.clone()
.into();
object.properties_mut().storage = vec![self.length.into(), self.name.into()];
*object.kind_mut() = ObjectKind::Function(function);
}
}
struct BuiltInBuilderConstructorStaticShape<'ctx> {
realm: &'ctx Realm,
function: NativeFunctionPointer,
constructor_property_index: usize,
constructor_object: JsObject,
constructor_shape: &'static RawStaticShape,
constructor_storage: Vec<JsValue>,
prototype_property_index: usize,
prototype_object: JsObject,
prototype_shape: &'static RawStaticShape,
prototype_storage: Vec<JsValue>,
__proto__: JsPrototype,
inherits: Option<JsObject>,
}
impl<'ctx> BuiltInBuilder<'ctx, Callable<Constructor>> {
fn from_standard_constructor_static_shape<SC: BuiltInConstructor>(
realm: &'ctx Realm,
constructor_shape: &'static RawStaticShape,
prototype_shape: &'static RawStaticShape,
) -> BuiltInBuilderConstructorStaticShape<'ctx> {
let constructor = SC::STANDARD_CONSTRUCTOR(realm.intrinsics().constructors());
// println!("{constructor_shape:#?}");
// println!("{prototype_shape:#?}");
let mut this = BuiltInBuilderConstructorStaticShape {
realm,
function: SC::constructor,
constructor_property_index: 0,
constructor_shape,
constructor_storage: Vec::with_capacity(constructor_shape.storage_len),
constructor_object: constructor.constructor(),
prototype_property_index: 0,
prototype_shape,
prototype_storage: Vec::with_capacity(prototype_shape.storage_len),
prototype_object: constructor.prototype(),
__proto__: Some(realm.intrinsics().constructors().function().prototype()),
inherits: Some(realm.intrinsics().constructors().object().prototype()),
};
this.constructor_storage.push(SC::LENGTH.into());
this.constructor_storage.push(js_string!(SC::NAME).into());
this.constructor_storage
.push(this.prototype_object.clone().into());
this.constructor_property_index += 3;
this.prototype_storage
.push(this.constructor_object.clone().into());
this.prototype_property_index += 1;
this
}
}
#[allow(dead_code)]
impl BuiltInBuilderConstructorStaticShape<'_> {
/// Adds a new static method to the builtin object.
fn static_method(mut self, function: NativeFunctionPointer, length: usize) -> Self {
let name = self
.constructor_shape
.get_string_key_expect(self.constructor_property_index);
let function = BuiltInBuilder::callable(self.realm, function)
.name(name)
.length(length)
.build();
self.constructor_storage.push(function.into());
self.constructor_property_index += 1;
self
}
/// Adds a new static data property to the builtin object.
fn static_property<V>(mut self, value: V) -> Self
where
V: Into<JsValue>,
{
self.constructor_storage.push(value.into());
self.constructor_property_index += 1;
self
}
/// Specify the `[[Prototype]]` internal field of the builtin object.
///
/// Default is `Function.prototype` for constructors and `Object.prototype` for statics.
fn prototype(mut self, prototype: JsObject) -> Self {
self.__proto__ = Some(prototype);
self
}
/// Adds a new method to the constructor's prototype.
fn method(mut self, function: NativeFunctionPointer, length: usize) -> Self {
let name = self
.prototype_shape
.get_string_key_expect(self.prototype_property_index);
let function = BuiltInBuilder::callable(self.realm, function)
.name(name)
.length(length)
.build();
self.prototype_storage.push(function.into());
self.prototype_property_index += 1;
self
}
fn method_with_name(
mut self,
function: NativeFunctionPointer,
name: JsString,
length: usize,
) -> Self {
let function = BuiltInBuilder::callable(self.realm, function)
.name(name)
.length(length)
.build();
self.prototype_storage.push(function.into());
self.prototype_property_index += 1;
self
}
/// Adds a new data property to the constructor's prototype.
fn property<V>(mut self, value: V) -> Self
where
V: Into<JsValue>,
{
self.prototype_storage.push(value.into());
self.prototype_property_index += 1;
self
}
/// Adds new accessor property to the constructor's prototype.
fn accessor(mut self, get: Option<JsFunction>, set: Option<JsFunction>) -> Self {
self.prototype_storage.extend([
get.map(JsValue::new).unwrap_or_default(),
set.map(JsValue::new).unwrap_or_default(),
]);
self.prototype_property_index += 1;
self
}
fn static_accessor(mut self, get: Option<JsFunction>, set: Option<JsFunction>) -> Self {
self.constructor_storage.extend([
get.map(JsValue::new).unwrap_or_default(),
set.map(JsValue::new).unwrap_or_default(),
]);
self.constructor_property_index += 1;
self
}
/// Specifies the parent prototype which objects created by this constructor inherit from.
///
/// Default is `Object.prototype`.
#[allow(clippy::missing_const_for_fn)]
fn inherits(mut self, prototype: JsPrototype) -> Self {
self.inherits = prototype;
self
}
fn build(mut self) {
debug_assert_eq!(
self.constructor_storage.len() + 1,
self.constructor_shape.storage_len
);
debug_assert_eq!(
self.constructor_storage.capacity(),
self.constructor_shape.storage_len
);
let function = function::Function::new(
function::FunctionKind::Native {
function: NativeFunction::from_fn_ptr(self.function),
constructor: (true).then_some(function::ConstructorKind::Base),
},
self.realm.clone(),
);
let mut object = self.constructor_object.borrow_mut();
*object.kind_mut() = ObjectKind::Function(function);
object.properties_mut().shape = StaticShape::new(self.constructor_shape).into();
self.constructor_storage.push(
self.__proto__
.unwrap_or_else(|| {
self.realm
.intrinsics()
.constructors()
.function()
.prototype()
})
.into(),
);
object.properties_mut().storage = self.constructor_storage;
debug_assert_eq!(
self.prototype_storage.len() + 1,
self.prototype_shape.storage_len
);
debug_assert_eq!(
self.prototype_storage.capacity(),
self.prototype_shape.storage_len
);
let mut prototype = self.prototype_object.borrow_mut();
prototype.properties_mut().shape = StaticShape::new(self.prototype_shape).into();
self.prototype_storage
.push(self.inherits.map(JsValue::new).unwrap_or_default());
prototype.properties_mut().storage = self.prototype_storage;
}
}
struct BuiltInBuilderStaticShape<'ctx> {
realm: &'ctx Realm,
shape: &'static RawStaticShape,
object: JsObject,
property_index: usize,
storage: Vec<JsValue>,
}
impl BuiltInBuilderStaticShape<'_> {
/// Adds a new static method to the builtin object.
fn static_method(mut self, function: NativeFunctionPointer, length: usize) -> Self {
let name = self.shape.get_string_key_expect(self.property_index);
let function = BuiltInBuilder::callable(self.realm, function)
.name(name)
.length(length)
.build();
self.storage.push(function.into());
self.property_index += 1;
self
}
/// Adds a new static data property to the builtin object.
fn static_property<V>(mut self, value: V) -> Self
where
V: Into<JsValue>,
{
self.storage.push(value.into());
self.property_index += 1;
self
}
fn build(mut self) {
debug_assert_eq!(self.storage.len(), self.shape.len());
debug_assert_eq!(self.storage.len() + 1, self.shape.storage_len);
debug_assert_eq!(self.storage.capacity(), self.shape.storage_len);
let mut object = self.object.borrow_mut();
object.properties_mut().shape = StaticShape::new(self.shape).into();
self.storage.push(
self.realm
.intrinsics()
.constructors()
.object()
.prototype()
.into(),
);
object.properties_mut().storage = self.storage;
}
}
impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
fn with_intrinsic<I: IntrinsicObject>( fn with_intrinsic<I: IntrinsicObject>(
realm: &'ctx Realm, realm: &'ctx Realm,
) -> BuiltInBuilder<'ctx, OrdinaryObject> { ) -> BuiltInBuilder<'ctx, OrdinaryObject> {
@ -610,6 +913,19 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
prototype: realm.intrinsics().constructors().object().prototype(), prototype: realm.intrinsics().constructors().object().prototype(),
} }
} }
fn with_intrinsic_static_shape<I: IntrinsicObject>(
realm: &'ctx Realm,
shape: &'static boa_builtins::StaticShape,
) -> BuiltInBuilderStaticShape<'ctx> {
BuiltInBuilderStaticShape {
realm,
shape,
object: I::get(realm.intrinsics()),
storage: Vec::with_capacity(shape.storage_len),
property_index: 0,
}
}
} }
struct BuiltInConstructorWithPrototype<'ctx> { struct BuiltInConstructorWithPrototype<'ctx> {
@ -625,6 +941,7 @@ struct BuiltInConstructorWithPrototype<'ctx> {
prototype_property_table: PropertyTableInner, prototype_property_table: PropertyTableInner,
prototype_storage: Vec<JsValue>, prototype_storage: Vec<JsValue>,
prototype: JsObject, prototype: JsObject,
__proto__: JsPrototype, __proto__: JsPrototype,
inherits: Option<JsObject>, inherits: Option<JsObject>,
attributes: Attribute, attributes: Attribute,
@ -943,21 +1260,16 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
} }
} }
fn callable_with_intrinsic<I: IntrinsicObject>( fn callable_intrinsic<I: IntrinsicObject>(
realm: &'ctx Realm, realm: &'ctx Realm,
function: NativeFunctionPointer, function: NativeFunctionPointer,
) -> BuiltInBuilder<'ctx, Callable<OrdinaryFunction>> { ) -> BuiltInBuilderCallableIntrinsic<'ctx> {
BuiltInBuilder { BuiltInBuilderCallableIntrinsic {
realm, realm,
object: BuiltInObjectInitializer::Shared(I::get(realm.intrinsics())), function,
kind: Callable { object: I::get(realm.intrinsics()),
function, name: JsString::default(),
name: js_string!(""), length: 0,
length: 0,
kind: OrdinaryFunction,
realm: realm.clone(),
},
prototype: realm.intrinsics().constructors().function().prototype(),
} }
} }
@ -1057,15 +1369,6 @@ impl<T> BuiltInBuilder<'_, T> {
} }
impl<FnTyp> BuiltInBuilder<'_, Callable<FnTyp>> { impl<FnTyp> BuiltInBuilder<'_, Callable<FnTyp>> {
/// Specify how many arguments the constructor function takes.
///
/// Default is `0`.
#[inline]
const fn length(mut self, length: usize) -> Self {
self.kind.length = length;
self
}
/// Specify the name of the constructor function. /// Specify the name of the constructor function.
/// ///
/// Default is `""` /// Default is `""`

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

@ -38,7 +38,7 @@ pub(crate) struct IsFinite;
impl IntrinsicObject for IsFinite { impl IntrinsicObject for IsFinite {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, is_finite) BuiltInBuilder::callable_intrinsic::<Self>(realm, is_finite)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();
@ -84,7 +84,7 @@ pub(crate) struct IsNaN;
impl IntrinsicObject for IsNaN { impl IntrinsicObject for IsNaN {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, is_nan) BuiltInBuilder::callable_intrinsic::<Self>(realm, is_nan)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();
@ -225,7 +225,7 @@ pub(crate) struct ParseInt;
impl IntrinsicObject for ParseInt { impl IntrinsicObject for ParseInt {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, parse_int) BuiltInBuilder::callable_intrinsic::<Self>(realm, parse_int)
.name(Self::NAME) .name(Self::NAME)
.length(2) .length(2)
.build(); .build();
@ -298,7 +298,7 @@ pub(crate) struct ParseFloat;
impl IntrinsicObject for ParseFloat { impl IntrinsicObject for ParseFloat {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, parse_float) BuiltInBuilder::callable_intrinsic::<Self>(realm, parse_float)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();

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

@ -18,9 +18,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm, realm::Realm,
string::utf16,
value::{AbstractRelation, IntegerOrInfinity, JsValue}, value::{AbstractRelation, IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
}; };
@ -49,46 +47,32 @@ 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(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
realm,
BuiltInBuilder::from_standard_constructor::<Self>(realm) &boa_builtins::NUMBER_CONSTRUCTOR_STATIC_SHAPE,
.static_property(utf16!("EPSILON"), f64::EPSILON, attribute) &boa_builtins::NUMBER_PROTOTYPE_STATIC_SHAPE,
.static_property( )
utf16!("MAX_SAFE_INTEGER"), .static_property(f64::EPSILON)
Self::MAX_SAFE_INTEGER, .static_property(Self::MAX_SAFE_INTEGER)
attribute, .static_property(Self::MIN_SAFE_INTEGER)
) .static_property(Self::MAX_VALUE)
.static_property( .static_property(Self::MIN_VALUE)
utf16!("MIN_SAFE_INTEGER"), .static_property(f64::NEG_INFINITY)
Self::MIN_SAFE_INTEGER, .static_property(f64::INFINITY)
attribute, .static_property(f64::NAN)
) .static_property(realm.intrinsics().objects().parse_int())
.static_property(utf16!("MAX_VALUE"), Self::MAX_VALUE, attribute) .static_property(realm.intrinsics().objects().parse_float())
.static_property(utf16!("MIN_VALUE"), Self::MIN_VALUE, attribute) .static_method(Self::number_is_finite, 1)
.static_property(utf16!("NEGATIVE_INFINITY"), f64::NEG_INFINITY, attribute) .static_method(Self::number_is_nan, 1)
.static_property(utf16!("POSITIVE_INFINITY"), f64::INFINITY, attribute) .static_method(Self::is_safe_integer, 1)
.static_property(utf16!("NaN"), f64::NAN, attribute) .static_method(Self::number_is_integer, 1)
.static_property( .method(Self::to_exponential, 1)
utf16!("parseInt"), .method(Self::to_fixed, 1)
realm.intrinsics().objects().parse_int(), .method(Self::to_locale_string, 0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::to_precision, 1)
) .method(Self::to_string, 1)
.static_property( .method(Self::value_of, 0)
utf16!("parseFloat"), .build();
realm.intrinsics().objects().parse_float(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_method(Self::number_is_finite, "isFinite", 1)
.static_method(Self::number_is_nan, "isNaN", 1)
.static_method(Self::is_safe_integer, "isSafeInteger", 1)
.static_method(Self::number_is_integer, "isInteger", 1)
.method(Self::to_exponential, "toExponential", 1)
.method(Self::to_fixed, "toFixed", 1)
.method(Self::to_locale_string, "toLocaleString", 0)
.method(Self::to_precision, "toPrecision", 1)
.method(Self::to_string, "toString", 1)
.method(Self::value_of, "valueOf", 0)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -26,7 +26,7 @@ use crate::{
internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, IntegrityLevel, internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, IntegrityLevel,
JsObject, ObjectData, ObjectKind, JsObject, ObjectData, ObjectKind,
}, },
property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, property::{PropertyDescriptor, PropertyKey, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
@ -56,55 +56,46 @@ impl IntrinsicObject for Object {
.name("set __proto__") .name("set __proto__")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.inherits(None) realm,
.accessor( &boa_builtins::OBJECT_CONSTRUCTOR_STATIC_SHAPE,
utf16!("__proto__"), &boa_builtins::OBJECT_PROTOTYPE_STATIC_SHAPE,
Some(legacy_proto_getter), )
Some(legacy_setter_proto), .inherits(None)
Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .accessor(Some(legacy_proto_getter), Some(legacy_setter_proto))
) .method(Self::has_own_property, 1)
.method(Self::has_own_property, "hasOwnProperty", 1) .method(Self::property_is_enumerable, 1)
.method(Self::property_is_enumerable, "propertyIsEnumerable", 1) .method(Self::to_string, 0)
.method(Self::to_string, "toString", 0) .method(Self::to_locale_string, 0)
.method(Self::to_locale_string, "toLocaleString", 0) .method(Self::value_of, 0)
.method(Self::value_of, "valueOf", 0) .method(Self::is_prototype_of, 1)
.method(Self::is_prototype_of, "isPrototypeOf", 1) .method(Self::legacy_define_getter, 2)
.method(Self::legacy_define_getter, "__defineGetter__", 2) .method(Self::legacy_define_setter, 2)
.method(Self::legacy_define_setter, "__defineSetter__", 2) .method(Self::legacy_lookup_getter, 1)
.method(Self::legacy_lookup_getter, "__lookupGetter__", 1) .method(Self::legacy_lookup_setter, 1)
.method(Self::legacy_lookup_setter, "__lookupSetter__", 1) .static_method(Self::create, 2)
.static_method(Self::create, "create", 2) .static_method(Self::set_prototype_of, 2)
.static_method(Self::set_prototype_of, "setPrototypeOf", 2) .static_method(Self::get_prototype_of, 1)
.static_method(Self::get_prototype_of, "getPrototypeOf", 1) .static_method(Self::define_property, 3)
.static_method(Self::define_property, "defineProperty", 3) .static_method(Self::define_properties, 2)
.static_method(Self::define_properties, "defineProperties", 2) .static_method(Self::assign, 2)
.static_method(Self::assign, "assign", 2) .static_method(Self::is, 2)
.static_method(Self::is, "is", 2) .static_method(Self::keys, 1)
.static_method(Self::keys, "keys", 1) .static_method(Self::values, 1)
.static_method(Self::values, "values", 1) .static_method(Self::entries, 1)
.static_method(Self::entries, "entries", 1) .static_method(Self::seal, 1)
.static_method(Self::seal, "seal", 1) .static_method(Self::is_sealed, 1)
.static_method(Self::is_sealed, "isSealed", 1) .static_method(Self::freeze, 1)
.static_method(Self::freeze, "freeze", 1) .static_method(Self::is_frozen, 1)
.static_method(Self::is_frozen, "isFrozen", 1) .static_method(Self::prevent_extensions, 1)
.static_method(Self::prevent_extensions, "preventExtensions", 1) .static_method(Self::is_extensible, 1)
.static_method(Self::is_extensible, "isExtensible", 1) .static_method(Self::get_own_property_descriptor, 2)
.static_method( .static_method(Self::get_own_property_descriptors, 1)
Self::get_own_property_descriptor, .static_method(Self::get_own_property_names, 1)
"getOwnPropertyDescriptor", .static_method(Self::get_own_property_symbols, 1)
2, .static_method(Self::has_own, 2)
) .static_method(Self::from_entries, 1)
.static_method( .build();
Self::get_own_property_descriptors,
"getOwnPropertyDescriptors",
1,
)
.static_method(Self::get_own_property_names, "getOwnPropertyNames", 1)
.static_method(Self::get_own_property_symbols, "getOwnPropertySymbols", 1)
.static_method(Self::has_own, "hasOwn", 2)
.static_method(Self::from_entries, "fromEntries", 1)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -14,10 +14,8 @@ use crate::{
internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction, internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction,
JsObject, ObjectData, CONSTRUCTOR, JsObject, ObjectData, CONSTRUCTOR,
}, },
property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsError, JsResult, Context, JsArgs, JsError, JsResult,
}; };
@ -324,29 +322,24 @@ impl IntrinsicObject for Promise {
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_method(Self::all, "all", 1) realm,
.static_method(Self::all_settled, "allSettled", 1) &boa_builtins::PROMISE_CONSTRUCTOR_STATIC_SHAPE,
.static_method(Self::any, "any", 1) &boa_builtins::PROMISE_PROTOTYPE_STATIC_SHAPE,
.static_method(Self::race, "race", 1) )
.static_method(Self::reject, "reject", 1) .static_method(Self::all, 1)
.static_method(Self::resolve, "resolve", 1) .static_method(Self::all_settled, 1)
.static_accessor( .static_method(Self::any, 1)
JsSymbol::species(), .static_method(Self::race, 1)
Some(get_species), .static_method(Self::reject, 1)
None, .static_method(Self::resolve, 1)
Attribute::CONFIGURABLE, .static_accessor(Some(get_species), None)
) .method(Self::then, 2)
.method(Self::then, "then", 2) .method(Self::catch, 1)
.method(Self::catch, "catch", 1) .method(Self::finally, 1)
.method(Self::finally, "finally", 1) // <https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag>
// <https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag> .property(Self::NAME)
.property( .build();
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -16,9 +16,7 @@ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
object::JsObject, object::JsObject,
property::Attribute,
realm::Realm, realm::Realm,
symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -34,32 +32,25 @@ 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(Self::NAME, "init");
let to_string_tag = JsSymbol::to_string_tag(); BuiltInBuilder::with_intrinsic_static_shape::<Self>(
realm,
BuiltInBuilder::with_intrinsic::<Self>(realm) &boa_builtins::REFLECT_OBJECT_STATIC_SHAPE,
.static_method(Self::apply, "apply", 3) )
.static_method(Self::construct, "construct", 2) .static_method(Self::apply, 3)
.static_method(Self::define_property, "defineProperty", 3) .static_method(Self::construct, 2)
.static_method(Self::delete_property, "deleteProperty", 2) .static_method(Self::define_property, 3)
.static_method(Self::get, "get", 2) .static_method(Self::delete_property, 2)
.static_method( .static_method(Self::get, 2)
Self::get_own_property_descriptor, .static_method(Self::get_own_property_descriptor, 2)
"getOwnPropertyDescriptor", .static_method(Self::get_prototype_of, 1)
2, .static_method(Self::has, 2)
) .static_method(Self::is_extensible, 1)
.static_method(Self::get_prototype_of, "getPrototypeOf", 1) .static_method(Self::own_keys, 1)
.static_method(Self::has, "has", 2) .static_method(Self::prevent_extensions, 1)
.static_method(Self::is_extensible, "isExtensible", 1) .static_method(Self::set, 3)
.static_method(Self::own_keys, "ownKeys", 1) .static_method(Self::set_prototype_of, 2)
.static_method(Self::prevent_extensions, "preventExtensions", 1) .static_property(Self::NAME)
.static_method(Self::set, "set", 3) .build();
.static_method(Self::set_prototype_of, "setPrototypeOf", 2)
.static_property(
to_string_tag,
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -18,7 +18,7 @@ use crate::{
internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind, internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind,
CONSTRUCTOR, CONSTRUCTOR,
}, },
property::{Attribute, PropertyDescriptorBuilder}, property::PropertyDescriptorBuilder,
realm::Realm, realm::Realm,
string::{utf16, CodePoint}, string::{utf16, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
@ -55,8 +55,6 @@ impl IntrinsicObject for RegExp {
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
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("get hasIndices")
.build(); .build();
@ -84,53 +82,34 @@ impl IntrinsicObject for RegExp {
let get_source = BuiltInBuilder::callable(realm, Self::get_source) let get_source = BuiltInBuilder::callable(realm, Self::get_source)
.name("get source") .name("get source")
.build(); .build();
let regexp = BuiltInBuilder::from_standard_constructor::<Self>(realm)
.static_accessor( let regexp = BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
JsSymbol::species(), realm,
Some(get_species), &boa_builtins::REGEXP_CONSTRUCTOR_STATIC_SHAPE,
None, &boa_builtins::REGEXP_PROTOTYPE_STATIC_SHAPE,
Attribute::CONFIGURABLE, )
) .static_accessor(Some(get_species), None)
.property(utf16!("lastIndex"), 0, Attribute::all()) .property(0)
.method(Self::test, "test", 1) .method(Self::test, 1)
.method(Self::exec, "exec", 1) .method(Self::exec, 1)
.method(Self::to_string, "toString", 0) .method(Self::to_string, 0)
.method(Self::r#match, (JsSymbol::r#match(), "[Symbol.match]"), 1) .method_with_name(Self::r#match, js_string!("[Symbol.match]"), 1)
.method( .method_with_name(Self::match_all, js_string!("[Symbol.matchAll]"), 1)
Self::match_all, .method_with_name(Self::replace, js_string!("[Symbol.replace]"), 2)
(JsSymbol::match_all(), "[Symbol.matchAll]"), .method_with_name(Self::search, js_string!("[Symbol.search]"), 1)
1, .method_with_name(Self::split, js_string!("[Symbol.split]"), 2)
) .accessor(Some(get_has_indices), None)
.method(Self::replace, (JsSymbol::replace(), "[Symbol.replace]"), 2) .accessor(Some(get_global), None)
.method(Self::search, (JsSymbol::search(), "[Symbol.search]"), 1) .accessor(Some(get_ignore_case), None)
.method(Self::split, (JsSymbol::split(), "[Symbol.split]"), 2) .accessor(Some(get_multiline), None)
.accessor( .accessor(Some(get_dot_all), None)
utf16!("hasIndices"), .accessor(Some(get_unicode), None)
Some(get_has_indices), .accessor(Some(get_sticky), None)
None, .accessor(Some(get_flags), None)
flag_attributes, .accessor(Some(get_source), None);
)
.accessor(utf16!("global"), Some(get_global), None, flag_attributes)
.accessor(
utf16!("ignoreCase"),
Some(get_ignore_case),
None,
flag_attributes,
)
.accessor(
utf16!("multiline"),
Some(get_multiline),
None,
flag_attributes,
)
.accessor(utf16!("dotAll"), Some(get_dot_all), None, flag_attributes)
.accessor(utf16!("unicode"), Some(get_unicode), None, flag_attributes)
.accessor(utf16!("sticky"), Some(get_sticky), None, flag_attributes)
.accessor(utf16!("flags"), Some(get_flags), None, flag_attributes)
.accessor(utf16!("source"), Some(get_source), None, flag_attributes);
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
let regexp = regexp.method(Self::compile, "compile", 2); let regexp = regexp.method(Self::compile, 2);
regexp.build(); regexp.build();
} }

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

@ -23,10 +23,9 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyNameKind}, property::PropertyNameKind,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsResult, JsValue, Context, JsArgs, JsResult, JsValue,
}; };
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -56,46 +55,24 @@ impl IntrinsicObject for Set {
.name("values") .name("values")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_accessor( realm,
JsSymbol::species(), &boa_builtins::SET_CONSTRUCTOR_STATIC_SHAPE,
Some(get_species), &boa_builtins::SET_PROTOTYPE_STATIC_SHAPE,
None, )
Attribute::CONFIGURABLE, .static_accessor(Some(get_species), None)
) .method(Self::add, 1)
.method(Self::add, "add", 1) .method(Self::clear, 0)
.method(Self::clear, "clear", 0) .method(Self::delete, 1)
.method(Self::delete, "delete", 1) .method(Self::entries, 0)
.method(Self::entries, "entries", 0) .method(Self::for_each, 1)
.method(Self::for_each, "forEach", 1) .method(Self::has, 1)
.method(Self::has, "has", 1) .property(values_function.clone())
.property( .accessor(Some(size_getter), None)
utf16!("keys"), .property(values_function.clone())
values_function.clone(), .property(values_function)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .property(Self::NAME)
) .build();
.accessor(
utf16!("size"),
Some(size_getter),
None,
Attribute::CONFIGURABLE,
)
.property(
utf16!("values"),
values_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
JsSymbol::iterator(),
values_function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
} }

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

@ -15,7 +15,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor}, property::PropertyDescriptor,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
string::{CodePoint, Utf16Trim}, string::{CodePoint, Utf16Trim},
@ -78,8 +78,6 @@ 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(Self::NAME, "init");
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("trimStart")
@ -96,81 +94,68 @@ impl IntrinsicObject for String {
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
let trim_right = trim_end.clone(); let trim_right = trim_end.clone();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let builder = BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm) realm,
.property(utf16!("length"), 0, attribute) &boa_builtins::STRING_CONSTRUCTOR_STATIC_SHAPE,
.property( &boa_builtins::STRING_PROTOTYPE_STATIC_SHAPE,
utf16!("trimStart"), )
trim_start, .property(0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .property(trim_start)
) .property(trim_end)
.property( .static_method(Self::raw, 1)
utf16!("trimEnd"), .static_method(Self::from_char_code, 1)
trim_end, .static_method(Self::from_code_point, 1)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::char_at, 1)
) .method(Self::char_code_at, 1)
.static_method(Self::raw, "raw", 1) .method(Self::code_point_at, 1)
.static_method(Self::from_char_code, "fromCharCode", 1) .method(Self::to_string, 0)
.static_method(Self::from_code_point, "fromCodePoint", 1) .method(Self::concat, 1)
.method(Self::char_at, "charAt", 1) .method(Self::repeat, 1)
.method(Self::char_code_at, "charCodeAt", 1) .method(Self::slice, 2)
.method(Self::code_point_at, "codePointAt", 1) .method(Self::starts_with, 1)
.method(Self::to_string, "toString", 0) .method(Self::ends_with, 1)
.method(Self::concat, "concat", 1) .method(Self::includes, 1)
.method(Self::repeat, "repeat", 1) .method(Self::index_of, 1)
.method(Self::slice, "slice", 2) .method(Self::last_index_of, 1)
.method(Self::starts_with, "startsWith", 1) .method(Self::locale_compare, 1)
.method(Self::ends_with, "endsWith", 1) .method(Self::r#match, 1)
.method(Self::includes, "includes", 1) .method(Self::normalize, 0)
.method(Self::index_of, "indexOf", 1) .method(Self::pad_end, 1)
.method(Self::last_index_of, "lastIndexOf", 1) .method(Self::pad_start, 1)
.method(Self::locale_compare, "localeCompare", 1) .method(Self::trim, 0)
.method(Self::r#match, "match", 1) .method(Self::to_case::<false>, 0)
.method(Self::normalize, "normalize", 0) .method(Self::to_case::<true>, 0)
.method(Self::pad_end, "padEnd", 1) .method(Self::to_locale_case::<false>, 0)
.method(Self::pad_start, "padStart", 1) .method(Self::to_locale_case::<true>, 0)
.method(Self::trim, "trim", 0) .method(Self::substring, 2)
.method(Self::to_case::<false>, "toLowerCase", 0) .method(Self::split, 2)
.method(Self::to_case::<true>, "toUpperCase", 0) .method(Self::value_of, 0)
.method(Self::to_locale_case::<false>, "toLocaleLowerCase", 0) .method(Self::match_all, 1)
.method(Self::to_locale_case::<true>, "toLocaleUpperCase", 0) .method(Self::replace, 2)
.method(Self::substring, "substring", 2) .method(Self::replace_all, 2)
.method(Self::split, "split", 2) .method_with_name(Self::iterator, js_string!("[Symbol.iterator]"), 0)
.method(Self::value_of, "valueOf", 0) .method(Self::search, 1)
.method(Self::match_all, "matchAll", 1) .method(Self::at, 1);
.method(Self::replace, "replace", 2)
.method(Self::replace_all, "replaceAll", 2)
.method(Self::iterator, (symbol_iterator, "[Symbol.iterator]"), 0)
.method(Self::search, "search", 1)
.method(Self::at, "at", 1);
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
{ {
builder builder
.property( .property(trim_left)
utf16!("trimLeft"), .property(trim_right)
trim_left, .method(Self::substr, 2)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::anchor, 1)
) .method(Self::big, 0)
.property( .method(Self::blink, 0)
utf16!("trimRight"), .method(Self::bold, 0)
trim_right, .method(Self::fixed, 0)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .method(Self::fontcolor, 1)
) .method(Self::fontsize, 1)
.method(Self::substr, "substr", 2) .method(Self::italics, 0)
.method(Self::anchor, "anchor", 1) .method(Self::link, 1)
.method(Self::big, "big", 0) .method(Self::small, 0)
.method(Self::blink, "blink", 0) .method(Self::strike, 0)
.method(Self::bold, "bold", 0) .method(Self::sub, 0)
.method(Self::fixed, "fixed", 0) .method(Self::sup, 0)
.method(Self::fontcolor, "fontcolor", 1)
.method(Self::fontsize, "fontsize", 1)
.method(Self::italics, "italics", 0)
.method(Self::link, "link", 1)
.method(Self::small, "small", 0)
.method(Self::strike, "strike", 0)
.method(Self::sub, "sub", 0)
.method(Self::sup, "sup", 0)
.build(); .build();
} }

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

@ -26,9 +26,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::JsObject, object::JsObject,
property::Attribute,
realm::Realm, realm::Realm,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
@ -109,8 +107,6 @@ impl IntrinsicObject for Symbol {
let symbol_to_string_tag = JsSymbol::to_string_tag(); let symbol_to_string_tag = JsSymbol::to_string_tag();
let symbol_unscopables = JsSymbol::unscopables(); let symbol_unscopables = JsSymbol::unscopables();
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("[Symbol.toPrimitive]")
.length(1) .length(1)
@ -120,53 +116,32 @@ impl IntrinsicObject for Symbol {
.name("get description") .name("get description")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_method(Self::for_, "for", 1) realm,
.static_method(Self::key_for, "keyFor", 1) &boa_builtins::SYMBOL_CONSTRUCTOR_STATIC_SHAPE,
.static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute) &boa_builtins::SYMBOL_PROTOTYPE_STATIC_SHAPE,
.static_property(utf16!("hasInstance"), symbol_has_instance, attribute) )
.static_property( .static_method(Self::for_, 1)
utf16!("isConcatSpreadable"), .static_method(Self::key_for, 1)
symbol_is_concat_spreadable, .static_property(symbol_async_iterator)
attribute, .static_property(symbol_has_instance)
) .static_property(symbol_is_concat_spreadable)
.static_property(utf16!("iterator"), symbol_iterator, attribute) .static_property(symbol_iterator)
.static_property(utf16!("match"), symbol_match, attribute) .static_property(symbol_match)
.static_property(utf16!("matchAll"), symbol_match_all, attribute) .static_property(symbol_match_all)
.static_property(utf16!("replace"), symbol_replace, attribute) .static_property(symbol_replace)
.static_property(utf16!("search"), symbol_search, attribute) .static_property(symbol_search)
.static_property(utf16!("species"), symbol_species, attribute) .static_property(symbol_species)
.static_property(utf16!("split"), symbol_split, attribute) .static_property(symbol_split)
.static_property( .static_property(symbol_to_primitive)
utf16!("toPrimitive"), .static_property(symbol_to_string_tag)
symbol_to_primitive.clone(), .static_property(symbol_unscopables)
attribute, .method(Self::to_string, 0)
) .method(Self::value_of, 0)
.static_property( .accessor(Some(get_description), None)
utf16!("toStringTag"), .property(Self::NAME)
symbol_to_string_tag.clone(), .property(to_primitive)
attribute, .build();
)
.static_property(utf16!("unscopables"), symbol_unscopables, attribute)
.method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0)
.accessor(
utf16!("description"),
Some(get_description),
None,
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,
)
.property(
symbol_to_string_tag,
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
symbol_to_primitive,
to_primitive,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -26,7 +26,6 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16,
symbol::JsSymbol, symbol::JsSymbol,
value::{IntegerOrInfinity, JsValue}, value::{IntegerOrInfinity, JsValue},
Context, JsArgs, JsResult, Context, JsArgs, JsResult,
@ -55,34 +54,25 @@ macro_rules! typed_array {
.name("get [Symbol.species]") .name("get [Symbol.species]")
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.prototype( realm,
realm &boa_builtins::TYPED_ARRAY_INSTANCE_CONSTRUCTOR_STATIC_SHAPE,
.intrinsics() &boa_builtins::TYPED_ARRAY_INSTANCE_PROTOTYPE_STATIC_SHAPE,
.constructors() )
.typed_array() .prototype(
.constructor(), realm
) .intrinsics()
.inherits(Some( .constructors()
realm.intrinsics().constructors().typed_array().prototype(), .typed_array()
)) .constructor(),
.static_accessor( )
JsSymbol::species(), .inherits(Some(
Some(get_species), realm.intrinsics().constructors().typed_array().prototype(),
None, ))
Attribute::CONFIGURABLE, .static_accessor(Some(get_species), None)
) .property(TypedArrayKind::$variant.element_size())
.property( .static_property(TypedArrayKind::$variant.element_size())
utf16!("BYTES_PER_ELEMENT"), .build();
TypedArrayKind::$variant.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
)
.static_property(
utf16!("BYTES_PER_ELEMENT"),
TypedArrayKind::$variant.element_size(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
)
.build();
} }
} }
@ -269,79 +259,49 @@ impl IntrinsicObject for TypedArray {
.length(0) .length(0)
.build(); .build();
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.static_accessor( realm,
JsSymbol::species(), &boa_builtins::TYPED_ARRAY_CONSTRUCTOR_STATIC_SHAPE,
Some(get_species), &boa_builtins::TYPED_ARRAY_PROTOTYPE_STATIC_SHAPE,
None, )
Attribute::CONFIGURABLE, .static_accessor(Some(get_species), None)
) .property(values_function)
.property( .accessor(Some(get_buffer), None)
JsSymbol::iterator(), .accessor(Some(get_byte_length), None)
values_function, .accessor(Some(get_byte_offset), None)
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, .accessor(Some(get_length), None)
) .accessor(Some(get_to_string_tag), None)
.accessor( .static_method(Self::from, 1)
utf16!("buffer"), .static_method(Self::of, 0)
Some(get_buffer), .method(Self::at, 1)
None, .method(Self::copy_within, 2)
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, .method(Self::entries, 0)
) .method(Self::every, 1)
.accessor( .method(Self::fill, 1)
utf16!("byteLength"), .method(Self::filter, 1)
Some(get_byte_length), .method(Self::find, 1)
None, .method(Self::findindex, 1)
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, .method(Self::foreach, 1)
) .method(Self::includes, 1)
.accessor( .method(Self::index_of, 1)
utf16!("byteOffset"), .method(Self::join, 1)
Some(get_byte_offset), .method(Self::keys, 0)
None, .method(Self::last_index_of, 1)
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, .method(Self::map, 1)
) .method(Self::reduce, 1)
.accessor( .method(Self::reduceright, 1)
utf16!("length"), .method(Self::reverse, 0)
Some(get_length), .method(Self::set, 1)
None, .method(Self::slice, 2)
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, .method(Self::some, 1)
) .method(Self::sort, 1)
.accessor( .method(Self::subarray, 2)
JsSymbol::to_string_tag(), .method(Self::values, 0)
Some(get_to_string_tag), // 23.2.3.29 %TypedArray%.prototype.toString ( )
None, // The initial value of the %TypedArray%.prototype.toString data property is the same
Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, // built-in function object as the Array.prototype.toString method defined in 23.1.3.30.
) .method(Array::to_string, 0)
.static_method(Self::from, "from", 1) .build();
.static_method(Self::of, "of", 0)
.method(Self::at, "at", 1)
.method(Self::copy_within, "copyWithin", 2)
.method(Self::entries, "entries", 0)
.method(Self::every, "every", 1)
.method(Self::fill, "fill", 1)
.method(Self::filter, "filter", 1)
.method(Self::find, "find", 1)
.method(Self::findindex, "findIndex", 1)
.method(Self::foreach, "forEach", 1)
.method(Self::includes, "includes", 1)
.method(Self::index_of, "indexOf", 1)
.method(Self::join, "join", 1)
.method(Self::keys, "keys", 0)
.method(Self::last_index_of, "lastIndexOf", 1)
.method(Self::map, "map", 1)
.method(Self::reduce, "reduce", 1)
.method(Self::reduceright, "reduceRight", 1)
.method(Self::reverse, "reverse", 0)
.method(Self::set, "set", 1)
.method(Self::slice, "slice", 2)
.method(Self::some, "some", 1)
.method(Self::sort, "sort", 1)
.method(Self::subarray, "subarray", 2)
.method(Self::values, "values", 0)
// 23.2.3.29 %TypedArray%.prototype.toString ( )
// 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.
.method(Array::to_string, "toString", 0)
.build();
} }
fn get(intrinsics: &Intrinsics) -> JsObject { fn get(intrinsics: &Intrinsics) -> JsObject {

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

@ -50,10 +50,10 @@ pub struct UriFunctions {
impl Default for UriFunctions { impl Default for UriFunctions {
fn default() -> Self { fn default() -> Self {
Self { Self {
decode_uri: JsFunction::empty_intrinsic_function(false), decode_uri: JsFunction::empty_intrinsic_function_static_shape(false),
decode_uri_component: JsFunction::empty_intrinsic_function(false), decode_uri_component: JsFunction::empty_intrinsic_function_static_shape(false),
encode_uri: JsFunction::empty_intrinsic_function(false), encode_uri: JsFunction::empty_intrinsic_function_static_shape(false),
encode_uri_component: JsFunction::empty_intrinsic_function(false), encode_uri_component: JsFunction::empty_intrinsic_function_static_shape(false),
} }
} }
} }
@ -82,7 +82,7 @@ pub(crate) struct DecodeUri;
impl IntrinsicObject for DecodeUri { impl IntrinsicObject for DecodeUri {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, decode_uri) BuiltInBuilder::callable_intrinsic::<Self>(realm, decode_uri)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();
@ -100,7 +100,7 @@ pub(crate) struct DecodeUriComponent;
impl IntrinsicObject for DecodeUriComponent { impl IntrinsicObject for DecodeUriComponent {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, decode_uri_component) BuiltInBuilder::callable_intrinsic::<Self>(realm, decode_uri_component)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();
@ -122,7 +122,7 @@ pub(crate) struct EncodeUri;
impl IntrinsicObject for EncodeUri { impl IntrinsicObject for EncodeUri {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, encode_uri) BuiltInBuilder::callable_intrinsic::<Self>(realm, encode_uri)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();
@ -139,7 +139,7 @@ pub(crate) struct EncodeUriComponent;
impl IntrinsicObject for EncodeUriComponent { impl IntrinsicObject for EncodeUriComponent {
fn init(realm: &Realm) { fn init(realm: &Realm) {
BuiltInBuilder::callable_with_intrinsic::<Self>(realm, encode_uri_component) BuiltInBuilder::callable_intrinsic::<Self>(realm, encode_uri_component)
.name(Self::NAME) .name(Self::NAME)
.length(1) .length(1)
.build(); .build();

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

@ -1,4 +1,5 @@
use boa_gc::{Finalize, Trace, WeakGc}; use boa_gc::{Finalize, Trace, WeakGc};
use boa_macros::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use crate::{ use crate::{
@ -7,7 +8,6 @@ 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,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
@ -31,14 +31,14 @@ 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(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.property( realm,
JsSymbol::to_string_tag(), &boa_builtins::WEAK_REF_CONSTRUCTOR_STATIC_SHAPE,
"WeakRef", &boa_builtins::WEAK_REF_PROTOTYPE_STATIC_SHAPE,
Attribute::CONFIGURABLE, )
) .property(utf16!("WeakRef"))
.method(Self::deref, "deref", 0) .method(Self::deref, 0)
.build(); .build();
} }
} }

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

@ -17,7 +17,6 @@ use crate::{
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -33,17 +32,17 @@ 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(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.property( realm,
JsSymbol::to_string_tag(), &boa_builtins::WEAK_MAP_CONSTRUCTOR_STATIC_SHAPE,
Self::NAME, &boa_builtins::WEAK_MAP_PROTOTYPE_STATIC_SHAPE,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, )
) .property(Self::NAME)
.method(Self::delete, "delete", 1) .method(Self::delete, 1)
.method(Self::get, "get", 1) .method(Self::get, 1)
.method(Self::has, "has", 1) .method(Self::has, 1)
.method(Self::set, "set", 2) .method(Self::set, 2)
.build(); .build();
} }
} }

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

@ -14,7 +14,6 @@ use crate::{
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::utf16, string::utf16,
symbol::JsSymbol,
Context, JsArgs, JsNativeError, JsResult, JsValue, Context, JsArgs, JsNativeError, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace, WeakMap}; use boa_gc::{Finalize, Trace, WeakMap};
@ -30,16 +29,16 @@ 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(Self::NAME, "init");
BuiltInBuilder::from_standard_constructor::<Self>(realm) BuiltInBuilder::from_standard_constructor_static_shape::<Self>(
.property( realm,
JsSymbol::to_string_tag(), &boa_builtins::WEAK_SET_CONSTRUCTOR_STATIC_SHAPE,
Self::NAME, &boa_builtins::WEAK_SET_PROTOTYPE_STATIC_SHAPE,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, )
) .property(Self::NAME)
.method(Self::add, "add", 1) .method(Self::add, 1)
.method(Self::delete, "delete", 1) .method(Self::delete, 1)
.method(Self::has, "has", 1) .method(Self::has, 1)
.build(); .build();
} }
} }

130
boa_engine/src/context/intrinsics.rs

@ -75,11 +75,18 @@ impl StandardConstructor {
/// Build a constructor with a defined prototype. /// Build a constructor with a defined prototype.
fn with_prototype(prototype: JsObject) -> Self { fn with_prototype(prototype: JsObject) -> Self {
Self { Self {
constructor: JsFunction::empty_intrinsic_function(true), constructor: JsFunction::empty_intrinsic_function_static_shape(true),
prototype, prototype,
} }
} }
fn default_static_shape() -> Self {
Self {
constructor: JsFunction::empty_intrinsic_function_static_shape(true),
prototype: JsObject::default_with_static_shape(),
}
}
/// Return the prototype of the constructor object. /// Return the prototype of the constructor object.
/// ///
/// This is the same as `Object.prototype`, `Array.prototype`, etc /// This is the same as `Object.prototype`, `Array.prototype`, etc
@ -156,67 +163,62 @@ pub struct StandardConstructors {
impl Default for StandardConstructors { impl Default for StandardConstructors {
fn default() -> Self { fn default() -> Self {
Self { Self {
object: StandardConstructor::with_prototype(JsObject::from_proto_and_data( object: StandardConstructor::with_prototype(
None, JsObject::from_data_and_empty_static_shape(ObjectData::object_prototype()),
ObjectData::object_prototype(), ),
)),
async_generator_function: StandardConstructor::default(), async_generator_function: StandardConstructor::default(),
proxy: StandardConstructor::default(), proxy: StandardConstructor::default(),
date: StandardConstructor::default(), date: StandardConstructor::default_static_shape(),
function: StandardConstructor { function: StandardConstructor {
constructor: JsFunction::empty_intrinsic_function(true), constructor: JsFunction::empty_intrinsic_function_static_shape(true),
prototype: JsFunction::empty_intrinsic_function(false).into(), prototype: JsFunction::empty_intrinsic_function_static_shape(false).into(),
}, },
async_function: StandardConstructor::default(), async_function: StandardConstructor::default(),
generator_function: StandardConstructor::default(), generator_function: StandardConstructor::default(),
array: StandardConstructor::with_prototype(JsObject::from_proto_and_data( array: StandardConstructor::with_prototype(JsObject::from_data_and_empty_static_shape(
None,
ObjectData::array(), ObjectData::array(),
)), )),
bigint: StandardConstructor::default(), bigint: StandardConstructor::default_static_shape(),
number: StandardConstructor::with_prototype(JsObject::from_proto_and_data( number: StandardConstructor::with_prototype(
None, JsObject::from_data_and_empty_static_shape(ObjectData::number(0.0)),
ObjectData::number(0.0), ),
)), boolean: StandardConstructor::with_prototype(
boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data( JsObject::from_data_and_empty_static_shape(ObjectData::boolean(false)),
None, ),
ObjectData::boolean(false), string: StandardConstructor::with_prototype(
)), JsObject::from_data_and_empty_static_shape(ObjectData::string("".into())),
string: StandardConstructor::with_prototype(JsObject::from_proto_and_data( ),
None, regexp: StandardConstructor::default_static_shape(),
ObjectData::string("".into()), symbol: StandardConstructor::default_static_shape(),
)), error: StandardConstructor::default_static_shape(),
regexp: StandardConstructor::default(), type_error: StandardConstructor::default_static_shape(),
symbol: StandardConstructor::default(), reference_error: StandardConstructor::default_static_shape(),
error: StandardConstructor::default(), range_error: StandardConstructor::default_static_shape(),
type_error: StandardConstructor::default(), syntax_error: StandardConstructor::default_static_shape(),
reference_error: StandardConstructor::default(), eval_error: StandardConstructor::default_static_shape(),
range_error: StandardConstructor::default(), uri_error: StandardConstructor::default_static_shape(),
syntax_error: StandardConstructor::default(), aggregate_error: StandardConstructor::default_static_shape(),
eval_error: StandardConstructor::default(), map: StandardConstructor::default_static_shape(),
uri_error: StandardConstructor::default(), set: StandardConstructor::default_static_shape(),
aggregate_error: StandardConstructor::default(), typed_array: StandardConstructor::default_static_shape(),
map: StandardConstructor::default(), typed_int8_array: StandardConstructor::default_static_shape(),
set: StandardConstructor::default(), typed_uint8_array: StandardConstructor::default_static_shape(),
typed_array: StandardConstructor::default(), typed_uint8clamped_array: StandardConstructor::default_static_shape(),
typed_int8_array: StandardConstructor::default(), typed_int16_array: StandardConstructor::default_static_shape(),
typed_uint8_array: StandardConstructor::default(), typed_uint16_array: StandardConstructor::default_static_shape(),
typed_uint8clamped_array: StandardConstructor::default(), typed_int32_array: StandardConstructor::default_static_shape(),
typed_int16_array: StandardConstructor::default(), typed_uint32_array: StandardConstructor::default_static_shape(),
typed_uint16_array: StandardConstructor::default(), typed_bigint64_array: StandardConstructor::default_static_shape(),
typed_int32_array: StandardConstructor::default(), typed_biguint64_array: StandardConstructor::default_static_shape(),
typed_uint32_array: StandardConstructor::default(), typed_float32_array: StandardConstructor::default_static_shape(),
typed_bigint64_array: StandardConstructor::default(), typed_float64_array: StandardConstructor::default_static_shape(),
typed_biguint64_array: StandardConstructor::default(), array_buffer: StandardConstructor::default_static_shape(),
typed_float32_array: StandardConstructor::default(), data_view: StandardConstructor::default_static_shape(),
typed_float64_array: StandardConstructor::default(),
array_buffer: StandardConstructor::default(),
data_view: StandardConstructor::default(),
date_time_format: StandardConstructor::default(), date_time_format: StandardConstructor::default(),
promise: StandardConstructor::default(), promise: StandardConstructor::default_static_shape(),
weak_ref: StandardConstructor::default(), weak_ref: StandardConstructor::default_static_shape(),
weak_map: StandardConstructor::default(), weak_map: StandardConstructor::default_static_shape(),
weak_set: StandardConstructor::default(), weak_set: StandardConstructor::default_static_shape(),
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
collator: StandardConstructor::default(), collator: StandardConstructor::default(),
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
@ -817,24 +819,24 @@ pub struct IntrinsicObjects {
impl Default for IntrinsicObjects { impl Default for IntrinsicObjects {
fn default() -> Self { fn default() -> Self {
Self { Self {
reflect: JsObject::default(), reflect: JsObject::default_with_static_shape(),
math: JsObject::default(), math: JsObject::default_with_static_shape(),
json: JsObject::default(), json: JsObject::default_with_static_shape(),
throw_type_error: JsFunction::empty_intrinsic_function(false), throw_type_error: JsFunction::empty_intrinsic_function(false),
array_prototype_values: JsFunction::empty_intrinsic_function(false), array_prototype_values: JsFunction::empty_intrinsic_function_static_shape(false),
iterator_prototypes: IteratorPrototypes::default(), iterator_prototypes: IteratorPrototypes::default(),
generator: JsObject::default(), generator: JsObject::default(),
async_generator: JsObject::default(), async_generator: JsObject::default(),
eval: JsFunction::empty_intrinsic_function(false), eval: JsFunction::empty_intrinsic_function_static_shape(false),
uri_functions: UriFunctions::default(), uri_functions: UriFunctions::default(),
is_finite: JsFunction::empty_intrinsic_function(false), is_finite: JsFunction::empty_intrinsic_function_static_shape(false),
is_nan: JsFunction::empty_intrinsic_function(false), is_nan: JsFunction::empty_intrinsic_function_static_shape(false),
parse_float: JsFunction::empty_intrinsic_function(false), parse_float: JsFunction::empty_intrinsic_function_static_shape(false),
parse_int: JsFunction::empty_intrinsic_function(false), parse_int: JsFunction::empty_intrinsic_function_static_shape(false),
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
escape: JsFunction::empty_intrinsic_function(false), escape: JsFunction::empty_intrinsic_function_static_shape(false),
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
unescape: JsFunction::empty_intrinsic_function(false), unescape: JsFunction::empty_intrinsic_function_static_shape(false),
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
intl: JsObject::default(), intl: JsObject::default(),
#[cfg(feature = "intl")] #[cfg(feature = "intl")]

13
boa_engine/src/object/builtins/jsfunction.rs

@ -40,6 +40,19 @@ impl JsFunction {
} }
} }
pub(crate) fn empty_intrinsic_function_static_shape(constructor: bool) -> Self {
Self {
inner: JsObject::from_object_and_vtable(
Object::with_empty_shape(),
if constructor {
&CONSTRUCTOR_INTERNAL_METHODS
} else {
&FUNCTION_INTERNAL_METHODS
},
),
}
}
/// Creates a [`JsFunction`] from a [`JsObject`], or returns `None` if the object is not a function. /// Creates a [`JsFunction`] from a [`JsObject`], or returns `None` if the object is not a function.
/// ///
/// This does not clone the fields of the function, it only does a shallow clone of the object. /// This does not clone the fields of the function, it only does a shallow clone of the object.

25
boa_engine/src/object/jsobject.rs

@ -4,7 +4,7 @@
use super::{ use super::{
internal_methods::{InternalObjectMethods, ARRAY_EXOTIC_INTERNAL_METHODS}, internal_methods::{InternalObjectMethods, ARRAY_EXOTIC_INTERNAL_METHODS},
shape::RootShape, shape::{static_shape::StaticShape, RootShape},
JsPrototype, NativeObject, Object, PrivateName, PropertyMap, JsPrototype, NativeObject, Object, PrivateName, PropertyMap,
}; };
use crate::{ use crate::{
@ -60,6 +60,12 @@ impl Default for JsObject {
} }
impl JsObject { impl JsObject {
/// TODO: doc
pub(crate) fn default_with_static_shape() -> Self {
let data = ObjectData::ordinary();
Self::from_object_and_vtable(Object::with_empty_shape(), data.internal_methods)
}
/// Creates a new `JsObject` from its inner object and its vtable. /// Creates a new `JsObject` from its inner object and its vtable.
pub(crate) fn from_object_and_vtable( pub(crate) fn from_object_and_vtable(
object: Object, object: Object,
@ -120,6 +126,23 @@ impl JsObject {
} }
} }
pub(crate) fn from_data_and_empty_static_shape(data: ObjectData) -> Self {
Self {
inner: Gc::new(VTableObject {
object: GcRefCell::new(Object {
kind: data.kind,
properties: PropertyMap::new(
StaticShape::new(&boa_builtins::EMPTY_OBJECT_STATIC_SHAPE).into(),
ThinVec::default(),
),
extensible: true,
private_elements: ThinVec::new(),
}),
vtable: data.internal_methods,
}),
}
}
/// Creates a new object with the provided prototype and object data. /// Creates a new object with the provided prototype and object data.
/// ///
/// This is equivalent to calling the specification's abstract operation [`OrdinaryObjectCreate`], /// This is equivalent to calling the specification's abstract operation [`OrdinaryObjectCreate`],

29
boa_engine/src/object/mod.rs

@ -26,7 +26,7 @@ use self::{
string::STRING_EXOTIC_INTERNAL_METHODS, string::STRING_EXOTIC_INTERNAL_METHODS,
InternalObjectMethods, ORDINARY_INTERNAL_METHODS, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,
}, },
shape::Shape, shape::{static_shape::StaticShape, Shape},
}; };
#[cfg(feature = "intl")] #[cfg(feature = "intl")]
use crate::builtins::intl::{ use crate::builtins::intl::{
@ -873,6 +873,18 @@ impl Debug for ObjectKind {
} }
impl Object { impl Object {
fn with_empty_shape() -> Self {
Self {
kind: ObjectKind::Ordinary,
properties: PropertyMap::new(
StaticShape::new(&boa_builtins::EMPTY_OBJECT_STATIC_SHAPE).into(),
ThinVec::default(),
),
extensible: true,
private_elements: ThinVec::new(),
}
}
/// Returns a mutable reference to the kind of an object. /// Returns a mutable reference to the kind of an object.
pub(crate) fn kind_mut(&mut self) -> &mut ObjectKind { pub(crate) fn kind_mut(&mut self) -> &mut ObjectKind {
&mut self.kind &mut self.kind
@ -1524,6 +1536,12 @@ impl Object {
/// Gets the prototype instance of this object. /// Gets the prototype instance of this object.
#[inline] #[inline]
pub fn prototype(&self) -> JsPrototype { pub fn prototype(&self) -> JsPrototype {
// If it is static then the prototype is stored on the (len - 1) position.
if self.properties.shape.is_static() {
return self.properties.storage[self.properties.storage.len() - 1]
.as_object()
.cloned();
}
self.properties.shape.prototype() self.properties.shape.prototype()
} }
@ -1536,7 +1554,14 @@ impl Object {
pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool { pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
let prototype = prototype.into(); let prototype = prototype.into();
if self.extensible { if self.extensible {
self.properties.shape = self.properties.shape.change_prototype_transition(prototype); if let Some(shape) = self.properties.shape.as_static() {
self.properties.storage.pop();
self.properties.shape = shape.to_unique(prototype).into();
} else {
self.properties.shape =
self.properties.shape.change_prototype_transition(prototype);
}
true true
} else { } else {
// If target is non-extensible, [[SetPrototypeOf]] must return false // If target is non-extensible, [[SetPrototypeOf]] must return false

38
boa_engine/src/object/property_map.rs

@ -314,7 +314,21 @@ impl PropertyMap {
property_key: key.clone(), property_key: key.clone(),
attributes, attributes,
}; };
let transition = self.shape.change_attributes_transition(key);
let transition = if let Some(shape) = self.shape.as_static() {
let prototype = self
.storage
.pop()
.as_ref()
.and_then(JsValue::as_object)
.cloned();
shape
.to_unique(prototype)
.change_attributes_transition(&key)
} else {
self.shape.change_attributes_transition(key)
};
self.shape = transition.shape; self.shape = transition.shape;
match transition.action { match transition.action {
ChangeTransitionAction::Nothing => {} ChangeTransitionAction::Nothing => {}
@ -349,6 +363,17 @@ impl PropertyMap {
return true; return true;
} }
// TODO: optimization, we could inline `insert_property_transition` to avoid the check
if let Some(shape) = self.shape.as_static() {
let prototype = self
.storage
.pop()
.as_ref()
.and_then(JsValue::as_object)
.cloned();
self.shape = shape.to_unique(prototype).into();
}
let transition_key = TransitionKey { let transition_key = TransitionKey {
property_key: key.clone(), property_key: key.clone(),
attributes, attributes,
@ -393,6 +418,17 @@ impl PropertyMap {
return self.indexed_properties.remove(*index); return self.indexed_properties.remove(*index);
} }
if let Some(slot) = self.shape.lookup(key) { if let Some(slot) = self.shape.lookup(key) {
if let Some(shape) = self.shape.as_static() {
let prototype = self
.storage
.pop()
.as_ref()
.and_then(JsValue::as_object)
.cloned();
self.shape = shape.to_unique(prototype).into();
}
// shift all elements when removing. // shift all elements when removing.
if slot.attributes.is_accessor_descriptor() { if slot.attributes.is_accessor_descriptor() {
self.storage.remove(slot.index as usize + 1); self.storage.remove(slot.index as usize + 1);

36
boa_engine/src/object/shape/mod.rs

@ -4,6 +4,7 @@ pub(crate) mod property_table;
mod root_shape; mod root_shape;
pub(crate) mod shared_shape; pub(crate) mod shared_shape;
pub(crate) mod slot; pub(crate) mod slot;
pub(crate) mod static_shape;
pub(crate) mod unique_shape; pub(crate) mod unique_shape;
pub use root_shape::RootShape; pub use root_shape::RootShape;
@ -16,7 +17,7 @@ use boa_gc::{Finalize, Trace};
use crate::property::PropertyKey; use crate::property::PropertyKey;
use self::{shared_shape::TransitionKey, slot::Slot}; use self::{shared_shape::TransitionKey, slot::Slot, static_shape::StaticShape};
use super::JsPrototype; use super::JsPrototype;
@ -56,6 +57,7 @@ pub(crate) struct ChangeTransition<T> {
enum Inner { enum Inner {
Unique(UniqueShape), Unique(UniqueShape),
Shared(SharedShape), Shared(SharedShape),
Static(StaticShape),
} }
/// Represents the shape of an object. /// Represents the shape of an object.
@ -84,12 +86,26 @@ impl Shape {
matches!(self.inner, Inner::Shared(_)) matches!(self.inner, Inner::Shared(_))
} }
/// Returns `true` if it's a static shape, `false` otherwise.
pub(crate) const fn as_static(&self) -> Option<&StaticShape> {
if let Inner::Static(shape) = &self.inner {
return Some(shape);
}
None
}
/// Returns `true` if it's a unique shape, `false` otherwise. /// Returns `true` if it's a unique shape, `false` otherwise.
#[inline] #[inline]
pub const fn is_unique(&self) -> bool { pub const fn is_unique(&self) -> bool {
matches!(self.inner, Inner::Unique(_)) matches!(self.inner, Inner::Unique(_))
} }
/// Returns `true` if it's a static shape, `false` otherwise.
#[inline]
pub const fn is_static(&self) -> bool {
matches!(self.inner, Inner::Static(_))
}
pub(crate) const fn as_unique(&self) -> Option<&UniqueShape> { pub(crate) const fn as_unique(&self) -> Option<&UniqueShape> {
if let Inner::Unique(shape) = &self.inner { if let Inner::Unique(shape) = &self.inner {
return Some(shape); return Some(shape);
@ -110,6 +126,7 @@ impl Shape {
shape.into() shape.into()
} }
Inner::Unique(shape) => shape.insert_property_transition(key).into(), Inner::Unique(shape) => shape.insert_property_transition(key).into(),
Inner::Static(shape) => shape.insert_property_transition(key).into(),
} }
} }
@ -136,6 +153,7 @@ impl Shape {
} }
} }
Inner::Unique(shape) => shape.change_attributes_transition(&key), Inner::Unique(shape) => shape.change_attributes_transition(&key),
Inner::Static(shape) => shape.change_attributes_transition(&key),
} }
} }
@ -152,6 +170,7 @@ impl Shape {
shape.into() shape.into()
} }
Inner::Unique(shape) => shape.remove_property_transition(key).into(), Inner::Unique(shape) => shape.remove_property_transition(key).into(),
Inner::Static(shape) => shape.remove_property_transition(key).into(),
} }
} }
@ -166,14 +185,16 @@ impl Shape {
shape.into() shape.into()
} }
Inner::Unique(shape) => shape.change_prototype_transition(prototype).into(), Inner::Unique(shape) => shape.change_prototype_transition(prototype).into(),
Inner::Static(shape) => shape.change_prototype_transition(prototype).into(),
} }
} }
/// Get the [`JsPrototype`] of the [`Shape`]. /// Get the [`JsPrototype`] of the [`Shape`].
pub fn prototype(&self) -> JsPrototype { pub(crate) fn prototype(&self) -> JsPrototype {
match &self.inner { match &self.inner {
Inner::Shared(shape) => shape.prototype(), Inner::Shared(shape) => shape.prototype(),
Inner::Unique(shape) => shape.prototype(), Inner::Unique(shape) => shape.prototype(),
Inner::Static(_) => unreachable!("Static shapes don't have prototypes in them"),
} }
} }
@ -183,6 +204,7 @@ impl Shape {
match &self.inner { match &self.inner {
Inner::Shared(shape) => shape.lookup(key), Inner::Shared(shape) => shape.lookup(key),
Inner::Unique(shape) => shape.lookup(key), Inner::Unique(shape) => shape.lookup(key),
Inner::Static(shape) => shape.lookup(key),
} }
} }
@ -192,6 +214,7 @@ impl Shape {
match &self.inner { match &self.inner {
Inner::Shared(shape) => shape.keys(), Inner::Shared(shape) => shape.keys(),
Inner::Unique(shape) => shape.keys(), Inner::Unique(shape) => shape.keys(),
Inner::Static(shape) => shape.keys(),
} }
} }
@ -201,6 +224,7 @@ impl Shape {
match &self.inner { match &self.inner {
Inner::Shared(shape) => shape.to_addr_usize(), Inner::Shared(shape) => shape.to_addr_usize(),
Inner::Unique(shape) => shape.to_addr_usize(), Inner::Unique(shape) => shape.to_addr_usize(),
Inner::Static(shape) => shape.to_addr_usize(),
} }
} }
} }
@ -220,3 +244,11 @@ impl From<SharedShape> for Shape {
} }
} }
} }
impl From<StaticShape> for Shape {
fn from(shape: StaticShape) -> Self {
Self {
inner: Inner::Static(shape),
}
}
}

2
boa_engine/src/object/shape/slot.rs

@ -42,7 +42,7 @@ impl SlotAttributes {
/// ///
/// Slots can have different width depending on its attributes, accessors properties have width `2`, /// Slots can have different width depending on its attributes, accessors properties have width `2`,
/// while data properties have width `1`. /// while data properties have width `1`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Slot { pub(crate) struct Slot {
pub(crate) index: SlotIndex, pub(crate) index: SlotIndex,
pub(crate) attributes: SlotAttributes, pub(crate) attributes: SlotAttributes,

149
boa_engine/src/object/shape/static_shape.rs

@ -0,0 +1,149 @@
// Temp allow
#![allow(dead_code)]
#![allow(clippy::needless_pass_by_value)]
use std::fmt::Debug;
use boa_builtins::{EncodedStaticPropertyKey, StaticPropertyKey};
use boa_gc::{Finalize, Trace};
use crate::{
object::shape::property_table::PropertyTableInner, property::PropertyKey, symbol::WellKnown,
JsString, JsSymbol,
};
use super::{
shared_shape::TransitionKey, slot::SlotAttributes, ChangeTransition, JsPrototype, Shape, Slot,
UniqueShape,
};
pub(crate) type StaticShapeInner = &'static boa_builtins::StaticShape;
/// TODO: doc
fn from_static_property_key(key: StaticPropertyKey) -> PropertyKey {
match key {
boa_builtins::StaticPropertyKey::String(index) => {
PropertyKey::String(unsafe { JsString::from_index(index) })
}
boa_builtins::StaticPropertyKey::Symbol(s) => {
let symbol = match WellKnown::try_from(s).expect("should be an well known symbol") {
WellKnown::AsyncIterator => JsSymbol::async_iterator(),
WellKnown::HasInstance => JsSymbol::has_instance(),
WellKnown::IsConcatSpreadable => JsSymbol::is_concat_spreadable(),
WellKnown::Iterator => JsSymbol::iterator(),
WellKnown::Match => JsSymbol::r#match(),
WellKnown::MatchAll => JsSymbol::match_all(),
WellKnown::Replace => JsSymbol::replace(),
WellKnown::Search => JsSymbol::search(),
WellKnown::Species => JsSymbol::species(),
WellKnown::Split => JsSymbol::split(),
WellKnown::ToPrimitive => JsSymbol::to_primitive(),
WellKnown::ToStringTag => JsSymbol::to_string_tag(),
WellKnown::Unscopables => JsSymbol::unscopables(),
};
PropertyKey::Symbol(symbol)
}
}
}
/// TODO: doc
fn to_static_property_key(key: &PropertyKey) -> Option<StaticPropertyKey> {
match key {
PropertyKey::String(s) => Some(StaticPropertyKey::String(s.as_static_string_index()?)),
PropertyKey::Symbol(s) => Some(StaticPropertyKey::Symbol(s.hash().try_into().ok()?)),
PropertyKey::Index(_) => None,
}
}
/// Represents a [`Shape`] that is not shared with any other object.
///
/// This is useful for objects that are inherently unique like,
/// the builtin object.
///
/// Cloning this does a shallow clone.
#[derive(Debug, Clone, Trace, Finalize)]
pub(crate) struct StaticShape {
inner: StaticShapeInner,
}
impl StaticShape {
/// Create a new [`UniqueShape`].
pub(crate) const fn new(inner: StaticShapeInner) -> Self {
Self { inner }
}
/// Inserts a new property into the [`UniqueShape`].
pub(crate) fn insert_property_transition(&self, _key: TransitionKey) -> UniqueShape {
todo!()
}
/// Remove a property from the [`UniqueShape`].
///
/// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.
pub(crate) fn remove_property_transition(&self, _key: &PropertyKey) -> UniqueShape {
todo!()
}
/// Does a property lookup on the [`UniqueShape`] returning the [`Slot`] where it's
/// located or [`None`] otherwise.
pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {
let key = to_static_property_key(key)?;
let (index, attributes) = self.inner.get(key)?;
Some(Slot {
index: u32::from(index),
attributes: SlotAttributes::from_bits_retain(attributes.bits()),
})
}
/// Change the attributes of a property from the [`UniqueShape`].
///
/// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.
///
/// NOTE: This assumes that the property had already been inserted.
pub(crate) fn change_attributes_transition(
&self,
_key: &TransitionKey,
) -> ChangeTransition<Shape> {
todo!()
}
/// Change the prototype of the [`UniqueShape`].
///
/// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.
pub(crate) fn change_prototype_transition(&self, _prototype: JsPrototype) -> UniqueShape {
todo!()
}
/// Gets all keys first strings then symbols in creation order.
pub(crate) fn keys(&self) -> Vec<PropertyKey> {
self.inner
.property_table
.keys()
.map(EncodedStaticPropertyKey::decode)
.map(from_static_property_key)
.collect()
}
/// TODO: doc
pub(crate) fn to_unique(&self, prototype: JsPrototype) -> UniqueShape {
// TODO: optimization, preallocate capacity.
// TODO: optimization, direct iniitialize the map.
let mut property_table = PropertyTableInner::default();
for (key, (_, slot_attributes)) in &self.inner.property_table {
property_table.insert(
from_static_property_key(key.decode()),
SlotAttributes::from_bits_retain(slot_attributes.bits()),
);
}
UniqueShape::new(prototype, property_table)
}
/// Return location in memory of the [`UniqueShape`].
pub(crate) fn to_addr_usize(&self) -> usize {
let ptr: *const _ = self.inner;
ptr as usize
}
}

465
boa_engine/src/string/common.rs

@ -6,6 +6,8 @@ use super::JsString;
use boa_macros::utf16; use boa_macros::utf16;
use rustc_hash::{FxHashMap, FxHasher}; use rustc_hash::{FxHashMap, FxHasher};
use boa_builtins::RAW_STATICS;
macro_rules! well_known_statics { macro_rules! well_known_statics {
( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => {
$( $(
@ -134,466 +136,3 @@ thread_local! {
constants constants
}; };
} }
/// 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]] = &[
utf16!(""),
// Misc
utf16!(","),
utf16!(":"),
// Generic use
utf16!("name"),
utf16!("length"),
utf16!("arguments"),
utf16!("prototype"),
utf16!("constructor"),
utf16!("return"),
utf16!("throw"),
utf16!("global"),
utf16!("globalThis"),
// typeof
utf16!("null"),
utf16!("undefined"),
utf16!("number"),
utf16!("string"),
utf16!("symbol"),
utf16!("bigint"),
utf16!("object"),
utf16!("function"),
// Property descriptor
utf16!("value"),
utf16!("get"),
utf16!("set"),
utf16!("writable"),
utf16!("enumerable"),
utf16!("configurable"),
// Object object
utf16!("Object"),
utf16!("assign"),
utf16!("create"),
utf16!("toString"),
utf16!("valueOf"),
utf16!("is"),
utf16!("seal"),
utf16!("isSealed"),
utf16!("freeze"),
utf16!("isFrozen"),
utf16!("isExtensible"),
utf16!("hasOwnProperty"),
utf16!("isPrototypeOf"),
utf16!("setPrototypeOf"),
utf16!("getPrototypeOf"),
utf16!("defineProperty"),
utf16!("defineProperties"),
utf16!("deleteProperty"),
utf16!("construct"),
utf16!("hasOwn"),
utf16!("ownKeys"),
utf16!("keys"),
utf16!("values"),
utf16!("entries"),
utf16!("fromEntries"),
// Function object
utf16!("Function"),
utf16!("apply"),
utf16!("bind"),
utf16!("call"),
// Generator object
utf16!("Generator"),
// Array object
utf16!("Array"),
utf16!("at"),
utf16!("from"),
utf16!("isArray"),
utf16!("of"),
utf16!("copyWithin"),
utf16!("every"),
utf16!("fill"),
utf16!("filter"),
utf16!("find"),
utf16!("findIndex"),
utf16!("findLast"),
utf16!("findLastIndex"),
utf16!("flat"),
utf16!("flatMap"),
utf16!("forEach"),
utf16!("includes"),
utf16!("indexOf"),
utf16!("join"),
utf16!("map"),
utf16!("next"),
utf16!("reduce"),
utf16!("reduceRight"),
utf16!("reverse"),
utf16!("shift"),
utf16!("slice"),
utf16!("splice"),
utf16!("some"),
utf16!("sort"),
utf16!("unshift"),
utf16!("push"),
utf16!("pop"),
// String object
utf16!("String"),
utf16!("charAt"),
utf16!("charCodeAt"),
utf16!("codePointAt"),
utf16!("concat"),
utf16!("endsWith"),
utf16!("fromCharCode"),
utf16!("fromCodePoint"),
utf16!("lastIndexOf"),
utf16!("match"),
utf16!("matchAll"),
utf16!("normalize"),
utf16!("padEnd"),
utf16!("padStart"),
utf16!("raw"),
utf16!("repeat"),
utf16!("replace"),
utf16!("replaceAll"),
utf16!("search"),
utf16!("split"),
utf16!("startsWith"),
utf16!("substr"),
utf16!("substring"),
utf16!("toLocaleString"),
utf16!("toLowerCase"),
utf16!("toUpperCase"),
utf16!("trim"),
utf16!("trimEnd"),
utf16!("trimStart"),
// Number object
utf16!("Number"),
utf16!("Infinity"),
utf16!("NaN"),
utf16!("parseInt"),
utf16!("parseFloat"),
utf16!("isFinite"),
utf16!("isNaN"),
utf16!("EPSILON"),
utf16!("MAX_SAFE_INTEGER"),
utf16!("MIN_SAFE_INTEGER"),
utf16!("MAX_VALUE"),
utf16!("MIN_VALUE"),
utf16!("isSafeInteger"),
utf16!("isInteger"),
utf16!("toExponential"),
utf16!("toFixed"),
utf16!("toPrecision"),
// Boolean object
utf16!("Boolean"),
// BigInt object
utf16!("BigInt"),
utf16!("asIntN"),
utf16!("asUintN"),
// RegExp object
utf16!("RegExp"),
utf16!("exec"),
utf16!("test"),
utf16!("flags"),
utf16!("index"),
utf16!("lastIndex"),
utf16!("hasIndices"),
utf16!("ignoreCase"),
utf16!("multiline"),
utf16!("dotAll"),
utf16!("unicode"),
utf16!("sticky"),
utf16!("source"),
utf16!("get hasIndices"),
utf16!("get global"),
utf16!("get ignoreCase"),
utf16!("get multiline"),
utf16!("get dotAll"),
utf16!("get unicode"),
utf16!("get sticky"),
utf16!("get flags"),
utf16!("get source"),
// Symbol object
utf16!("Symbol"),
utf16!("for"),
utf16!("keyFor"),
utf16!("description"),
utf16!("asyncIterator"),
utf16!("hasInstance"),
utf16!("species"),
utf16!("unscopables"),
utf16!("iterator"),
utf16!("toStringTag"),
utf16!("toPrimitive"),
utf16!("get description"),
// Map object
utf16!("Map"),
utf16!("clear"),
utf16!("delete"),
utf16!("has"),
utf16!("size"),
// Set object
utf16!("Set"),
utf16!("add"),
// Reflect object
utf16!("Reflect"),
// Proxy object
utf16!("Proxy"),
utf16!("revocable"),
// Error objects
utf16!("Error"),
utf16!("AggregateError"),
utf16!("TypeError"),
utf16!("RangeError"),
utf16!("SyntaxError"),
utf16!("ReferenceError"),
utf16!("EvalError"),
utf16!("ThrowTypeError"),
utf16!("URIError"),
utf16!("message"),
// Date object
utf16!("Date"),
utf16!("toJSON"),
utf16!("getDate"),
utf16!("getDay"),
utf16!("getFullYear"),
utf16!("getHours"),
utf16!("getMilliseconds"),
utf16!("getMinutes"),
utf16!("getMonth"),
utf16!("getSeconds"),
utf16!("getTime"),
utf16!("getYear"),
utf16!("getUTCDate"),
utf16!("getUTCDay"),
utf16!("getUTCFullYear"),
utf16!("getUTCHours"),
utf16!("getUTCMinutes"),
utf16!("getUTCMonth"),
utf16!("getUTCSeconds"),
utf16!("setDate"),
utf16!("setFullYear"),
utf16!("setHours"),
utf16!("setMilliseconds"),
utf16!("setMinutes"),
utf16!("setMonth"),
utf16!("setSeconds"),
utf16!("setYear"),
utf16!("setTime"),
utf16!("setUTCDate"),
utf16!("setUTCFullYear"),
utf16!("setUTCHours"),
utf16!("setUTCMinutes"),
utf16!("setUTCMonth"),
utf16!("setUTCSeconds"),
utf16!("toDateString"),
utf16!("toGMTString"),
utf16!("toISOString"),
utf16!("toTimeString"),
utf16!("toUTCString"),
utf16!("now"),
utf16!("UTC"),
// JSON object
utf16!("JSON"),
utf16!("parse"),
utf16!("stringify"),
// Iterator object
utf16!("Array Iterator"),
utf16!("Set Iterator"),
utf16!("String Iterator"),
utf16!("Map Iterator"),
utf16!("For In Iterator"),
// Math object
utf16!("Math"),
utf16!("LN10"),
utf16!("LN2"),
utf16!("LOG10E"),
utf16!("LOG2E"),
utf16!("PI"),
utf16!("SQRT1_2"),
utf16!("SQRT2"),
utf16!("abs"),
utf16!("acos"),
utf16!("acosh"),
utf16!("asin"),
utf16!("asinh"),
utf16!("atan"),
utf16!("atanh"),
utf16!("atan2"),
utf16!("cbrt"),
utf16!("ceil"),
utf16!("clz32"),
utf16!("cos"),
utf16!("cosh"),
utf16!("exp"),
utf16!("expm1"),
utf16!("floor"),
utf16!("fround"),
utf16!("hypot"),
utf16!("imul"),
utf16!("log"),
utf16!("log1p"),
utf16!("log10"),
utf16!("log2"),
utf16!("max"),
utf16!("min"),
utf16!("pow"),
utf16!("random"),
utf16!("round"),
utf16!("sign"),
utf16!("sin"),
utf16!("sinh"),
utf16!("sqrt"),
utf16!("tan"),
utf16!("tanh"),
utf16!("trunc"),
// Intl object
utf16!("Intl"),
utf16!("DateTimeFormat"),
// 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!("byteLength"),
utf16!("byteOffset"),
utf16!("isView"),
utf16!("subarray"),
utf16!("get byteLength"),
utf16!("get buffer"),
utf16!("get byteOffset"),
utf16!("get size"),
utf16!("get length"),
// DataView object
utf16!("DataView"),
utf16!("getBigInt64"),
utf16!("getBigUint64"),
utf16!("getFloat32"),
utf16!("getFloat64"),
utf16!("getInt8"),
utf16!("getInt16"),
utf16!("getInt32"),
utf16!("getUint8"),
utf16!("getUint16"),
utf16!("getUint32"),
utf16!("setBigInt64"),
utf16!("setBigUint64"),
utf16!("setFloat32"),
utf16!("setFloat64"),
utf16!("setInt8"),
utf16!("setInt16"),
utf16!("setInt32"),
utf16!("setUint8"),
utf16!("setUint16"),
utf16!("setUint32"),
// Console object
utf16!("console"),
utf16!("assert"),
utf16!("debug"),
utf16!("error"),
utf16!("info"),
utf16!("trace"),
utf16!("warn"),
utf16!("exception"),
utf16!("count"),
utf16!("countReset"),
utf16!("group"),
utf16!("groupCollapsed"),
utf16!("groupEnd"),
utf16!("time"),
utf16!("timeLog"),
utf16!("timeEnd"),
utf16!("dir"),
utf16!("dirxml"),
// Minified name
utf16!("a"),
utf16!("b"),
utf16!("c"),
utf16!("d"),
utf16!("e"),
utf16!("f"),
utf16!("g"),
utf16!("h"),
utf16!("i"),
utf16!("j"),
utf16!("k"),
utf16!("l"),
utf16!("m"),
utf16!("n"),
utf16!("o"),
utf16!("p"),
utf16!("q"),
utf16!("r"),
utf16!("s"),
utf16!("t"),
utf16!("u"),
utf16!("v"),
utf16!("w"),
utf16!("x"),
utf16!("y"),
utf16!("z"),
utf16!("A"),
utf16!("B"),
utf16!("C"),
utf16!("D"),
utf16!("E"),
utf16!("F"),
utf16!("G"),
utf16!("H"),
utf16!("I"),
utf16!("J"),
utf16!("K"),
utf16!("L"),
utf16!("M"),
utf16!("N"),
utf16!("O"),
utf16!("P"),
utf16!("Q"),
utf16!("R"),
utf16!("S"),
utf16!("T"),
utf16!("U"),
utf16!("V"),
utf16!("W"),
utf16!("X"),
utf16!("Y"),
utf16!("Z"),
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]"),
];

22
boa_engine/src/string/mod.rs

@ -28,6 +28,7 @@ use crate::{
tagged::{Tagged, UnwrappedTagged}, tagged::{Tagged, UnwrappedTagged},
JsBigInt, JsBigInt,
}; };
use boa_builtins::{StaticString, RAW_STATICS};
use boa_gc::{empty_trace, Finalize, Trace}; use boa_gc::{empty_trace, Finalize, Trace};
pub use boa_macros::utf16; pub use boa_macros::utf16;
@ -616,6 +617,27 @@ impl JsString {
ptr: Tagged::from_non_null(ptr), ptr: Tagged::from_non_null(ptr),
} }
} }
pub(crate) fn as_static_string_index(&self) -> Option<u16> {
if let UnwrappedTagged::Tag(index) = self.ptr.unwrap() {
return Some(index as u16);
}
None
}
pub(crate) const unsafe fn from_index(index: u16) -> Self {
debug_assert!((index as usize) < RAW_STATICS.len());
Self {
ptr: Tagged::from_tag(index as usize),
}
}
}
impl From<StaticString> for JsString {
fn from(string: StaticString) -> Self {
Self {
ptr: Tagged::from_tag(string.index() as usize),
}
}
} }
impl AsRef<[u16]> for JsString { impl AsRef<[u16]> for JsString {

2
boa_engine/src/symbol.rs

@ -61,7 +61,7 @@ fn get_id() -> Option<u64> {
/// List of well known symbols. /// List of well known symbols.
#[derive(Debug, Clone, Copy, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Clone, Copy, TryFromPrimitive, IntoPrimitive)]
#[repr(u8)] #[repr(u8)]
enum WellKnown { pub(crate) enum WellKnown {
AsyncIterator, AsyncIterator,
HasInstance, HasInstance,
IsConcatSpreadable, IsConcatSpreadable,

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

@ -94,9 +94,11 @@ impl Operation for SetPropertyByValue {
// Cannot use fast path if the [[prototype]] is a proxy object, // Cannot use fast path if the [[prototype]] is a proxy object,
// because we have to the call prototypes [[set]] on non-existing property, // because we have to the call prototypes [[set]] on non-existing property,
// and proxy objects can override [[set]]. // and proxy objects can override [[set]].
let prototype = shape.prototype(); if !shape.is_static() {
if prototype.map_or(false, |x| x.is_proxy()) { let prototype = shape.prototype();
break 'fast_path; if prototype.map_or(false, |x| x.is_proxy()) {
break 'fast_path;
}
} }
dense_elements.push(value.clone()); dense_elements.push(value.clone());

Loading…
Cancel
Save