Skip to content

Commit

Permalink
Implement prototype transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Mar 25, 2023
1 parent 4ec340c commit 8f3dbb6
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 64 deletions.
12 changes: 11 additions & 1 deletion boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub struct Context<'host> {
job_queue: &'host dyn JobQueue,

pub(crate) root_shape: Shape,
pub(crate) empty_object_shape: Shape,
}

impl std::fmt::Debug for Context<'_> {
Expand Down Expand Up @@ -631,6 +632,9 @@ impl<'icu, 'hooks, 'queue> ContextBuilder<'icu, 'hooks, 'queue> {
{
let host_hooks = self.host_hooks.unwrap_or(&DefaultHooks);

let root_shape = Shape::root();
// let empty_object_shape = root_shape.change_prototype_transition(prototype);

let mut context = Context {
realm: Realm::create(host_hooks),
interner: self.interner.unwrap_or_default(),
Expand All @@ -646,10 +650,16 @@ impl<'icu, 'hooks, 'queue> ContextBuilder<'icu, 'hooks, 'queue> {
kept_alive: Vec::new(),
host_hooks,
job_queue: self.job_queue.unwrap_or(&IdleJobQueue),
root_shape: Shape::root(),
// TODO: probably should be moved to realm, maybe into intrinsics.
root_shape: root_shape.clone(),
empty_object_shape: root_shape,
};

builtins::set_default_global_bindings(&mut context)?;
let object_prototype = context.intrinsics().constructors().object().prototype();
context.empty_object_shape = context
.root_shape
.change_prototype_transition(Some(object_prototype));

Ok(context)
}
Expand Down
14 changes: 6 additions & 8 deletions boa_engine/src/object/internal_methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,14 +348,12 @@ pub(crate) fn ordinary_set_prototype_of(
_: &mut Context<'_>,
) -> JsResult<bool> {
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
{
// 2. Let current be O.[[Prototype]].
let current = obj.prototype();
// 2. Let current be O.[[Prototype]].
let current = obj.prototype();

// 3. If SameValue(V, current) is true, return true.
if val == *current {
return Ok(true);
}
// 3. If SameValue(V, current) is true, return true.
if val == current {
return Ok(true);
}

// 4. Let extensible be O.[[Extensible]].
Expand Down Expand Up @@ -384,7 +382,7 @@ pub(crate) fn ordinary_set_prototype_of(
break;
}
// ii. Else, set p to p.[[Prototype]].
p = proto.prototype().clone();
p = proto.prototype();
}

// 9. Set O.[[Prototype]] to V.
Expand Down
16 changes: 12 additions & 4 deletions boa_engine/src/object/jsobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::{
collections::HashMap,
error::Error,
fmt::{self, Debug, Display},
hash::Hash,
result::Result as StdResult,
};

Expand Down Expand Up @@ -77,9 +78,8 @@ impl JsObject {
Self {
inner: Gc::new(GcRefCell::new(Object {
data,
prototype: prototype.into(),
extensible: true,
properties: PropertyMap::default(),
properties: PropertyMap::from_prototype(prototype.into()),
private_elements: Vec::new(),
})),
}
Expand Down Expand Up @@ -278,8 +278,8 @@ impl JsObject {
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn prototype(&self) -> Ref<'_, JsPrototype> {
Ref::map(self.borrow(), Object::prototype)
pub fn prototype(&self) -> JsPrototype {
self.borrow().prototype()
}

/// Get the extensibility of the object.
Expand Down Expand Up @@ -786,6 +786,14 @@ impl PartialEq for JsObject {
}
}

impl Eq for JsObject {}

impl Hash for JsObject {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::ptr::hash(self.as_ref(), state);
}
}

/// An error returned by [`JsObject::try_borrow`](struct.JsObject.html#method.try_borrow).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BorrowError;
Expand Down
12 changes: 4 additions & 8 deletions boa_engine/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ pub struct Object {
pub data: ObjectData,
/// The collection of properties contained in the object
properties: PropertyMap,
/// Instance prototype `__proto__`.
prototype: JsPrototype,
/// Whether it can have new properties added to it.
extensible: bool,
/// The `[[PrivateElements]]` internal slot.
Expand All @@ -136,7 +134,6 @@ unsafe impl Trace for Object {
boa_gc::custom_trace!(this, {
mark(&this.data);
mark(&this.properties);
mark(&this.prototype);
for (_, element) in &this.private_elements {
mark(element);
}
Expand Down Expand Up @@ -781,7 +778,6 @@ impl Default for Object {
Self {
data: ObjectData::ordinary(),
properties: PropertyMap::default(),
prototype: None,
extensible: true,
private_elements: Vec::default(),
}
Expand Down Expand Up @@ -1621,8 +1617,8 @@ impl Object {

/// Gets the prototype instance of this object.
#[inline]
pub const fn prototype(&self) -> &JsPrototype {
&self.prototype
pub fn prototype(&self) -> JsPrototype {
self.properties.shape.prototype()
}

/// Sets the prototype instance of the object.
Expand All @@ -1634,12 +1630,12 @@ impl Object {
pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
let prototype = prototype.into();
if self.extensible {
self.prototype = prototype;
self.properties.shape = self.properties.shape.change_prototype_transition(prototype);
true
} else {
// If target is non-extensible, [[SetPrototypeOf]] must return false
// unless V is the SameValue as the target's observed [[GetPrototypeOf]] value.
self.prototype == prototype
self.prototype() == prototype
}
}

Expand Down
20 changes: 17 additions & 3 deletions boa_engine/src/object/property_map.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use super::{
shape::{PropertyDescriptorAttribute, Shape, Slot, TransitionKey},
PropertyDescriptor, PropertyKey,
shape::{PropertyDescriptorAttribute, Shape, Slot, TransitionKey, UniqueShape},
JsPrototype, PropertyDescriptor, PropertyKey,
};
use crate::{property::PropertyDescriptorBuilder, JsString, JsSymbol, JsValue};
use boa_gc::{custom_trace, Finalize, Trace};
use boa_gc::{custom_trace, Finalize, GcRefCell, Trace};
use indexmap::IndexMap;
use rustc_hash::{FxHashMap, FxHasher};
use std::{collections::hash_map, hash::BuildHasherDefault, iter::FusedIterator};
Expand Down Expand Up @@ -235,6 +235,20 @@ impl PropertyMap {
}
}

/// TOOD: doc
#[must_use]
#[inline]
pub fn from_prototype(prototype: JsPrototype) -> Self {
Self {
indexed_properties: IndexedProperties::default(),
shape: Shape::unique(UniqueShape::new(
GcRefCell::new(prototype),
IndexMap::default(),
)),
storage: Vec::default(),
}
}

/// Get the property with the given key from the [`PropertyMap`].
#[must_use]
pub fn get(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {
Expand Down
27 changes: 22 additions & 5 deletions boa_engine/src/object/shape/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
mod shared_shape;
mod unique_shape;

pub(crate) use unique_shape::UniqueShape;

use std::{
any::Any,
cell::{Cell, RefCell},
Expand All @@ -28,7 +30,7 @@ use crate::{
JsString,
};

use self::{shared_shape::SharedShape, unique_shape::UniqueShape};
use self::shared_shape::SharedShape;

use super::JsPrototype;

Expand Down Expand Up @@ -75,10 +77,12 @@ unsafe impl Trace for TransitionKey {
enum TransitionType {
/// Inserts a new property.
Insert,
// Change existing property attributes.

/// Change existing property attributes.
Configure,
// TODO:
// Prototype,

/// Change prototype.
Prototype,
}

unsafe impl Trace for TransitionType {
Expand All @@ -87,7 +91,7 @@ unsafe impl Trace for TransitionType {

#[derive(Debug, Trace, Finalize, Clone)]
enum Inner {
Unique(#[unsafe_ignore_trace] UniqueShape),
Unique(UniqueShape),
Shared(SharedShape),
}

Expand Down Expand Up @@ -148,6 +152,19 @@ impl Shape {
}
}

pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {
match &self.inner {
Inner::Shared(shape) => Self::shared(shape.change_prototype_transition(prototype)),
Inner::Unique(shape) => Self::unique(shape.change_prototype_transition(prototype)),
}
}
pub(crate) fn prototype(&self) -> JsPrototype {
match &self.inner {
Inner::Shared(shape) => shape.prototype(),
Inner::Unique(shape) => shape.prototype(),
}
}

#[inline]
pub fn lookup(&self, key: &PropertyKey) -> Option<Slot> {
match &self.inner {
Expand Down
Loading

0 comments on commit 8f3dbb6

Please sign in to comment.