Skip to content

Commit

Permalink
Specialized Number, String, Boolean, BigInt, Function objects
Browse files Browse the repository at this point in the history
 - Added helper functions for Object and ValueData
 - Added `Hash` trait to `Value`
 - Abstracted Object field access
 - Deprecated `ObjectInternalMethods`
  • Loading branch information
HalidOdat committed May 30, 2020
1 parent 82908df commit d3d7fb8
Show file tree
Hide file tree
Showing 21 changed files with 810 additions and 711 deletions.
10 changes: 5 additions & 5 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::{
error::RangeError,
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{same_value_zero, ResultValue, Value, ValueData},
},
Expand All @@ -42,7 +42,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 +117,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 @@ -176,7 +176,7 @@ impl Array {
// 1.
ValueData::Object(ref obj) => {
// 2.
if (*obj).deref().borrow().kind == ObjectKind::Array {
if let ObjectData::Array = (*obj).deref().borrow().data {
return Ok(value_true);
}
Ok(value_false)
Expand Down Expand Up @@ -1008,7 +1008,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 Down
13 changes: 4 additions & 9 deletions boa/src/builtins/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,11 @@ 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) => {
if let Some(bigint) = value.to_bigint() {
Value::from(bigint)
bigint
} else {
return Err(RangeError::run_new(
format!(
Expand All @@ -59,9 +55,9 @@ impl BigInt {
)?);
}
}
None => Value::from(AstBigInt::from(0)),
None => AstBigInt::from(0),
};
Ok(data)
Ok(Value::from(data))
}

/// `BigInt.prototype.toString( [radix] )`
Expand Down Expand Up @@ -119,7 +115,6 @@ 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(AstBigInt::from(0)));

make_builtin_fn(Self::to_string, "toString", &prototype, 1);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
Expand Down
45 changes: 12 additions & 33 deletions boa/src/builtins/boolean/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::{internal_methods_trait::ObjectInternalMethods, ObjectKind},
object::ObjectData,
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
Expand All @@ -35,19 +35,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 @@ -73,22 +65,7 @@ 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),
}
Ok(Value::from(Self::this_boolean_value(this)))
}

/// An Utility function used to get the internal BooleanData.
Expand All @@ -97,11 +74,14 @@ impl Boolean {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
pub(crate) fn this_boolean_value(value: &Value) -> Value {
pub(crate) fn this_boolean_value(value: &Value) -> bool {
match *value.deref().borrow() {
ValueData::Boolean(v) => Value::from(v),
ValueData::Object(ref v) => (v).deref().borrow().get_internal_slot("BooleanData"),
_ => Value::from(false),
ValueData::Boolean(v) => v,
ValueData::Object(ref v) => match v.deref().borrow().data {
ObjectData::Boolean(boolean) => boolean,
_ => unreachable!(),
},
_ => false,
}
}

Expand All @@ -110,7 +90,6 @@ impl Boolean {
// 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);
Expand Down
4 changes: 2 additions & 2 deletions boa/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use crate::{
builtins::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectKind,
object::ObjectData,
value::{ResultValue, Value},
},
exec::Interpreter,
Expand Down Expand Up @@ -47,7 +47,7 @@ impl Error {
}
// 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::Error);
this.set_data(ObjectData::Error);
Ok(Value::undefined())
}

Expand Down
4 changes: 2 additions & 2 deletions boa/src/builtins/error/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
builtins::{
function::make_builtin_fn,
function::make_constructor_fn,
object::ObjectKind,
object::ObjectData,
value::{ResultValue, Value},
},
exec::Interpreter,
Expand All @@ -38,7 +38,7 @@ impl RangeError {
}
// 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::Error);
this.set_data(ObjectData::Error);
Ok(Value::undefined())
}

Expand Down
59 changes: 35 additions & 24 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use crate::{
builtins::{
array::Array,
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{ResultValue, Value},
},
Expand All @@ -39,7 +39,7 @@ pub enum ConstructorKind {
/// Defines how this references are interpreted within the formal parameters and code body of the function.
///
/// Arrow functions don't define a `this` and thus are lexical, `function`s do define a this and thus are NonLexical
#[derive(Trace, Finalize, Debug, Clone)]
#[derive(Trace, Finalize, Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum ThisMode {
Lexical,
NonLexical,
Expand All @@ -55,12 +55,24 @@ pub enum FunctionBody {
impl Debug for FunctionBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BuiltIn(_) => write!(f, "native code"),
Self::BuiltIn(_) => write!(f, "[native]"),
Self::Ordinary(statements) => write!(f, "{:?}", statements),
}
}
}

impl PartialEq for FunctionBody {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::BuiltIn(a), Self::BuiltIn(b)) => std::ptr::eq(a, b),
(Self::Ordinary(a), Self::Ordinary(b)) => a == b,
(_, _) => false,
}
}
}

impl Eq for FunctionBody {}

/// `Trace` implementation for `FunctionBody`.
///
/// This is indeed safe, but we need to mark this as an empty trace because neither
Expand Down Expand Up @@ -158,19 +170,19 @@ impl Function {
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
pub fn call(
&self,
this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
this: &mut Value,
args_list: &[Value],
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
if self.callable {
match self.body {
FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
FunctionBody::BuiltIn(func) => func(this, args_list, interpreter),
FunctionBody::Ordinary(ref body) => {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this.clone(),
function,
None,
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Uninitialized,
Expand Down Expand Up @@ -216,23 +228,23 @@ impl Function {
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
pub fn construct(
&self,
this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
this: &mut Value,
args_list: &[Value],
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
if self.constructable {
match self.body {
FunctionBody::BuiltIn(func) => {
func(this_obj, args_list, interpreter).unwrap();
Ok(this_obj.clone())
func(this, args_list, interpreter)?;
Ok(this.clone())
}
FunctionBody::Ordinary(ref body) => {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this.clone(),
Some(this_obj.clone()),
function,
Some(this.clone()),
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Initialized,
);
Expand Down Expand Up @@ -357,7 +369,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
.writable(true)
.configurable(true);

obj.properties.insert(index.to_string(), prop);
obj.properties_mut().insert(index.to_string(), prop);
index += 1;
}

Expand All @@ -368,7 +380,10 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
///
// This gets called when a new Function() is created.
pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.set_kind(ObjectKind::Function);
this.set_data(ObjectData::Function(Function::builtin(
Vec::new(),
|_, _, _| Ok(Value::undefined()),
)));
Ok(this.clone())
}

Expand Down Expand Up @@ -399,8 +414,7 @@ pub fn make_constructor_fn(
let func_prototype = global.get_field("Function").get_field(PROTOTYPE);

// Create the function object and point its instance prototype to Function.prototype
let mut constructor_obj = Object::function();
constructor_obj.set_func(constructor_fn);
let mut constructor_obj = Object::function(constructor_fn);

constructor_obj.set_internal_slot(INSTANCE_PROTOTYPE, func_prototype);
let constructor_val = Value::from(constructor_obj);
Expand All @@ -414,14 +428,14 @@ pub fn make_constructor_fn(
.writable(false)
.configurable(false)
.enumerable(false);
constructor_val.set_property_slice("length", length);
constructor_val.set_property("length", length);

let name = Property::new()
.value(Value::from(name))
.writable(false)
.configurable(false)
.enumerable(false);
constructor_val.set_property_slice("name", name);
constructor_val.set_property("name", name);

constructor_val
}
Expand All @@ -433,12 +447,9 @@ pub fn make_builtin_fn<N>(function: NativeFunctionData, name: N, parent: &Value,
where
N: Into<String>,
{
let func = Function::builtin(Vec::new(), function);

let mut new_func = Object::function();
new_func.set_func(func);
let func = Object::function(Function::builtin(Vec::new(), function));

let new_func_obj = Value::from(new_func);
let new_func_obj = Value::from(func);
new_func_obj.set_field("length", length);

parent.set_field(name.into(), new_func_obj);
Expand Down
9 changes: 4 additions & 5 deletions boa/src/builtins/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

use crate::builtins::{
function::make_builtin_fn,
object::ObjectKind,
property::Property,
value::{ResultValue, Value},
};
Expand Down Expand Up @@ -87,7 +86,7 @@ pub fn stringify(_: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
.map(|obj| {
let object_to_return = Value::new_object(None);
for (key, val) in obj
.properties
.properties()
.iter()
.filter_map(|(k, v)| v.value.as_ref().map(|value| (k, value)))
{
Expand All @@ -104,10 +103,10 @@ pub fn stringify(_: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
Ok(Value::from(object_to_return.to_json().to_string()))
})
.ok_or_else(Value::undefined)?
} else if replacer_as_object.kind == ObjectKind::Array {
} else if replacer_as_object.is_array() {
let mut obj_to_return =
serde_json::Map::with_capacity(replacer_as_object.properties.len() - 1);
let fields = replacer_as_object.properties.keys().filter_map(|key| {
serde_json::Map::with_capacity(replacer_as_object.properties().len() - 1);
let fields = replacer_as_object.properties().keys().filter_map(|key| {
if key == "length" {
None
} else {
Expand Down
Loading

0 comments on commit d3d7fb8

Please sign in to comment.