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

Object specialization #419

Merged
merged 9 commits into from
Jun 15, 2020
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)),
}
HalidOdat marked this conversation as resolved.
Show resolved Hide resolved
}

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 {
HalidOdat marked this conversation as resolved.
Show resolved Hide resolved
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