Skip to content

Commit

Permalink
Implement static shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Apr 23, 2023
1 parent be6ad76 commit 5c0899c
Show file tree
Hide file tree
Showing 17 changed files with 561 additions and 18 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"boa_ast",
"boa_builtins",
"boa_cli",
"boa_engine",
"boa_examples",
Expand All @@ -27,6 +28,7 @@ description = "Boa is a Javascript lexer, parser and compiler written in Rust. C

[workspace.dependencies]
boa_engine = { version = "0.16.0", path = "boa_engine" }
boa_builtins = { version = "0.16.0", path = "boa_builtins" }
boa_interner = { version = "0.16.0", path = "boa_interner" }
boa_gc = { version = "0.16.0", path = "boa_gc" }
boa_profiler = { version = "0.16.0", path = "boa_profiler" }
Expand Down
21 changes: 21 additions & 0 deletions boa_builtins/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "boa_builtins"
description = "Builtins of the Boa JavaScript engine."
publish = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
build = "build.rs"

[dependencies]
bitflags = "2.1.0"
phf = "^0.11.1"
phf_shared = "^0.11.1"

[build-dependencies]
boa_macros.workspace = true
phf_codegen = "^0.11.1"
phf_shared = "^0.11.1"
1 change: 1 addition & 0 deletions boa_builtins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TOOD
192 changes: 192 additions & 0 deletions boa_builtins/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::{self, BufWriter, Write};
use std::path::Path;
use std::{env, fmt};

use phf_shared::{FmtConst, PhfBorrow, PhfHash};

use boa_macros::utf16;

/// List of well known symbols.
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
#[allow(dead_code)]
enum WellKnown {
AsyncIterator,
HasInstance,
IsConcatSpreadable,
Iterator,
Match,
MatchAll,
Replace,
Search,
Species,
Split,
ToPrimitive,
ToStringTag,
Unscopables,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum StaticPropertyKey<'a> {
String(&'a [u16]),
Symbol(u8),
}

impl PhfHash for StaticPropertyKey<'static> {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}

impl FmtConst for StaticPropertyKey<'static> {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if matches!(self, StaticPropertyKey::String(_)) {
f.write_str("StaticPropertyKey::String(")?;
} else {
f.write_str("StaticPropertyKey::Symbol(")?;
}

match self {
StaticPropertyKey::String(s) => write!(f, "&{:?})", s),
StaticPropertyKey::Symbol(s) => write!(f, "{})", s),
}
}
}

impl<'b, 'a: 'b> PhfBorrow<StaticPropertyKey<'b>> for StaticPropertyKey<'a> {
fn borrow(&self) -> &StaticPropertyKey<'b> {
self
}
}

fn main() -> io::Result<()> {
let file = Path::new(&env::var("OUT_DIR").unwrap()).join("static_shapes_codegen.rs");
let mut file = BufWriter::new(File::create(file)?);

// writeln!(&mut file, "\n\n")?;

writeln!(
&mut file,
"pub static EMPTY_OBJECT_STATIC_SHAPE: ::phf::OrderedMap::<StaticPropertyKey<'static>, (u32, Attribute)> = \n{};",
phf_codegen::OrderedMap::<StaticPropertyKey<'static>>::new()
.build()
)?;

writeln!(
&mut file,
"pub static JSON_OBJECT_STATIC_SHAPE: ::phf::OrderedMap::<StaticPropertyKey<'static>, (u32, Attribute)> = \n{};",
phf_codegen::OrderedMap::new()
.entry(StaticPropertyKey::String(utf16!("parse")), "(0, Attribute::WRITABLE.union(Attribute::CONFIGURABLE))")
.entry(StaticPropertyKey::String(utf16!("stringify")), "(1, Attribute::WRITABLE.union(Attribute::CONFIGURABLE))")
.entry(StaticPropertyKey::Symbol(WellKnown::ToStringTag as u8), "(2, Attribute::WRITABLE.union(Attribute::CONFIGURABLE))")
.build()
)?;

// writeln!(
// &mut file,
// "static BYTE_STR_KEYS: ::phf::Map<&[u8], u32> = \n{};",
// phf_codegen::Map::<&[u8]>::new()
// .entry(b"foo", "0")
// .entry(b"bar", "1")
// .entry(b"baz", "2")
// .entry(b"quux4555", "3")
// .build()
// )?;

// writeln!(
// &mut file,
// "static SET: ::phf::Set<u32> = \n{};",
// phf_codegen::Set::new()
// .entry(1u32)
// .entry(2u32)
// .entry(3u32)
// .build()
// )?;

// writeln!(
// &mut file,
// "static ORDERED_MAP: ::phf::OrderedMap<u32, &'static str> = \n{};",
// phf_codegen::OrderedMap::new()
// .entry(1u32, "\"a\"")
// .entry(2u32, "\"b\"")
// .entry(3u32, "\"c\"")
// .build()
// )?;

// writeln!(
// &mut file,
// "static ORDERED_SET: ::phf::OrderedSet<u32> = \n{};",
// phf_codegen::OrderedSet::new()
// .entry(1u32)
// .entry(2u32)
// .entry(3u32)
// .build()
// )?;

// writeln!(
// &mut file,
// "static STR_KEYS: ::phf::Map<&'static str, u32> = \n{};",
// phf_codegen::Map::new()
// .entry("a", "1")
// .entry("b", "2")
// .entry("c", "3")
// .build()
// )?;

// write!(
// &mut file,
// "static UNICASE_MAP: ::phf::Map<::unicase::UniCase<&'static str>, &'static str> = \n{};",
// phf_codegen::Map::new()
// .entry(UniCase::new("abc"), "\"a\"")
// .entry(UniCase::new("DEF"), "\"b\"")
// .build()
// )?;

// write!(
// &mut file,
// "static UNCASED_MAP: ::phf::Map<&'static ::uncased::UncasedStr, &'static str> = \n{};",
// phf_codegen::Map::new()
// .entry(UncasedStr::new("abc"), "\"a\"")
// .entry(UncasedStr::new("DEF"), "\"b\"")
// .build()
// )?;

// //u32 is used here purely for a type that impls `Hash+PhfHash+Eq+fmt::Debug`, but is not required for the empty test itself
// writeln!(
// &mut file,
// "static EMPTY: ::phf::Map<u32, u32> = \n{};",
// phf_codegen::Map::<u32>::new().build()
// )?;

// writeln!(
// &mut file,
// "static EMPTY_ORDERED: ::phf::OrderedMap<u32, u32> = \n{};",
// phf_codegen::OrderedMap::<u32>::new().build()
// )?;

// writeln!(
// &mut file,
// "static ARRAY_KEYS: ::phf::Map<[u8; 3], u32> = \n{};",
// phf_codegen::Map::<[u8; 3]>::new()
// .entry(*b"foo", "0")
// .entry(*b"bar", "1")
// .entry(*b"baz", "2")
// .build()
// )?;

// // key type required here as it will infer `&'static [u8; 3]` instead
// writeln!(
// &mut file,
// "static BYTE_STR_KEYS: ::phf::Map<&[u8], u32> = \n{};",
// phf_codegen::Map::<&[u8]>::new()
// .entry(b"foo", "0")
// .entry(b"bar", "1")
// .entry(b"baz", "2")
// .entry(b"quux", "3")
// .build()
// )
Ok(())
}
47 changes: 47 additions & 0 deletions boa_builtins/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::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(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StaticPropertyKey<'a> {
String(&'a [u16]),
Symbol(u8),
}

impl PhfHash for StaticPropertyKey<'static> {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}

impl<'b, 'a: 'b> PhfBorrow<StaticPropertyKey<'b>> for StaticPropertyKey<'a> {
#[inline]
fn borrow(&self) -> &StaticPropertyKey<'b> {
self
}
}

pub type Slot = (u32, Attribute);
pub type StaticShape = phf::OrderedMap<StaticPropertyKey<'static>, Slot>;

include!(concat!(env!("OUT_DIR"), "/static_shapes_codegen.rs"));

// static NUMBER_BUITIN_OBJECT_STATIC_SHAPE_REF: &StaticShape = &NUMBER_BUITIN_OBJECT_STATIC_SHAPE;
6 changes: 5 additions & 1 deletion boa_cli/src/debug/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ fn r#type(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValu

Ok(if shape.is_shared() {
js_string!("shared")
} else {
} else if shape.is_unique() {
js_string!("unique")
} else if shape.is_static() {
js_string!("static")
} else {
unreachable!("shapes can only be shared, unique, or static")
}
.into())
}
Expand Down
2 changes: 2 additions & 0 deletions boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ annex-b = ["boa_parser/annex-b"]

[dependencies]
boa_interner.workspace = true
boa_builtins.workspace = true
boa_gc = { workspace = true, features = [ "thinvec" ] }
boa_profiler.workspace = true
boa_macros.workspace = true
Expand Down Expand Up @@ -77,6 +78,7 @@ num_enum = "0.6.1"
pollster = "0.3.0"
thin-vec = "0.2.12"
itertools = { version = "0.10.5", default-features = false }
phf = "*"

# intl deps
boa_icu_provider = { workspace = true, optional = true }
Expand Down
19 changes: 9 additions & 10 deletions boa_engine/src/builtins/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ use crate::{
error::JsNativeError,
js_string,
object::{JsObject, RecursionLimiter},
property::{Attribute, PropertyNameKind},
property::PropertyNameKind,
realm::Realm,
string::{utf16, CodePoint},
symbol::JsSymbol,
value::IntegerOrInfinity,
Context, JsArgs, JsResult, JsString, JsValue,
};
Expand All @@ -49,14 +48,14 @@ impl IntrinsicObject for Json {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");

let to_string_tag = JsSymbol::to_string_tag();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;

BuiltInBuilder::with_intrinsic::<Self>(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::<Self>(
realm,
&boa_builtins::JSON_OBJECT_STATIC_SHAPE,
)
.static_method(Self::parse, js_string!("parse"), 2)
.static_method(Self::stringify, js_string!("stringify"), 3)
.static_property(Self::NAME)
.build();
}

fn get(intrinsics: &Intrinsics) -> JsObject {
Expand Down

0 comments on commit 5c0899c

Please sign in to comment.