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