From 9d3f0f9821b95497a9e14bfb20fadc27f77b673c Mon Sep 17 00:00:00 2001 From: Haled Odat Date: Fri, 14 Apr 2023 06:21:49 +0200 Subject: [PATCH 1/5] Implement static shapes --- Cargo.lock | 22 + Cargo.toml | 2 + boa_builtins/Cargo.toml | 22 + boa_builtins/README.md | 1 + boa_builtins/build.rs | 588 +++++++++++++++++++ boa_builtins/src/lib.rs | 109 ++++ boa_cli/src/debug/shape.rs | 6 +- boa_engine/Cargo.toml | 1 + boa_engine/src/builtins/array/mod.rs | 118 ++-- boa_engine/src/builtins/bigint/mod.rs | 24 +- boa_engine/src/builtins/boolean/mod.rs | 12 +- boa_engine/src/builtins/date/mod.rs | 122 ++-- boa_engine/src/builtins/function/mod.rs | 89 ++- boa_engine/src/builtins/json/mod.rs | 19 +- boa_engine/src/builtins/math/mod.rs | 109 ++-- boa_engine/src/builtins/mod.rs | 285 ++++++++- boa_engine/src/builtins/number/mod.rs | 68 +-- boa_engine/src/builtins/object/mod.rs | 91 ++- boa_engine/src/builtins/reflect/mod.rs | 47 +- boa_engine/src/builtins/symbol/mod.rs | 77 +-- boa_engine/src/context/intrinsics.rs | 53 +- boa_engine/src/object/builtins/jsfunction.rs | 13 + boa_engine/src/object/jsobject.rs | 25 +- boa_engine/src/object/mod.rs | 29 +- boa_engine/src/object/property_map.rs | 36 +- boa_engine/src/object/shape/mod.rs | 35 +- boa_engine/src/object/shape/slot.rs | 2 +- boa_engine/src/object/shape/static_shape.rs | 145 +++++ boa_engine/src/symbol.rs | 2 +- boa_engine/src/vm/opcode/set/property.rs | 8 +- 30 files changed, 1695 insertions(+), 465 deletions(-) create mode 100644 boa_builtins/Cargo.toml create mode 100644 boa_builtins/README.md create mode 100644 boa_builtins/build.rs create mode 100644 boa_builtins/src/lib.rs create mode 100644 boa_engine/src/object/shape/static_shape.rs diff --git a/Cargo.lock b/Cargo.lock index 484f6e1e5ad..acb65b13475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -370,6 +370,17 @@ dependencies = [ "serde", ] +[[package]] +name = "boa_builtins" +version = "0.16.0" +dependencies = [ + "bitflags 2.2.1", + "boa_macros", + "phf", + "phf_codegen", + "phf_shared", +] + [[package]] name = "boa_cli" version = "0.16.0" @@ -396,6 +407,7 @@ version = "0.16.0" dependencies = [ "bitflags 2.2.1", "boa_ast", + "boa_builtins", "boa_gc", "boa_icu_provider", "boa_interner", @@ -3013,6 +3025,16 @@ dependencies = [ "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]] name = "phf_generator" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index d47cb01a06e..c31054aeb72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "boa_ast", + "boa_builtins", "boa_cli", "boa_engine", "boa_examples", @@ -28,6 +29,7 @@ description = "Boa is a Javascript lexer, parser and compiler written in Rust. C [workspace.dependencies] 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_gc = { version = "0.16.0", path = "boa_gc" } boa_icu_provider = { version = "0.16.0", path = "boa_icu_provider" } diff --git a/boa_builtins/Cargo.toml b/boa_builtins/Cargo.toml new file mode 100644 index 00000000000..c047486599a --- /dev/null +++ b/boa_builtins/Cargo.toml @@ -0,0 +1,22 @@ +[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" + +[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" diff --git a/boa_builtins/README.md b/boa_builtins/README.md new file mode 100644 index 00000000000..29b26df1359 --- /dev/null +++ b/boa_builtins/README.md @@ -0,0 +1 @@ +# TOOD diff --git a/boa_builtins/build.rs b/boa_builtins/build.rs new file mode 100644 index 00000000000..9f59d9dcf0e --- /dev/null +++ b/boa_builtins/build.rs @@ -0,0 +1,588 @@ +use std::fs::File; +use std::hash::{Hash, Hasher}; +use std::io::{self, BufWriter, Write}; +use std::path::Path; +use std::{env, fmt}; + +use bitflags::bitflags; +use phf_shared::{FmtConst, PhfBorrow, PhfHash}; + +use boa_macros::utf16; + +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; + + const GET = 0b0000_1000; + const SET = 0b0001_0000; + } +} + +/// List of well known symbols. +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +#[allow(dead_code)] +enum WellKnown { + AsyncIterator, + HasInstance, + IsConcatSpreadable, + Iterator, + Match, + MatchAll, + Replace, + Search, + Species, + Split, + ToPrimitive, + ToStringTag, + Unscopables, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum StaticPropertyKey<'a> { + String(&'a [u16]), + Symbol(u8), +} + +impl PhfHash for StaticPropertyKey<'static> { + #[inline] + fn phf_hash(&self, state: &mut H) { + self.hash(state) + } +} + +impl FmtConst for StaticPropertyKey<'static> { + fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if matches!(self, StaticPropertyKey::String(_)) { + f.write_str("StaticPropertyKey::String(")?; + } else { + f.write_str("StaticPropertyKey::Symbol(")?; + } + + match self { + StaticPropertyKey::String(s) => write!(f, "&{:?})", s), + StaticPropertyKey::Symbol(s) => write!(f, "{})", s), + } + } +} + +impl<'b, 'a: 'b> PhfBorrow> for StaticPropertyKey<'a> { + fn borrow(&self) -> &StaticPropertyKey<'b> { + self + } +} + +trait ToPropertyKey { + fn to_property_key(self) -> StaticPropertyKey<'static>; +} + +impl ToPropertyKey for &'static [u16] { + fn to_property_key(self) -> StaticPropertyKey<'static> { + StaticPropertyKey::String(self) + } +} + +impl ToPropertyKey for WellKnown { + fn to_property_key(self) -> StaticPropertyKey<'static> { + StaticPropertyKey::Symbol(self as u8) + } +} + +struct BuiltInBuilder { + name: &'static str, + map: phf_codegen::OrderedMap>, + prototype: Option<&'static str>, + + slot_index: usize, +} + +impl BuiltInBuilder { + fn new(name: &'static str) -> Self { + Self { + name, + map: phf_codegen::OrderedMap::new(), + prototype: None, + slot_index: 0, + } + } + + fn inherits(&mut self, prototype: &'static str) -> &mut Self { + self.prototype = Some(prototype); + self + } + + fn method(&mut self, key: K) -> &mut Self + where + K: ToPropertyKey, + { + let key = key.to_property_key(); + let attributes = Attribute::WRITABLE | Attribute::CONFIGURABLE; + self.map.entry( + key, + &format!( + "({}, Attribute::from_bits_retain({}))", + self.slot_index, + attributes.bits() + ), + ); + self.slot_index += 1; + self + } + + fn accessor(&mut self, key: K, mut attributes: Attribute) -> &mut Self + where + K: ToPropertyKey, + { + // TODO: should they always be set? + attributes |= Attribute::GET; + attributes |= Attribute::SET; + + let key = key.to_property_key(); + self.map.entry( + key, + &format!( + "({}, Attribute::from_bits_retain({}))", + self.slot_index, + attributes.bits() + ), + ); + self.slot_index += 2; + self + } + + fn property(&mut self, key: K, attributes: Attribute) -> &mut Self + where + K: ToPropertyKey, + { + let key = key.to_property_key(); + self.map.entry( + key, + &format!( + "({}, Attribute::from_bits_retain({}))", + self.slot_index, + attributes.bits() + ), + ); + self.slot_index += 1; + self + } + + fn build(&mut self, file: &mut BufWriter) -> io::Result<&'static str> { + let prototype = if let Some(prototype) = self.prototype { + format!("Some(&'static {})", prototype) + } else { + "None".into() + }; + writeln!( + file, + "pub static {}_STATIC_SHAPE: StaticShape = StaticShape {{\n storage_len: {},\n prototype: {},\n property_table: {} }};", + self.name, + self.slot_index + 1, + prototype, + self.map.build(), + )?; + + Ok(self.name) + } +} + +struct BuiltInBuilderConstructor { + object: BuiltInBuilder, + prototype: BuiltInBuilder, +} + +impl BuiltInBuilderConstructor { + fn new(name: &'static str) -> Self { + let object_name = Box::leak(format!("{name}_CONSTRUCTOR").into_boxed_str()); + let prototype_name = Box::leak(format!("{name}_PROTOTYPE").into_boxed_str()); + let mut this = Self { + object: BuiltInBuilder::new(object_name), + prototype: BuiltInBuilder::new(prototype_name), + }; + + this.object + .property(utf16!("length"), Attribute::CONFIGURABLE); + this.object + .property(utf16!("name"), Attribute::CONFIGURABLE); + this.object + .property(utf16!("prototype"), Attribute::empty()); + + this.prototype.property( + utf16!("constructor"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ); + + this + } + + fn inherits(&mut self, prototype: &'static str) -> &mut Self { + self.object.inherits(prototype); + self + } + + fn method(&mut self, key: K) -> &mut Self + where + K: ToPropertyKey, + { + self.prototype.method(key); + self + } + + fn static_method(&mut self, key: K) -> &mut Self + where + K: ToPropertyKey, + { + self.object.method(key); + self + } + + fn accessor(&mut self, key: K, attributes: Attribute) -> &mut Self + where + K: ToPropertyKey, + { + self.prototype.accessor(key, attributes); + self + } + + fn static_accessor(&mut self, key: K, attributes: Attribute) -> &mut Self + where + K: ToPropertyKey, + { + self.object.accessor(key, attributes); + self + } + + fn static_property(&mut self, key: K, attributes: Attribute) -> &mut Self + where + K: ToPropertyKey, + { + self.object.property(key, attributes); + self + } + + fn property(&mut self, key: K, attributes: Attribute) -> &mut Self + where + K: ToPropertyKey, + { + self.prototype.property(key, attributes); + self + } + + fn build(&mut self, file: &mut BufWriter) -> io::Result<()> { + self.object.build(file)?; + self.prototype.build(file)?; + + Ok(()) + } +} + +fn main() -> io::Result<()> { + let file = Path::new(&env::var("OUT_DIR").unwrap()).join("static_shapes_codegen.rs"); + let file = &mut BufWriter::new(File::create(file)?); + + BuiltInBuilder::new("EMPTY_OBJECT").build(file)?; + + BuiltInBuilder::new("JSON_OBJECT") + .method(utf16!("parse")) + .method(utf16!("stringify")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilder::new("MATH_OBJECT") + .property(utf16!("E"), Attribute::empty()) + .property(utf16!("LN10"), Attribute::empty()) + .property(utf16!("LN2"), Attribute::empty()) + .property(utf16!("LOG10E"), Attribute::empty()) + .property(utf16!("LOG2E"), Attribute::empty()) + .property(utf16!("PI"), Attribute::empty()) + .property(utf16!("SQRT1_2"), Attribute::empty()) + .property(utf16!("SQRT2"), Attribute::empty()) + .method(utf16!("abs")) + .method(utf16!("acos")) + .method(utf16!("acosh")) + .method(utf16!("asin")) + .method(utf16!("asinh")) + .method(utf16!("atan")) + .method(utf16!("atanh")) + .method(utf16!("atan2")) + .method(utf16!("cbrt")) + .method(utf16!("ceil")) + .method(utf16!("clz32")) + .method(utf16!("cos")) + .method(utf16!("cosh")) + .method(utf16!("exp")) + .method(utf16!("expm1")) + .method(utf16!("floor")) + .method(utf16!("fround")) + .method(utf16!("hypot")) + .method(utf16!("imul")) + .method(utf16!("log")) + .method(utf16!("log1p")) + .method(utf16!("log10")) + .method(utf16!("log2")) + .method(utf16!("max")) + .method(utf16!("min")) + .method(utf16!("pow")) + .method(utf16!("random")) + .method(utf16!("round")) + .method(utf16!("sign")) + .method(utf16!("sin")) + .method(utf16!("sinh")) + .method(utf16!("sqrt")) + .method(utf16!("tan")) + .method(utf16!("tanh")) + .method(utf16!("trunc")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilder::new("REFLECT_OBJECT") + .method(utf16!("apply")) + .method(utf16!("construct")) + .method(utf16!("defineProperty")) + .method(utf16!("deleteProperty")) + .method(utf16!("get")) + .method(utf16!("getOwnPropertyDescriptor")) + .method(utf16!("getPrototypeOf")) + .method(utf16!("has")) + .method(utf16!("isExtensible")) + .method(utf16!("ownKeys")) + .method(utf16!("preventExtensions")) + .method(utf16!("set")) + .method(utf16!("setPrototypeOf")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new("OBJECT") + .accessor(utf16!("__proto__"), Attribute::CONFIGURABLE) + .method(utf16!("hasOwnProperty")) + .method(utf16!("propertyIsEnumerable")) + .method(utf16!("toString")) + .method(utf16!("toLocaleString")) + .method(utf16!("valueOf")) + .method(utf16!("isPrototypeOf")) + .method(utf16!("__defineGetter__")) + .method(utf16!("__defineSetter__")) + .method(utf16!("__lookupGetter__")) + .method(utf16!("__lookupSetter__")) + .static_method(utf16!("create")) + .static_method(utf16!("setPrototypeOf")) + .static_method(utf16!("getPrototypeOf")) + .static_method(utf16!("defineProperty")) + .static_method(utf16!("defineProperties")) + .static_method(utf16!("assign")) + .static_method(utf16!("is")) + .static_method(utf16!("keys")) + .static_method(utf16!("values")) + .static_method(utf16!("entries")) + .static_method(utf16!("seal")) + .static_method(utf16!("isSealed")) + .static_method(utf16!("freeze")) + .static_method(utf16!("isFrozen")) + .static_method(utf16!("preventExtensions")) + .static_method(utf16!("isExtensible")) + .static_method(utf16!("getOwnPropertyDescriptor")) + .static_method(utf16!("getOwnPropertyDescriptors")) + .static_method(utf16!("getOwnPropertyNames")) + .static_method(utf16!("getOwnPropertySymbols")) + .static_method(utf16!("hasOwn")) + .static_method(utf16!("fromEntries")) + .build(file)?; + + BuiltInBuilderConstructor::new("FUNCTION") + .property(utf16!("length"), Attribute::CONFIGURABLE) + .property(utf16!("name"), Attribute::CONFIGURABLE) + .method(utf16!("apply")) + .method(utf16!("bind")) + .method(utf16!("call")) + .method(utf16!("toString")) + .property(WellKnown::HasInstance, Attribute::empty()) + .accessor(utf16!("caller"), Attribute::CONFIGURABLE) + .accessor(utf16!("arguments"), Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new("ARRAY") + .property(utf16!("length"), Attribute::WRITABLE) + .property( + utf16!("values"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property( + WellKnown::Iterator, + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property(WellKnown::Unscopables, Attribute::CONFIGURABLE) + .method(utf16!("at")) + .method(utf16!("concat")) + .method(utf16!("push")) + .method(utf16!("indexOf")) + .method(utf16!("lastIndexOf")) + .method(utf16!("includes")) + .method(utf16!("map")) + .method(utf16!("fill")) + .method(utf16!("forEach")) + .method(utf16!("filter")) + .method(utf16!("pop")) + .method(utf16!("join")) + .method(utf16!("toString")) + .method(utf16!("reverse")) + .method(utf16!("shift")) + .method(utf16!("unshift")) + .method(utf16!("every")) + .method(utf16!("find")) + .method(utf16!("findIndex")) + .method(utf16!("findLast")) + .method(utf16!("findLastIndex")) + .method(utf16!("flat")) + .method(utf16!("flatMap")) + .method(utf16!("slice")) + .method(utf16!("some")) + .method(utf16!("sort")) + .method(utf16!("splice")) + .method(utf16!("toLocaleString")) + .method(utf16!("reduce")) + .method(utf16!("reduceRight")) + .method(utf16!("keys")) + .method(utf16!("entries")) + .method(utf16!("copyWithin")) + // Static properties + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .static_method(utf16!("from")) + .static_method(utf16!("isArray")) + .static_method(utf16!("of")) + .build(file)?; + + BuiltInBuilderConstructor::new("DATE") + .static_method(utf16!("now")) + .static_method(utf16!("parse")) + .static_method(utf16!("UTC")) + .method(utf16!("getDate")) + .method(utf16!("getDay")) + .method(utf16!("getFullYear")) + .method(utf16!("getHours")) + .method(utf16!("getMilliseconds")) + .method(utf16!("getMinutes")) + .method(utf16!("getMonth")) + .method(utf16!("getSeconds")) + .method(utf16!("getTime")) + .method(utf16!("getTimezoneOffset")) + .method(utf16!("getUTCDate")) + .method(utf16!("getUTCDay")) + .method(utf16!("getUTCFullYear")) + .method(utf16!("getUTCHours")) + .method(utf16!("getUTCMilliseconds")) + .method(utf16!("getUTCMinutes")) + .method(utf16!("getUTCMonth")) + .method(utf16!("getUTCSeconds")) + .method(utf16!("getYear")) + .method(utf16!("setDate")) + .method(utf16!("setFullYear")) + .method(utf16!("setHours")) + .method(utf16!("setMilliseconds")) + .method(utf16!("setMinutes")) + .method(utf16!("setMonth")) + .method(utf16!("setSeconds")) + .method(utf16!("setTime")) + .method(utf16!("setUTCDate")) + .method(utf16!("setUTCFullYear")) + .method(utf16!("setUTCHours")) + .method(utf16!("setUTCMilliseconds")) + .method(utf16!("setUTCMinutes")) + .method(utf16!("setUTCMonth")) + .method(utf16!("setUTCSeconds")) + .method(utf16!("setYear")) + .method(utf16!("toDateString")) + .method(utf16!("toISOString")) + .method(utf16!("toJSON")) + .method(utf16!("toLocaleDateString")) + .method(utf16!("toLocaleString")) + .method(utf16!("toLocaleTimeString")) + .method(utf16!("toString")) + .method(utf16!("toTimeString")) + .method(utf16!("valueOf")) + .property( + utf16!("toGMTString"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("toUTCString"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property(WellKnown::ToPrimitive, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new("NUMBER") + .static_property(utf16!("EPSILON"), Attribute::empty()) + .static_property(utf16!("MAX_SAFE_INTEGER"), Attribute::empty()) + .static_property(utf16!("MIN_SAFE_INTEGER"), Attribute::empty()) + .static_property(utf16!("MAX_VALUE"), Attribute::empty()) + .static_property(utf16!("MIN_VALUE"), Attribute::empty()) + .static_property(utf16!("NEGATIVE_INFINITY"), Attribute::empty()) + .static_property(utf16!("POSITIVE_INFINITY"), Attribute::empty()) + .static_property(utf16!("NaN"), Attribute::empty()) + .static_property( + utf16!("parseInt"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .static_property( + utf16!("parseFloat"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .static_method(utf16!("isFinite")) + .static_method(utf16!("isNaN")) + .static_method(utf16!("isSafeInteger")) + .static_method(utf16!("isInteger")) + .method(utf16!("toExponential")) + .method(utf16!("toFixed")) + .method(utf16!("toLocaleString")) + .method(utf16!("toPrecision")) + .method(utf16!("toString")) + .method(utf16!("valueOf")) + .build(file)?; + + BuiltInBuilderConstructor::new("BOOLEAN") + .method(utf16!("toString")) + .method(utf16!("valueOf")) + .build(file)?; + + BuiltInBuilderConstructor::new("BIGINT") + .method(utf16!("toString")) + .method(utf16!("valueOf")) + .static_method(utf16!("asIntN")) + .static_method(utf16!("asUintN")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new("SYMBOL") + .static_method(utf16!("for")) + .static_method(utf16!("keyFor")) + .static_property(utf16!("asyncIterator"), Attribute::empty()) + .static_property(utf16!("hasInstance"), Attribute::empty()) + .static_property(utf16!("isConcatSpreadable"), Attribute::empty()) + .static_property(utf16!("iterator"), Attribute::empty()) + .static_property(utf16!("match"), Attribute::empty()) + .static_property(utf16!("matchAll"), Attribute::empty()) + .static_property(utf16!("replace"), Attribute::empty()) + .static_property(utf16!("search"), Attribute::empty()) + .static_property(utf16!("species"), Attribute::empty()) + .static_property(utf16!("split"), Attribute::empty()) + .static_property(utf16!("toPrimitive"), Attribute::empty()) + .static_property(utf16!("toStringTag"), Attribute::empty()) + .static_property(utf16!("unscopables"), Attribute::empty()) + .method(utf16!("toString")) + .method(utf16!("valueOf")) + .accessor(utf16!("description"), Attribute::CONFIGURABLE) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .property(WellKnown::ToPrimitive, Attribute::CONFIGURABLE) + .build(file)?; + + Ok(()) +} diff --git a/boa_builtins/src/lib.rs b/boa_builtins/src/lib.rs new file mode 100644 index 00000000000..c2ed539cf33 --- /dev/null +++ b/boa_builtins/src/lib.rs @@ -0,0 +1,109 @@ +use std::{ + fmt::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 enum StaticPropertyKey<'a> { + String(&'a [u16]), + Symbol(u8), +} + +impl<'a> Debug for StaticPropertyKey<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + StaticPropertyKey::String(string) => { + let string = String::from_utf16_lossy(string); + write!(f, "String(\"{string}\")") + } + StaticPropertyKey::Symbol(symbol) => { + write!(f, "Symbol({symbol})") + } + } + } +} + +impl PhfHash for StaticPropertyKey<'static> { + #[inline] + fn phf_hash(&self, state: &mut H) { + self.hash(state) + } +} + +impl<'b, 'a: 'b> PhfBorrow> for StaticPropertyKey<'a> { + #[inline] + fn borrow(&self) -> &StaticPropertyKey<'b> { + self + } +} + +pub type Slot = (u32, Attribute); + +#[derive(Debug)] +pub struct StaticShape { + pub property_table: phf::OrderedMap, Slot>, + + pub storage_len: usize, + + /// \[\[Prototype\]\] + pub prototype: Option<&'static StaticShape>, +} + +impl StaticShape { + #[inline] + pub fn get(&self, key: StaticPropertyKey<'_>) -> Option { + // SAFETY: only used to extend the lifetime, so we are able to call get. + let key: &StaticPropertyKey<'static> = unsafe { std::mem::transmute(&key) }; + self.property_table + .get(key) + .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) -> &'static [u16] { + match self + .property_table + .index(index) + .expect("there should be a key at the given index") + .0 + { + StaticPropertyKey::String(s) => s, + 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; diff --git a/boa_cli/src/debug/shape.rs b/boa_cli/src/debug/shape.rs index 167bc5b3c47..e874008cf92 100644 --- a/boa_cli/src/debug/shape.rs +++ b/boa_cli/src/debug/shape.rs @@ -31,8 +31,12 @@ fn r#type(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult(realm) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .property( - utf16!("length"), - 0, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ) - .property( - utf16!("values"), - values_function.clone(), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - symbol_iterator, - values_function, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - symbol_unscopables, - unscopables_object, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .method(Self::at, "at", 1) - .method(Self::concat, "concat", 1) - .method(Self::push, "push", 1) - .method(Self::index_of, "indexOf", 1) - .method(Self::last_index_of, "lastIndexOf", 1) - .method(Self::includes_value, "includes", 1) - .method(Self::map, "map", 1) - .method(Self::fill, "fill", 1) - .method(Self::for_each, "forEach", 1) - .method(Self::filter, "filter", 1) - .method(Self::pop, "pop", 0) - .method(Self::join, "join", 1) - .method(Self::to_string, "toString", 0) - .method(Self::reverse, "reverse", 0) - .method(Self::shift, "shift", 0) - .method(Self::unshift, "unshift", 1) - .method(Self::every, "every", 1) - .method(Self::find, "find", 1) - .method(Self::find_index, "findIndex", 1) - .method(Self::find_last, "findLast", 1) - .method(Self::find_last_index, "findLastIndex", 1) - .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(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::ARRAY_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::ARRAY_PROTOTYPE_STATIC_SHAPE, + ) + .property(0) + .property(values_function.clone()) + .property(values_function) + .property(unscopables_object) + .method(Self::at, 1) + .method(Self::concat, 1) + .method(Self::push, 1) + .method(Self::index_of, 1) + .method(Self::last_index_of, 1) + .method(Self::includes_value, 1) + .method(Self::map, 1) + .method(Self::fill, 1) + .method(Self::for_each, 1) + .method(Self::filter, 1) + .method(Self::pop, 0) + .method(Self::join, 1) + .method(Self::to_string, 0) + .method(Self::reverse, 0) + .method(Self::shift, 0) + .method(Self::unshift, 1) + .method(Self::every, 1) + .method(Self::find, 1) + .method(Self::find_index, 1) + .method(Self::find_last, 1) + .method(Self::find_last_index, 1) + .method(Self::flat, 0) + .method(Self::flat_map, 1) + .method(Self::slice, 2) + .method(Self::some, 1) + .method(Self::sort, 1) + .method(Self::splice, 2) + .method(Self::to_locale_string, 0) + .method(Self::reduce, 1) + .method(Self::reduce_right, 1) + .method(Self::keys, 0) + .method(Self::entries, 0) + .method(Self::copy_within, 2) + // Static Methods + .static_accessor(Some(get_species), None) + .static_method(Self::from, 1) + .static_method(Self::is_array, 1) + .static_method(Self::of, 0) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/bigint/mod.rs b/boa_engine/src/builtins/bigint/mod.rs index d649bf60830..77845981bc7 100644 --- a/boa_engine/src/builtins/bigint/mod.rs +++ b/boa_engine/src/builtins/bigint/mod.rs @@ -17,9 +17,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, object::JsObject, - property::Attribute, realm::Realm, - symbol::JsSymbol, value::{IntegerOrInfinity, PreferredType}, Context, JsArgs, JsBigInt, JsResult, JsValue, }; @@ -39,17 +37,17 @@ impl IntrinsicObject for BigInt { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(realm) - .method(Self::to_string, "toString", 0) - .method(Self::value_of, "valueOf", 0) - .static_method(Self::as_int_n, "asIntN", 2) - .static_method(Self::as_uint_n, "asUintN", 2) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::BIGINT_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::BIGINT_PROTOTYPE_STATIC_SHAPE, + ) + .method(Self::to_string, 0) + .method(Self::value_of, 0) + .static_method(Self::as_int_n, 2) + .static_method(Self::as_uint_n, 2) + .property(Self::NAME) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/boolean/mod.rs b/boa_engine/src/builtins/boolean/mod.rs index 91f8186f566..2d36b6dce48 100644 --- a/boa_engine/src/builtins/boolean/mod.rs +++ b/boa_engine/src/builtins/boolean/mod.rs @@ -32,10 +32,14 @@ impl IntrinsicObject for Boolean { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(realm) - .method(Self::to_string, "toString", 0) - .method(Self::value_of, "valueOf", 0) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::BOOLEAN_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::BOOLEAN_PROTOTYPE_STATIC_SHAPE, + ) + .method(Self::to_string, 0) + .method(Self::value_of, 0) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index e9360e1dffe..9f16f40e263 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -22,10 +22,8 @@ use crate::{ error::JsNativeError, js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, - symbol::JsSymbol, value::{IntegerOrNan, JsValue, PreferredType}, Context, JsArgs, JsError, JsResult, }; @@ -106,70 +104,62 @@ impl IntrinsicObject for Date { .length(1) .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .static_method(Self::now, "now", 0) - .static_method(Self::parse, "parse", 1) - .static_method(Self::utc, "UTC", 7) - .method(Self::get_date::, "getDate", 0) - .method(Self::get_day::, "getDay", 0) - .method(Self::get_full_year::, "getFullYear", 0) - .method(Self::get_hours::, "getHours", 0) - .method(Self::get_milliseconds::, "getMilliseconds", 0) - .method(Self::get_minutes::, "getMinutes", 0) - .method(Self::get_month::, "getMonth", 0) - .method(Self::get_seconds::, "getSeconds", 0) - .method(Self::get_time, "getTime", 0) - .method(Self::get_timezone_offset, "getTimezoneOffset", 0) - .method(Self::get_date::, "getUTCDate", 0) - .method(Self::get_day::, "getUTCDay", 0) - .method(Self::get_full_year::, "getUTCFullYear", 0) - .method(Self::get_hours::, "getUTCHours", 0) - .method(Self::get_milliseconds::, "getUTCMilliseconds", 0) - .method(Self::get_minutes::, "getUTCMinutes", 0) - .method(Self::get_month::, "getUTCMonth", 0) - .method(Self::get_seconds::, "getUTCSeconds", 0) - .method(Self::get_year, "getYear", 0) - .method(Self::set_date::, "setDate", 1) - .method(Self::set_full_year::, "setFullYear", 3) - .method(Self::set_hours::, "setHours", 4) - .method(Self::set_milliseconds::, "setMilliseconds", 1) - .method(Self::set_minutes::, "setMinutes", 3) - .method(Self::set_month::, "setMonth", 2) - .method(Self::set_seconds::, "setSeconds", 2) - .method(Self::set_time, "setTime", 1) - .method(Self::set_date::, "setUTCDate", 1) - .method(Self::set_full_year::, "setUTCFullYear", 3) - .method(Self::set_hours::, "setUTCHours", 4) - .method(Self::set_milliseconds::, "setUTCMilliseconds", 1) - .method(Self::set_minutes::, "setUTCMinutes", 3) - .method(Self::set_month::, "setUTCMonth", 2) - .method(Self::set_seconds::, "setUTCSeconds", 2) - .method(Self::set_year, "setYear", 1) - .method(Self::to_date_string, "toDateString", 0) - .method(Self::to_iso_string, "toISOString", 0) - .method(Self::to_json, "toJSON", 1) - .method(Self::to_locale_date_string, "toLocaleDateString", 0) - .method(Self::to_locale_string, "toLocaleString", 0) - .method(Self::to_locale_time_string, "toLocaleTimeString", 0) - .method(Self::to_string, "toString", 0) - .method(Self::to_time_string, "toTimeString", 0) - .method(Self::value_of, "valueOf", 0) - .property( - "toGMTString", - to_utc_string.clone(), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - "toUTCString", - to_utc_string, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - JsSymbol::to_primitive(), - to_primitive, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::DATE_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::DATE_PROTOTYPE_STATIC_SHAPE, + ) + .static_method(Self::now, 0) + .static_method(Self::parse, 1) + .static_method(Self::utc, 7) + .method(Self::get_date::, 0) + .method(Self::get_day::, 0) + .method(Self::get_full_year::, 0) + .method(Self::get_hours::, 0) + .method(Self::get_milliseconds::, 0) + .method(Self::get_minutes::, 0) + .method(Self::get_month::, 0) + .method(Self::get_seconds::, 0) + .method(Self::get_time, 0) + .method(Self::get_timezone_offset, 0) + .method(Self::get_date::, 0) + .method(Self::get_day::, 0) + .method(Self::get_full_year::, 0) + .method(Self::get_hours::, 0) + .method(Self::get_milliseconds::, 0) + .method(Self::get_minutes::, 0) + .method(Self::get_month::, 0) + .method(Self::get_seconds::, 0) + .method(Self::get_year, 0) + .method(Self::set_date::, 1) + .method(Self::set_full_year::, 3) + .method(Self::set_hours::, 4) + .method(Self::set_milliseconds::, 1) + .method(Self::set_minutes::, 3) + .method(Self::set_month::, 2) + .method(Self::set_seconds::, 2) + .method(Self::set_time, 1) + .method(Self::set_date::, 1) + .method(Self::set_full_year::, 3) + .method(Self::set_hours::, 4) + .method(Self::set_milliseconds::, 1) + .method(Self::set_minutes::, 3) + .method(Self::set_month::, 2) + .method(Self::set_seconds::, 2) + .method(Self::set_year, 1) + .method(Self::to_date_string, 0) + .method(Self::to_iso_string, 0) + .method(Self::to_json, 1) + .method(Self::to_locale_date_string, 0) + .method(Self::to_locale_string, 0) + .method(Self::to_locale_time_string, 0) + .method(Self::to_string, 0) + .method(Self::to_time_string, 0) + .method(Self::value_of, 0) + .property(to_utc_string.clone()) + .property(to_utc_string) + .property(to_primitive) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index dab238ef99b..df457c6f125 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -19,12 +19,13 @@ use crate::{ error::JsNativeError, js_string, native_function::NativeFunction, - object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, + object::{ + internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData, ObjectKind, + }, object::{JsFunction, PrivateElement}, - property::{Attribute, PropertyDescriptor, PropertyKey}, + property::{PropertyDescriptor, PropertyKey}, realm::Realm, string::utf16, - symbol::JsSymbol, value::IntegerOrInfinity, vm::CodeBlock, Context, JsArgs, JsResult, JsString, JsValue, @@ -449,33 +450,64 @@ impl IntrinsicObject for BuiltInFunctionObject { let throw_type_error = realm.intrinsics().objects().throw_type_error(); - BuiltInBuilder::from_standard_constructor::(realm) - .method(Self::apply, "apply", 2) - .method(Self::bind, "bind", 1) - .method(Self::call, "call", 1) - .method(Self::to_string, "toString", 0) - .property(JsSymbol::has_instance(), has_instance, Attribute::default()) - .accessor( - utf16!("caller"), - Some(throw_type_error.clone()), - Some(throw_type_error.clone()), - Attribute::CONFIGURABLE, - ) - .accessor( - utf16!("arguments"), - Some(throw_type_error.clone()), - Some(throw_type_error), - Attribute::CONFIGURABLE, - ) - .build(); + // BuiltInBuilder::from_standard_constructor::(realm) + // .method(Self::apply, "apply", 2) + // .method(Self::bind, "bind", 1) + // .method(Self::call, "call", 1) + // .method(Self::to_string, "toString", 0) + // .property(JsSymbol::has_instance(), has_instance, Attribute::default()) + // .accessor( + // utf16!("caller"), + // Some(throw_type_error.clone()), + // Some(throw_type_error.clone()), + // Attribute::CONFIGURABLE, + // ) + // .accessor( + // utf16!("arguments"), + // Some(throw_type_error.clone()), + // Some(throw_type_error), + // Attribute::CONFIGURABLE, + // ) + // .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::( + 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(); - - BuiltInBuilder::callable_with_object(realm, prototype.clone(), Self::prototype) - .name("") - .length(0) - .build(); - + { + let mut prototype = prototype.borrow_mut(); + let function = Function::new( + FunctionKind::Native { + function: NativeFunction::from_fn_ptr(Self::prototype), + constructor: (true).then_some(ConstructorKind::Base), + }, + realm.clone(), + ); + *prototype.kind_mut() = ObjectKind::Function(function); + } prototype.set_prototype(Some(realm.intrinsics().constructors().object().prototype())); } @@ -997,6 +1029,7 @@ impl BuiltInFunctionObject { Ok(JsValue::ordinary_has_instance(this, args.get_or_undefined(0), context)?.into()) } + #[allow(dead_code)] #[allow(clippy::unnecessary_wraps)] fn prototype(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { Ok(JsValue::undefined()) diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index 8c07c8e99db..9f33d920e56 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -24,10 +24,9 @@ use crate::{ error::JsNativeError, js_string, object::{JsObject, RecursionLimiter}, - property::{Attribute, PropertyNameKind}, + property::PropertyNameKind, realm::Realm, string::{utf16, CodePoint}, - symbol::JsSymbol, value::IntegerOrInfinity, Context, JsArgs, JsResult, JsString, JsValue, }; @@ -49,14 +48,14 @@ impl IntrinsicObject for Json { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let to_string_tag = JsSymbol::to_string_tag(); - let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - - BuiltInBuilder::with_intrinsic::(realm) - .static_method(Self::parse, "parse", 2) - .static_method(Self::stringify, "stringify", 3) - .static_property(to_string_tag, Self::NAME, attribute) - .build(); + BuiltInBuilder::with_intrinsic_static_shape::( + realm, + &boa_builtins::JSON_OBJECT_STATIC_SHAPE, + ) + .static_method(Self::parse, 2) + .static_method(Self::stringify, 3) + .static_property(Self::NAME) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/math/mod.rs b/boa_engine/src/builtins/math/mod.rs index 406661c5ff6..78c259b76a6 100644 --- a/boa_engine/src/builtins/math/mod.rs +++ b/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 use crate::{ - builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject, - property::Attribute, realm::Realm, string::utf16, symbol::JsSymbol, Context, JsArgs, JsResult, - JsValue, + builtins::BuiltInObject, context::intrinsics::Intrinsics, object::JsObject, realm::Realm, + Context, JsArgs, JsResult, JsValue, }; use boa_profiler::Profiler; @@ -31,61 +30,55 @@ impl IntrinsicObject for Math { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - BuiltInBuilder::with_intrinsic::(realm) - .static_property(utf16!("E"), std::f64::consts::E, attribute) - .static_property(utf16!("LN10"), std::f64::consts::LN_10, attribute) - .static_property(utf16!("LN2"), std::f64::consts::LN_2, attribute) - .static_property(utf16!("LOG10E"), std::f64::consts::LOG10_E, attribute) - .static_property(utf16!("LOG2E"), std::f64::consts::LOG2_E, attribute) - .static_property(utf16!("PI"), std::f64::consts::PI, attribute) - .static_property( - utf16!("SQRT1_2"), - std::f64::consts::FRAC_1_SQRT_2, - attribute, - ) - .static_property(utf16!("SQRT2"), std::f64::consts::SQRT_2, attribute) - .static_method(Self::abs, "abs", 1) - .static_method(Self::acos, "acos", 1) - .static_method(Self::acosh, "acosh", 1) - .static_method(Self::asin, "asin", 1) - .static_method(Self::asinh, "asinh", 1) - .static_method(Self::atan, "atan", 1) - .static_method(Self::atanh, "atanh", 1) - .static_method(Self::atan2, "atan2", 2) - .static_method(Self::cbrt, "cbrt", 1) - .static_method(Self::ceil, "ceil", 1) - .static_method(Self::clz32, "clz32", 1) - .static_method(Self::cos, "cos", 1) - .static_method(Self::cosh, "cosh", 1) - .static_method(Self::exp, "exp", 1) - .static_method(Self::expm1, "expm1", 1) - .static_method(Self::floor, "floor", 1) - .static_method(Self::fround, "fround", 1) - .static_method(Self::hypot, "hypot", 2) - .static_method(Self::imul, "imul", 2) - .static_method(Self::log, "log", 1) - .static_method(Self::log1p, "log1p", 1) - .static_method(Self::log10, "log10", 1) - .static_method(Self::log2, "log2", 1) - .static_method(Self::max, "max", 2) - .static_method(Self::min, "min", 2) - .static_method(Self::pow, "pow", 2) - .static_method(Self::random, "random", 0) - .static_method(Self::round, "round", 1) - .static_method(Self::sign, "sign", 1) - .static_method(Self::sin, "sin", 1) - .static_method(Self::sinh, "sinh", 1) - .static_method(Self::sqrt, "sqrt", 1) - .static_method(Self::tan, "tan", 1) - .static_method(Self::tanh, "tanh", 1) - .static_method(Self::trunc, "trunc", 1) - .static_property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::with_intrinsic_static_shape::( + realm, + &boa_builtins::MATH_OBJECT_STATIC_SHAPE, + ) + .static_property(std::f64::consts::E) + .static_property(std::f64::consts::LN_10) + .static_property(std::f64::consts::LN_2) + .static_property(std::f64::consts::LOG10_E) + .static_property(std::f64::consts::LOG2_E) + .static_property(std::f64::consts::PI) + .static_property(std::f64::consts::FRAC_1_SQRT_2) + .static_property(std::f64::consts::SQRT_2) + .static_method(Self::abs, 1) + .static_method(Self::acos, 1) + .static_method(Self::acosh, 1) + .static_method(Self::asin, 1) + .static_method(Self::asinh, 1) + .static_method(Self::atan, 1) + .static_method(Self::atanh, 1) + .static_method(Self::atan2, 2) + .static_method(Self::cbrt, 1) + .static_method(Self::ceil, 1) + .static_method(Self::clz32, 1) + .static_method(Self::cos, 1) + .static_method(Self::cosh, 1) + .static_method(Self::exp, 1) + .static_method(Self::expm1, 1) + .static_method(Self::floor, 1) + .static_method(Self::fround, 1) + .static_method(Self::hypot, 2) + .static_method(Self::imul, 2) + .static_method(Self::log, 1) + .static_method(Self::log1p, 1) + .static_method(Self::log10, 1) + .static_method(Self::log2, 1) + .static_method(Self::max, 2) + .static_method(Self::min, 2) + .static_method(Self::pow, 2) + .static_method(Self::random, 0) + .static_method(Self::round, 1) + .static_method(Self::sign, 1) + .static_method(Self::sin, 1) + .static_method(Self::sinh, 1) + .static_method(Self::sqrt, 1) + .static_method(Self::tan, 1) + .static_method(Self::tanh, 1) + .static_method(Self::trunc, 1) + .static_property(Self::NAME) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index e726d54a011..1d89c514555 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -39,6 +39,8 @@ pub mod escape; #[cfg(feature = "intl")] pub mod intl; +use boa_builtins::StaticShape as RawStaticShape; + pub(crate) use self::{ array::Array, async_function::AsyncFunction, @@ -95,7 +97,10 @@ use crate::{ js_string, native_function::{NativeFunction, NativeFunctionPointer}, object::{ - shape::{property_table::PropertyTableInner, slot::SlotAttributes}, + shape::{ + property_table::PropertyTableInner, slot::SlotAttributes, static_shape::StaticShape, + Shape, + }, FunctionBinding, JsFunction, JsObject, JsPrototype, Object, ObjectData, ObjectKind, CONSTRUCTOR, PROTOTYPE, }, @@ -584,19 +589,259 @@ struct BuiltInBuilder<'ctx, Kind> { prototype: JsObject, } -impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { - // fn new(realm: &'ctx Realm) -> BuiltInBuilder<'ctx, OrdinaryObject> { - // BuiltInBuilder { - // realm, - // object: BuiltInObjectInitializer::Unique { - // object: Object::default(), - // data: ObjectData::ordinary(), - // }, - // kind: OrdinaryObject, - // prototype: realm.intrinsics().constructors().object().prototype(), - // } - // } +struct BuiltInBuilderConstructorStaticShape<'ctx> { + realm: &'ctx Realm, + function: NativeFunctionPointer, + + constructor_property_index: usize, + constructor_object: JsObject, + constructor_shape: &'static RawStaticShape, + constructor_storage: Vec, + + prototype_property_index: usize, + prototype_object: JsObject, + prototype_shape: &'static RawStaticShape, + prototype_storage: Vec, + + __proto__: JsPrototype, + inherits: Option, +} + +impl<'ctx> BuiltInBuilder<'ctx, Callable> { + fn from_standard_constructor_static_shape( + 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(mut self, value: V) -> Self + where + V: Into, + { + 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 + } + + /// Adds a new data property to the constructor's prototype. + fn property(mut self, value: V) -> Self + where + V: Into, + { + 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, set: Option) -> 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, set: Option) -> 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 = Shape::r#static(StaticShape::new(self.constructor_shape)); + 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 = Shape::r#static(StaticShape::new(self.prototype_shape)); + 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, +} + +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(mut self, value: V) -> Self + where + V: Into, + { + 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 = Shape::r#static(StaticShape::new(self.shape)); + self.storage.push( + self.realm + .intrinsics() + .constructors() + .object() + .prototype() + .into(), + ); + object.properties_mut().storage = self.storage; + } +} + +impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { fn with_intrinsic( realm: &'ctx Realm, ) -> BuiltInBuilder<'ctx, OrdinaryObject> { @@ -607,6 +852,19 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { prototype: realm.intrinsics().constructors().object().prototype(), } } + + fn with_intrinsic_static_shape( + 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> { @@ -622,6 +880,7 @@ struct BuiltInConstructorWithPrototype<'ctx> { prototype_property_table: PropertyTableInner, prototype_storage: Vec, prototype: JsObject, + __proto__: JsPrototype, inherits: Option, attributes: Attribute, diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index d87e2b2732b..2e3985a1fb7 100644 --- a/boa_engine/src/builtins/number/mod.rs +++ b/boa_engine/src/builtins/number/mod.rs @@ -18,9 +18,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, - string::utf16, value::{AbstractRelation, IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, }; @@ -49,46 +47,32 @@ impl IntrinsicObject for Number { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - - BuiltInBuilder::from_standard_constructor::(realm) - .static_property(utf16!("EPSILON"), f64::EPSILON, attribute) - .static_property( - utf16!("MAX_SAFE_INTEGER"), - Self::MAX_SAFE_INTEGER, - attribute, - ) - .static_property( - utf16!("MIN_SAFE_INTEGER"), - Self::MIN_SAFE_INTEGER, - attribute, - ) - .static_property(utf16!("MAX_VALUE"), Self::MAX_VALUE, attribute) - .static_property(utf16!("MIN_VALUE"), Self::MIN_VALUE, attribute) - .static_property(utf16!("NEGATIVE_INFINITY"), f64::NEG_INFINITY, attribute) - .static_property(utf16!("POSITIVE_INFINITY"), f64::INFINITY, attribute) - .static_property(utf16!("NaN"), f64::NAN, attribute) - .static_property( - utf16!("parseInt"), - realm.intrinsics().objects().parse_int(), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .static_property( - utf16!("parseFloat"), - 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(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NUMBER_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NUMBER_PROTOTYPE_STATIC_SHAPE, + ) + .static_property(f64::EPSILON) + .static_property(Self::MAX_SAFE_INTEGER) + .static_property(Self::MIN_SAFE_INTEGER) + .static_property(Self::MAX_VALUE) + .static_property(Self::MIN_VALUE) + .static_property(f64::NEG_INFINITY) + .static_property(f64::INFINITY) + .static_property(f64::NAN) + .static_property(realm.intrinsics().objects().parse_int()) + .static_property(realm.intrinsics().objects().parse_float()) + .static_method(Self::number_is_finite, 1) + .static_method(Self::number_is_nan, 1) + .static_method(Self::is_safe_integer, 1) + .static_method(Self::number_is_integer, 1) + .method(Self::to_exponential, 1) + .method(Self::to_fixed, 1) + .method(Self::to_locale_string, 0) + .method(Self::to_precision, 1) + .method(Self::to_string, 1) + .method(Self::value_of, 0) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/object/mod.rs b/boa_engine/src/builtins/object/mod.rs index 4c4fc5202a1..d1abf0ba49d 100644 --- a/boa_engine/src/builtins/object/mod.rs +++ b/boa_engine/src/builtins/object/mod.rs @@ -26,7 +26,7 @@ use crate::{ internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, IntegrityLevel, JsObject, ObjectData, ObjectKind, }, - property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, + property::{PropertyDescriptor, PropertyKey, PropertyNameKind}, realm::Realm, string::utf16, symbol::JsSymbol, @@ -56,55 +56,46 @@ impl IntrinsicObject for Object { .name("set __proto__") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .inherits(None) - .accessor( - utf16!("__proto__"), - Some(legacy_proto_getter), - Some(legacy_setter_proto), - Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .method(Self::has_own_property, "hasOwnProperty", 1) - .method(Self::property_is_enumerable, "propertyIsEnumerable", 1) - .method(Self::to_string, "toString", 0) - .method(Self::to_locale_string, "toLocaleString", 0) - .method(Self::value_of, "valueOf", 0) - .method(Self::is_prototype_of, "isPrototypeOf", 1) - .method(Self::legacy_define_getter, "__defineGetter__", 2) - .method(Self::legacy_define_setter, "__defineSetter__", 2) - .method(Self::legacy_lookup_getter, "__lookupGetter__", 1) - .method(Self::legacy_lookup_setter, "__lookupSetter__", 1) - .static_method(Self::create, "create", 2) - .static_method(Self::set_prototype_of, "setPrototypeOf", 2) - .static_method(Self::get_prototype_of, "getPrototypeOf", 1) - .static_method(Self::define_property, "defineProperty", 3) - .static_method(Self::define_properties, "defineProperties", 2) - .static_method(Self::assign, "assign", 2) - .static_method(Self::is, "is", 2) - .static_method(Self::keys, "keys", 1) - .static_method(Self::values, "values", 1) - .static_method(Self::entries, "entries", 1) - .static_method(Self::seal, "seal", 1) - .static_method(Self::is_sealed, "isSealed", 1) - .static_method(Self::freeze, "freeze", 1) - .static_method(Self::is_frozen, "isFrozen", 1) - .static_method(Self::prevent_extensions, "preventExtensions", 1) - .static_method(Self::is_extensible, "isExtensible", 1) - .static_method( - Self::get_own_property_descriptor, - "getOwnPropertyDescriptor", - 2, - ) - .static_method( - 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(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::OBJECT_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::OBJECT_PROTOTYPE_STATIC_SHAPE, + ) + .inherits(None) + .accessor(Some(legacy_proto_getter), Some(legacy_setter_proto)) + .method(Self::has_own_property, 1) + .method(Self::property_is_enumerable, 1) + .method(Self::to_string, 0) + .method(Self::to_locale_string, 0) + .method(Self::value_of, 0) + .method(Self::is_prototype_of, 1) + .method(Self::legacy_define_getter, 2) + .method(Self::legacy_define_setter, 2) + .method(Self::legacy_lookup_getter, 1) + .method(Self::legacy_lookup_setter, 1) + .static_method(Self::create, 2) + .static_method(Self::set_prototype_of, 2) + .static_method(Self::get_prototype_of, 1) + .static_method(Self::define_property, 3) + .static_method(Self::define_properties, 2) + .static_method(Self::assign, 2) + .static_method(Self::is, 2) + .static_method(Self::keys, 1) + .static_method(Self::values, 1) + .static_method(Self::entries, 1) + .static_method(Self::seal, 1) + .static_method(Self::is_sealed, 1) + .static_method(Self::freeze, 1) + .static_method(Self::is_frozen, 1) + .static_method(Self::prevent_extensions, 1) + .static_method(Self::is_extensible, 1) + .static_method(Self::get_own_property_descriptor, 2) + .static_method(Self::get_own_property_descriptors, 1) + .static_method(Self::get_own_property_names, 1) + .static_method(Self::get_own_property_symbols, 1) + .static_method(Self::has_own, 2) + .static_method(Self::from_entries, 1) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/reflect/mod.rs b/boa_engine/src/builtins/reflect/mod.rs index a0b2807fe0d..38fb3d25905 100644 --- a/boa_engine/src/builtins/reflect/mod.rs +++ b/boa_engine/src/builtins/reflect/mod.rs @@ -16,9 +16,7 @@ use crate::{ context::intrinsics::Intrinsics, error::JsNativeError, object::JsObject, - property::Attribute, realm::Realm, - symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, }; use boa_profiler::Profiler; @@ -34,32 +32,25 @@ impl IntrinsicObject for Reflect { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let to_string_tag = JsSymbol::to_string_tag(); - - BuiltInBuilder::with_intrinsic::(realm) - .static_method(Self::apply, "apply", 3) - .static_method(Self::construct, "construct", 2) - .static_method(Self::define_property, "defineProperty", 3) - .static_method(Self::delete_property, "deleteProperty", 2) - .static_method(Self::get, "get", 2) - .static_method( - Self::get_own_property_descriptor, - "getOwnPropertyDescriptor", - 2, - ) - .static_method(Self::get_prototype_of, "getPrototypeOf", 1) - .static_method(Self::has, "has", 2) - .static_method(Self::is_extensible, "isExtensible", 1) - .static_method(Self::own_keys, "ownKeys", 1) - .static_method(Self::prevent_extensions, "preventExtensions", 1) - .static_method(Self::set, "set", 3) - .static_method(Self::set_prototype_of, "setPrototypeOf", 2) - .static_property( - to_string_tag, - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::with_intrinsic_static_shape::( + realm, + &boa_builtins::REFLECT_OBJECT_STATIC_SHAPE, + ) + .static_method(Self::apply, 3) + .static_method(Self::construct, 2) + .static_method(Self::define_property, 3) + .static_method(Self::delete_property, 2) + .static_method(Self::get, 2) + .static_method(Self::get_own_property_descriptor, 2) + .static_method(Self::get_prototype_of, 1) + .static_method(Self::has, 2) + .static_method(Self::is_extensible, 1) + .static_method(Self::own_keys, 1) + .static_method(Self::prevent_extensions, 1) + .static_method(Self::set, 3) + .static_method(Self::set_prototype_of, 2) + .static_property(Self::NAME) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/symbol/mod.rs b/boa_engine/src/builtins/symbol/mod.rs index 64a5a56632e..3dd9d070329 100644 --- a/boa_engine/src/builtins/symbol/mod.rs +++ b/boa_engine/src/builtins/symbol/mod.rs @@ -26,9 +26,7 @@ use crate::{ error::JsNativeError, js_string, object::JsObject, - property::Attribute, realm::Realm, - string::utf16, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsResult, JsString, @@ -109,8 +107,6 @@ impl IntrinsicObject for Symbol { let symbol_to_string_tag = JsSymbol::to_string_tag(); let symbol_unscopables = JsSymbol::unscopables(); - let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive) .name("[Symbol.toPrimitive]") .length(1) @@ -120,53 +116,32 @@ impl IntrinsicObject for Symbol { .name("get description") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .static_method(Self::for_, "for", 1) - .static_method(Self::key_for, "keyFor", 1) - .static_property(utf16!("asyncIterator"), symbol_async_iterator, attribute) - .static_property(utf16!("hasInstance"), symbol_has_instance, attribute) - .static_property( - utf16!("isConcatSpreadable"), - symbol_is_concat_spreadable, - attribute, - ) - .static_property(utf16!("iterator"), symbol_iterator, attribute) - .static_property(utf16!("match"), symbol_match, attribute) - .static_property(utf16!("matchAll"), symbol_match_all, attribute) - .static_property(utf16!("replace"), symbol_replace, attribute) - .static_property(utf16!("search"), symbol_search, attribute) - .static_property(utf16!("species"), symbol_species, attribute) - .static_property(utf16!("split"), symbol_split, attribute) - .static_property( - utf16!("toPrimitive"), - symbol_to_primitive.clone(), - attribute, - ) - .static_property( - utf16!("toStringTag"), - symbol_to_string_tag.clone(), - attribute, - ) - .static_property(utf16!("unscopables"), symbol_unscopables, attribute) - .method(Self::to_string, "toString", 0) - .method(Self::value_of, "valueOf", 0) - .accessor( - 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(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::SYMBOL_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::SYMBOL_PROTOTYPE_STATIC_SHAPE, + ) + .static_method(Self::for_, 1) + .static_method(Self::key_for, 1) + .static_property(symbol_async_iterator) + .static_property(symbol_has_instance) + .static_property(symbol_is_concat_spreadable) + .static_property(symbol_iterator) + .static_property(symbol_match) + .static_property(symbol_match_all) + .static_property(symbol_replace) + .static_property(symbol_search) + .static_property(symbol_species) + .static_property(symbol_split) + .static_property(symbol_to_primitive) + .static_property(symbol_to_string_tag) + .static_property(symbol_unscopables) + .method(Self::to_string, 0) + .method(Self::value_of, 0) + .accessor(Some(get_description), None) + .property(Self::NAME) + .property(to_primitive) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 879ce8dd015..c47aa4f1b4f 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -80,6 +80,20 @@ impl StandardConstructor { } } + fn with_prototype_static_shape(prototype: JsObject) -> Self { + Self { + constructor: JsFunction::empty_intrinsic_function_static_shape(true), + 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. /// /// This is the same as `Object.prototype`, `Array.prototype`, etc @@ -157,34 +171,31 @@ impl Default for StandardConstructors { fn default() -> Self { Self { async_generator_function: StandardConstructor::default(), - object: StandardConstructor::default(), + object: StandardConstructor::default_static_shape(), proxy: StandardConstructor::default(), - date: StandardConstructor::default(), + date: StandardConstructor::default_static_shape(), function: StandardConstructor { - constructor: JsFunction::empty_intrinsic_function(true), - prototype: JsFunction::empty_intrinsic_function(false).into(), + constructor: JsFunction::empty_intrinsic_function_static_shape(true), + prototype: JsFunction::empty_intrinsic_function_static_shape(false).into(), }, async_function: StandardConstructor::default(), generator_function: StandardConstructor::default(), - array: StandardConstructor::with_prototype(JsObject::from_proto_and_data( - None, - ObjectData::array(), - )), - bigint: StandardConstructor::default(), - number: StandardConstructor::with_prototype(JsObject::from_proto_and_data( - None, - ObjectData::number(0.0), - )), - boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data( - None, - ObjectData::boolean(false), - )), + array: StandardConstructor::with_prototype_static_shape( + JsObject::from_data_and_empty_static_shape(ObjectData::array()), + ), + bigint: StandardConstructor::default_static_shape(), + number: StandardConstructor::with_prototype_static_shape( + JsObject::from_data_and_empty_static_shape(ObjectData::number(0.0)), + ), + boolean: StandardConstructor::with_prototype_static_shape( + JsObject::from_data_and_empty_static_shape(ObjectData::boolean(false)), + ), string: StandardConstructor::with_prototype(JsObject::from_proto_and_data( None, ObjectData::string("".into()), )), regexp: StandardConstructor::default(), - symbol: StandardConstructor::default(), + symbol: StandardConstructor::default_static_shape(), error: StandardConstructor::default(), type_error: StandardConstructor::default(), reference_error: StandardConstructor::default(), @@ -814,9 +825,9 @@ pub struct IntrinsicObjects { impl Default for IntrinsicObjects { fn default() -> Self { Self { - reflect: JsObject::default(), - math: JsObject::default(), - json: JsObject::default(), + reflect: JsObject::default_with_static_shape(), + math: JsObject::default_with_static_shape(), + json: JsObject::default_with_static_shape(), throw_type_error: JsFunction::empty_intrinsic_function(false), array_prototype_values: JsFunction::empty_intrinsic_function(false), iterator_prototypes: IteratorPrototypes::default(), diff --git a/boa_engine/src/object/builtins/jsfunction.rs b/boa_engine/src/object/builtins/jsfunction.rs index 522242acc69..26a5fa6b988 100644 --- a/boa_engine/src/object/builtins/jsfunction.rs +++ b/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. /// /// This does not clone the fields of the function, it only does a shallow clone of the object. diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index 9ad7cd52f6a..e815e457d59 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -4,7 +4,7 @@ use super::{ internal_methods::{InternalObjectMethods, ARRAY_EXOTIC_INTERNAL_METHODS}, - shape::{shared_shape::SharedShape, Shape}, + shape::{shared_shape::SharedShape, static_shape::StaticShape, Shape}, JsPrototype, NativeObject, Object, PropertyMap, }; use crate::{ @@ -59,6 +59,12 @@ impl Default for 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. pub(crate) fn from_object_and_vtable( object: Object, @@ -119,6 +125,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( + Shape::r#static(StaticShape::new(&boa_builtins::EMPTY_OBJECT_STATIC_SHAPE)), + ThinVec::default(), + ), + extensible: true, + private_elements: ThinVec::new(), + }), + vtable: data.internal_methods, + }), + } + } + /// Creates a new object with the provided prototype and object data. /// /// This is equivalent to calling the specification's abstract operation [`OrdinaryObjectCreate`], diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index ed69064d16c..8a4aedfff37 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -24,7 +24,7 @@ use self::{ string::STRING_EXOTIC_INTERNAL_METHODS, InternalObjectMethods, ORDINARY_INTERNAL_METHODS, }, - shape::Shape, + shape::{static_shape::StaticShape, Shape}, }; #[cfg(feature = "intl")] use crate::builtins::intl::{ @@ -838,6 +838,18 @@ impl Debug for ObjectKind { } impl Object { + fn with_empty_shape() -> Self { + Self { + kind: ObjectKind::Ordinary, + properties: PropertyMap::new( + Shape::r#static(StaticShape::new(&boa_builtins::EMPTY_OBJECT_STATIC_SHAPE)), + ThinVec::default(), + ), + extensible: true, + private_elements: ThinVec::new(), + } + } + /// Returns a mutable reference to the kind of an object. pub(crate) fn kind_mut(&mut self) -> &mut ObjectKind { &mut self.kind @@ -1489,6 +1501,12 @@ impl Object { /// Gets the prototype instance of this object. #[inline] 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() } @@ -1501,7 +1519,14 @@ impl Object { pub fn set_prototype>(&mut self, prototype: O) -> bool { let prototype = prototype.into(); 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::unique(shape.to_unique(prototype)); + } else { + self.properties.shape = + self.properties.shape.change_prototype_transition(prototype); + } + true } else { // If target is non-extensible, [[SetPrototypeOf]] must return false diff --git a/boa_engine/src/object/property_map.rs b/boa_engine/src/object/property_map.rs index 90ffb50989b..4ac989cc67d 100644 --- a/boa_engine/src/object/property_map.rs +++ b/boa_engine/src/object/property_map.rs @@ -311,7 +311,21 @@ impl PropertyMap { property_key: key.clone(), 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; match transition.action { ChangeTransitionAction::Nothing => {} @@ -346,6 +360,16 @@ impl PropertyMap { return true; } + if let Some(shape) = self.shape.as_static() { + let prototype = self + .storage + .pop() + .as_ref() + .and_then(JsValue::as_object) + .cloned(); + self.shape = Shape::unique(shape.to_unique(prototype)); + } + let transition_key = TransitionKey { property_key: key.clone(), attributes, @@ -390,6 +414,16 @@ impl PropertyMap { return self.indexed_properties.remove(*index); } 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::unique(shape.to_unique(prototype)); + } + // shift all elements when removing. if slot.attributes.is_accessor_descriptor() { self.storage.remove(slot.index as usize + 1); diff --git a/boa_engine/src/object/shape/mod.rs b/boa_engine/src/object/shape/mod.rs index 0d7f6e8c13e..fcd21d986a0 100644 --- a/boa_engine/src/object/shape/mod.rs +++ b/boa_engine/src/object/shape/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod property_table; pub(crate) mod shared_shape; pub(crate) mod slot; +pub(crate) mod static_shape; pub(crate) mod unique_shape; pub use shared_shape::SharedShape; @@ -14,7 +15,7 @@ use boa_gc::{Finalize, Trace}; use crate::property::PropertyKey; -use self::{shared_shape::TransitionKey, slot::Slot}; +use self::{shared_shape::TransitionKey, slot::Slot, static_shape::StaticShape}; use super::JsPrototype; @@ -54,6 +55,7 @@ pub(crate) struct ChangeTransition { enum Inner { Unique(UniqueShape), Shared(SharedShape), + Static(StaticShape), } /// Represents the shape of an object. @@ -89,18 +91,39 @@ impl Shape { } } + /// Create a [`Shape`] from a [`StaticShape`]. + pub(crate) const fn r#static(shape: StaticShape) -> Self { + Self { + inner: Inner::Static(shape), + } + } + /// Returns `true` if it's a shared shape, `false` otherwise. #[inline] pub const fn is_shared(&self) -> bool { 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. #[inline] pub const fn is_unique(&self) -> bool { 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> { if let Inner::Unique(shape) = &self.inner { return Some(shape); @@ -121,6 +144,7 @@ impl Shape { Self::shared(shape) } Inner::Unique(shape) => Self::unique(shape.insert_property_transition(key)), + Inner::Static(shape) => Self::unique(shape.insert_property_transition(key)), } } @@ -147,6 +171,7 @@ impl Shape { } } Inner::Unique(shape) => shape.change_attributes_transition(&key), + Inner::Static(shape) => shape.change_attributes_transition(&key), } } @@ -163,6 +188,7 @@ impl Shape { Self::shared(shape) } Inner::Unique(shape) => Self::unique(shape.remove_property_transition(key)), + Inner::Static(shape) => Self::unique(shape.remove_property_transition(key)), } } @@ -177,14 +203,16 @@ impl Shape { Self::shared(shape) } Inner::Unique(shape) => Self::unique(shape.change_prototype_transition(prototype)), + Inner::Static(shape) => Self::unique(shape.change_prototype_transition(prototype)), } } /// Get the [`JsPrototype`] of the [`Shape`]. - pub fn prototype(&self) -> JsPrototype { + pub(crate) fn prototype(&self) -> JsPrototype { match &self.inner { Inner::Shared(shape) => shape.prototype(), Inner::Unique(shape) => shape.prototype(), + Inner::Static(_) => unreachable!("Static shapes don't have prototypes in them"), } } @@ -194,6 +222,7 @@ impl Shape { match &self.inner { Inner::Shared(shape) => shape.lookup(key), Inner::Unique(shape) => shape.lookup(key), + Inner::Static(shape) => shape.lookup(key), } } @@ -203,6 +232,7 @@ impl Shape { match &self.inner { Inner::Shared(shape) => shape.keys(), Inner::Unique(shape) => shape.keys(), + Inner::Static(shape) => shape.keys(), } } @@ -212,6 +242,7 @@ impl Shape { match &self.inner { Inner::Shared(shape) => shape.to_addr_usize(), Inner::Unique(shape) => shape.to_addr_usize(), + Inner::Static(shape) => shape.to_addr_usize(), } } } diff --git a/boa_engine/src/object/shape/slot.rs b/boa_engine/src/object/shape/slot.rs index b219d08fd24..6aab8b7484a 100644 --- a/boa_engine/src/object/shape/slot.rs +++ b/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`, /// 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) index: SlotIndex, pub(crate) attributes: SlotAttributes, diff --git a/boa_engine/src/object/shape/static_shape.rs b/boa_engine/src/object/shape/static_shape.rs new file mode 100644 index 00000000000..9b88f61dae7 --- /dev/null +++ b/boa_engine/src/object/shape/static_shape.rs @@ -0,0 +1,145 @@ +// Temp allow +#![allow(dead_code)] +#![allow(clippy::needless_pass_by_value)] + +use std::fmt::Debug; + +use boa_builtins::StaticPropertyKey; +use boa_gc::{Finalize, Trace}; + +use crate::{ + object::shape::property_table::PropertyTableInner, property::PropertyKey, symbol::WellKnown, + 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(s) => PropertyKey::from(*s), + 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> { + match key { + PropertyKey::String(s) => Some(StaticPropertyKey::String(s.as_slice())), + 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 { + let key = to_static_property_key(key)?; + + let (index, attributes) = self.inner.get(key)?; + + Some(Slot { + 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 { + 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 { + self.inner + .property_table + .keys() + .map(from_static_property_key) + .collect() + } + + /// TODO: doc + pub(crate) fn to_unique(&self, prototype: JsPrototype) -> UniqueShape { + // TODO: optimization, preallocate capacity. + let mut property_table = PropertyTableInner::default(); + for (key, (_, slot_attributes)) in &self.inner.property_table { + property_table.insert( + from_static_property_key(key), + 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 + } +} diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index 925dd6f9e03..423fed64626 100644 --- a/boa_engine/src/symbol.rs +++ b/boa_engine/src/symbol.rs @@ -61,7 +61,7 @@ fn get_id() -> Option { /// List of well known symbols. #[derive(Debug, Clone, Copy, TryFromPrimitive, IntoPrimitive)] #[repr(u8)] -enum WellKnown { +pub(crate) enum WellKnown { AsyncIterator, HasInstance, IsConcatSpreadable, diff --git a/boa_engine/src/vm/opcode/set/property.rs b/boa_engine/src/vm/opcode/set/property.rs index c3307358887..5d3a3ba6cc0 100644 --- a/boa_engine/src/vm/opcode/set/property.rs +++ b/boa_engine/src/vm/opcode/set/property.rs @@ -93,9 +93,11 @@ impl Operation for SetPropertyByValue { // Cannot use fast path if the [[prototype]] is a proxy object, // because we have to the call prototypes [[set]] on non-existing property, // and proxy objects can override [[set]]. - let prototype = shape.prototype(); - if prototype.map_or(false, |x| x.is_proxy()) { - break 'fast_path; + if !shape.is_static() { + let prototype = shape.prototype(); + if prototype.map_or(false, |x| x.is_proxy()) { + break 'fast_path; + } } dense_elements.push(value.clone()); From 0107a93f3085a4e9492e4409bae64f0be2d58c04 Mon Sep 17 00:00:00 2001 From: Haled Odat Date: Mon, 1 May 2023 12:19:33 +0200 Subject: [PATCH 2/5] Add static shape to String --- boa_builtins/Cargo.toml | 3 + boa_builtins/build.rs | 74 ++++++++++++++ boa_engine/Cargo.toml | 2 +- boa_engine/src/builtins/string/mod.rs | 138 ++++++++++++-------------- 4 files changed, 142 insertions(+), 75 deletions(-) diff --git a/boa_builtins/Cargo.toml b/boa_builtins/Cargo.toml index c047486599a..099b7df8271 100644 --- a/boa_builtins/Cargo.toml +++ b/boa_builtins/Cargo.toml @@ -10,6 +10,9 @@ repository.workspace = true rust-version.workspace = true build = "build.rs" +[features] +annex-b = [] + [dependencies] bitflags = "2.1.0" phf = "^0.11.1" diff --git a/boa_builtins/build.rs b/boa_builtins/build.rs index 9f59d9dcf0e..a9e9dc6faa5 100644 --- a/boa_builtins/build.rs +++ b/boa_builtins/build.rs @@ -584,5 +584,79 @@ fn main() -> io::Result<()> { .property(WellKnown::ToPrimitive, Attribute::CONFIGURABLE) .build(file)?; + let mut builder = BuiltInBuilderConstructor::new("STRING"); + builder + .property(utf16!("length"), Attribute::empty()) + .property( + utf16!("trimStart"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("trimEnd"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .static_method(utf16!("raw")) + .static_method(utf16!("fromCharCode")) + .static_method(utf16!("fromCodePoint")) + .method(utf16!("charAt")) + .method(utf16!("charCodeAt")) + .method(utf16!("codePointAt")) + .method(utf16!("toString")) + .method(utf16!("concat")) + .method(utf16!("repeat")) + .method(utf16!("slice")) + .method(utf16!("startsWith")) + .method(utf16!("endsWith")) + .method(utf16!("includes")) + .method(utf16!("indexOf")) + .method(utf16!("lastIndexOf")) + .method(utf16!("localeCompare")) + .method(utf16!("match")) + .method(utf16!("normalize")) + .method(utf16!("padEnd")) + .method(utf16!("padStart")) + .method(utf16!("trim")) + .method(utf16!("toLowerCase")) + .method(utf16!("toUpperCase")) + .method(utf16!("toLocaleLowerCase")) + .method(utf16!("toLocaleUpperCase")) + .method(utf16!("substring")) + .method(utf16!("split")) + .method(utf16!("valueOf")) + .method(utf16!("matchAll")) + .method(utf16!("replace")) + .method(utf16!("replaceAll")) + .method(WellKnown::Iterator) + .method(utf16!("search")) + .method(utf16!("at")); + + #[cfg(feature = "annex-b")] + { + builder + .property( + utf16!("trimLeft"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("trimRight"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .method(utf16!("substr")) + .method(utf16!("anchor")) + .method(utf16!("big")) + .method(utf16!("blink")) + .method(utf16!("bold")) + .method(utf16!("fixed")) + .method(utf16!("fontcolor")) + .method(utf16!("fontsize")) + .method(utf16!("italics")) + .method(utf16!("link")) + .method(utf16!("small")) + .method(utf16!("strike")) + .method(utf16!("sub")) + .method(utf16!("sup")); + } + builder.build(file)?; + Ok(()) } diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index b9f70cce376..603a10b2b23 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -43,7 +43,7 @@ flowgraph = [] trace = [] # 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] boa_interner.workspace = true diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index c8380283ecf..045c9b0bb14 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -15,7 +15,7 @@ use crate::{ error::JsNativeError, js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::{Attribute, PropertyDescriptor}, + property::PropertyDescriptor, realm::Realm, string::utf16, string::{CodePoint, Utf16Trim}, @@ -78,8 +78,6 @@ impl IntrinsicObject for String { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let symbol_iterator = JsSymbol::iterator(); - let trim_start = BuiltInBuilder::callable(realm, Self::trim_start) .length(0) .name("trimStart") @@ -90,87 +88,79 @@ impl IntrinsicObject for String { .name("trimEnd") .build(); + let iterator = BuiltInBuilder::callable(realm, Self::iterator) + .length(0) + .name("[Symbol.iterator]") + .build(); + #[cfg(feature = "annex-b")] let trim_left = trim_start.clone(); #[cfg(feature = "annex-b")] let trim_right = trim_end.clone(); - let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - let builder = BuiltInBuilder::from_standard_constructor::(realm) - .property(utf16!("length"), 0, attribute) - .property( - utf16!("trimStart"), - trim_start, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - utf16!("trimEnd"), - trim_end, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .static_method(Self::raw, "raw", 1) - .static_method(Self::from_char_code, "fromCharCode", 1) - .static_method(Self::from_code_point, "fromCodePoint", 1) - .method(Self::char_at, "charAt", 1) - .method(Self::char_code_at, "charCodeAt", 1) - .method(Self::code_point_at, "codePointAt", 1) - .method(Self::to_string, "toString", 0) - .method(Self::concat, "concat", 1) - .method(Self::repeat, "repeat", 1) - .method(Self::slice, "slice", 2) - .method(Self::starts_with, "startsWith", 1) - .method(Self::ends_with, "endsWith", 1) - .method(Self::includes, "includes", 1) - .method(Self::index_of, "indexOf", 1) - .method(Self::last_index_of, "lastIndexOf", 1) - .method(Self::locale_compare, "localeCompare", 1) - .method(Self::r#match, "match", 1) - .method(Self::normalize, "normalize", 0) - .method(Self::pad_end, "padEnd", 1) - .method(Self::pad_start, "padStart", 1) - .method(Self::trim, "trim", 0) - .method(Self::to_case::, "toLowerCase", 0) - .method(Self::to_case::, "toUpperCase", 0) - .method(Self::to_locale_case::, "toLocaleLowerCase", 0) - .method(Self::to_locale_case::, "toLocaleUpperCase", 0) - .method(Self::substring, "substring", 2) - .method(Self::split, "split", 2) - .method(Self::value_of, "valueOf", 0) - .method(Self::match_all, "matchAll", 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); + let builder = BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::STRING_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::STRING_PROTOTYPE_STATIC_SHAPE, + ) + .property(0) + .property(trim_start) + .property(trim_end) + .static_method(Self::raw, 1) + .static_method(Self::from_char_code, 1) + .static_method(Self::from_code_point, 1) + .method(Self::char_at, 1) + .method(Self::char_code_at, 1) + .method(Self::code_point_at, 1) + .method(Self::to_string, 0) + .method(Self::concat, 1) + .method(Self::repeat, 1) + .method(Self::slice, 2) + .method(Self::starts_with, 1) + .method(Self::ends_with, 1) + .method(Self::includes, 1) + .method(Self::index_of, 1) + .method(Self::last_index_of, 1) + .method(Self::locale_compare, 1) + .method(Self::r#match, 1) + .method(Self::normalize, 0) + .method(Self::pad_end, 1) + .method(Self::pad_start, 1) + .method(Self::trim, 0) + .method(Self::to_case::, 0) + .method(Self::to_case::, 0) + .method(Self::to_locale_case::, 0) + .method(Self::to_locale_case::, 0) + .method(Self::substring, 2) + .method(Self::split, 2) + .method(Self::value_of, 0) + .method(Self::match_all, 1) + .method(Self::replace, 2) + .method(Self::replace_all, 2) + .property(iterator) + .method(Self::search, 1) + .method(Self::at, 1); #[cfg(feature = "annex-b")] { builder - .property( - utf16!("trimLeft"), - trim_left, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - utf16!("trimRight"), - trim_right, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .method(Self::substr, "substr", 2) - .method(Self::anchor, "anchor", 1) - .method(Self::big, "big", 0) - .method(Self::blink, "blink", 0) - .method(Self::bold, "bold", 0) - .method(Self::fixed, "fixed", 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) + .property(trim_left) + .property(trim_right) + .method(Self::substr, 2) + .method(Self::anchor, 1) + .method(Self::big, 0) + .method(Self::blink, 0) + .method(Self::bold, 0) + .method(Self::fixed, 0) + .method(Self::fontcolor, 1) + .method(Self::fontsize, 1) + .method(Self::italics, 0) + .method(Self::link, 1) + .method(Self::small, 0) + .method(Self::strike, 0) + .method(Self::sub, 0) + .method(Self::sup, 0) .build(); } From f49f1f5d3a52e2f336a1db33857a6f7c78a2ce50 Mon Sep 17 00:00:00 2001 From: Haled Odat Date: Mon, 1 May 2023 13:02:43 +0200 Subject: [PATCH 3/5] Add static shape to RegExp --- boa_builtins/build.rs | 27 ++++++++++ boa_engine/src/builtins/mod.rs | 16 ++++++ boa_engine/src/builtins/regexp/mod.rs | 75 ++++++++++----------------- boa_engine/src/builtins/string/mod.rs | 7 +-- boa_engine/src/context/intrinsics.rs | 26 ++++------ 5 files changed, 80 insertions(+), 71 deletions(-) diff --git a/boa_builtins/build.rs b/boa_builtins/build.rs index a9e9dc6faa5..8adcda513e6 100644 --- a/boa_builtins/build.rs +++ b/boa_builtins/build.rs @@ -658,5 +658,32 @@ fn main() -> io::Result<()> { } builder.build(file)?; + let mut regexp = BuiltInBuilderConstructor::new("REGEXP"); + regexp + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .property(utf16!("lastIndex"), Attribute::all()) + .method(utf16!("test")) + .method(utf16!("exec")) + .method(utf16!("toString")) + .method(WellKnown::Match) + .method(WellKnown::MatchAll) + .method(WellKnown::Replace) + .method(WellKnown::Search) + .method(WellKnown::Split) + .accessor(utf16!("hasIndices"), Attribute::CONFIGURABLE) + .accessor(utf16!("global"), Attribute::CONFIGURABLE) + .accessor(utf16!("ignoreCase"), Attribute::CONFIGURABLE) + .accessor(utf16!("multiline"), Attribute::CONFIGURABLE) + .accessor(utf16!("dotAll"), Attribute::CONFIGURABLE) + .accessor(utf16!("unicode"), Attribute::CONFIGURABLE) + .accessor(utf16!("sticky"), Attribute::CONFIGURABLE) + .accessor(utf16!("flags"), Attribute::CONFIGURABLE) + .accessor(utf16!("source"), Attribute::CONFIGURABLE); + + #[cfg(feature = "annex-b")] + regexp.method(utf16!("compile")); + + regexp.build(file)?; + Ok(()) } diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 1d89c514555..dd1fa1807a0 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -700,6 +700,22 @@ impl BuiltInBuilderConstructorStaticShape<'_> { 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(mut self, value: V) -> Self where diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 95e1daff127..02b17000c2e 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -18,7 +18,7 @@ use crate::{ internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind, CONSTRUCTOR, }, - property::{Attribute, PropertyDescriptorBuilder}, + property::PropertyDescriptorBuilder, realm::Realm, string::{utf16, CodePoint}, symbol::JsSymbol, @@ -55,8 +55,6 @@ impl IntrinsicObject for RegExp { .name("get [Symbol.species]") .build(); - let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_has_indices = BuiltInBuilder::callable(realm, Self::get_has_indices) .name("get hasIndices") .build(); @@ -84,53 +82,34 @@ impl IntrinsicObject for RegExp { let get_source = BuiltInBuilder::callable(realm, Self::get_source) .name("get source") .build(); - let regexp = BuiltInBuilder::from_standard_constructor::(realm) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .property(utf16!("lastIndex"), 0, Attribute::all()) - .method(Self::test, "test", 1) - .method(Self::exec, "exec", 1) - .method(Self::to_string, "toString", 0) - .method(Self::r#match, (JsSymbol::r#match(), "[Symbol.match]"), 1) - .method( - Self::match_all, - (JsSymbol::match_all(), "[Symbol.matchAll]"), - 1, - ) - .method(Self::replace, (JsSymbol::replace(), "[Symbol.replace]"), 2) - .method(Self::search, (JsSymbol::search(), "[Symbol.search]"), 1) - .method(Self::split, (JsSymbol::split(), "[Symbol.split]"), 2) - .accessor( - utf16!("hasIndices"), - Some(get_has_indices), - None, - flag_attributes, - ) - .accessor(utf16!("global"), Some(get_global), None, flag_attributes) - .accessor( - utf16!("ignoreCase"), - Some(get_ignore_case), - None, - flag_attributes, - ) - .accessor( - utf16!("multiline"), - Some(get_multiline), - None, - flag_attributes, - ) - .accessor(utf16!("dotAll"), Some(get_dot_all), None, flag_attributes) - .accessor(utf16!("unicode"), Some(get_unicode), None, flag_attributes) - .accessor(utf16!("sticky"), Some(get_sticky), None, flag_attributes) - .accessor(utf16!("flags"), Some(get_flags), None, flag_attributes) - .accessor(utf16!("source"), Some(get_source), None, flag_attributes); + + let regexp = BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::REGEXP_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::REGEXP_PROTOTYPE_STATIC_SHAPE, + ) + .static_accessor(Some(get_species), None) + .property(0) + .method(Self::test, 1) + .method(Self::exec, 1) + .method(Self::to_string, 0) + .method_with_name(Self::r#match, js_string!("[Symbol.match]"), 1) + .method_with_name(Self::match_all, js_string!("[Symbol.matchAll]"), 1) + .method_with_name(Self::replace, js_string!("[Symbol.replace]"), 2) + .method_with_name(Self::search, js_string!("[Symbol.search]"), 1) + .method_with_name(Self::split, js_string!("[Symbol.split]"), 2) + .accessor(Some(get_has_indices), None) + .accessor(Some(get_global), None) + .accessor(Some(get_ignore_case), None) + .accessor(Some(get_multiline), None) + .accessor(Some(get_dot_all), None) + .accessor(Some(get_unicode), None) + .accessor(Some(get_sticky), None) + .accessor(Some(get_flags), None) + .accessor(Some(get_source), None); #[cfg(feature = "annex-b")] - let regexp = regexp.method(Self::compile, "compile", 2); + let regexp = regexp.method(Self::compile, 2); regexp.build(); } diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index 045c9b0bb14..ee29b63be2d 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -88,11 +88,6 @@ impl IntrinsicObject for String { .name("trimEnd") .build(); - let iterator = BuiltInBuilder::callable(realm, Self::iterator) - .length(0) - .name("[Symbol.iterator]") - .build(); - #[cfg(feature = "annex-b")] let trim_left = trim_start.clone(); @@ -138,7 +133,7 @@ impl IntrinsicObject for String { .method(Self::match_all, 1) .method(Self::replace, 2) .method(Self::replace_all, 2) - .property(iterator) + .method_with_name(Self::iterator, js_string!("[Symbol.iterator]"), 0) .method(Self::search, 1) .method(Self::at, 1); diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index c47aa4f1b4f..971e7e83f47 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -74,13 +74,6 @@ impl Default for StandardConstructor { impl StandardConstructor { /// Build a constructor with a defined prototype. fn with_prototype(prototype: JsObject) -> Self { - Self { - constructor: JsFunction::empty_intrinsic_function(true), - prototype, - } - } - - fn with_prototype_static_shape(prototype: JsObject) -> Self { Self { constructor: JsFunction::empty_intrinsic_function_static_shape(true), prototype, @@ -180,21 +173,20 @@ impl Default for StandardConstructors { }, async_function: StandardConstructor::default(), generator_function: StandardConstructor::default(), - array: StandardConstructor::with_prototype_static_shape( - JsObject::from_data_and_empty_static_shape(ObjectData::array()), - ), + array: StandardConstructor::with_prototype(JsObject::from_data_and_empty_static_shape( + ObjectData::array(), + )), bigint: StandardConstructor::default_static_shape(), - number: StandardConstructor::with_prototype_static_shape( + number: StandardConstructor::with_prototype( JsObject::from_data_and_empty_static_shape(ObjectData::number(0.0)), ), - boolean: StandardConstructor::with_prototype_static_shape( + boolean: StandardConstructor::with_prototype( JsObject::from_data_and_empty_static_shape(ObjectData::boolean(false)), ), - string: StandardConstructor::with_prototype(JsObject::from_proto_and_data( - None, - ObjectData::string("".into()), - )), - regexp: StandardConstructor::default(), + string: StandardConstructor::with_prototype( + JsObject::from_data_and_empty_static_shape(ObjectData::string("".into())), + ), + regexp: StandardConstructor::default_static_shape(), symbol: StandardConstructor::default_static_shape(), error: StandardConstructor::default(), type_error: StandardConstructor::default(), From 2b19bb59edec31f449bf178f247f12b595efed5a Mon Sep 17 00:00:00 2001 From: Haled Odat Date: Mon, 1 May 2023 13:23:28 +0200 Subject: [PATCH 4/5] Add static shape to Error --- boa_builtins/build.rs | 7 +++++++ boa_engine/src/builtins/error/mod.rs | 16 +++++++++------- boa_engine/src/context/intrinsics.rs | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/boa_builtins/build.rs b/boa_builtins/build.rs index 8adcda513e6..66dcc4aa2f1 100644 --- a/boa_builtins/build.rs +++ b/boa_builtins/build.rs @@ -685,5 +685,12 @@ fn main() -> io::Result<()> { regexp.build(file)?; + let attribute = Attribute::WRITABLE | Attribute::CONFIGURABLE; + BuiltInBuilderConstructor::new("ERROR") + .property(utf16!("name"), attribute) + .property(utf16!("message"), attribute) + .method(utf16!("toString")) + .build(file)?; + Ok(()) } diff --git a/boa_engine/src/builtins/error/mod.rs b/boa_engine/src/builtins/error/mod.rs index 0430ddd26f0..4f77430e2ce 100644 --- a/boa_engine/src/builtins/error/mod.rs +++ b/boa_engine/src/builtins/error/mod.rs @@ -16,7 +16,6 @@ use crate::{ error::JsNativeError, js_string, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, Context, JsArgs, JsResult, JsValue, @@ -131,12 +130,15 @@ impl IntrinsicObject for Error { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .method(Self::to_string, "toString", 0) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .property(Self::NAME) + .property(js_string!("")) + .method(Self::to_string, 0) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 971e7e83f47..e855660a9e2 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -188,7 +188,7 @@ impl Default for StandardConstructors { ), regexp: StandardConstructor::default_static_shape(), symbol: StandardConstructor::default_static_shape(), - error: StandardConstructor::default(), + error: StandardConstructor::default_static_shape(), type_error: StandardConstructor::default(), reference_error: StandardConstructor::default(), range_error: StandardConstructor::default(), From 34b540de5ed52bff771a021eee7fb05801607813 Mon Sep 17 00:00:00 2001 From: Haled Odat Date: Mon, 1 May 2023 13:50:01 +0200 Subject: [PATCH 5/5] Add static shape to all `NativeError`s --- boa_builtins/build.rs | 6 ++++++ boa_engine/src/builtins/error/aggregate.rs | 21 ++++++++++++--------- boa_engine/src/builtins/error/eval.rs | 20 +++++++++++--------- boa_engine/src/builtins/error/range.rs | 20 +++++++++++--------- boa_engine/src/builtins/error/reference.rs | 20 +++++++++++--------- boa_engine/src/builtins/error/syntax.rs | 20 +++++++++++--------- boa_engine/src/builtins/error/type.rs | 19 +++++++++++-------- boa_engine/src/builtins/error/uri.rs | 20 +++++++++++--------- 8 files changed, 84 insertions(+), 62 deletions(-) diff --git a/boa_builtins/build.rs b/boa_builtins/build.rs index 66dcc4aa2f1..50dbe7cbf33 100644 --- a/boa_builtins/build.rs +++ b/boa_builtins/build.rs @@ -692,5 +692,11 @@ fn main() -> io::Result<()> { .method(utf16!("toString")) .build(file)?; + let attribute = Attribute::WRITABLE | Attribute::CONFIGURABLE; + BuiltInBuilderConstructor::new("NATIVE_ERROR") + .property(utf16!("name"), attribute) + .property(utf16!("message"), attribute) + .build(file)?; + Ok(()) } diff --git a/boa_engine/src/builtins/error/aggregate.rs b/boa_engine/src/builtins/error/aggregate.rs index f9ce1fd70fd..0adbdf20d7e 100644 --- a/boa_engine/src/builtins/error/aggregate.rs +++ b/boa_engine/src/builtins/error/aggregate.rs @@ -14,10 +14,10 @@ use crate::{ }, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::{Attribute, PropertyDescriptorBuilder}, + property::PropertyDescriptorBuilder, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; @@ -30,13 +30,16 @@ impl IntrinsicObject for AggregateError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/error/eval.rs b/boa_engine/src/builtins/error/eval.rs index ac3d915ed5f..d59ff8c2eed 100644 --- a/boa_engine/src/builtins/error/eval.rs +++ b/boa_engine/src/builtins/error/eval.rs @@ -15,10 +15,9 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; @@ -32,13 +31,16 @@ impl IntrinsicObject for EvalError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/error/range.rs b/boa_engine/src/builtins/error/range.rs index 36c657b9c68..921c3c9140d 100644 --- a/boa_engine/src/builtins/error/range.rs +++ b/boa_engine/src/builtins/error/range.rs @@ -13,10 +13,9 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; @@ -30,13 +29,16 @@ impl IntrinsicObject for RangeError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/error/reference.rs b/boa_engine/src/builtins/error/reference.rs index e72dd09883b..dad820a4a05 100644 --- a/boa_engine/src/builtins/error/reference.rs +++ b/boa_engine/src/builtins/error/reference.rs @@ -13,10 +13,9 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; @@ -29,13 +28,16 @@ impl IntrinsicObject for ReferenceError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/error/syntax.rs b/boa_engine/src/builtins/error/syntax.rs index 444e32a3d78..6ede2d51b45 100644 --- a/boa_engine/src/builtins/error/syntax.rs +++ b/boa_engine/src/builtins/error/syntax.rs @@ -15,10 +15,9 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; @@ -32,13 +31,16 @@ impl IntrinsicObject for SyntaxError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/error/type.rs b/boa_engine/src/builtins/error/type.rs index 87cdc6fae5e..ed2e2cca8e0 100644 --- a/boa_engine/src/builtins/error/type.rs +++ b/boa_engine/src/builtins/error/type.rs @@ -26,7 +26,7 @@ use crate::{ property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, NativeFunction, + Context, JsArgs, JsResult, JsString, JsValue, NativeFunction, }; use boa_profiler::Profiler; @@ -40,13 +40,16 @@ impl IntrinsicObject for TypeError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/error/uri.rs b/boa_engine/src/builtins/error/uri.rs index 34a7514d6cf..4b8aed9416d 100644 --- a/boa_engine/src/builtins/error/uri.rs +++ b/boa_engine/src/builtins/error/uri.rs @@ -14,10 +14,9 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; @@ -31,13 +30,16 @@ impl IntrinsicObject for UriError { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) - .prototype(realm.intrinsics().constructors().error().constructor()) - .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), "", attribute) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::NATIVE_ERROR_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::NATIVE_ERROR_PROTOTYPE_STATIC_SHAPE, + ) + .prototype(realm.intrinsics().constructors().error().constructor()) + .inherits(Some(realm.intrinsics().constructors().error().prototype())) + .property(Self::NAME) + .property(JsString::default()) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject {