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 26, 2023
1 parent ed37448 commit b26077c
Show file tree
Hide file tree
Showing 18 changed files with 592 additions and 19 deletions.
22 changes: 22 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 Down Expand Up @@ -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" }
Expand Down
22 changes: 22 additions & 0 deletions boa_builtins/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "boa_builtins"
description = "Builtins of the Boa JavaScript engine."
publish = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
build = "build.rs"

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

[build-dependencies]
boa_macros.workspace = true
phf_codegen = "^0.11.1"
phf_shared = "^0.11.1"
bitflags = "2.1.0"
1 change: 1 addition & 0 deletions boa_builtins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TOOD
177 changes: 177 additions & 0 deletions boa_builtins/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
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;
}
}

/// 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
}
}

trait ToPropertyKey {
fn to_property_key(self) -> StaticPropertyKey<'static>;
}

impl ToPropertyKey for &'static [u16] {
fn to_property_key(self) -> StaticPropertyKey<'static> {
StaticPropertyKey::String(self)
}
}

impl ToPropertyKey for WellKnown {
fn to_property_key(self) -> StaticPropertyKey<'static> {
StaticPropertyKey::Symbol(self as u8)
}
}

struct BuiltInBuilder<'a> {
file: &'a mut BufWriter<File>,

name: &'static str,
map: phf_codegen::OrderedMap<StaticPropertyKey<'static>>,

slot_index: usize,
}

impl<'a> BuiltInBuilder<'a> {
fn new(file: &'a mut BufWriter<File>, name: &'static str) -> Self {
Self {
file,
name,
map: phf_codegen::OrderedMap::new(),
slot_index: 0,
}
}

fn method<K>(&mut self, key: K) -> &mut Self
where
K: ToPropertyKey,
{
let key = key.to_property_key();
let attributes = Attribute::WRITABLE | Attribute::CONFIGURABLE;
self.map.entry(
key,
&format!(
"({}, Attribute::from_bits_retain({}))",
self.slot_index,
attributes.bits()
),
);
self.slot_index += 1;
self
}

fn property<K>(&mut self, key: K, attributes: Attribute) -> &mut Self
where
K: ToPropertyKey,
{
let key = key.to_property_key();
self.map.entry(
key,
&format!(
"({}, Attribute::from_bits_retain({}))",
self.slot_index,
attributes.bits()
),
);
self.slot_index += 1;
self
}

fn build(&mut self) -> io::Result<()> {
writeln!(
self.file,
"pub static {}_STATIC_SHAPE: ::phf::OrderedMap::<StaticPropertyKey<'static>, (u32, Attribute)> = \n{};",
self.name,
self.map.build(),
)
}
}

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

BuiltInBuilder::new(file, "EMPTY_OBJECT").build()?;

BuiltInBuilder::new(file, "JSON_OBJECT")
.method(utf16!("parse"))
.method(utf16!("stringify"))
.property(
WellKnown::ToStringTag,
Attribute::WRITABLE | Attribute::CONFIGURABLE,
)
.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
1 change: 1 addition & 0 deletions boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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
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 b26077c

Please sign in to comment.