Skip to content

Commit

Permalink
Merge 7f746d1 into ff0a37f
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 authored Apr 11, 2023
2 parents ff0a37f + 7f746d1 commit 3f8b8e8
Show file tree
Hide file tree
Showing 13 changed files with 539 additions and 702 deletions.
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl IntrinsicObject for Array {

let values_function = BuiltInBuilder::with_object(
realm,
realm.intrinsics().objects().array_prototype_values(),
realm.intrinsics().objects().array_prototype_values().into(),
)
.callable(Self::values)
.name("values")
Expand Down
7 changes: 3 additions & 4 deletions boa_engine/src/builtins/error/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ use crate::{
},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError,
native_function::NativeFunction,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind},
property::Attribute,
realm::Realm,
string::utf16,
Context, JsArgs, JsResult, JsValue,
Context, JsArgs, JsResult, JsValue, NativeFunction,
};
use boa_profiler::Profiler;

Expand Down Expand Up @@ -134,7 +133,7 @@ impl IntrinsicObject for ThrowTypeError {
let mut obj = obj.borrow_mut();

obj.extensible = false;
obj.data = ObjectData::function(Function::new(
*obj.kind_mut() = ObjectKind::Function(Function::new(
FunctionKind::Native {
function: NativeFunction::from_fn_ptr(throw_type_error),
constructor: None,
Expand Down
207 changes: 145 additions & 62 deletions boa_engine/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ use crate::{
js_string,
native_function::{NativeFunction, NativeFunctionPointer},
object::{
FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE,
FunctionBinding, JsFunction, JsObject, JsPrototype, Object, ObjectData, CONSTRUCTOR,
PROTOTYPE,
},
property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
Expand Down Expand Up @@ -390,6 +391,85 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult

// === Builder typestate ===

#[derive(Debug)]
enum BuiltInObjectInitializer {
Shared(JsObject),
Unique { object: Object, data: ObjectData },
}

impl BuiltInObjectInitializer {
/// Inserts a new property descriptor into the builtin.
fn insert<K, P>(&mut self, key: K, property: P)
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
match self {
BuiltInObjectInitializer::Shared(obj) => obj.borrow_mut().insert(key, property),
BuiltInObjectInitializer::Unique { object, .. } => object.insert(key, property),
};
}

/// Sets the prototype of the builtin
fn set_prototype(&mut self, prototype: JsObject) {
match self {
BuiltInObjectInitializer::Shared(obj) => {
let mut obj = obj.borrow_mut();
obj.set_prototype(prototype);
}
BuiltInObjectInitializer::Unique { object, .. } => {
object.set_prototype(prototype);
}
}
}

/// Sets the `ObjectData` of the builtin.
///
/// # Panics
///
/// Panics if the builtin is a shared builtin and the data's vtable is not the same as the
/// builtin's vtable.
fn set_data(&mut self, new_data: ObjectData) {
match self {
BuiltInObjectInitializer::Shared(obj) => {
assert!(
std::ptr::eq(obj.vtable(), new_data.internal_methods),
"intrinsic object's vtable didn't match with new data"
);
*obj.borrow_mut().kind_mut() = new_data.kind;
}
BuiltInObjectInitializer::Unique { ref mut data, .. } => *data = new_data,
}
}

/// Gets a shared object from the builtin, transitioning its state if it's necessary.
fn as_shared(&mut self) -> JsObject {
match std::mem::replace(
self,
BuiltInObjectInitializer::Unique {
object: Object::default(),
data: ObjectData::ordinary(),
},
) {
BuiltInObjectInitializer::Shared(obj) => {
*self = BuiltInObjectInitializer::Shared(obj.clone());
obj
}
BuiltInObjectInitializer::Unique { mut object, data } => {
*object.kind_mut() = data.kind;
let obj = JsObject::from_object_and_vtable(object, data.internal_methods);
*self = BuiltInObjectInitializer::Shared(obj.clone());
obj
}
}
}

/// Converts the builtin into a shared object.
fn into_shared(mut self) -> JsObject {
self.as_shared()
}
}

/// Marker for a constructor function.
struct Constructor {
prototype: JsObject,
Expand Down Expand Up @@ -434,77 +514,78 @@ struct OrdinaryObject;

/// Applies the pending builder data to the object.
trait ApplyToObject {
fn apply_to(self, object: &JsObject);
fn apply_to(self, object: &mut BuiltInObjectInitializer);
}

impl ApplyToObject for Constructor {
fn apply_to(self, object: &JsObject) {
fn apply_to(self, object: &mut BuiltInObjectInitializer) {
object.insert(
PROTOTYPE,
PropertyDescriptor::builder()
.value(self.prototype.clone())
.writable(false)
.enumerable(false)
.configurable(false),
);

let object = object.as_shared();

{
let mut prototype = self.prototype.borrow_mut();
prototype.set_prototype(self.inherits);
prototype.insert(
CONSTRUCTOR,
PropertyDescriptor::builder()
.value(object.clone())
.value(object)
.writable(self.attributes.writable())
.enumerable(self.attributes.enumerable())
.configurable(self.attributes.configurable()),
);
}
let mut object = object.borrow_mut();
object.insert(
PROTOTYPE,
PropertyDescriptor::builder()
.value(self.prototype)
.writable(false)
.enumerable(false)
.configurable(false),
);
}
}

impl ApplyToObject for ConstructorNoProto {
fn apply_to(self, _: &JsObject) {}
fn apply_to(self, _: &mut BuiltInObjectInitializer) {}
}

impl ApplyToObject for OrdinaryFunction {
fn apply_to(self, _: &JsObject) {}
fn apply_to(self, _: &mut BuiltInObjectInitializer) {}
}

impl<S: ApplyToObject + IsConstructor> ApplyToObject for Callable<S> {
fn apply_to(self, object: &JsObject) {
self.kind.apply_to(object);

let function = function::Function::new(
fn apply_to(self, object: &mut BuiltInObjectInitializer) {
let function = ObjectData::function(function::Function::new(
function::FunctionKind::Native {
function: NativeFunction::from_fn_ptr(self.function),
constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base),
},
self.realm,
));
object.set_data(function);
object.insert(
utf16!("length"),
PropertyDescriptor::builder()
.value(self.length)
.writable(false)
.enumerable(false)
.configurable(true),
);
object.insert(
utf16!("name"),
PropertyDescriptor::builder()
.value(self.name)
.writable(false)
.enumerable(false)
.configurable(true),
);

let length = PropertyDescriptor::builder()
.value(self.length)
.writable(false)
.enumerable(false)
.configurable(true);
let name = PropertyDescriptor::builder()
.value(self.name)
.writable(false)
.enumerable(false)
.configurable(true);

{
let mut constructor = object.borrow_mut();
constructor.data = ObjectData::function(function);
constructor.insert(utf16!("length"), length);
constructor.insert(utf16!("name"), name);
}
self.kind.apply_to(object);
}
}

impl ApplyToObject for OrdinaryObject {
fn apply_to(self, _: &JsObject) {}
fn apply_to(self, _: &mut BuiltInObjectInitializer) {}
}

/// Builder for creating built-in objects, like `Array`.
Expand All @@ -515,7 +596,7 @@ impl ApplyToObject for OrdinaryObject {
#[must_use = "You need to call the `build` method in order for this to correctly assign the inner data"]
struct BuiltInBuilder<'ctx, Kind> {
realm: &'ctx Realm,
object: JsObject,
object: BuiltInObjectInitializer,
kind: Kind,
prototype: JsObject,
}
Expand All @@ -524,7 +605,10 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
fn new(realm: &'ctx Realm) -> BuiltInBuilder<'ctx, OrdinaryObject> {
BuiltInBuilder {
realm,
object: JsObject::with_null_proto(),
object: BuiltInObjectInitializer::Unique {
object: Object::default(),
data: ObjectData::ordinary(),
},
kind: OrdinaryObject,
prototype: realm.intrinsics().constructors().object().prototype(),
}
Expand All @@ -535,7 +619,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
) -> BuiltInBuilder<'ctx, OrdinaryObject> {
BuiltInBuilder {
realm,
object: I::get(realm.intrinsics()),
object: BuiltInObjectInitializer::Shared(I::get(realm.intrinsics())),
kind: OrdinaryObject,
prototype: realm.intrinsics().constructors().object().prototype(),
}
Expand All @@ -544,7 +628,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {
fn with_object(realm: &'ctx Realm, object: JsObject) -> BuiltInBuilder<'ctx, OrdinaryObject> {
BuiltInBuilder {
realm,
object,
object: BuiltInObjectInitializer::Shared(object),
kind: OrdinaryObject,
prototype: realm.intrinsics().constructors().object().prototype(),
}
Expand Down Expand Up @@ -583,7 +667,7 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable<Constructor>> {
let constructor = SC::STANDARD_CONSTRUCTOR(realm.intrinsics().constructors());
BuiltInBuilder {
realm,
object: constructor.constructor(),
object: BuiltInObjectInitializer::Shared(constructor.constructor()),
kind: Callable {
function: SC::constructor,
name: js_string!(SC::NAME),
Expand Down Expand Up @@ -617,7 +701,12 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable<Constructor>> {

impl<T> BuiltInBuilder<'_, T> {
/// Adds a new static method to the builtin object.
fn static_method<B>(self, function: NativeFunctionPointer, binding: B, length: usize) -> Self
fn static_method<B>(
mut self,
function: NativeFunctionPointer,
binding: B,
length: usize,
) -> Self
where
B: Into<FunctionBinding>,
{
Expand All @@ -628,7 +717,7 @@ impl<T> BuiltInBuilder<'_, T> {
.length(length)
.build();

self.object.borrow_mut().insert(
self.object.insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
Expand All @@ -640,7 +729,7 @@ impl<T> BuiltInBuilder<'_, T> {
}

/// Adds a new static data property to the builtin object.
fn static_property<K, V>(self, key: K, value: V, attribute: Attribute) -> Self
fn static_property<K, V>(mut self, key: K, value: V, attribute: Attribute) -> Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
Expand All @@ -650,13 +739,13 @@ impl<T> BuiltInBuilder<'_, T> {
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self.object.insert(key, property);
self
}

/// Adds a new static accessor property to the builtin object.
fn static_accessor<K>(
self,
mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
Expand All @@ -670,7 +759,7 @@ impl<T> BuiltInBuilder<'_, T> {
.maybe_set(set)
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self.object.insert(key, property);
self
}

Expand Down Expand Up @@ -779,28 +868,22 @@ impl<FnTyp> BuiltInBuilder<'_, Callable<FnTyp>> {

impl BuiltInBuilder<'_, OrdinaryObject> {
/// Build the builtin object.
fn build(self) -> JsObject {
self.kind.apply_to(&self.object);
fn build(mut self) -> JsObject {
self.kind.apply_to(&mut self.object);

{
let mut object = self.object.borrow_mut();
object.set_prototype(self.prototype);
}
self.object.set_prototype(self.prototype);

self.object
self.object.into_shared()
}
}

impl<FnTyp: ApplyToObject + IsConstructor> BuiltInBuilder<'_, Callable<FnTyp>> {
/// Build the builtin callable.
fn build(self) -> JsFunction {
self.kind.apply_to(&self.object);
fn build(mut self) -> JsFunction {
self.kind.apply_to(&mut self.object);

{
let mut object = self.object.borrow_mut();
object.set_prototype(self.prototype);
}
self.object.set_prototype(self.prototype);

JsFunction::from_object_unchecked(self.object)
JsFunction::from_object_unchecked(self.object.into_shared())
}
}
Loading

0 comments on commit 3f8b8e8

Please sign in to comment.