mirror of https://github.com/boa-dev/boa.git
Haled Odat
2 years ago
55 changed files with 3179 additions and 1511 deletions
@ -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" |
@ -0,0 +1,181 @@ |
|||||||
|
use std::{ |
||||||
|
fmt::{self, Debug}, |
||||||
|
hash::{Hash, Hasher}, |
||||||
|
}; |
||||||
|
|
||||||
|
use bitflags::bitflags; |
||||||
|
use phf::PhfHash; |
||||||
|
use phf_shared::PhfBorrow; |
||||||
|
|
||||||
|
bitflags! { |
||||||
|
/// This struct constains the property flags as described in the ECMAScript specification.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub struct Attribute: u8 { |
||||||
|
/// The `Writable` attribute decides whether the value associated with the property can be changed or not, from its initial value.
|
||||||
|
const WRITABLE = 0b0000_0001; |
||||||
|
|
||||||
|
/// If the property can be enumerated by a `for-in` loop.
|
||||||
|
const ENUMERABLE = 0b0000_0010; |
||||||
|
|
||||||
|
/// If the property descriptor can be changed later.
|
||||||
|
const CONFIGURABLE = 0b0000_0100; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub struct StaticString { |
||||||
|
index: u16, |
||||||
|
} |
||||||
|
|
||||||
|
impl StaticString { |
||||||
|
#[inline] |
||||||
|
pub fn index(self) -> u16 { |
||||||
|
self.index |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Copy)] |
||||||
|
pub struct EncodedStaticPropertyKey(u16); |
||||||
|
|
||||||
|
impl EncodedStaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
pub fn decode(&self) -> StaticPropertyKey { |
||||||
|
let value = self.0 >> 1; |
||||||
|
if self.0 & 1 == 0 { |
||||||
|
StaticPropertyKey::String(value) |
||||||
|
} else { |
||||||
|
StaticPropertyKey::Symbol(value as u8) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const fn string(index: u16) -> EncodedStaticPropertyKey { |
||||||
|
debug_assert!(index < 2u16.pow(15)); |
||||||
|
|
||||||
|
EncodedStaticPropertyKey(index << 1) |
||||||
|
} |
||||||
|
|
||||||
|
const fn symbol(index: u8) -> EncodedStaticPropertyKey { |
||||||
|
EncodedStaticPropertyKey(((index as u16) << 1) | 1) |
||||||
|
} |
||||||
|
|
||||||
|
impl Debug for EncodedStaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
self.decode().fmt(f) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)] |
||||||
|
pub enum StaticPropertyKey { |
||||||
|
String(u16), |
||||||
|
Symbol(u8), |
||||||
|
} |
||||||
|
|
||||||
|
impl StaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
pub fn encode(self) -> EncodedStaticPropertyKey { |
||||||
|
match self { |
||||||
|
StaticPropertyKey::String(x) => string(x), |
||||||
|
StaticPropertyKey::Symbol(x) => symbol(x), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Debug for StaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
match *self { |
||||||
|
StaticPropertyKey::String(index) => { |
||||||
|
let string = RAW_STATICS[index as usize]; |
||||||
|
let string = String::from_utf16_lossy(string); |
||||||
|
write!(f, "String(\"{string}\")") |
||||||
|
} |
||||||
|
StaticPropertyKey::Symbol(symbol) => { |
||||||
|
write!(f, "Symbol({symbol})") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Eq for EncodedStaticPropertyKey {} |
||||||
|
|
||||||
|
impl PartialEq for EncodedStaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
fn eq(&self, other: &Self) -> bool { |
||||||
|
self.0 == other.0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Hash for EncodedStaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) { |
||||||
|
self.0.hash(state); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl PhfHash for EncodedStaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
fn phf_hash<H: Hasher>(&self, state: &mut H) { |
||||||
|
self.hash(state) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl PhfBorrow<EncodedStaticPropertyKey> for EncodedStaticPropertyKey { |
||||||
|
#[inline] |
||||||
|
fn borrow(&self) -> &EncodedStaticPropertyKey { |
||||||
|
self |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub type Slot = (u8, Attribute); |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct StaticShape { |
||||||
|
pub property_table: phf::OrderedMap<EncodedStaticPropertyKey, Slot>, |
||||||
|
|
||||||
|
pub storage_len: usize, |
||||||
|
|
||||||
|
/// \[\[Prototype\]\]
|
||||||
|
pub prototype: Option<&'static StaticShape>, |
||||||
|
} |
||||||
|
|
||||||
|
impl StaticShape { |
||||||
|
#[inline] |
||||||
|
pub fn get(&self, key: StaticPropertyKey) -> Option<Slot> { |
||||||
|
// SAFETY: only used to extend the lifetime, so we are able to call get.
|
||||||
|
self.property_table |
||||||
|
.get(&key.encode()) |
||||||
|
.map(|(index, attributes)| (*index, *attributes)) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn is_empty(&self) -> bool { |
||||||
|
self.property_table.is_empty() |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn len(&self) -> usize { |
||||||
|
self.property_table.len() |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn get_string_key_expect(&self, index: usize) -> StaticString { |
||||||
|
match self |
||||||
|
.property_table |
||||||
|
.index(index) |
||||||
|
.expect("there should be a key at the given index") |
||||||
|
.0 |
||||||
|
.decode() |
||||||
|
{ |
||||||
|
StaticPropertyKey::String(index) => StaticString { index }, |
||||||
|
StaticPropertyKey::Symbol(s) => { |
||||||
|
panic!("The key should be a string at position {index}, but symbol {s}") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/static_shapes_codegen.rs")); |
||||||
|
|
||||||
|
// static NUMBER_BUITIN_OBJECT_STATIC_SHAPE_REF: &StaticShape = &NUMBER_BUITIN_OBJECT_STATIC_SHAPE;
|
@ -0,0 +1,149 @@ |
|||||||
|
// Temp allow
|
||||||
|
#![allow(dead_code)] |
||||||
|
#![allow(clippy::needless_pass_by_value)] |
||||||
|
|
||||||
|
use std::fmt::Debug; |
||||||
|
|
||||||
|
use boa_builtins::{EncodedStaticPropertyKey, StaticPropertyKey}; |
||||||
|
use boa_gc::{Finalize, Trace}; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
object::shape::property_table::PropertyTableInner, property::PropertyKey, symbol::WellKnown, |
||||||
|
JsString, JsSymbol, |
||||||
|
}; |
||||||
|
|
||||||
|
use super::{ |
||||||
|
shared_shape::TransitionKey, slot::SlotAttributes, ChangeTransition, JsPrototype, Shape, Slot, |
||||||
|
UniqueShape, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) type StaticShapeInner = &'static boa_builtins::StaticShape; |
||||||
|
|
||||||
|
/// TODO: doc
|
||||||
|
fn from_static_property_key(key: StaticPropertyKey) -> PropertyKey { |
||||||
|
match key { |
||||||
|
boa_builtins::StaticPropertyKey::String(index) => { |
||||||
|
PropertyKey::String(unsafe { JsString::from_index(index) }) |
||||||
|
} |
||||||
|
boa_builtins::StaticPropertyKey::Symbol(s) => { |
||||||
|
let symbol = match WellKnown::try_from(s).expect("should be an well known symbol") { |
||||||
|
WellKnown::AsyncIterator => JsSymbol::async_iterator(), |
||||||
|
WellKnown::HasInstance => JsSymbol::has_instance(), |
||||||
|
WellKnown::IsConcatSpreadable => JsSymbol::is_concat_spreadable(), |
||||||
|
WellKnown::Iterator => JsSymbol::iterator(), |
||||||
|
WellKnown::Match => JsSymbol::r#match(), |
||||||
|
WellKnown::MatchAll => JsSymbol::match_all(), |
||||||
|
WellKnown::Replace => JsSymbol::replace(), |
||||||
|
WellKnown::Search => JsSymbol::search(), |
||||||
|
WellKnown::Species => JsSymbol::species(), |
||||||
|
WellKnown::Split => JsSymbol::split(), |
||||||
|
WellKnown::ToPrimitive => JsSymbol::to_primitive(), |
||||||
|
WellKnown::ToStringTag => JsSymbol::to_string_tag(), |
||||||
|
WellKnown::Unscopables => JsSymbol::unscopables(), |
||||||
|
}; |
||||||
|
|
||||||
|
PropertyKey::Symbol(symbol) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// TODO: doc
|
||||||
|
fn to_static_property_key(key: &PropertyKey) -> Option<StaticPropertyKey> { |
||||||
|
match key { |
||||||
|
PropertyKey::String(s) => Some(StaticPropertyKey::String(s.as_static_string_index()?)), |
||||||
|
PropertyKey::Symbol(s) => Some(StaticPropertyKey::Symbol(s.hash().try_into().ok()?)), |
||||||
|
PropertyKey::Index(_) => None, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Represents a [`Shape`] that is not shared with any other object.
|
||||||
|
///
|
||||||
|
/// This is useful for objects that are inherently unique like,
|
||||||
|
/// the builtin object.
|
||||||
|
///
|
||||||
|
/// Cloning this does a shallow clone.
|
||||||
|
#[derive(Debug, Clone, Trace, Finalize)] |
||||||
|
pub(crate) struct StaticShape { |
||||||
|
inner: StaticShapeInner, |
||||||
|
} |
||||||
|
|
||||||
|
impl StaticShape { |
||||||
|
/// Create a new [`UniqueShape`].
|
||||||
|
pub(crate) const fn new(inner: StaticShapeInner) -> Self { |
||||||
|
Self { inner } |
||||||
|
} |
||||||
|
|
||||||
|
/// Inserts a new property into the [`UniqueShape`].
|
||||||
|
pub(crate) fn insert_property_transition(&self, _key: TransitionKey) -> UniqueShape { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
/// Remove a property from the [`UniqueShape`].
|
||||||
|
///
|
||||||
|
/// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.
|
||||||
|
pub(crate) fn remove_property_transition(&self, _key: &PropertyKey) -> UniqueShape { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
/// Does a property lookup on the [`UniqueShape`] returning the [`Slot`] where it's
|
||||||
|
/// located or [`None`] otherwise.
|
||||||
|
pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> { |
||||||
|
let key = to_static_property_key(key)?; |
||||||
|
|
||||||
|
let (index, attributes) = self.inner.get(key)?; |
||||||
|
|
||||||
|
Some(Slot { |
||||||
|
index: u32::from(index), |
||||||
|
attributes: SlotAttributes::from_bits_retain(attributes.bits()), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Change the attributes of a property from the [`UniqueShape`].
|
||||||
|
///
|
||||||
|
/// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.
|
||||||
|
///
|
||||||
|
/// NOTE: This assumes that the property had already been inserted.
|
||||||
|
pub(crate) fn change_attributes_transition( |
||||||
|
&self, |
||||||
|
_key: &TransitionKey, |
||||||
|
) -> ChangeTransition<Shape> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
/// Change the prototype of the [`UniqueShape`].
|
||||||
|
///
|
||||||
|
/// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.
|
||||||
|
pub(crate) fn change_prototype_transition(&self, _prototype: JsPrototype) -> UniqueShape { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
/// Gets all keys first strings then symbols in creation order.
|
||||||
|
pub(crate) fn keys(&self) -> Vec<PropertyKey> { |
||||||
|
self.inner |
||||||
|
.property_table |
||||||
|
.keys() |
||||||
|
.map(EncodedStaticPropertyKey::decode) |
||||||
|
.map(from_static_property_key) |
||||||
|
.collect() |
||||||
|
} |
||||||
|
|
||||||
|
/// TODO: doc
|
||||||
|
pub(crate) fn to_unique(&self, prototype: JsPrototype) -> UniqueShape { |
||||||
|
// TODO: optimization, preallocate capacity.
|
||||||
|
// TODO: optimization, direct iniitialize the map.
|
||||||
|
let mut property_table = PropertyTableInner::default(); |
||||||
|
for (key, (_, slot_attributes)) in &self.inner.property_table { |
||||||
|
property_table.insert( |
||||||
|
from_static_property_key(key.decode()), |
||||||
|
SlotAttributes::from_bits_retain(slot_attributes.bits()), |
||||||
|
); |
||||||
|
} |
||||||
|
UniqueShape::new(prototype, property_table) |
||||||
|
} |
||||||
|
|
||||||
|
/// Return location in memory of the [`UniqueShape`].
|
||||||
|
pub(crate) fn to_addr_usize(&self) -> usize { |
||||||
|
let ptr: *const _ = self.inner; |
||||||
|
ptr as usize |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue