From 85b2861b3ec1c52bc0a21dfea7ccf43c74669059 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Fri, 5 May 2023 18:58:36 +0200 Subject: [PATCH] Implement static shapes --- Cargo.lock | 22 + Cargo.toml | 2 + boa_builtins/Cargo.toml | 25 + boa_builtins/README.md | 1 + boa_builtins/build.rs | 1485 ++++++++++++++++++ boa_builtins/src/lib.rs | 181 +++ boa_cli/src/debug/shape.rs | 6 +- boa_engine/Cargo.toml | 3 +- boa_engine/src/builtins/array/mod.rs | 118 +- boa_engine/src/builtins/array_buffer/mod.rs | 37 +- boa_engine/src/builtins/bigint/mod.rs | 24 +- boa_engine/src/builtins/boolean/mod.rs | 12 +- boa_engine/src/builtins/dataview/mod.rs | 75 +- boa_engine/src/builtins/date/mod.rs | 122 +- boa_engine/src/builtins/error/aggregate.rs | 21 +- boa_engine/src/builtins/error/eval.rs | 20 +- boa_engine/src/builtins/error/mod.rs | 16 +- 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 +- boa_engine/src/builtins/escape/mod.rs | 4 +- boa_engine/src/builtins/eval/mod.rs | 2 +- boa_engine/src/builtins/function/mod.rs | 93 +- boa_engine/src/builtins/json/mod.rs | 19 +- boa_engine/src/builtins/map/mod.rs | 59 +- boa_engine/src/builtins/math/mod.rs | 109 +- boa_engine/src/builtins/mod.rs | 371 ++++- boa_engine/src/builtins/number/globals.rs | 8 +- boa_engine/src/builtins/number/mod.rs | 68 +- boa_engine/src/builtins/object/mod.rs | 91 +- boa_engine/src/builtins/promise/mod.rs | 43 +- boa_engine/src/builtins/reflect/mod.rs | 47 +- boa_engine/src/builtins/regexp/mod.rs | 75 +- boa_engine/src/builtins/set/mod.rs | 61 +- boa_engine/src/builtins/string/mod.rs | 133 +- boa_engine/src/builtins/symbol/mod.rs | 77 +- boa_engine/src/builtins/typed_array/mod.rs | 164 +- boa_engine/src/builtins/uri/mod.rs | 16 +- boa_engine/src/builtins/weak/weak_ref.rs | 18 +- boa_engine/src/builtins/weak_map/mod.rs | 23 +- boa_engine/src/builtins/weak_set/mod.rs | 21 +- boa_engine/src/context/intrinsics.rs | 130 +- 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 | 38 +- boa_engine/src/object/shape/mod.rs | 36 +- boa_engine/src/object/shape/slot.rs | 2 +- boa_engine/src/object/shape/static_shape.rs | 149 ++ boa_engine/src/string/common.rs | 465 +----- boa_engine/src/string/mod.rs | 22 + boa_engine/src/symbol.rs | 2 +- boa_engine/src/vm/opcode/set/property.rs | 8 +- 55 files changed, 3179 insertions(+), 1511 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 3fc4a3bbfb..48314baf52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,6 +377,17 @@ dependencies = [ "serde", ] +[[package]] +name = "boa_builtins" +version = "0.16.0" +dependencies = [ + "bitflags 2.3.1", + "boa_macros", + "phf", + "phf_codegen", + "phf_shared", +] + [[package]] name = "boa_cli" version = "0.16.0" @@ -403,6 +414,7 @@ version = "0.16.0" dependencies = [ "bitflags 2.3.1", "boa_ast", + "boa_builtins", "boa_gc", "boa_icu_provider", "boa_interner", @@ -3018,6 +3030,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 d47cb01a06..c31054aeb7 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 0000000000..099b7df827 --- /dev/null +++ b/boa_builtins/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "boa_builtins" +description = "Builtins of the Boa JavaScript engine." +publish = true +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +build = "build.rs" + +[features] +annex-b = [] + +[dependencies] +bitflags = "2.1.0" +phf = "^0.11.1" +phf_shared = "^0.11.1" + +[build-dependencies] +boa_macros.workspace = true +phf_codegen = "^0.11.1" +phf_shared = "^0.11.1" +bitflags = "2.1.0" diff --git a/boa_builtins/README.md b/boa_builtins/README.md new file mode 100644 index 0000000000..29b26df135 --- /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 0000000000..20990dfa05 --- /dev/null +++ b/boa_builtins/build.rs @@ -0,0 +1,1485 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::fmt::Debug; +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, +} + +pub struct EncodedStaticPropertyKey(u16); + +impl EncodedStaticPropertyKey { + #[inline] + pub fn decode(&self) -> StaticPropertyKey { + let value = self.0 >> 1; + if self.0 & 1 == 0 { + StaticPropertyKey::String(value) + } else { + StaticPropertyKey::Symbol(value as u8) + } + } +} + +const fn string(index: u16) -> EncodedStaticPropertyKey { + debug_assert!(index < 2u16.pow(15)); + + EncodedStaticPropertyKey(index << 1) +} + +const fn symbol(index: u8) -> EncodedStaticPropertyKey { + EncodedStaticPropertyKey(((index as u16) << 1) | 1) +} + +impl Debug for EncodedStaticPropertyKey { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.decode().fmt(f) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum StaticPropertyKey { + String(u16), + Symbol(u8), +} + +impl StaticPropertyKey { + #[inline] + pub fn encode(self) -> EncodedStaticPropertyKey { + match self { + StaticPropertyKey::String(x) => string(x), + StaticPropertyKey::Symbol(x) => symbol(x), + } + } +} + +impl Debug for StaticPropertyKey { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + StaticPropertyKey::String(index) => { + let string = RAW_STATICS[index as usize]; + let string = String::from_utf16_lossy(string); + write!(f, "String(\"{string}\")") + } + StaticPropertyKey::Symbol(symbol) => { + write!(f, "Symbol({symbol})") + } + } + } +} + +impl Eq for EncodedStaticPropertyKey {} + +impl PartialEq for EncodedStaticPropertyKey { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for EncodedStaticPropertyKey { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl PhfHash for EncodedStaticPropertyKey { + #[inline] + fn phf_hash(&self, state: &mut H) { + self.hash(state) + } +} + +impl PhfBorrow for EncodedStaticPropertyKey { + #[inline] + fn borrow(&self) -> &EncodedStaticPropertyKey { + self + } +} + +impl FmtConst for EncodedStaticPropertyKey { + #[inline] + fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let key = self.decode(); + if matches!(key, StaticPropertyKey::String { .. }) { + f.write_str("string(")?; + } else { + f.write_str("symbol(")?; + } + + match key { + StaticPropertyKey::String(index) => { + write!( + f, + "/* */ {index})", + // String::from_utf16_lossy(value), + ) + } + StaticPropertyKey::Symbol(s) => write!(f, "{})", s), + } + } +} + +trait ToPropertyKey { + fn to_property_key(self, _context: &Context) -> StaticPropertyKey; +} + +impl ToPropertyKey for &'static [u16] { + fn to_property_key(self, context: &Context) -> StaticPropertyKey { + let index = context.insert_or_get(self); + StaticPropertyKey::String(index) + } +} + +impl ToPropertyKey for WellKnown { + fn to_property_key(self, _context: &Context) -> StaticPropertyKey { + StaticPropertyKey::Symbol(self as u8) + } +} + +#[allow(clippy::type_complexity)] +struct Context { + strings: RefCell<(HashMap<&'static [u16], u16>, Vec<&'static [u16]>)>, +} + +impl Context { + fn new() -> Self { + Self { + strings: RefCell::default(), + } + } + + fn insert_or_get(&self, value: &'static [u16]) -> u16 { + let mut strings = self.strings.borrow_mut(); + if let Some(index) = strings.0.get(value) { + return *index; + } + let index = strings.1.len(); + debug_assert!(index < u16::MAX as usize); + let index = index as u16; + + strings.0.insert(value, index); + strings.1.push(value); + + index + } + + fn build(&self, file: &mut BufWriter) -> io::Result<()> { + let strings = self.strings.borrow(); + + let len = strings.1.len(); + + writeln!(file, "\npub const RAW_STATICS: &[&[u16]; {len}] = &[")?; + for string in &strings.1 { + writeln!( + file, + " /* {} */ &{:?},", + String::from_utf16_lossy(string), + string + )?; + } + writeln!(file, "];")?; + Ok(()) + } +} + +struct BuiltInBuilder<'a> { + context: &'a Context, + name: &'static str, + map: phf_codegen::OrderedMap, + prototype: Option<&'static str>, + + slot_index: usize, +} + +impl<'a> BuiltInBuilder<'a> { + fn new(context: &'a Context, name: &'static str) -> Self { + Self { + context, + 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(self.context).encode(); + 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.context).encode(); + 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, + { + assert!(!attributes.contains(Attribute::GET) && !attributes.contains(Attribute::SET)); + + let key = key.to_property_key(self.context).encode(); + 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<'a> { + object: BuiltInBuilder<'a>, + prototype: BuiltInBuilder<'a>, +} + +impl<'a> BuiltInBuilderConstructor<'a> { + fn new(context: &'a Context, 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(context, object_name), + prototype: BuiltInBuilder::new(context, 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<()> { + // TODO: split into separate files + // TODO: Move common parts between build and lib.rs into common.rs file. + + // TODO: because the generated static shapes for builtin file does not change that often and it's not that big, + // it's kept as rust source code which changes only in certain places when a property is added or removed. + // We could properly cache this into the git history, to avoid generating it on a fresh build. + // (even though it's fast to build). + + let file = Path::new(&env::var("OUT_DIR").unwrap()).join("static_shapes_codegen.rs"); + let file = &mut BufWriter::new(File::create(file)?); + + let context = Context::new(); + + for string in RAW_STATICS { + context.insert_or_get(string); + } + + BuiltInBuilder::new(&context, "EMPTY_OBJECT").build(file)?; + + BuiltInBuilder::new(&context, "JSON_OBJECT") + .method(utf16!("parse")) + .method(utf16!("stringify")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilder::new(&context, "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(&context, "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(&context, "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(&context, "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(&context, "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(&context, "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(&context, "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(&context, "BOOLEAN") + .method(utf16!("toString")) + .method(utf16!("valueOf")) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "BIGINT") + .method(utf16!("toString")) + .method(utf16!("valueOf")) + .static_method(utf16!("asIntN")) + .static_method(utf16!("asUintN")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "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)?; + + let mut builder = BuiltInBuilderConstructor::new(&context, "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)?; + + let mut regexp = BuiltInBuilderConstructor::new(&context, "REGEXP"); + regexp + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .property( + utf16!("lastIndex"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .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)?; + + let attribute = Attribute::WRITABLE | Attribute::CONFIGURABLE; + BuiltInBuilderConstructor::new(&context, "ERROR") + .property(utf16!("name"), attribute) + .property(utf16!("message"), attribute) + .method(utf16!("toString")) + .build(file)?; + + let attribute = Attribute::WRITABLE | Attribute::CONFIGURABLE; + BuiltInBuilderConstructor::new(&context, "NATIVE_ERROR") + .property(utf16!("name"), attribute) + .property(utf16!("message"), attribute) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "MAP") + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .property( + utf16!("entries"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property( + WellKnown::Iterator, + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .method(utf16!("clear")) + .method(utf16!("delete")) + .method(utf16!("forEach")) + .method(utf16!("get")) + .method(utf16!("has")) + .method(utf16!("keys")) + .method(utf16!("set")) + .method(utf16!("values")) + .accessor(utf16!("size"), Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "SET") + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .method(utf16!("add")) + .method(utf16!("clear")) + .method(utf16!("delete")) + .method(utf16!("entries")) + .method(utf16!("forEach")) + .method(utf16!("has")) + .property( + utf16!("keys"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .accessor(utf16!("size"), Attribute::CONFIGURABLE) + .property( + utf16!("values"), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property( + WellKnown::Iterator, + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "TYPED_ARRAY") + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .property( + WellKnown::Iterator, + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .accessor(utf16!("buffer"), Attribute::CONFIGURABLE) + .accessor(utf16!("byteLength"), Attribute::CONFIGURABLE) + .accessor(utf16!("byteOffset"), Attribute::CONFIGURABLE) + .accessor(utf16!("length"), Attribute::CONFIGURABLE) + .accessor(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .static_method(utf16!("from")) + .static_method(utf16!("of")) + .method(utf16!("at")) + .method(utf16!("copyWithin")) + .method(utf16!("entries")) + .method(utf16!("every")) + .method(utf16!("fill")) + .method(utf16!("filter")) + .method(utf16!("find")) + .method(utf16!("findIndex")) + .method(utf16!("forEach")) + .method(utf16!("includes")) + .method(utf16!("indexOf")) + .method(utf16!("join")) + .method(utf16!("keys")) + .method(utf16!("lastIndexOf")) + .method(utf16!("map")) + .method(utf16!("reduce")) + .method(utf16!("reduceRight")) + .method(utf16!("reverse")) + .method(utf16!("set")) + .method(utf16!("slice")) + .method(utf16!("some")) + .method(utf16!("sort")) + .method(utf16!("subarray")) + .method(utf16!("values")) + // 23.2.3.29 %TypedArray%.prototype.toString ( ) + // The initial value of the %TypedArray%.prototype.toString data property is the same + // built-in function object as the Array.prototype.toString method defined in 23.1.3.30. + .method(utf16!("toString")) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "TYPED_ARRAY_INSTANCE") + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .property(utf16!("BYTES_PER_ELEMENT"), Attribute::empty()) + .static_property(utf16!("BYTES_PER_ELEMENT"), Attribute::empty()) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "ARRAY_BUFFER") + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .static_method(utf16!("isView")) + .accessor(utf16!("byteLength"), Attribute::CONFIGURABLE) + .method(utf16!("slice")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "DATA_VIEW") + .accessor(utf16!("buffer"), Attribute::CONFIGURABLE) + .accessor(utf16!("byteLength"), Attribute::CONFIGURABLE) + .accessor(utf16!("byteOffset"), Attribute::CONFIGURABLE) + .method(utf16!("getBigInt64")) + .method(utf16!("getBigUint64")) + .method(utf16!("getFloat32")) + .method(utf16!("getFloat64")) + .method(utf16!("getInt8")) + .method(utf16!("getInt16")) + .method(utf16!("getInt32")) + .method(utf16!("getUint8")) + .method(utf16!("getUint16")) + .method(utf16!("getUint32")) + .method(utf16!("setBigInt64")) + .method(utf16!("setBigUint64")) + .method(utf16!("setFloat32")) + .method(utf16!("setFloat64")) + .method(utf16!("setInt8")) + .method(utf16!("setInt16")) + .method(utf16!("setInt32")) + .method(utf16!("setUint8")) + .method(utf16!("setUint16")) + .method(utf16!("setUint32")) + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "WEAK_REF") + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .method(utf16!("deref")) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "PROMISE") + .static_method(utf16!("all")) + .static_method(utf16!("allSettled")) + .static_method(utf16!("any")) + .static_method(utf16!("race")) + .static_method(utf16!("reject")) + .static_method(utf16!("resolve")) + .static_accessor(WellKnown::Species, Attribute::CONFIGURABLE) + .method(utf16!("then")) + .method(utf16!("catch")) + .method(utf16!("finally")) + // + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "WEAK_MAP") + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .method(utf16!("delete")) + .method(utf16!("get")) + .method(utf16!("has")) + .method(utf16!("set")) + .build(file)?; + + BuiltInBuilderConstructor::new(&context, "WEAK_SET") + .property(WellKnown::ToStringTag, Attribute::CONFIGURABLE) + .method(utf16!("add")) + .method(utf16!("delete")) + .method(utf16!("has")) + .build(file)?; + + context.build(file)?; + + Ok(()) +} + +/// Array of raw static strings that aren't reference counted. +/// +/// The macro `static_strings` automatically sorts the array of strings, making it faster +/// for searches by using `binary_search`. +const RAW_STATICS: &[&[u16]] = &[ + utf16!(""), + // Misc + utf16!(","), + utf16!(":"), + // Generic use + utf16!("name"), + utf16!("length"), + utf16!("arguments"), + utf16!("prototype"), + utf16!("constructor"), + utf16!("return"), + utf16!("throw"), + utf16!("global"), + utf16!("globalThis"), + // typeof + utf16!("null"), + utf16!("undefined"), + utf16!("number"), + utf16!("string"), + utf16!("symbol"), + utf16!("bigint"), + utf16!("object"), + utf16!("function"), + // Property descriptor + utf16!("value"), + utf16!("get"), + utf16!("set"), + utf16!("writable"), + utf16!("enumerable"), + utf16!("configurable"), + // Object object + utf16!("Object"), + utf16!("assign"), + utf16!("create"), + utf16!("toString"), + utf16!("valueOf"), + utf16!("is"), + utf16!("seal"), + utf16!("isSealed"), + utf16!("freeze"), + utf16!("isFrozen"), + utf16!("isExtensible"), + utf16!("hasOwnProperty"), + utf16!("isPrototypeOf"), + utf16!("setPrototypeOf"), + utf16!("getPrototypeOf"), + utf16!("defineProperty"), + utf16!("defineProperties"), + utf16!("deleteProperty"), + utf16!("construct"), + utf16!("hasOwn"), + utf16!("ownKeys"), + utf16!("keys"), + utf16!("values"), + utf16!("entries"), + utf16!("fromEntries"), + // Function object + utf16!("Function"), + utf16!("apply"), + utf16!("bind"), + utf16!("call"), + // Generator object + utf16!("Generator"), + // Array object + utf16!("Array"), + utf16!("at"), + utf16!("from"), + utf16!("isArray"), + utf16!("of"), + utf16!("copyWithin"), + utf16!("every"), + utf16!("fill"), + utf16!("filter"), + utf16!("find"), + utf16!("findIndex"), + utf16!("findLast"), + utf16!("findLastIndex"), + utf16!("flat"), + utf16!("flatMap"), + utf16!("forEach"), + utf16!("includes"), + utf16!("indexOf"), + utf16!("join"), + utf16!("map"), + utf16!("next"), + utf16!("reduce"), + utf16!("reduceRight"), + utf16!("reverse"), + utf16!("shift"), + utf16!("slice"), + utf16!("splice"), + utf16!("some"), + utf16!("sort"), + utf16!("unshift"), + utf16!("push"), + utf16!("pop"), + // String object + utf16!("String"), + utf16!("charAt"), + utf16!("charCodeAt"), + utf16!("codePointAt"), + utf16!("concat"), + utf16!("endsWith"), + utf16!("fromCharCode"), + utf16!("fromCodePoint"), + utf16!("lastIndexOf"), + utf16!("match"), + utf16!("matchAll"), + utf16!("normalize"), + utf16!("padEnd"), + utf16!("padStart"), + utf16!("raw"), + utf16!("repeat"), + utf16!("replace"), + utf16!("replaceAll"), + utf16!("search"), + utf16!("split"), + utf16!("startsWith"), + utf16!("substr"), + utf16!("substring"), + utf16!("toLocaleString"), + utf16!("toLowerCase"), + utf16!("toUpperCase"), + utf16!("trim"), + utf16!("trimEnd"), + utf16!("trimStart"), + // Number object + utf16!("Number"), + utf16!("Infinity"), + utf16!("NaN"), + utf16!("parseInt"), + utf16!("parseFloat"), + utf16!("isFinite"), + utf16!("isNaN"), + utf16!("EPSILON"), + utf16!("MAX_SAFE_INTEGER"), + utf16!("MIN_SAFE_INTEGER"), + utf16!("MAX_VALUE"), + utf16!("MIN_VALUE"), + utf16!("isSafeInteger"), + utf16!("isInteger"), + utf16!("toExponential"), + utf16!("toFixed"), + utf16!("toPrecision"), + // Boolean object + utf16!("Boolean"), + // BigInt object + utf16!("BigInt"), + utf16!("asIntN"), + utf16!("asUintN"), + // RegExp object + utf16!("RegExp"), + utf16!("exec"), + utf16!("test"), + utf16!("flags"), + utf16!("index"), + utf16!("lastIndex"), + utf16!("hasIndices"), + utf16!("ignoreCase"), + utf16!("multiline"), + utf16!("dotAll"), + utf16!("unicode"), + utf16!("sticky"), + utf16!("source"), + utf16!("get hasIndices"), + utf16!("get global"), + utf16!("get ignoreCase"), + utf16!("get multiline"), + utf16!("get dotAll"), + utf16!("get unicode"), + utf16!("get sticky"), + utf16!("get flags"), + utf16!("get source"), + // Symbol object + utf16!("Symbol"), + utf16!("for"), + utf16!("keyFor"), + utf16!("description"), + utf16!("asyncIterator"), + utf16!("hasInstance"), + utf16!("species"), + utf16!("unscopables"), + utf16!("iterator"), + utf16!("toStringTag"), + utf16!("toPrimitive"), + utf16!("get description"), + // Map object + utf16!("Map"), + utf16!("clear"), + utf16!("delete"), + utf16!("has"), + utf16!("size"), + // Set object + utf16!("Set"), + utf16!("add"), + // Reflect object + utf16!("Reflect"), + // Proxy object + utf16!("Proxy"), + utf16!("revocable"), + // Error objects + utf16!("Error"), + utf16!("AggregateError"), + utf16!("TypeError"), + utf16!("RangeError"), + utf16!("SyntaxError"), + utf16!("ReferenceError"), + utf16!("EvalError"), + utf16!("ThrowTypeError"), + utf16!("URIError"), + utf16!("message"), + // Date object + utf16!("Date"), + utf16!("toJSON"), + utf16!("getDate"), + utf16!("getDay"), + utf16!("getFullYear"), + utf16!("getHours"), + utf16!("getMilliseconds"), + utf16!("getMinutes"), + utf16!("getMonth"), + utf16!("getSeconds"), + utf16!("getTime"), + utf16!("getYear"), + utf16!("getUTCDate"), + utf16!("getUTCDay"), + utf16!("getUTCFullYear"), + utf16!("getUTCHours"), + utf16!("getUTCMinutes"), + utf16!("getUTCMonth"), + utf16!("getUTCSeconds"), + utf16!("setDate"), + utf16!("setFullYear"), + utf16!("setHours"), + utf16!("setMilliseconds"), + utf16!("setMinutes"), + utf16!("setMonth"), + utf16!("setSeconds"), + utf16!("setYear"), + utf16!("setTime"), + utf16!("setUTCDate"), + utf16!("setUTCFullYear"), + utf16!("setUTCHours"), + utf16!("setUTCMinutes"), + utf16!("setUTCMonth"), + utf16!("setUTCSeconds"), + utf16!("toDateString"), + utf16!("toGMTString"), + utf16!("toISOString"), + utf16!("toTimeString"), + utf16!("toUTCString"), + utf16!("now"), + utf16!("UTC"), + // JSON object + utf16!("JSON"), + utf16!("parse"), + utf16!("stringify"), + // Iterator object + utf16!("Array Iterator"), + utf16!("Set Iterator"), + utf16!("String Iterator"), + utf16!("Map Iterator"), + utf16!("For In Iterator"), + // Math object + utf16!("Math"), + utf16!("LN10"), + utf16!("LN2"), + utf16!("LOG10E"), + utf16!("LOG2E"), + utf16!("PI"), + utf16!("SQRT1_2"), + utf16!("SQRT2"), + utf16!("abs"), + utf16!("acos"), + utf16!("acosh"), + utf16!("asin"), + utf16!("asinh"), + utf16!("atan"), + utf16!("atanh"), + utf16!("atan2"), + utf16!("cbrt"), + utf16!("ceil"), + utf16!("clz32"), + utf16!("cos"), + utf16!("cosh"), + utf16!("exp"), + utf16!("expm1"), + utf16!("floor"), + utf16!("fround"), + utf16!("hypot"), + utf16!("imul"), + utf16!("log"), + utf16!("log1p"), + utf16!("log10"), + utf16!("log2"), + utf16!("max"), + utf16!("min"), + utf16!("pow"), + utf16!("random"), + utf16!("round"), + utf16!("sign"), + utf16!("sin"), + utf16!("sinh"), + utf16!("sqrt"), + utf16!("tan"), + utf16!("tanh"), + utf16!("trunc"), + // Intl object + utf16!("Intl"), + utf16!("DateTimeFormat"), + // TypedArray object + utf16!("TypedArray"), + utf16!("ArrayBuffer"), + utf16!("Int8Array"), + utf16!("Uint8Array"), + utf16!("Int16Array"), + utf16!("Uint16Array"), + utf16!("Int32Array"), + utf16!("Uint32Array"), + utf16!("BigInt64Array"), + utf16!("BigUint64Array"), + utf16!("Float32Array"), + utf16!("Float64Array"), + utf16!("buffer"), + utf16!("byteLength"), + utf16!("byteOffset"), + utf16!("isView"), + utf16!("subarray"), + utf16!("get byteLength"), + utf16!("get buffer"), + utf16!("get byteOffset"), + utf16!("get size"), + utf16!("get length"), + // DataView object + utf16!("DataView"), + utf16!("getBigInt64"), + utf16!("getBigUint64"), + utf16!("getFloat32"), + utf16!("getFloat64"), + utf16!("getInt8"), + utf16!("getInt16"), + utf16!("getInt32"), + utf16!("getUint8"), + utf16!("getUint16"), + utf16!("getUint32"), + utf16!("setBigInt64"), + utf16!("setBigUint64"), + utf16!("setFloat32"), + utf16!("setFloat64"), + utf16!("setInt8"), + utf16!("setInt16"), + utf16!("setInt32"), + utf16!("setUint8"), + utf16!("setUint16"), + utf16!("setUint32"), + // Console object + utf16!("console"), + utf16!("assert"), + utf16!("debug"), + utf16!("error"), + utf16!("info"), + utf16!("trace"), + utf16!("warn"), + utf16!("exception"), + utf16!("count"), + utf16!("countReset"), + utf16!("group"), + utf16!("groupCollapsed"), + utf16!("groupEnd"), + utf16!("time"), + utf16!("timeLog"), + utf16!("timeEnd"), + utf16!("dir"), + utf16!("dirxml"), + // Minified name + utf16!("a"), + utf16!("b"), + utf16!("c"), + utf16!("d"), + utf16!("e"), + utf16!("f"), + utf16!("g"), + utf16!("h"), + utf16!("i"), + utf16!("j"), + utf16!("k"), + utf16!("l"), + utf16!("m"), + utf16!("n"), + utf16!("o"), + utf16!("p"), + utf16!("q"), + utf16!("r"), + utf16!("s"), + utf16!("t"), + utf16!("u"), + utf16!("v"), + utf16!("w"), + utf16!("x"), + utf16!("y"), + utf16!("z"), + utf16!("A"), + utf16!("B"), + utf16!("C"), + utf16!("D"), + utf16!("E"), + utf16!("F"), + utf16!("G"), + utf16!("H"), + utf16!("I"), + utf16!("J"), + utf16!("K"), + utf16!("L"), + utf16!("M"), + utf16!("N"), + utf16!("O"), + utf16!("P"), + utf16!("Q"), + utf16!("R"), + utf16!("S"), + utf16!("T"), + utf16!("U"), + utf16!("V"), + utf16!("W"), + utf16!("X"), + utf16!("Y"), + utf16!("Z"), + utf16!("_"), + utf16!("$"), + // Well known symbols + utf16!("Symbol.asyncIterator"), + utf16!("[Symbol.asyncIterator]"), + utf16!("Symbol.hasInstance"), + utf16!("[Symbol.hasInstance]"), + utf16!("Symbol.isConcatSpreadable"), + utf16!("[Symbol.isConcatSpreadable]"), + utf16!("Symbol.iterator"), + utf16!("[Symbol.iterator]"), + utf16!("Symbol.match"), + utf16!("[Symbol.match]"), + utf16!("Symbol.matchAll"), + utf16!("[Symbol.matchAll]"), + utf16!("Symbol.replace"), + utf16!("[Symbol.replace]"), + utf16!("Symbol.search"), + utf16!("[Symbol.search]"), + utf16!("Symbol.species"), + utf16!("[Symbol.species]"), + utf16!("Symbol.split"), + utf16!("[Symbol.split]"), + utf16!("Symbol.toPrimitive"), + utf16!("[Symbol.toPrimitive]"), + utf16!("Symbol.toStringTag"), + utf16!("[Symbol.toStringTag]"), + utf16!("Symbol.unscopables"), + utf16!("[Symbol.unscopables]"), +]; diff --git a/boa_builtins/src/lib.rs b/boa_builtins/src/lib.rs new file mode 100644 index 0000000000..4f61654213 --- /dev/null +++ b/boa_builtins/src/lib.rs @@ -0,0 +1,181 @@ +use std::{ + fmt::{self, Debug}, + hash::{Hash, Hasher}, +}; + +use bitflags::bitflags; +use phf::PhfHash; +use phf_shared::PhfBorrow; + +bitflags! { + /// This struct constains the property flags as described in the ECMAScript specification. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct Attribute: u8 { + /// The `Writable` attribute decides whether the value associated with the property can be changed or not, from its initial value. + const WRITABLE = 0b0000_0001; + + /// If the property can be enumerated by a `for-in` loop. + const ENUMERABLE = 0b0000_0010; + + /// If the property descriptor can be changed later. + const CONFIGURABLE = 0b0000_0100; + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct StaticString { + index: u16, +} + +impl StaticString { + #[inline] + pub fn index(self) -> u16 { + self.index + } +} + +#[derive(Clone, Copy)] +pub struct EncodedStaticPropertyKey(u16); + +impl EncodedStaticPropertyKey { + #[inline] + pub fn decode(&self) -> StaticPropertyKey { + let value = self.0 >> 1; + if self.0 & 1 == 0 { + StaticPropertyKey::String(value) + } else { + StaticPropertyKey::Symbol(value as u8) + } + } +} + +const fn string(index: u16) -> EncodedStaticPropertyKey { + debug_assert!(index < 2u16.pow(15)); + + EncodedStaticPropertyKey(index << 1) +} + +const fn symbol(index: u8) -> EncodedStaticPropertyKey { + EncodedStaticPropertyKey(((index as u16) << 1) | 1) +} + +impl Debug for EncodedStaticPropertyKey { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.decode().fmt(f) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum StaticPropertyKey { + String(u16), + Symbol(u8), +} + +impl StaticPropertyKey { + #[inline] + pub fn encode(self) -> EncodedStaticPropertyKey { + match self { + StaticPropertyKey::String(x) => string(x), + StaticPropertyKey::Symbol(x) => symbol(x), + } + } +} + +impl Debug for StaticPropertyKey { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + StaticPropertyKey::String(index) => { + let string = RAW_STATICS[index as usize]; + let string = String::from_utf16_lossy(string); + write!(f, "String(\"{string}\")") + } + StaticPropertyKey::Symbol(symbol) => { + write!(f, "Symbol({symbol})") + } + } + } +} + +impl Eq for EncodedStaticPropertyKey {} + +impl PartialEq for EncodedStaticPropertyKey { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for EncodedStaticPropertyKey { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl PhfHash for EncodedStaticPropertyKey { + #[inline] + fn phf_hash(&self, state: &mut H) { + self.hash(state) + } +} + +impl PhfBorrow for EncodedStaticPropertyKey { + #[inline] + fn borrow(&self) -> &EncodedStaticPropertyKey { + self + } +} + +pub type Slot = (u8, Attribute); + +#[derive(Debug)] +pub struct StaticShape { + pub property_table: phf::OrderedMap, + + 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. + self.property_table + .get(&key.encode()) + .map(|(index, attributes)| (*index, *attributes)) + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.property_table.is_empty() + } + + #[inline] + pub fn len(&self) -> usize { + self.property_table.len() + } + + #[inline] + pub fn get_string_key_expect(&self, index: usize) -> StaticString { + match self + .property_table + .index(index) + .expect("there should be a key at the given index") + .0 + .decode() + { + StaticPropertyKey::String(index) => StaticString { index }, + StaticPropertyKey::Symbol(s) => { + panic!("The key should be a string at position {index}, but symbol {s}") + } + } + } +} + +include!(concat!(env!("OUT_DIR"), "/static_shapes_codegen.rs")); + +// static NUMBER_BUITIN_OBJECT_STATIC_SHAPE_REF: &StaticShape = &NUMBER_BUITIN_OBJECT_STATIC_SHAPE; diff --git a/boa_cli/src/debug/shape.rs b/boa_cli/src/debug/shape.rs index 167bc5b3c4..e874008cf9 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/array_buffer/mod.rs b/boa_engine/src/builtins/array_buffer/mod.rs index 6d57ec67e2..1a1100540b 100644 --- a/boa_engine/src/builtins/array_buffer/mod.rs +++ b/boa_engine/src/builtins/array_buffer/mod.rs @@ -15,10 +15,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, - symbol::JsSymbol, value::{IntegerOrInfinity, Numeric}, Context, JsArgs, JsResult, JsValue, }; @@ -51,8 +48,6 @@ impl IntrinsicObject for ArrayBuffer { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_species = BuiltInBuilder::callable(realm, Self::get_species) .name("get [Symbol.species]") .build(); @@ -61,27 +56,17 @@ impl IntrinsicObject for ArrayBuffer { .name("get byteLength") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .accessor( - utf16!("byteLength"), - Some(get_byte_length), - None, - flag_attributes, - ) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .static_method(Self::is_view, "isView", 1) - .method(Self::slice, "slice", 2) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::ARRAY_BUFFER_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::ARRAY_BUFFER_PROTOTYPE_STATIC_SHAPE, + ) + .static_accessor(Some(get_species), None) + .static_method(Self::is_view, 1) + .accessor(Some(get_byte_length), None) + .method(Self::slice, 2) + .property(Self::NAME) + .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 d649bf6083..77845981bc 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 91f8186f56..2d36b6dce4 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/dataview/mod.rs b/boa_engine/src/builtins/dataview/mod.rs index 56ba390303..ba07e4f77b 100644 --- a/boa_engine/src/builtins/dataview/mod.rs +++ b/boa_engine/src/builtins/dataview/mod.rs @@ -12,10 +12,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, - symbol::JsSymbol, value::JsValue, Context, JsArgs, JsResult, }; @@ -33,8 +30,6 @@ pub struct DataView { impl IntrinsicObject for DataView { fn init(realm: &Realm) { - let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_buffer = BuiltInBuilder::callable(realm, Self::get_buffer) .name("get buffer") .build(); @@ -47,46 +42,36 @@ impl IntrinsicObject for DataView { .name("get byteOffset") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .accessor(utf16!("buffer"), Some(get_buffer), None, flag_attributes) - .accessor( - utf16!("byteLength"), - Some(get_byte_length), - None, - flag_attributes, - ) - .accessor( - utf16!("byteOffset"), - Some(get_byte_offset), - None, - flag_attributes, - ) - .method(Self::get_big_int64, "getBigInt64", 1) - .method(Self::get_big_uint64, "getBigUint64", 1) - .method(Self::get_float32, "getFloat32", 1) - .method(Self::get_float64, "getFloat64", 1) - .method(Self::get_int8, "getInt8", 1) - .method(Self::get_int16, "getInt16", 1) - .method(Self::get_int32, "getInt32", 1) - .method(Self::get_uint8, "getUint8", 1) - .method(Self::get_uint16, "getUint16", 1) - .method(Self::get_uint32, "getUint32", 1) - .method(Self::set_big_int64, "setBigInt64", 2) - .method(Self::set_big_uint64, "setBigUint64", 2) - .method(Self::set_float32, "setFloat32", 2) - .method(Self::set_float64, "setFloat64", 2) - .method(Self::set_int8, "setInt8", 2) - .method(Self::set_int16, "setInt16", 2) - .method(Self::set_int32, "setInt32", 2) - .method(Self::set_uint8, "setUint8", 2) - .method(Self::set_uint16, "setUint16", 2) - .method(Self::set_uint32, "setUint32", 2) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::DATA_VIEW_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::DATA_VIEW_PROTOTYPE_STATIC_SHAPE, + ) + .accessor(Some(get_buffer), None) + .accessor(Some(get_byte_length), None) + .accessor(Some(get_byte_offset), None) + .method(Self::get_big_int64, 1) + .method(Self::get_big_uint64, 1) + .method(Self::get_float32, 1) + .method(Self::get_float64, 1) + .method(Self::get_int8, 1) + .method(Self::get_int16, 1) + .method(Self::get_int32, 1) + .method(Self::get_uint8, 1) + .method(Self::get_uint16, 1) + .method(Self::get_uint32, 1) + .method(Self::set_big_int64, 2) + .method(Self::set_big_uint64, 2) + .method(Self::set_float32, 2) + .method(Self::set_float64, 2) + .method(Self::set_int8, 2) + .method(Self::set_int16, 2) + .method(Self::set_int32, 2) + .method(Self::set_uint8, 2) + .method(Self::set_uint16, 2) + .method(Self::set_uint32, 2) + .property(Self::NAME) + .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 f6ce1d798e..2d0d1a7c10 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/error/aggregate.rs b/boa_engine/src/builtins/error/aggregate.rs index f9ce1fd70f..0adbdf20d7 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 ac3d915ed5..d59ff8c2ee 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/mod.rs b/boa_engine/src/builtins/error/mod.rs index 0430ddd26f..4f77430e2c 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/builtins/error/range.rs b/boa_engine/src/builtins/error/range.rs index 36c657b9c6..921c3c9140 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 e72dd09883..dad820a4a0 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 444e32a3d7..6ede2d51b4 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 87cdc6fae5..ed2e2cca8e 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 34a7514d6c..4b8aed9416 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 { diff --git a/boa_engine/src/builtins/escape/mod.rs b/boa_engine/src/builtins/escape/mod.rs index a889714207..d5357245cf 100644 --- a/boa_engine/src/builtins/escape/mod.rs +++ b/boa_engine/src/builtins/escape/mod.rs @@ -23,7 +23,7 @@ pub(crate) struct Escape; impl IntrinsicObject for Escape { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, escape) + BuiltInBuilder::callable_intrinsic::(realm, escape) .name(Self::NAME) .length(1) .build(); @@ -95,7 +95,7 @@ pub(crate) struct Unescape; impl IntrinsicObject for Unescape { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, unescape) + BuiltInBuilder::callable_intrinsic::(realm, unescape) .name(Self::NAME) .length(1) .build(); diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 443d3249a4..ce426cbd62 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -35,7 +35,7 @@ impl IntrinsicObject for Eval { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::callable_with_intrinsic::(realm, Self::eval) + BuiltInBuilder::callable_intrinsic::(realm, Self::eval) .name(Self::NAME) .length(1) .build(); diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 2eeb1ab864..c5c9cfc16a 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -19,12 +19,14 @@ use crate::{ error::JsNativeError, js_string, native_function::NativeFunction, - object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, - object::{JsFunction, PrivateElement, PrivateName}, - property::{Attribute, PropertyDescriptor, PropertyKey}, + object::{ + internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData, ObjectKind, + PrivateName, + }, + object::{JsFunction, PrivateElement}, + property::{PropertyDescriptor, PropertyKey}, realm::Realm, string::utf16, - symbol::JsSymbol, value::IntegerOrInfinity, vm::{ActiveRunnable, CodeBlock}, Context, JsArgs, JsResult, JsString, JsValue, @@ -468,33 +470,65 @@ 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); + } + // TODO: improve this, because it's converting the static shape into a unique one, on initialization. prototype.set_prototype(Some(realm.intrinsics().constructors().object().prototype())); } @@ -1013,6 +1047,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 20b80fef10..5badff6283 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, - property::{Attribute, PropertyNameKind}, + property::PropertyNameKind, realm::Realm, string::{utf16, CodePoint}, - symbol::JsSymbol, value::IntegerOrInfinity, vm::CallFrame, Context, JsArgs, JsResult, JsString, JsValue, @@ -50,14 +49,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/map/mod.rs b/boa_engine/src/builtins/map/mod.rs index f8d04e4fc1..e93b7faa52 100644 --- a/boa_engine/src/builtins/map/mod.rs +++ b/boa_engine/src/builtins/map/mod.rs @@ -15,10 +15,9 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::{Attribute, PropertyNameKind}, + property::PropertyNameKind, realm::Realm, string::utf16, - symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, }; use boa_profiler::Profiler; @@ -53,43 +52,25 @@ impl IntrinsicObject for Map { .name("entries") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .property( - utf16!("entries"), - entries_function.clone(), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - JsSymbol::iterator(), - entries_function, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .method(Self::clear, "clear", 0) - .method(Self::delete, "delete", 1) - .method(Self::for_each, "forEach", 1) - .method(Self::get, "get", 1) - .method(Self::has, "has", 1) - .method(Self::keys, "keys", 0) - .method(Self::set, "set", 2) - .method(Self::values, "values", 0) - .accessor( - utf16!("size"), - Some(get_size), - None, - Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::MAP_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::MAP_PROTOTYPE_STATIC_SHAPE, + ) + .static_accessor(Some(get_species), None) + .property(entries_function.clone()) + .property(entries_function) + .property(Self::NAME) + .method(Self::clear, 0) + .method(Self::delete, 1) + .method(Self::for_each, 1) + .method(Self::get, 1) + .method(Self::has, 1) + .method(Self::keys, 0) + .method(Self::set, 2) + .method(Self::values, 0) + .accessor(Some(get_size), None) + .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 406661c5ff..78c259b76a 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 798f8d70b8..f8d3496d33 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,9 @@ use crate::{ js_string, native_function::{NativeFunction, NativeFunctionPointer}, object::{ - shape::{property_table::PropertyTableInner, slot::SlotAttributes}, + shape::{ + property_table::PropertyTableInner, slot::SlotAttributes, static_shape::StaticShape, + }, FunctionBinding, JsFunction, JsObject, JsPrototype, Object, ObjectData, ObjectKind, CONSTRUCTOR, PROTOTYPE, }, @@ -587,19 +591,318 @@ 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 BuiltInBuilderCallableIntrinsic<'ctx> { + realm: &'ctx Realm, + function: NativeFunctionPointer, + object: JsObject, + name: JsString, + length: usize, +} + +impl BuiltInBuilderCallableIntrinsic<'_> { + fn name>(mut self, name: N) -> Self { + self.name = name.into(); + self + } + /// Specify how many arguments the constructor function takes. + /// + /// Default is `0`. + const fn length(mut self, length: usize) -> Self { + self.length = length; + self + } + fn build(self) { + let function = function::Function::new( + function::FunctionKind::Native { + function: NativeFunction::from_fn_ptr(self.function), + constructor: (true).then_some(function::ConstructorKind::Base), + }, + self.realm.clone(), + ); + + let mut object = self.object.borrow_mut(); + object.properties_mut().shape = self + .realm + .intrinsics() + .templates() + .function() + .shape() + .clone() + .into(); + object.properties_mut().storage = vec![self.length.into(), self.name.into()]; + *object.kind_mut() = ObjectKind::Function(function); + } +} + +struct BuiltInBuilderConstructorStaticShape<'ctx> { + realm: &'ctx Realm, + function: NativeFunctionPointer, + + constructor_property_index: usize, + constructor_object: JsObject, + constructor_shape: &'static RawStaticShape, + constructor_storage: Vec, + + 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 + } + + 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 + 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 = StaticShape::new(self.constructor_shape).into(); + self.constructor_storage.push( + self.__proto__ + .unwrap_or_else(|| { + self.realm + .intrinsics() + .constructors() + .function() + .prototype() + }) + .into(), + ); + object.properties_mut().storage = self.constructor_storage; + + debug_assert_eq!( + self.prototype_storage.len() + 1, + self.prototype_shape.storage_len + ); + debug_assert_eq!( + self.prototype_storage.capacity(), + self.prototype_shape.storage_len + ); + let mut prototype = self.prototype_object.borrow_mut(); + prototype.properties_mut().shape = StaticShape::new(self.prototype_shape).into(); + self.prototype_storage + .push(self.inherits.map(JsValue::new).unwrap_or_default()); + prototype.properties_mut().storage = self.prototype_storage; + } +} + +struct BuiltInBuilderStaticShape<'ctx> { + realm: &'ctx Realm, + shape: &'static RawStaticShape, + object: JsObject, + property_index: usize, + storage: Vec, +} + +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 = StaticShape::new(self.shape).into(); + self.storage.push( + self.realm + .intrinsics() + .constructors() + .object() + .prototype() + .into(), + ); + object.properties_mut().storage = self.storage; + } +} + +impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { fn with_intrinsic( realm: &'ctx Realm, ) -> BuiltInBuilder<'ctx, OrdinaryObject> { @@ -610,6 +913,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> { @@ -625,6 +941,7 @@ struct BuiltInConstructorWithPrototype<'ctx> { prototype_property_table: PropertyTableInner, prototype_storage: Vec, prototype: JsObject, + __proto__: JsPrototype, inherits: Option, attributes: Attribute, @@ -943,21 +1260,16 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { } } - fn callable_with_intrinsic( + fn callable_intrinsic( realm: &'ctx Realm, function: NativeFunctionPointer, - ) -> BuiltInBuilder<'ctx, Callable> { - BuiltInBuilder { + ) -> BuiltInBuilderCallableIntrinsic<'ctx> { + BuiltInBuilderCallableIntrinsic { realm, - object: BuiltInObjectInitializer::Shared(I::get(realm.intrinsics())), - kind: Callable { - function, - name: js_string!(""), - length: 0, - kind: OrdinaryFunction, - realm: realm.clone(), - }, - prototype: realm.intrinsics().constructors().function().prototype(), + function, + object: I::get(realm.intrinsics()), + name: JsString::default(), + length: 0, } } @@ -1057,15 +1369,6 @@ impl BuiltInBuilder<'_, T> { } impl BuiltInBuilder<'_, Callable> { - /// Specify how many arguments the constructor function takes. - /// - /// Default is `0`. - #[inline] - const fn length(mut self, length: usize) -> Self { - self.kind.length = length; - self - } - /// Specify the name of the constructor function. /// /// Default is `""` diff --git a/boa_engine/src/builtins/number/globals.rs b/boa_engine/src/builtins/number/globals.rs index 15ae25b6f4..33f4a457f4 100644 --- a/boa_engine/src/builtins/number/globals.rs +++ b/boa_engine/src/builtins/number/globals.rs @@ -38,7 +38,7 @@ pub(crate) struct IsFinite; impl IntrinsicObject for IsFinite { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, is_finite) + BuiltInBuilder::callable_intrinsic::(realm, is_finite) .name(Self::NAME) .length(1) .build(); @@ -84,7 +84,7 @@ pub(crate) struct IsNaN; impl IntrinsicObject for IsNaN { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, is_nan) + BuiltInBuilder::callable_intrinsic::(realm, is_nan) .name(Self::NAME) .length(1) .build(); @@ -225,7 +225,7 @@ pub(crate) struct ParseInt; impl IntrinsicObject for ParseInt { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, parse_int) + BuiltInBuilder::callable_intrinsic::(realm, parse_int) .name(Self::NAME) .length(2) .build(); @@ -298,7 +298,7 @@ pub(crate) struct ParseFloat; impl IntrinsicObject for ParseFloat { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, parse_float) + BuiltInBuilder::callable_intrinsic::(realm, parse_float) .name(Self::NAME) .length(1) .build(); diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index d87e2b2732..2e3985a1fb 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 4c4fc5202a..d1abf0ba49 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/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index 7ced9a8848..91efd33fd2 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -14,10 +14,8 @@ use crate::{ internal_methods::get_prototype_from_constructor, FunctionObjectBuilder, JsFunction, JsObject, ObjectData, CONSTRUCTOR, }, - property::Attribute, realm::Realm, string::utf16, - symbol::JsSymbol, value::JsValue, Context, JsArgs, JsError, JsResult, }; @@ -324,29 +322,24 @@ impl IntrinsicObject for Promise { .name("get [Symbol.species]") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .static_method(Self::all, "all", 1) - .static_method(Self::all_settled, "allSettled", 1) - .static_method(Self::any, "any", 1) - .static_method(Self::race, "race", 1) - .static_method(Self::reject, "reject", 1) - .static_method(Self::resolve, "resolve", 1) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .method(Self::then, "then", 2) - .method(Self::catch, "catch", 1) - .method(Self::finally, "finally", 1) - // - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::PROMISE_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::PROMISE_PROTOTYPE_STATIC_SHAPE, + ) + .static_method(Self::all, 1) + .static_method(Self::all_settled, 1) + .static_method(Self::any, 1) + .static_method(Self::race, 1) + .static_method(Self::reject, 1) + .static_method(Self::resolve, 1) + .static_accessor(Some(get_species), None) + .method(Self::then, 2) + .method(Self::catch, 1) + .method(Self::finally, 1) + // + .property(Self::NAME) + .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 a0b2807fe0..38fb3d2590 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/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 7db76a671b..4894e6947b 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/set/mod.rs b/boa_engine/src/builtins/set/mod.rs index 656f27c89e..24ec6cbe8b 100644 --- a/boa_engine/src/builtins/set/mod.rs +++ b/boa_engine/src/builtins/set/mod.rs @@ -23,10 +23,9 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, - property::{Attribute, PropertyNameKind}, + property::PropertyNameKind, realm::Realm, string::utf16, - symbol::JsSymbol, Context, JsArgs, JsResult, JsValue, }; use boa_profiler::Profiler; @@ -56,46 +55,24 @@ impl IntrinsicObject for Set { .name("values") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .method(Self::add, "add", 1) - .method(Self::clear, "clear", 0) - .method(Self::delete, "delete", 1) - .method(Self::entries, "entries", 0) - .method(Self::for_each, "forEach", 1) - .method(Self::has, "has", 1) - .property( - utf16!("keys"), - values_function.clone(), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .accessor( - utf16!("size"), - Some(size_getter), - None, - Attribute::CONFIGURABLE, - ) - .property( - utf16!("values"), - values_function.clone(), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - JsSymbol::iterator(), - values_function, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::SET_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::SET_PROTOTYPE_STATIC_SHAPE, + ) + .static_accessor(Some(get_species), None) + .method(Self::add, 1) + .method(Self::clear, 0) + .method(Self::delete, 1) + .method(Self::entries, 0) + .method(Self::for_each, 1) + .method(Self::has, 1) + .property(values_function.clone()) + .accessor(Some(size_getter), None) + .property(values_function.clone()) + .property(values_function) + .property(Self::NAME) + .build(); } } diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index efeeda192a..f9758a31df 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") @@ -96,81 +94,68 @@ impl IntrinsicObject for String { #[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) + .method_with_name(Self::iterator, js_string!("[Symbol.iterator]"), 0) + .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(); } diff --git a/boa_engine/src/builtins/symbol/mod.rs b/boa_engine/src/builtins/symbol/mod.rs index 64a5a56632..3dd9d07032 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/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index e80ec6054b..9b4bb20230 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -26,7 +26,6 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::utf16, symbol::JsSymbol, value::{IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, @@ -55,34 +54,25 @@ macro_rules! typed_array { .name("get [Symbol.species]") .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .prototype( - realm - .intrinsics() - .constructors() - .typed_array() - .constructor(), - ) - .inherits(Some( - realm.intrinsics().constructors().typed_array().prototype(), - )) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .property( - utf16!("BYTES_PER_ELEMENT"), - TypedArrayKind::$variant.element_size(), - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ) - .static_property( - utf16!("BYTES_PER_ELEMENT"), - TypedArrayKind::$variant.element_size(), - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::TYPED_ARRAY_INSTANCE_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::TYPED_ARRAY_INSTANCE_PROTOTYPE_STATIC_SHAPE, + ) + .prototype( + realm + .intrinsics() + .constructors() + .typed_array() + .constructor(), + ) + .inherits(Some( + realm.intrinsics().constructors().typed_array().prototype(), + )) + .static_accessor(Some(get_species), None) + .property(TypedArrayKind::$variant.element_size()) + .static_property(TypedArrayKind::$variant.element_size()) + .build(); } } @@ -269,79 +259,49 @@ impl IntrinsicObject for TypedArray { .length(0) .build(); - BuiltInBuilder::from_standard_constructor::(realm) - .static_accessor( - JsSymbol::species(), - Some(get_species), - None, - Attribute::CONFIGURABLE, - ) - .property( - JsSymbol::iterator(), - values_function, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .accessor( - utf16!("buffer"), - Some(get_buffer), - None, - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ) - .accessor( - utf16!("byteLength"), - Some(get_byte_length), - None, - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ) - .accessor( - utf16!("byteOffset"), - Some(get_byte_offset), - None, - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ) - .accessor( - utf16!("length"), - Some(get_length), - None, - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ) - .accessor( - JsSymbol::to_string_tag(), - Some(get_to_string_tag), - None, - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ) - .static_method(Self::from, "from", 1) - .static_method(Self::of, "of", 0) - .method(Self::at, "at", 1) - .method(Self::copy_within, "copyWithin", 2) - .method(Self::entries, "entries", 0) - .method(Self::every, "every", 1) - .method(Self::fill, "fill", 1) - .method(Self::filter, "filter", 1) - .method(Self::find, "find", 1) - .method(Self::findindex, "findIndex", 1) - .method(Self::foreach, "forEach", 1) - .method(Self::includes, "includes", 1) - .method(Self::index_of, "indexOf", 1) - .method(Self::join, "join", 1) - .method(Self::keys, "keys", 0) - .method(Self::last_index_of, "lastIndexOf", 1) - .method(Self::map, "map", 1) - .method(Self::reduce, "reduce", 1) - .method(Self::reduceright, "reduceRight", 1) - .method(Self::reverse, "reverse", 0) - .method(Self::set, "set", 1) - .method(Self::slice, "slice", 2) - .method(Self::some, "some", 1) - .method(Self::sort, "sort", 1) - .method(Self::subarray, "subarray", 2) - .method(Self::values, "values", 0) - // 23.2.3.29 %TypedArray%.prototype.toString ( ) - // The initial value of the %TypedArray%.prototype.toString data property is the same - // built-in function object as the Array.prototype.toString method defined in 23.1.3.30. - .method(Array::to_string, "toString", 0) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::TYPED_ARRAY_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::TYPED_ARRAY_PROTOTYPE_STATIC_SHAPE, + ) + .static_accessor(Some(get_species), None) + .property(values_function) + .accessor(Some(get_buffer), None) + .accessor(Some(get_byte_length), None) + .accessor(Some(get_byte_offset), None) + .accessor(Some(get_length), None) + .accessor(Some(get_to_string_tag), None) + .static_method(Self::from, 1) + .static_method(Self::of, 0) + .method(Self::at, 1) + .method(Self::copy_within, 2) + .method(Self::entries, 0) + .method(Self::every, 1) + .method(Self::fill, 1) + .method(Self::filter, 1) + .method(Self::find, 1) + .method(Self::findindex, 1) + .method(Self::foreach, 1) + .method(Self::includes, 1) + .method(Self::index_of, 1) + .method(Self::join, 1) + .method(Self::keys, 0) + .method(Self::last_index_of, 1) + .method(Self::map, 1) + .method(Self::reduce, 1) + .method(Self::reduceright, 1) + .method(Self::reverse, 0) + .method(Self::set, 1) + .method(Self::slice, 2) + .method(Self::some, 1) + .method(Self::sort, 1) + .method(Self::subarray, 2) + .method(Self::values, 0) + // 23.2.3.29 %TypedArray%.prototype.toString ( ) + // The initial value of the %TypedArray%.prototype.toString data property is the same + // built-in function object as the Array.prototype.toString method defined in 23.1.3.30. + .method(Array::to_string, 0) + .build(); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/uri/mod.rs b/boa_engine/src/builtins/uri/mod.rs index d059fa9846..0c506cb94c 100644 --- a/boa_engine/src/builtins/uri/mod.rs +++ b/boa_engine/src/builtins/uri/mod.rs @@ -50,10 +50,10 @@ pub struct UriFunctions { impl Default for UriFunctions { fn default() -> Self { Self { - decode_uri: JsFunction::empty_intrinsic_function(false), - decode_uri_component: JsFunction::empty_intrinsic_function(false), - encode_uri: JsFunction::empty_intrinsic_function(false), - encode_uri_component: JsFunction::empty_intrinsic_function(false), + decode_uri: JsFunction::empty_intrinsic_function_static_shape(false), + decode_uri_component: JsFunction::empty_intrinsic_function_static_shape(false), + encode_uri: JsFunction::empty_intrinsic_function_static_shape(false), + encode_uri_component: JsFunction::empty_intrinsic_function_static_shape(false), } } } @@ -82,7 +82,7 @@ pub(crate) struct DecodeUri; impl IntrinsicObject for DecodeUri { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, decode_uri) + BuiltInBuilder::callable_intrinsic::(realm, decode_uri) .name(Self::NAME) .length(1) .build(); @@ -100,7 +100,7 @@ pub(crate) struct DecodeUriComponent; impl IntrinsicObject for DecodeUriComponent { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, decode_uri_component) + BuiltInBuilder::callable_intrinsic::(realm, decode_uri_component) .name(Self::NAME) .length(1) .build(); @@ -122,7 +122,7 @@ pub(crate) struct EncodeUri; impl IntrinsicObject for EncodeUri { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, encode_uri) + BuiltInBuilder::callable_intrinsic::(realm, encode_uri) .name(Self::NAME) .length(1) .build(); @@ -139,7 +139,7 @@ pub(crate) struct EncodeUriComponent; impl IntrinsicObject for EncodeUriComponent { fn init(realm: &Realm) { - BuiltInBuilder::callable_with_intrinsic::(realm, encode_uri_component) + BuiltInBuilder::callable_intrinsic::(realm, encode_uri_component) .name(Self::NAME) .length(1) .build(); diff --git a/boa_engine/src/builtins/weak/weak_ref.rs b/boa_engine/src/builtins/weak/weak_ref.rs index 4ec3bbe1ce..e126f7f23f 100644 --- a/boa_engine/src/builtins/weak/weak_ref.rs +++ b/boa_engine/src/builtins/weak/weak_ref.rs @@ -1,4 +1,5 @@ use boa_gc::{Finalize, Trace, WeakGc}; +use boa_macros::utf16; use boa_profiler::Profiler; use crate::{ @@ -7,7 +8,6 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::Attribute, realm::Realm, - symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, }; @@ -31,14 +31,14 @@ impl IntrinsicObject for WeakRef { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(realm) - .property( - JsSymbol::to_string_tag(), - "WeakRef", - Attribute::CONFIGURABLE, - ) - .method(Self::deref, "deref", 0) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::WEAK_REF_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::WEAK_REF_PROTOTYPE_STATIC_SHAPE, + ) + .property(utf16!("WeakRef")) + .method(Self::deref, 0) + .build(); } } diff --git a/boa_engine/src/builtins/weak_map/mod.rs b/boa_engine/src/builtins/weak_map/mod.rs index 66f5e5aed4..8d4b6f688c 100644 --- a/boa_engine/src/builtins/weak_map/mod.rs +++ b/boa_engine/src/builtins/weak_map/mod.rs @@ -17,7 +17,6 @@ use crate::{ property::Attribute, realm::Realm, string::utf16, - symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -33,17 +32,17 @@ impl IntrinsicObject for WeakMap { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(realm) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .method(Self::delete, "delete", 1) - .method(Self::get, "get", 1) - .method(Self::has, "has", 1) - .method(Self::set, "set", 2) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::WEAK_MAP_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::WEAK_MAP_PROTOTYPE_STATIC_SHAPE, + ) + .property(Self::NAME) + .method(Self::delete, 1) + .method(Self::get, 1) + .method(Self::has, 1) + .method(Self::set, 2) + .build(); } } diff --git a/boa_engine/src/builtins/weak_set/mod.rs b/boa_engine/src/builtins/weak_set/mod.rs index 13304a39f8..885f5556b8 100644 --- a/boa_engine/src/builtins/weak_set/mod.rs +++ b/boa_engine/src/builtins/weak_set/mod.rs @@ -14,7 +14,6 @@ use crate::{ property::Attribute, realm::Realm, string::utf16, - symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsValue, }; use boa_gc::{Finalize, Trace, WeakMap}; @@ -30,16 +29,16 @@ impl IntrinsicObject for WeakSet { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(Self::NAME, "init"); - BuiltInBuilder::from_standard_constructor::(realm) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .method(Self::add, "add", 1) - .method(Self::delete, "delete", 1) - .method(Self::has, "has", 1) - .build(); + BuiltInBuilder::from_standard_constructor_static_shape::( + realm, + &boa_builtins::WEAK_SET_CONSTRUCTOR_STATIC_SHAPE, + &boa_builtins::WEAK_SET_PROTOTYPE_STATIC_SHAPE, + ) + .property(Self::NAME) + .method(Self::add, 1) + .method(Self::delete, 1) + .method(Self::has, 1) + .build(); } } diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 92d3c825d5..56a9b4dee2 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -75,11 +75,18 @@ impl StandardConstructor { /// Build a constructor with a defined prototype. fn with_prototype(prototype: JsObject) -> Self { Self { - constructor: JsFunction::empty_intrinsic_function(true), + 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 @@ -156,67 +163,62 @@ pub struct StandardConstructors { impl Default for StandardConstructors { fn default() -> Self { Self { - object: StandardConstructor::with_prototype(JsObject::from_proto_and_data( - None, - ObjectData::object_prototype(), - )), + object: StandardConstructor::with_prototype( + JsObject::from_data_and_empty_static_shape(ObjectData::object_prototype()), + ), async_generator_function: StandardConstructor::default(), 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, + array: StandardConstructor::with_prototype(JsObject::from_data_and_empty_static_shape( 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), - )), - string: StandardConstructor::with_prototype(JsObject::from_proto_and_data( - None, - ObjectData::string("".into()), - )), - regexp: StandardConstructor::default(), - symbol: StandardConstructor::default(), - error: StandardConstructor::default(), - type_error: StandardConstructor::default(), - reference_error: StandardConstructor::default(), - range_error: StandardConstructor::default(), - syntax_error: StandardConstructor::default(), - eval_error: StandardConstructor::default(), - uri_error: StandardConstructor::default(), - aggregate_error: StandardConstructor::default(), - map: StandardConstructor::default(), - set: StandardConstructor::default(), - typed_array: StandardConstructor::default(), - typed_int8_array: StandardConstructor::default(), - typed_uint8_array: StandardConstructor::default(), - typed_uint8clamped_array: StandardConstructor::default(), - typed_int16_array: StandardConstructor::default(), - typed_uint16_array: StandardConstructor::default(), - typed_int32_array: StandardConstructor::default(), - typed_uint32_array: StandardConstructor::default(), - typed_bigint64_array: StandardConstructor::default(), - typed_biguint64_array: StandardConstructor::default(), - typed_float32_array: StandardConstructor::default(), - typed_float64_array: StandardConstructor::default(), - array_buffer: StandardConstructor::default(), - data_view: StandardConstructor::default(), + bigint: StandardConstructor::default_static_shape(), + number: StandardConstructor::with_prototype( + JsObject::from_data_and_empty_static_shape(ObjectData::number(0.0)), + ), + boolean: StandardConstructor::with_prototype( + JsObject::from_data_and_empty_static_shape(ObjectData::boolean(false)), + ), + 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_static_shape(), + type_error: StandardConstructor::default_static_shape(), + reference_error: StandardConstructor::default_static_shape(), + range_error: StandardConstructor::default_static_shape(), + syntax_error: StandardConstructor::default_static_shape(), + eval_error: StandardConstructor::default_static_shape(), + uri_error: StandardConstructor::default_static_shape(), + aggregate_error: StandardConstructor::default_static_shape(), + map: StandardConstructor::default_static_shape(), + set: StandardConstructor::default_static_shape(), + typed_array: StandardConstructor::default_static_shape(), + typed_int8_array: StandardConstructor::default_static_shape(), + typed_uint8_array: StandardConstructor::default_static_shape(), + typed_uint8clamped_array: StandardConstructor::default_static_shape(), + typed_int16_array: StandardConstructor::default_static_shape(), + typed_uint16_array: StandardConstructor::default_static_shape(), + typed_int32_array: StandardConstructor::default_static_shape(), + typed_uint32_array: StandardConstructor::default_static_shape(), + typed_bigint64_array: StandardConstructor::default_static_shape(), + typed_biguint64_array: StandardConstructor::default_static_shape(), + typed_float32_array: StandardConstructor::default_static_shape(), + typed_float64_array: StandardConstructor::default_static_shape(), + array_buffer: StandardConstructor::default_static_shape(), + data_view: StandardConstructor::default_static_shape(), date_time_format: StandardConstructor::default(), - promise: StandardConstructor::default(), - weak_ref: StandardConstructor::default(), - weak_map: StandardConstructor::default(), - weak_set: StandardConstructor::default(), + promise: StandardConstructor::default_static_shape(), + weak_ref: StandardConstructor::default_static_shape(), + weak_map: StandardConstructor::default_static_shape(), + weak_set: StandardConstructor::default_static_shape(), #[cfg(feature = "intl")] collator: StandardConstructor::default(), #[cfg(feature = "intl")] @@ -817,24 +819,24 @@ 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), + array_prototype_values: JsFunction::empty_intrinsic_function_static_shape(false), iterator_prototypes: IteratorPrototypes::default(), generator: JsObject::default(), async_generator: JsObject::default(), - eval: JsFunction::empty_intrinsic_function(false), + eval: JsFunction::empty_intrinsic_function_static_shape(false), uri_functions: UriFunctions::default(), - is_finite: JsFunction::empty_intrinsic_function(false), - is_nan: JsFunction::empty_intrinsic_function(false), - parse_float: JsFunction::empty_intrinsic_function(false), - parse_int: JsFunction::empty_intrinsic_function(false), + is_finite: JsFunction::empty_intrinsic_function_static_shape(false), + is_nan: JsFunction::empty_intrinsic_function_static_shape(false), + parse_float: JsFunction::empty_intrinsic_function_static_shape(false), + parse_int: JsFunction::empty_intrinsic_function_static_shape(false), #[cfg(feature = "annex-b")] - escape: JsFunction::empty_intrinsic_function(false), + escape: JsFunction::empty_intrinsic_function_static_shape(false), #[cfg(feature = "annex-b")] - unescape: JsFunction::empty_intrinsic_function(false), + unescape: JsFunction::empty_intrinsic_function_static_shape(false), #[cfg(feature = "intl")] intl: JsObject::default(), #[cfg(feature = "intl")] diff --git a/boa_engine/src/object/builtins/jsfunction.rs b/boa_engine/src/object/builtins/jsfunction.rs index 522242acc6..26a5fa6b98 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 56eed87144..26f54e066c 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::RootShape, + shape::{static_shape::StaticShape, RootShape}, JsPrototype, NativeObject, Object, PrivateName, PropertyMap, }; use crate::{ @@ -60,6 +60,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, @@ -120,6 +126,23 @@ impl JsObject { } } + pub(crate) fn from_data_and_empty_static_shape(data: ObjectData) -> Self { + Self { + inner: Gc::new(VTableObject { + object: GcRefCell::new(Object { + kind: data.kind, + properties: PropertyMap::new( + StaticShape::new(&boa_builtins::EMPTY_OBJECT_STATIC_SHAPE).into(), + ThinVec::default(), + ), + extensible: true, + private_elements: ThinVec::new(), + }), + vtable: data.internal_methods, + }), + } + } + /// Creates a new object with the provided prototype and object data. /// /// 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 74f123105a..8e6df742a9 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -26,7 +26,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::{ @@ -873,6 +873,18 @@ impl Debug for ObjectKind { } impl Object { + fn with_empty_shape() -> Self { + Self { + kind: ObjectKind::Ordinary, + properties: PropertyMap::new( + StaticShape::new(&boa_builtins::EMPTY_OBJECT_STATIC_SHAPE).into(), + ThinVec::default(), + ), + extensible: true, + private_elements: ThinVec::new(), + } + } + /// Returns a mutable reference to the kind of an object. pub(crate) fn kind_mut(&mut self) -> &mut ObjectKind { &mut self.kind @@ -1524,6 +1536,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() } @@ -1536,7 +1554,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.to_unique(prototype).into(); + } 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 9a06080a13..9ee225e0da 100644 --- a/boa_engine/src/object/property_map.rs +++ b/boa_engine/src/object/property_map.rs @@ -314,7 +314,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 => {} @@ -349,6 +363,17 @@ impl PropertyMap { return true; } + // TODO: optimization, we could inline `insert_property_transition` to avoid the check + if let Some(shape) = self.shape.as_static() { + let prototype = self + .storage + .pop() + .as_ref() + .and_then(JsValue::as_object) + .cloned(); + self.shape = shape.to_unique(prototype).into(); + } + let transition_key = TransitionKey { property_key: key.clone(), attributes, @@ -393,6 +418,17 @@ 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.to_unique(prototype).into(); + } + // 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 352ca91567..afbb477c05 100644 --- a/boa_engine/src/object/shape/mod.rs +++ b/boa_engine/src/object/shape/mod.rs @@ -4,6 +4,7 @@ pub(crate) mod property_table; mod root_shape; pub(crate) mod shared_shape; pub(crate) mod slot; +pub(crate) mod static_shape; pub(crate) mod unique_shape; pub use root_shape::RootShape; @@ -16,7 +17,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; @@ -56,6 +57,7 @@ pub(crate) struct ChangeTransition { enum Inner { Unique(UniqueShape), Shared(SharedShape), + Static(StaticShape), } /// Represents the shape of an object. @@ -84,12 +86,26 @@ impl Shape { 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); @@ -110,6 +126,7 @@ impl Shape { shape.into() } Inner::Unique(shape) => shape.insert_property_transition(key).into(), + Inner::Static(shape) => shape.insert_property_transition(key).into(), } } @@ -136,6 +153,7 @@ impl Shape { } } Inner::Unique(shape) => shape.change_attributes_transition(&key), + Inner::Static(shape) => shape.change_attributes_transition(&key), } } @@ -152,6 +170,7 @@ impl Shape { shape.into() } Inner::Unique(shape) => shape.remove_property_transition(key).into(), + Inner::Static(shape) => shape.remove_property_transition(key).into(), } } @@ -166,14 +185,16 @@ impl Shape { shape.into() } Inner::Unique(shape) => shape.change_prototype_transition(prototype).into(), + Inner::Static(shape) => shape.change_prototype_transition(prototype).into(), } } /// 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"), } } @@ -183,6 +204,7 @@ impl Shape { match &self.inner { Inner::Shared(shape) => shape.lookup(key), Inner::Unique(shape) => shape.lookup(key), + Inner::Static(shape) => shape.lookup(key), } } @@ -192,6 +214,7 @@ impl Shape { match &self.inner { Inner::Shared(shape) => shape.keys(), Inner::Unique(shape) => shape.keys(), + Inner::Static(shape) => shape.keys(), } } @@ -201,6 +224,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(), } } } @@ -220,3 +244,11 @@ impl From for Shape { } } } + +impl From for Shape { + fn from(shape: StaticShape) -> Self { + Self { + inner: Inner::Static(shape), + } + } +} diff --git a/boa_engine/src/object/shape/slot.rs b/boa_engine/src/object/shape/slot.rs index 64fb3c8f43..16800b72dc 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 0000000000..0277ef4d74 --- /dev/null +++ b/boa_engine/src/object/shape/static_shape.rs @@ -0,0 +1,149 @@ +// Temp allow +#![allow(dead_code)] +#![allow(clippy::needless_pass_by_value)] + +use std::fmt::Debug; + +use boa_builtins::{EncodedStaticPropertyKey, StaticPropertyKey}; +use boa_gc::{Finalize, Trace}; + +use crate::{ + object::shape::property_table::PropertyTableInner, property::PropertyKey, symbol::WellKnown, + JsString, JsSymbol, +}; + +use super::{ + shared_shape::TransitionKey, slot::SlotAttributes, ChangeTransition, JsPrototype, Shape, Slot, + UniqueShape, +}; + +pub(crate) type StaticShapeInner = &'static boa_builtins::StaticShape; + +/// TODO: doc +fn from_static_property_key(key: StaticPropertyKey) -> PropertyKey { + match key { + boa_builtins::StaticPropertyKey::String(index) => { + PropertyKey::String(unsafe { JsString::from_index(index) }) + } + boa_builtins::StaticPropertyKey::Symbol(s) => { + let symbol = match WellKnown::try_from(s).expect("should be an well known symbol") { + WellKnown::AsyncIterator => JsSymbol::async_iterator(), + WellKnown::HasInstance => JsSymbol::has_instance(), + WellKnown::IsConcatSpreadable => JsSymbol::is_concat_spreadable(), + WellKnown::Iterator => JsSymbol::iterator(), + WellKnown::Match => JsSymbol::r#match(), + WellKnown::MatchAll => JsSymbol::match_all(), + WellKnown::Replace => JsSymbol::replace(), + WellKnown::Search => JsSymbol::search(), + WellKnown::Species => JsSymbol::species(), + WellKnown::Split => JsSymbol::split(), + WellKnown::ToPrimitive => JsSymbol::to_primitive(), + WellKnown::ToStringTag => JsSymbol::to_string_tag(), + WellKnown::Unscopables => JsSymbol::unscopables(), + }; + + PropertyKey::Symbol(symbol) + } + } +} + +/// TODO: doc +fn to_static_property_key(key: &PropertyKey) -> Option { + match key { + PropertyKey::String(s) => Some(StaticPropertyKey::String(s.as_static_string_index()?)), + PropertyKey::Symbol(s) => Some(StaticPropertyKey::Symbol(s.hash().try_into().ok()?)), + PropertyKey::Index(_) => None, + } +} + +/// Represents a [`Shape`] that is not shared with any other object. +/// +/// This is useful for objects that are inherently unique like, +/// the builtin object. +/// +/// Cloning this does a shallow clone. +#[derive(Debug, Clone, Trace, Finalize)] +pub(crate) struct StaticShape { + inner: StaticShapeInner, +} + +impl StaticShape { + /// Create a new [`UniqueShape`]. + pub(crate) const fn new(inner: StaticShapeInner) -> Self { + Self { inner } + } + + /// Inserts a new property into the [`UniqueShape`]. + pub(crate) fn insert_property_transition(&self, _key: TransitionKey) -> UniqueShape { + todo!() + } + + /// Remove a property from the [`UniqueShape`]. + /// + /// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned. + pub(crate) fn remove_property_transition(&self, _key: &PropertyKey) -> UniqueShape { + todo!() + } + + /// Does a property lookup on the [`UniqueShape`] returning the [`Slot`] where it's + /// located or [`None`] otherwise. + pub(crate) fn lookup(&self, key: &PropertyKey) -> Option { + let key = to_static_property_key(key)?; + + let (index, attributes) = self.inner.get(key)?; + + Some(Slot { + index: u32::from(index), + attributes: SlotAttributes::from_bits_retain(attributes.bits()), + }) + } + + /// Change the attributes of a property from the [`UniqueShape`]. + /// + /// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned. + /// + /// NOTE: This assumes that the property had already been inserted. + pub(crate) fn change_attributes_transition( + &self, + _key: &TransitionKey, + ) -> ChangeTransition { + 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(EncodedStaticPropertyKey::decode) + .map(from_static_property_key) + .collect() + } + + /// TODO: doc + pub(crate) fn to_unique(&self, prototype: JsPrototype) -> UniqueShape { + // TODO: optimization, preallocate capacity. + // TODO: optimization, direct iniitialize the map. + let mut property_table = PropertyTableInner::default(); + for (key, (_, slot_attributes)) in &self.inner.property_table { + property_table.insert( + from_static_property_key(key.decode()), + SlotAttributes::from_bits_retain(slot_attributes.bits()), + ); + } + UniqueShape::new(prototype, property_table) + } + + /// Return location in memory of the [`UniqueShape`]. + pub(crate) fn to_addr_usize(&self) -> usize { + let ptr: *const _ = self.inner; + ptr as usize + } +} diff --git a/boa_engine/src/string/common.rs b/boa_engine/src/string/common.rs index 50a3b711e8..3fabd2f543 100644 --- a/boa_engine/src/string/common.rs +++ b/boa_engine/src/string/common.rs @@ -6,6 +6,8 @@ use super::JsString; use boa_macros::utf16; use rustc_hash::{FxHashMap, FxHasher}; +use boa_builtins::RAW_STATICS; + macro_rules! well_known_statics { ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { $( @@ -134,466 +136,3 @@ thread_local! { constants }; } - -/// Array of raw static strings that aren't reference counted. -/// -/// The macro `static_strings` automatically sorts the array of strings, making it faster -/// for searches by using `binary_search`. -const RAW_STATICS: &[&[u16]] = &[ - utf16!(""), - // Misc - utf16!(","), - utf16!(":"), - // Generic use - utf16!("name"), - utf16!("length"), - utf16!("arguments"), - utf16!("prototype"), - utf16!("constructor"), - utf16!("return"), - utf16!("throw"), - utf16!("global"), - utf16!("globalThis"), - // typeof - utf16!("null"), - utf16!("undefined"), - utf16!("number"), - utf16!("string"), - utf16!("symbol"), - utf16!("bigint"), - utf16!("object"), - utf16!("function"), - // Property descriptor - utf16!("value"), - utf16!("get"), - utf16!("set"), - utf16!("writable"), - utf16!("enumerable"), - utf16!("configurable"), - // Object object - utf16!("Object"), - utf16!("assign"), - utf16!("create"), - utf16!("toString"), - utf16!("valueOf"), - utf16!("is"), - utf16!("seal"), - utf16!("isSealed"), - utf16!("freeze"), - utf16!("isFrozen"), - utf16!("isExtensible"), - utf16!("hasOwnProperty"), - utf16!("isPrototypeOf"), - utf16!("setPrototypeOf"), - utf16!("getPrototypeOf"), - utf16!("defineProperty"), - utf16!("defineProperties"), - utf16!("deleteProperty"), - utf16!("construct"), - utf16!("hasOwn"), - utf16!("ownKeys"), - utf16!("keys"), - utf16!("values"), - utf16!("entries"), - utf16!("fromEntries"), - // Function object - utf16!("Function"), - utf16!("apply"), - utf16!("bind"), - utf16!("call"), - // Generator object - utf16!("Generator"), - // Array object - utf16!("Array"), - utf16!("at"), - utf16!("from"), - utf16!("isArray"), - utf16!("of"), - utf16!("copyWithin"), - utf16!("every"), - utf16!("fill"), - utf16!("filter"), - utf16!("find"), - utf16!("findIndex"), - utf16!("findLast"), - utf16!("findLastIndex"), - utf16!("flat"), - utf16!("flatMap"), - utf16!("forEach"), - utf16!("includes"), - utf16!("indexOf"), - utf16!("join"), - utf16!("map"), - utf16!("next"), - utf16!("reduce"), - utf16!("reduceRight"), - utf16!("reverse"), - utf16!("shift"), - utf16!("slice"), - utf16!("splice"), - utf16!("some"), - utf16!("sort"), - utf16!("unshift"), - utf16!("push"), - utf16!("pop"), - // String object - utf16!("String"), - utf16!("charAt"), - utf16!("charCodeAt"), - utf16!("codePointAt"), - utf16!("concat"), - utf16!("endsWith"), - utf16!("fromCharCode"), - utf16!("fromCodePoint"), - utf16!("lastIndexOf"), - utf16!("match"), - utf16!("matchAll"), - utf16!("normalize"), - utf16!("padEnd"), - utf16!("padStart"), - utf16!("raw"), - utf16!("repeat"), - utf16!("replace"), - utf16!("replaceAll"), - utf16!("search"), - utf16!("split"), - utf16!("startsWith"), - utf16!("substr"), - utf16!("substring"), - utf16!("toLocaleString"), - utf16!("toLowerCase"), - utf16!("toUpperCase"), - utf16!("trim"), - utf16!("trimEnd"), - utf16!("trimStart"), - // Number object - utf16!("Number"), - utf16!("Infinity"), - utf16!("NaN"), - utf16!("parseInt"), - utf16!("parseFloat"), - utf16!("isFinite"), - utf16!("isNaN"), - utf16!("EPSILON"), - utf16!("MAX_SAFE_INTEGER"), - utf16!("MIN_SAFE_INTEGER"), - utf16!("MAX_VALUE"), - utf16!("MIN_VALUE"), - utf16!("isSafeInteger"), - utf16!("isInteger"), - utf16!("toExponential"), - utf16!("toFixed"), - utf16!("toPrecision"), - // Boolean object - utf16!("Boolean"), - // BigInt object - utf16!("BigInt"), - utf16!("asIntN"), - utf16!("asUintN"), - // RegExp object - utf16!("RegExp"), - utf16!("exec"), - utf16!("test"), - utf16!("flags"), - utf16!("index"), - utf16!("lastIndex"), - utf16!("hasIndices"), - utf16!("ignoreCase"), - utf16!("multiline"), - utf16!("dotAll"), - utf16!("unicode"), - utf16!("sticky"), - utf16!("source"), - utf16!("get hasIndices"), - utf16!("get global"), - utf16!("get ignoreCase"), - utf16!("get multiline"), - utf16!("get dotAll"), - utf16!("get unicode"), - utf16!("get sticky"), - utf16!("get flags"), - utf16!("get source"), - // Symbol object - utf16!("Symbol"), - utf16!("for"), - utf16!("keyFor"), - utf16!("description"), - utf16!("asyncIterator"), - utf16!("hasInstance"), - utf16!("species"), - utf16!("unscopables"), - utf16!("iterator"), - utf16!("toStringTag"), - utf16!("toPrimitive"), - utf16!("get description"), - // Map object - utf16!("Map"), - utf16!("clear"), - utf16!("delete"), - utf16!("has"), - utf16!("size"), - // Set object - utf16!("Set"), - utf16!("add"), - // Reflect object - utf16!("Reflect"), - // Proxy object - utf16!("Proxy"), - utf16!("revocable"), - // Error objects - utf16!("Error"), - utf16!("AggregateError"), - utf16!("TypeError"), - utf16!("RangeError"), - utf16!("SyntaxError"), - utf16!("ReferenceError"), - utf16!("EvalError"), - utf16!("ThrowTypeError"), - utf16!("URIError"), - utf16!("message"), - // Date object - utf16!("Date"), - utf16!("toJSON"), - utf16!("getDate"), - utf16!("getDay"), - utf16!("getFullYear"), - utf16!("getHours"), - utf16!("getMilliseconds"), - utf16!("getMinutes"), - utf16!("getMonth"), - utf16!("getSeconds"), - utf16!("getTime"), - utf16!("getYear"), - utf16!("getUTCDate"), - utf16!("getUTCDay"), - utf16!("getUTCFullYear"), - utf16!("getUTCHours"), - utf16!("getUTCMinutes"), - utf16!("getUTCMonth"), - utf16!("getUTCSeconds"), - utf16!("setDate"), - utf16!("setFullYear"), - utf16!("setHours"), - utf16!("setMilliseconds"), - utf16!("setMinutes"), - utf16!("setMonth"), - utf16!("setSeconds"), - utf16!("setYear"), - utf16!("setTime"), - utf16!("setUTCDate"), - utf16!("setUTCFullYear"), - utf16!("setUTCHours"), - utf16!("setUTCMinutes"), - utf16!("setUTCMonth"), - utf16!("setUTCSeconds"), - utf16!("toDateString"), - utf16!("toGMTString"), - utf16!("toISOString"), - utf16!("toTimeString"), - utf16!("toUTCString"), - utf16!("now"), - utf16!("UTC"), - // JSON object - utf16!("JSON"), - utf16!("parse"), - utf16!("stringify"), - // Iterator object - utf16!("Array Iterator"), - utf16!("Set Iterator"), - utf16!("String Iterator"), - utf16!("Map Iterator"), - utf16!("For In Iterator"), - // Math object - utf16!("Math"), - utf16!("LN10"), - utf16!("LN2"), - utf16!("LOG10E"), - utf16!("LOG2E"), - utf16!("PI"), - utf16!("SQRT1_2"), - utf16!("SQRT2"), - utf16!("abs"), - utf16!("acos"), - utf16!("acosh"), - utf16!("asin"), - utf16!("asinh"), - utf16!("atan"), - utf16!("atanh"), - utf16!("atan2"), - utf16!("cbrt"), - utf16!("ceil"), - utf16!("clz32"), - utf16!("cos"), - utf16!("cosh"), - utf16!("exp"), - utf16!("expm1"), - utf16!("floor"), - utf16!("fround"), - utf16!("hypot"), - utf16!("imul"), - utf16!("log"), - utf16!("log1p"), - utf16!("log10"), - utf16!("log2"), - utf16!("max"), - utf16!("min"), - utf16!("pow"), - utf16!("random"), - utf16!("round"), - utf16!("sign"), - utf16!("sin"), - utf16!("sinh"), - utf16!("sqrt"), - utf16!("tan"), - utf16!("tanh"), - utf16!("trunc"), - // Intl object - utf16!("Intl"), - utf16!("DateTimeFormat"), - // TypedArray object - utf16!("TypedArray"), - utf16!("ArrayBuffer"), - utf16!("Int8Array"), - utf16!("Uint8Array"), - utf16!("Int16Array"), - utf16!("Uint16Array"), - utf16!("Int32Array"), - utf16!("Uint32Array"), - utf16!("BigInt64Array"), - utf16!("BigUint64Array"), - utf16!("Float32Array"), - utf16!("Float64Array"), - utf16!("buffer"), - utf16!("byteLength"), - utf16!("byteOffset"), - utf16!("isView"), - utf16!("subarray"), - utf16!("get byteLength"), - utf16!("get buffer"), - utf16!("get byteOffset"), - utf16!("get size"), - utf16!("get length"), - // DataView object - utf16!("DataView"), - utf16!("getBigInt64"), - utf16!("getBigUint64"), - utf16!("getFloat32"), - utf16!("getFloat64"), - utf16!("getInt8"), - utf16!("getInt16"), - utf16!("getInt32"), - utf16!("getUint8"), - utf16!("getUint16"), - utf16!("getUint32"), - utf16!("setBigInt64"), - utf16!("setBigUint64"), - utf16!("setFloat32"), - utf16!("setFloat64"), - utf16!("setInt8"), - utf16!("setInt16"), - utf16!("setInt32"), - utf16!("setUint8"), - utf16!("setUint16"), - utf16!("setUint32"), - // Console object - utf16!("console"), - utf16!("assert"), - utf16!("debug"), - utf16!("error"), - utf16!("info"), - utf16!("trace"), - utf16!("warn"), - utf16!("exception"), - utf16!("count"), - utf16!("countReset"), - utf16!("group"), - utf16!("groupCollapsed"), - utf16!("groupEnd"), - utf16!("time"), - utf16!("timeLog"), - utf16!("timeEnd"), - utf16!("dir"), - utf16!("dirxml"), - // Minified name - utf16!("a"), - utf16!("b"), - utf16!("c"), - utf16!("d"), - utf16!("e"), - utf16!("f"), - utf16!("g"), - utf16!("h"), - utf16!("i"), - utf16!("j"), - utf16!("k"), - utf16!("l"), - utf16!("m"), - utf16!("n"), - utf16!("o"), - utf16!("p"), - utf16!("q"), - utf16!("r"), - utf16!("s"), - utf16!("t"), - utf16!("u"), - utf16!("v"), - utf16!("w"), - utf16!("x"), - utf16!("y"), - utf16!("z"), - utf16!("A"), - utf16!("B"), - utf16!("C"), - utf16!("D"), - utf16!("E"), - utf16!("F"), - utf16!("G"), - utf16!("H"), - utf16!("I"), - utf16!("J"), - utf16!("K"), - utf16!("L"), - utf16!("M"), - utf16!("N"), - utf16!("O"), - utf16!("P"), - utf16!("Q"), - utf16!("R"), - utf16!("S"), - utf16!("T"), - utf16!("U"), - utf16!("V"), - utf16!("W"), - utf16!("X"), - utf16!("Y"), - utf16!("Z"), - utf16!("_"), - utf16!("$"), - // Well known symbols - utf16!("Symbol.asyncIterator"), - utf16!("[Symbol.asyncIterator]"), - utf16!("Symbol.hasInstance"), - utf16!("[Symbol.hasInstance]"), - utf16!("Symbol.isConcatSpreadable"), - utf16!("[Symbol.isConcatSpreadable]"), - utf16!("Symbol.iterator"), - utf16!("[Symbol.iterator]"), - utf16!("Symbol.match"), - utf16!("[Symbol.match]"), - utf16!("Symbol.matchAll"), - utf16!("[Symbol.matchAll]"), - utf16!("Symbol.replace"), - utf16!("[Symbol.replace]"), - utf16!("Symbol.search"), - utf16!("[Symbol.search]"), - utf16!("Symbol.species"), - utf16!("[Symbol.species]"), - utf16!("Symbol.split"), - utf16!("[Symbol.split]"), - utf16!("Symbol.toPrimitive"), - utf16!("[Symbol.toPrimitive]"), - utf16!("Symbol.toStringTag"), - utf16!("[Symbol.toStringTag]"), - utf16!("Symbol.unscopables"), - utf16!("[Symbol.unscopables]"), -]; diff --git a/boa_engine/src/string/mod.rs b/boa_engine/src/string/mod.rs index 90c5aa79b2..750087a388 100644 --- a/boa_engine/src/string/mod.rs +++ b/boa_engine/src/string/mod.rs @@ -28,6 +28,7 @@ use crate::{ tagged::{Tagged, UnwrappedTagged}, JsBigInt, }; +use boa_builtins::{StaticString, RAW_STATICS}; use boa_gc::{empty_trace, Finalize, Trace}; pub use boa_macros::utf16; @@ -616,6 +617,27 @@ impl JsString { ptr: Tagged::from_non_null(ptr), } } + + pub(crate) fn as_static_string_index(&self) -> Option { + if let UnwrappedTagged::Tag(index) = self.ptr.unwrap() { + return Some(index as u16); + } + None + } + pub(crate) const unsafe fn from_index(index: u16) -> Self { + debug_assert!((index as usize) < RAW_STATICS.len()); + Self { + ptr: Tagged::from_tag(index as usize), + } + } +} + +impl From for JsString { + fn from(string: StaticString) -> Self { + Self { + ptr: Tagged::from_tag(string.index() as usize), + } + } } impl AsRef<[u16]> for JsString { diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index 925dd6f9e0..423fed6462 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 c632c7412f..ae31254893 100644 --- a/boa_engine/src/vm/opcode/set/property.rs +++ b/boa_engine/src/vm/opcode/set/property.rs @@ -94,9 +94,11 @@ impl Operation for SetPropertyByValue { // Cannot use fast path if the [[prototype]] is a proxy object, // 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());