Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Implement Hidden classes #2723

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions boa_cli/src/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ mod gc;
mod object;
mod optimizer;
mod realm;
mod shape;

fn create_boa_object(context: &mut Context<'_>) -> JsObject {
let function_module = function::create_object(context);
let object_module = object::create_object(context);
let shape_module = shape::create_object(context);
let optimizer_module = optimizer::create_object(context);
let gc_module = gc::create_object(context);
let realm_module = realm::create_object(context);
Expand All @@ -27,6 +29,11 @@ fn create_boa_object(context: &mut Context<'_>) -> JsObject {
object_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
"shape",
shape_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
"optimizer",
optimizer_module,
Expand Down
66 changes: 66 additions & 0 deletions boa_cli/src/debug/shape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use boa_engine::{
js_string, object::ObjectInitializer, Context, JsArgs, JsNativeError, JsObject, JsResult,
JsValue, NativeFunction,
};

fn get_object(args: &[JsValue], position: usize) -> JsResult<&JsObject> {
let value = args.get_or_undefined(position);

let Some(object) = value.as_object() else {
return Err(JsNativeError::typ()
.with_message(format!("expected object in argument position {position}, got {}", value.type_of()))
.into());
};

Ok(object)
}

/// Returns object's shape pointer in memory.
fn id(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
let object = get_object(args, 0)?;
let object = object.borrow();
let shape = object.shape();
Ok(format!("0x{:X}", shape.to_addr_usize()).into())
}

/// Returns object's shape type.
fn r#type(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
let object = get_object(args, 0)?;
let object = object.borrow();
let shape = object.shape();

Ok(if shape.is_shared() {
js_string!("shared")
} else {
js_string!("unique")
}
.into())
}

/// Returns object's shape type.
fn same(_: &JsValue, args: &[JsValue], _: &mut Context<'_>) -> JsResult<JsValue> {
let lhs = get_object(args, 0)?;
let rhs = get_object(args, 1)?;

let lhs_shape_ptr = {
let object = lhs.borrow();
let shape = object.shape();
shape.to_addr_usize()
};

let rhs_shape_ptr = {
let object = rhs.borrow();
let shape = object.shape();
shape.to_addr_usize()
};

Ok(JsValue::new(lhs_shape_ptr == rhs_shape_ptr))
}

pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
ObjectInitializer::new(context)
.function(NativeFunction::from_fn_ptr(id), "id", 1)
.function(NativeFunction::from_fn_ptr(r#type), "type", 1)
.function(NativeFunction::from_fn_ptr(same), "same", 2)
.build()
}
6 changes: 4 additions & 2 deletions boa_engine/benches/full.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Benchmarks of the whole execution engine in Boa.

use boa_engine::{
context::DefaultHooks, optimizer::OptimizerOptions, realm::Realm, Context, Source,
context::DefaultHooks, object::shape::SharedShape, optimizer::OptimizerOptions, realm::Realm,
Context, Source,
};
use criterion::{criterion_group, criterion_main, Criterion};
use std::hint::black_box;
Expand All @@ -15,7 +16,8 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn create_realm(c: &mut Criterion) {
c.bench_function("Create Realm", move |b| {
b.iter(|| Realm::create(&DefaultHooks))
let root_shape = SharedShape::root();
b.iter(|| Realm::create(&DefaultHooks, &root_shape))
});
}

Expand Down
3 changes: 2 additions & 1 deletion boa_engine/src/builtins/array/array_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ impl ArrayIterator {
kind: PropertyNameKind,
context: &Context<'_>,
) -> JsValue {
let array_iterator = JsObject::from_proto_and_data(
let array_iterator = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
context.intrinsics().objects().iterator_prototypes().array(),
ObjectData::array_iterator(Self::new(array, kind)),
);
Expand Down
80 changes: 53 additions & 27 deletions boa_engine/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,15 @@ impl IntrinsicObject for Array {
let symbol_iterator = JsSymbol::iterator();
let symbol_unscopables = JsSymbol::unscopables();

let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species)
let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]")
.build();

let values_function = BuiltInBuilder::with_object(
let values_function = BuiltInBuilder::callable_with_object(
realm,
realm.intrinsics().objects().array_prototype_values().into(),
Self::values,
)
.callable(Self::values)
.name("values")
.build();

Expand Down Expand Up @@ -217,17 +216,18 @@ impl BuiltInConstructor for Array {

// b. Let array be ? ArrayCreate(numberOfArgs, proto).
let array = Self::array_create(number_of_args as u64, Some(prototype), context)?;

// c. Let k be 0.
// d. Repeat, while k < numberOfArgs,
for (i, item) in args.iter().cloned().enumerate() {
// i. Let Pk be ! ToString(𝔽(k)).
// ii. Let itemK be values[k].
// iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK).
array
.create_data_property_or_throw(i, item, context)
.expect("this CreateDataPropertyOrThrow must not fail");
// iv. Set k to k + 1.
}
// i. Let Pk be ! ToString(𝔽(k)).
// ii. Let itemK be values[k].
// iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK).
// iv. Set k to k + 1.
array
.borrow_mut()
.properties_mut()
.override_indexed_properties(args.iter().cloned().collect());

// e. Assert: The mathematical value of array's "length" property is numberOfArgs.
// f. Return array.
Ok(array.into())
Expand All @@ -253,14 +253,43 @@ impl Array {
.with_message("array exceeded max size")
.into());
}

// Fast path:
if prototype.is_none() {
return Ok(context
.intrinsics()
.templates()
.array()
.create(ObjectData::array(), vec![JsValue::new(length)]));
}

// 7. Return A.
// 2. If proto is not present, set proto to %Array.prototype%.
// 3. Let A be ! MakeBasicObject(« [[Prototype]], [[Extensible]] »).
// 4. Set A.[[Prototype]] to proto.
// 5. Set A.[[DefineOwnProperty]] as specified in 10.4.2.1.
let prototype =
prototype.unwrap_or_else(|| context.intrinsics().constructors().array().prototype());
let array = JsObject::from_proto_and_data(prototype, ObjectData::array());

// Fast path:
if context
.intrinsics()
.templates()
.array()
.has_prototype(&prototype)
{
return Ok(context
.intrinsics()
.templates()
.array()
.create(ObjectData::array(), vec![JsValue::new(length)]));
}

let array = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::array(),
);

// 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
crate::object::internal_methods::ordinary_define_own_property(
Expand Down Expand Up @@ -290,27 +319,24 @@ impl Array {
{
// 1. Assert: elements is a List whose elements are all ECMAScript language values.
// 2. Let array be ! ArrayCreate(0).
let array = Self::array_create(0, None, context)
.expect("creating an empty array with the default prototype must not fail");

// 3. Let n be 0.
// 4. For each element e of elements, do
// a. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(n)), e).
// b. Set n to n + 1.
//
// 5. Return array.
// NOTE: This deviates from the spec, but it should have the same behaviour.
let elements: ThinVec<_> = elements.into_iter().collect();
let length = elements.len();
array
.borrow_mut()
.properties_mut()
.override_indexed_properties(elements);
array
.set(utf16!("length"), length, true, context)
.expect("Should not fail");

// 5. Return array.
array
context
.intrinsics()
.templates()
.array()
.create_with_indexed_properties(
ObjectData::array(),
vec![JsValue::new(length)],
elements,
)
}

/// Utility function for concatenating array objects.
Expand Down
9 changes: 4 additions & 5 deletions boa_engine/src/builtins/array_buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,11 @@ impl IntrinsicObject for ArrayBuffer {

let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;

let get_species = BuiltInBuilder::new(realm)
.callable(Self::get_species)
let get_species = BuiltInBuilder::callable(realm, Self::get_species)
.name("get [Symbol.species]")
.build();

let get_byte_length = BuiltInBuilder::new(realm)
.callable(Self::get_byte_length)
let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)
.name("get byteLength")
.build();

Expand Down Expand Up @@ -355,7 +353,8 @@ impl ArrayBuffer {

// 3. Set obj.[[ArrayBufferData]] to block.
// 4. Set obj.[[ArrayBufferByteLength]] to byteLength.
let obj = JsObject::from_proto_and_data(
let obj = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::array_buffer(Self {
array_buffer_data: Some(block),
Expand Down
6 changes: 5 additions & 1 deletion boa_engine/src/builtins/boolean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ impl BuiltInConstructor for Boolean {
}
let prototype =
get_prototype_from_constructor(new_target, StandardConstructors::boolean, context)?;
let boolean = JsObject::from_proto_and_data(prototype, ObjectData::boolean(data));
let boolean = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::boolean(data),
);

Ok(boolean.into())
}
Expand Down
12 changes: 5 additions & 7 deletions boa_engine/src/builtins/dataview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,15 @@ impl IntrinsicObject for DataView {
fn init(realm: &Realm) {
let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;

let get_buffer = BuiltInBuilder::new(realm)
.callable(Self::get_buffer)
let get_buffer = BuiltInBuilder::callable(realm, Self::get_buffer)
.name("get buffer")
.build();

let get_byte_length = BuiltInBuilder::new(realm)
.callable(Self::get_byte_length)
let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)
.name("get byteLength")
.build();

let get_byte_offset = BuiltInBuilder::new(realm)
.callable(Self::get_byte_offset)
let get_byte_offset = BuiltInBuilder::callable(realm, Self::get_byte_offset)
.name("get byteOffset")
.build();

Expand Down Expand Up @@ -194,7 +191,8 @@ impl BuiltInConstructor for DataView {
.into());
}

let obj = JsObject::from_proto_and_data(
let obj = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::data_view(Self {
// 11. Set O.[[ViewedArrayBuffer]] to buffer.
Expand Down
12 changes: 7 additions & 5 deletions boa_engine/src/builtins/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,12 @@ impl IntrinsicObject for Date {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");

let to_utc_string = BuiltInBuilder::new(realm)
.callable(Self::to_utc_string)
let to_utc_string = BuiltInBuilder::callable(realm, Self::to_utc_string)
.name("toUTCString")
.length(0)
.build();

let to_primitive = BuiltInBuilder::new(realm)
.callable(Self::to_primitive)
let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive)
.name("[Symbol.toPrimitive]")
.length(1)
.build();
Expand Down Expand Up @@ -289,7 +287,11 @@ impl BuiltInConstructor for Date {
get_prototype_from_constructor(new_target, StandardConstructors::date, context)?;

// 7. Set O.[[DateValue]] to dv.
let obj = JsObject::from_proto_and_data(prototype, ObjectData::date(dv));
let obj = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::date(dv),
);

// 8. Return O.
Ok(obj.into())
Expand Down
6 changes: 5 additions & 1 deletion boa_engine/src/builtins/error/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ impl BuiltInConstructor for AggregateError {
StandardConstructors::aggregate_error,
context,
)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Aggregate));
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::error(ErrorKind::Aggregate),
);

// 3. If message is not undefined, then
let message = args.get_or_undefined(1);
Expand Down
6 changes: 5 additions & 1 deletion boa_engine/src/builtins/error/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ impl BuiltInConstructor for EvalError {
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
let prototype =
get_prototype_from_constructor(new_target, StandardConstructors::eval_error, context)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Eval));
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::error(ErrorKind::Eval),
);

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
Expand Down
6 changes: 5 additions & 1 deletion boa_engine/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,11 @@ impl BuiltInConstructor for Error {
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »).
let prototype =
get_prototype_from_constructor(new_target, StandardConstructors::error, context)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Error));
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::error(ErrorKind::Error),
);

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
Expand Down
Loading