Skip to content

Commit

Permalink
Specialized String and Boolean objects
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed May 26, 2020
1 parent 4123eb5 commit 928ee40
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 121 deletions.
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, ObjectData},
object::ObjectData,
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
Expand All @@ -35,19 +35,11 @@ impl Boolean {
args: &[Value],
_: &mut Interpreter,
) -> ResultValue {
this.set_data(ObjectData::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
72 changes: 24 additions & 48 deletions boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ use crate::{
},
exec::Interpreter,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
use std::{
borrow::Borrow,
fmt::{Debug, Display, Error, Formatter},
ops::Deref,
};
Expand Down Expand Up @@ -369,51 +368,39 @@ impl Object {
}

/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
fn from_boolean(argument: &Value) -> Self {
let mut obj = Self {
data: ObjectData::Boolean,
fn from_boolean(value: bool) -> Self {
Self {
data: ObjectData::Boolean(value),
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
sym_properties: FxHashMap::default(),
state: None,
func: None,
};

obj.internal_slots
.insert("BooleanData".to_string(), argument.clone());
obj
}
}

/// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
fn from_number(argument: &Value) -> Self {
let mut obj = Self {
data: ObjectData::Number(argument.to_number()),
fn from_number(value: f64) -> Self {
Self {
data: ObjectData::Number(value),
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
sym_properties: FxHashMap::default(),
state: None,
func: None,
};

obj.internal_slots
.insert("NumberData".to_string(), argument.clone());
obj
}
}

/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
fn from_string(argument: &Value) -> Self {
let mut obj = Self {
data: ObjectData::String,
fn from_string(value: String) -> Self {
Self {
data: ObjectData::String(value),
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
sym_properties: FxHashMap::default(),
state: None,
func: None,
};

obj.internal_slots
.insert("StringData".to_string(), argument.clone());
obj
}
}

/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
Expand All @@ -439,10 +426,11 @@ impl Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-toobject
pub fn from(value: &Value) -> Result<Self, ()> {
match *value.deref().borrow() {
ValueData::Boolean(_) => Ok(Self::from_boolean(value)),
ValueData::Rational(_) => Ok(Self::from_number(value)),
ValueData::String(_) => Ok(Self::from_string(value)),
match *value.data() {
ValueData::Boolean(a) => Ok(Self::from_boolean(a)),
ValueData::Rational(a) => Ok(Self::from_number(a)),
ValueData::Integer(a) => Ok(Self::from_number(f64::from(a))),
ValueData::String(ref a) => Ok(Self::from_string(a.clone())),
ValueData::BigInt(_) => Ok(Self::from_bigint(value)),
ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()),
_ => Err(()),
Expand Down Expand Up @@ -471,17 +459,17 @@ impl Object {
}

/// Defines the different types of objects.
#[derive(Finalize, Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Trace, Finalize, Clone, PartialEq)]
pub enum ObjectData {
Function,
Array,
String,
String(String),
Symbol,
Error,
Ordinary,
Boolean,
Boolean(bool),
Number(f64),
BigInt,
Ordinary,
}

impl Display for ObjectData {
Expand All @@ -492,30 +480,18 @@ impl Display for ObjectData {
match self {
Self::Function => "Function",
Self::Array => "Array",
Self::String => "String",
Self::String(_) => "String",
Self::Symbol => "Symbol",
Self::Error => "Error",
Self::Ordinary => "Ordinary",
Self::Boolean => "Boolean",
Self::Boolean(_) => "Boolean",
Self::Number(_) => "Number",
Self::BigInt => "BigInt",
}
)
}
}

/// `Trace` implementation for `ObjectData`.
///
/// This is indeed safe, but we need to mark this as an empty trace because neither
// `NativeFunctionData` nor Node hold any GC'd objects, but Gc doesn't know that. So we need to
/// signal it manually. `rust-gc` does not have a `Trace` implementation for `fn(_, _, _)`.
///
/// <https://github.com/Manishearth/rust-gc/blob/master/gc/src/trace.rs>
/// Waiting on <https://github.com/Manishearth/rust-gc/issues/87> until we can derive Copy
unsafe impl Trace for ObjectData {
unsafe_empty_trace!();
}

/// Create a new object.
pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
if let Some(arg) = args.get(0) {
Expand Down
24 changes: 6 additions & 18 deletions boa/src/builtins/string/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,20 @@ impl String {
) -> ResultValue {
// This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe
// to its Javascript Identifier (global constructor method name)
let s = args.get(0).unwrap_or(&Value::string("")).clone();
let length_str = s.to_string().chars().count();
let string = args.get(0).map(|x| x.to_string()).unwrap_or_default();
let length = string.chars().count();

this.set_field("length", Value::from(length_str as i32));
this.set_field("length", Value::from(length as i32));

this.set_data(ObjectData::String);
this.set_internal_slot("StringData", s);
this.set_data(ObjectData::String(string.clone()));

let arg = match args.get(0) {
Some(v) => v.clone(),
None => Value::undefined(),
};

if arg.is_undefined() {
return Ok("".into());
}

Ok(Value::from(arg.to_string()))
Ok(Value::from(string))
}

/// Get the string value to a primitive string
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
// Get String from String Object and send it back as a new value
let primitive_val = this.get_internal_slot("StringData");
Ok(Value::from(format!("{}", primitive_val)))
Ok(Value::from(this.to_string()))
}

/// `String.prototype.charAt( index )`
Expand Down
27 changes: 16 additions & 11 deletions boa/src/builtins/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,20 @@ impl ValueData {
}
}

/// Creates a new boolean value from the input
pub fn to_boolean(&self) -> bool {
match *self {
Self::Undefined | Self::Null => false,
Self::Symbol(_) | Self::Object(_) => true,
Self::String(ref s) if !s.is_empty() => true,
Self::Rational(n) if n != 0.0 && !n.is_nan() => true,
Self::Integer(n) if n != 0 => true,
Self::BigInt(ref n) if *n != 0 => true,
Self::Boolean(v) => v,
_ => false,
}
}

pub fn as_object(&self) -> Option<GcCellRef<'_, Object>> {
match *self {
ValueData::Object(ref o) => Some(o.borrow()),
Expand Down Expand Up @@ -837,17 +851,8 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
// Can use the private "type" field of an Object to match on
// which type of Object it represents for special printing
match v.borrow().data {
ObjectData::String => String::from(
v.borrow()
.internal_slots
.get("StringData")
.expect("Cannot get primitive value from String"),
),
ObjectData::Boolean => {
let bool_data = v.borrow().get_internal_slot("BooleanData").to_string();

format!("Boolean {{ {} }}", bool_data)
}
ObjectData::String(ref string) => string.clone(),
ObjectData::Boolean(boolean) => format!("Boolean {{ {} }}", boolean),
ObjectData::Array => {
let len = i32::from(
&v.borrow()
Expand Down
27 changes: 16 additions & 11 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,36 +272,41 @@ impl Interpreter {
ValueData::Undefined | ValueData::Integer(_) | ValueData::Null => {
Err(Value::undefined())
}
ValueData::Boolean(_) => {
ValueData::Boolean(boolean) => {
let proto = self
.realm
.environment
.get_binding_value("Boolean")
.get_field(PROTOTYPE);

let bool_obj = Value::new_object_from_prototype(proto, ObjectData::Boolean);
bool_obj.set_internal_slot("BooleanData", value.clone());
Ok(bool_obj)
Ok(Value::new_object_from_prototype(
proto,
ObjectData::Boolean(boolean),
))
}
ValueData::Rational(rational) => {
let proto = self
.realm
.environment
.get_binding_value("Number")
.get_field(PROTOTYPE);
let number_obj =
Value::new_object_from_prototype(proto, ObjectData::Number(rational));
Ok(number_obj)

Ok(Value::new_object_from_prototype(
proto,
ObjectData::Number(rational),
))
}
ValueData::String(_) => {
ValueData::String(ref string) => {
let proto = self
.realm
.environment
.get_binding_value("String")
.get_field(PROTOTYPE);
let string_obj = Value::new_object_from_prototype(proto, ObjectData::String);
string_obj.set_internal_slot("StringData", value.clone());
Ok(string_obj)

Ok(Value::new_object_from_prototype(
proto,
ObjectData::String(string.clone()),
))
}
ValueData::Object(_) | ValueData::Symbol(_) => Ok(value.clone()),
ValueData::BigInt(_) => {
Expand Down

0 comments on commit 928ee40

Please sign in to comment.