Skip to content

Commit

Permalink
Object specialization (#419)
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored Jun 15, 2020
1 parent 0d52a40 commit df13272
Show file tree
Hide file tree
Showing 31 changed files with 2,127 additions and 1,759 deletions.
53 changes: 25 additions & 28 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::{
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{same_value_zero, ResultValue, Value, ValueData},
},
Expand All @@ -25,14 +25,19 @@ use crate::{
use std::{
borrow::Borrow,
cmp::{max, min},
ops::Deref,
};

/// JavaScript `Array` built-in implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct Array;

impl Array {
/// The name of the object.
pub(crate) const NAME: &'static str = "Array";

/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;

/// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
let array = Value::new_object(Some(
Expand All @@ -42,7 +47,7 @@ impl Array {
.get_global_object()
.expect("Could not get global object"),
));
array.set_kind(ObjectKind::Array);
array.set_data(ObjectData::Array);
array.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
interpreter
Expand Down Expand Up @@ -117,7 +122,7 @@ impl Array {
this.set_internal_slot(INSTANCE_PROTOTYPE, prototype);
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_kind(ObjectKind::Array);
this.set_data(ObjectData::Array);

// add our arguments in
let mut length = args.len() as i32;
Expand Down Expand Up @@ -167,25 +172,9 @@ impl Array {
args: &[Value],
_interpreter: &mut Interpreter,
) -> ResultValue {
let value_true = Value::boolean(true);
let value_false = Value::boolean(false);

match args.get(0) {
Some(arg) => {
match arg.data() {
// 1.
ValueData::Object(ref obj) => {
// 2.
if (*obj).deref().borrow().kind == ObjectKind::Array {
return Ok(value_true);
}
Ok(value_false)
}
// 3.
_ => Ok(value_false),
}
}
None => Ok(value_false),
match args.get(0).and_then(|x| x.as_object()) {
Some(object) => Ok(Value::from(object.is_array())),
None => Ok(Value::from(false)),
}
}

Expand Down Expand Up @@ -1008,7 +997,7 @@ impl Array {
let prototype = Value::new_object(None);
let length = Property::default().value(Value::from(0));

prototype.set_property_slice("length", length);
prototype.set_property("length", length);

make_builtin_fn(Self::concat, "concat", &prototype, 1);
make_builtin_fn(Self::push, "push", &prototype, 1);
Expand All @@ -1031,7 +1020,14 @@ impl Array {
make_builtin_fn(Self::slice, "slice", &prototype, 2);
make_builtin_fn(Self::some, "some", &prototype, 2);

let array = make_constructor_fn("Array", 1, Self::make_array, global, prototype, true);
let array = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_array,
global,
prototype,
true,
);

// Static Methods
make_builtin_fn(Self::is_array, "isArray", &array, 1);
Expand All @@ -1041,8 +1037,9 @@ impl Array {

/// Initialise the `Array` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("array", "init");
global.set_field("Array", Self::create(global));
pub(crate) fn init(global: &Value) -> (&str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");

(Self::NAME, Self::create(global))
}
}
44 changes: 26 additions & 18 deletions boa/src/builtins/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use crate::{
builtins::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
Expand Down Expand Up @@ -43,6 +44,12 @@ mod tests;
pub struct BigInt(num_bigint::BigInt);

impl BigInt {
/// The name of the object.
pub(crate) const NAME: &'static str = "BigInt";

/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;

/// The abstract operation thisBigIntValue takes argument value.
///
/// The phrase “this BigInt value” within the specification of a method refers to the
Expand All @@ -62,18 +69,16 @@ impl BigInt {
// 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
// a. Assert: Type(value.[[BigIntData]]) is BigInt.
// b. Return value.[[BigIntData]].
ValueData::Object(_) => {
let bigint = value.get_internal_slot("BigIntData");
if let ValueData::BigInt(bigint) = bigint.data() {
ValueData::Object(ref object) => {
if let ObjectData::BigInt(ref bigint) = object.borrow().data {
return Ok(bigint.clone());
}
}
_ => {}
}

// 3. Throw a TypeError exception.
ctx.throw_type_error("'this' is not a BigInt")?;
unreachable!();
Err(ctx.construct_type_error("'this' is not a BigInt"))
}

/// `BigInt()`
Expand All @@ -86,16 +91,12 @@ impl BigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
pub(crate) fn make_bigint(
_this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn make_bigint(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => Value::from(ctx.to_bigint(value)?),
None => Value::from(Self::from(0)),
Some(ref value) => ctx.to_bigint(value)?,
None => Self::from(0),
};
Ok(data)
Ok(Value::from(data))
}

/// `BigInt.prototype.toString( [radix] )`
Expand Down Expand Up @@ -213,12 +214,18 @@ impl BigInt {
/// Create a new `Number` object
pub(crate) fn create(global: &Value) -> Value {
let prototype = Value::new_object(Some(global));
prototype.set_internal_slot("BigIntData", Value::from(Self::from(0)));

make_builtin_fn(Self::to_string, "toString", &prototype, 1);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);

let big_int = make_constructor_fn("BigInt", 1, Self::make_bigint, global, prototype, false);
let big_int = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_bigint,
global,
prototype,
false,
);

make_builtin_fn(Self::as_int_n, "asIntN", &big_int, 2);
make_builtin_fn(Self::as_uint_n, "asUintN", &big_int, 2);
Expand All @@ -228,9 +235,10 @@ impl BigInt {

/// Initialise the `BigInt` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("bigint", "init");
global.set_field("BigInt", Self::create(global));
pub(crate) fn init(global: &Value) -> (&str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");

(Self::NAME, Self::create(global))
}
}

Expand Down
96 changes: 43 additions & 53 deletions boa/src/builtins/boolean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,45 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, ObjectKind},
object::ObjectData,
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
use std::{borrow::Borrow, ops::Deref};

/// Boolean implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct Boolean;

impl Boolean {
/// The name of the object.
pub(crate) const NAME: &'static str = "Boolean";

/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;

/// An Utility function used to get the internal [[BooleanData]].
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
fn this_boolean_value(value: &Value, ctx: &mut Interpreter) -> Result<bool, Value> {
match value.data() {
ValueData::Boolean(boolean) => return Ok(*boolean),
ValueData::Object(ref object) => {
let object = object.borrow();
if let Some(boolean) = object.as_boolean() {
return Ok(boolean);
}
}
_ => {}
}

Err(ctx.construct_type_error("'this' is not a boolean"))
}

/// `[[Construct]]` Create a new boolean object
///
/// `[[Call]]` Creates a new boolean primitive
Expand All @@ -36,19 +62,11 @@ impl Boolean {
args: &[Value],
_: &mut Interpreter,
) -> ResultValue {
this.set_kind(ObjectKind::Boolean);

// Get the argument, if any
if let Some(ref value) = args.get(0) {
this.set_internal_slot("BooleanData", Self::to_boolean(value));
} else {
this.set_internal_slot("BooleanData", Self::to_boolean(&Value::from(false)));
}
let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false);
this.set_data(ObjectData::Boolean(data));

match args.get(0) {
Some(ref value) => Ok(Self::to_boolean(value)),
None => Ok(Self::to_boolean(&Value::from(false))),
}
Ok(Value::from(data))
}

/// The `toString()` method returns a string representing the specified `Boolean` object.
Expand All @@ -60,9 +78,9 @@ impl Boolean {
/// [spec]: https://tc39.es/ecma262/#sec-boolean-object
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let b = Self::this_boolean_value(this);
Ok(Value::from(b.to_string()))
pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let boolean = Self::this_boolean_value(this, ctx)?;
Ok(Value::from(boolean.to_string()))
}

/// The valueOf() method returns the primitive value of a `Boolean` object.
Expand All @@ -73,52 +91,23 @@ impl Boolean {
///
/// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf
pub(crate) fn value_of(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Self::this_boolean_value(this))
}

// === Utility Functions ===
/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
/// Creates a new boolean value from the input
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_boolean(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Object(_) => Value::from(true),
ValueData::String(ref s) if !s.is_empty() => Value::from(true),
ValueData::Rational(n) if n != 0.0 && !n.is_nan() => Value::from(true),
ValueData::Integer(n) if n != 0 => Value::from(true),
ValueData::Boolean(v) => Value::from(v),
_ => Value::from(false),
}
}

/// An Utility function used to get the internal BooleanData.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
pub(crate) fn this_boolean_value(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Boolean(v) => Value::from(v),
ValueData::Object(ref v) => (v).deref().borrow().get_internal_slot("BooleanData"),
_ => Value::from(false),
}
#[inline]
pub(crate) fn value_of(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
Ok(Value::from(Self::this_boolean_value(this, ctx)?))
}

/// Create a new `Boolean` object.
pub(crate) fn create(global: &Value) -> Value {
// Create Prototype
// https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object
let prototype = Value::new_object(Some(global));
prototype.set_internal_slot("BooleanData", Self::to_boolean(&Value::from(false)));

make_builtin_fn(Self::to_string, "toString", &prototype, 0);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);

make_constructor_fn(
"Boolean",
1,
Self::NAME,
Self::LENGTH,
Self::construct_boolean,
global,
prototype,
Expand All @@ -128,8 +117,9 @@ impl Boolean {

/// Initialise the `Boolean` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("boolean", "init");
global.set_field("Boolean", Self::create(global));
pub(crate) fn init(global: &Value) -> (&str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");

(Self::NAME, Self::create(global))
}
}
5 changes: 3 additions & 2 deletions boa/src/builtins/console/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,8 @@ pub fn create(global: &Value) -> Value {

/// Initialise the `console` object on the global object.
#[inline]
pub fn init(global: &Value) {
pub fn init(global: &Value) -> (&str, Value) {
let _timer = BoaProfiler::global().start_event("console", "init");
global.set_field("console", create(global));

("console", create(global))
}
Loading

0 comments on commit df13272

Please sign in to comment.