From 0e92d37756091bdfda6a59ded27185f549301b75 Mon Sep 17 00:00:00 2001 From: Jason Williams <936006+jasonwilliams@users.noreply.github.com> Date: Tue, 9 Jul 2019 12:10:30 +0100 Subject: [PATCH] Refactor objects (#69) Refactoring Objects into Structs. Changing the way objects are accessed. New `internal_slots` and `sym_properties` fields --- src/lib/exec.rs | 16 ++-- src/lib/js/console.rs | 6 +- src/lib/js/function.rs | 9 +- src/lib/js/object.rs | 33 ++++++- src/lib/js/string.rs | 32 +++---- src/lib/js/value.rs | 198 ++++++++++++++++++--------------------- src/lib/syntax/parser.rs | 8 +- tests/js/test.js | 6 +- 8 files changed, 158 insertions(+), 150 deletions(-) diff --git a/src/lib/exec.rs b/src/lib/exec.rs index f0b2372a23..022ac4a64d 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -8,7 +8,6 @@ use crate::syntax::ast::expr::{Expr, ExprDef}; use crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}; use gc::{Gc, GcCell}; use std::borrow::Borrow; -use std::collections::HashMap; /// An execution engine pub trait Executor { @@ -100,7 +99,7 @@ impl Executor for Interpreter { v_args.push(self.run(arg)?); } match *func { - ValueData::Function(ref inner_func) => match *inner_func.borrow() { + ValueData::Function(ref inner_func) => match *inner_func.as_ref().borrow() { Function::NativeFunc(ref ntv) => { let func = ntv.data; func(this, self.run(callee)?, v_args) @@ -203,7 +202,7 @@ impl Executor for Interpreter { ExprDef::FunctionDeclExpr(ref name, ref args, ref expr) => { let function = Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); - let val = Gc::new(ValueData::Function(GcCell::new(function))); + let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function)))); if name.is_some() { self.environment .create_mutable_binding(name.clone().unwrap(), false); @@ -215,7 +214,9 @@ impl Executor for Interpreter { ExprDef::ArrowFunctionDeclExpr(ref args, ref expr) => { let function = Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); - Ok(Gc::new(ValueData::Function(GcCell::new(function)))) + Ok(Gc::new(ValueData::Function(Box::new(GcCell::new( + function, + ))))) } ExprDef::BinOpExpr(BinOp::Num(ref op), ref a, ref b) => { let v_r_a = self.run(a)?; @@ -287,10 +288,7 @@ impl Executor for Interpreter { for arg in args.iter() { v_args.push(self.run(arg)?); } - let this = Gc::new(ValueData::Object( - GcCell::new(HashMap::new()), - GcCell::new(HashMap::new()), - )); + let this = ValueData::new_obj(None); // Create a blank object, then set its __proto__ property to the [Constructor].prototype this.borrow() .set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE)); @@ -380,7 +378,7 @@ impl Executor for Interpreter { let val = self.run(val_e)?; Ok(to_value(match *val { ValueData::Undefined => "undefined", - ValueData::Null | ValueData::Object(_, _) => "object", + ValueData::Null | ValueData::Object(_) => "object", ValueData::Boolean(_) => "boolean", ValueData::Number(_) | ValueData::Integer(_) => "number", ValueData::String(_) => "string", diff --git a/src/lib/js/console.rs b/src/lib/js/console.rs index 5fe02cfa53..7926ddde7e 100644 --- a/src/lib/js/console.rs +++ b/src/lib/js/console.rs @@ -14,13 +14,13 @@ pub fn log(_: Value, _: Value, args: Vec) -> ResultValue { // The input is a vector of Values, we generate a vector of strings then pass them to println! match *x.clone() { // We don't want to print private (compiler) or prototype properties - ValueData::Object(ref v, _) => { + ValueData::Object(ref v) => { // Create empty formatted string to start writing to // TODO: once constructor is set on objects, we can do specific output for Strings, Numbers etc let mut s = String::new(); write!(s, "{{").unwrap(); - if let Some((last_key, _)) = v.borrow().iter().last() { - for (key, val) in v.borrow().iter() { + if let Some((last_key, _)) = v.borrow().properties.iter().last() { + for (key, val) in v.borrow().properties.iter() { // Don't print prototype properties if key == INSTANCE_PROTOTYPE { continue; diff --git a/src/lib/js/function.rs b/src/lib/js/function.rs index e7b66dc6c2..eb5b76feb9 100644 --- a/src/lib/js/function.rs +++ b/src/lib/js/function.rs @@ -2,7 +2,6 @@ use crate::js::object::{ObjectData, Property}; use crate::js::value::{to_value, ResultValue, Value, ValueData}; use crate::syntax::ast::expr::Expr; use gc::Gc; -use std::collections::HashMap; /// fn(this, callee, arguments) pub type NativeFunctionData = fn(Value, Value, Vec) -> ResultValue; @@ -35,8 +34,8 @@ pub struct RegularFunction { impl RegularFunction { /// Make a new regular function pub fn new(expr: Expr, args: Vec) -> Self { - let mut object = HashMap::new(); - object.insert( + let mut object = ObjectData::default(); + object.properties.insert( "arguments".to_string(), Property::new(Gc::new(ValueData::Integer(args.len() as i32))), ); @@ -55,14 +54,14 @@ pub struct NativeFunction { impl NativeFunction { /// Make a new native function with the given function data pub fn new(data: NativeFunctionData) -> Self { - let object = HashMap::new(); + let object = ObjectData::default(); Self { object, data } } } /// Create a new `Function` object pub fn _create() -> Value { - let function: ObjectData = HashMap::new(); + let function: ObjectData = ObjectData::default(); to_value(function) } /// Initialise the global object with the `Function` object diff --git a/src/lib/js/object.rs b/src/lib/js/object.rs index 3578ff013c..5207b23583 100644 --- a/src/lib/js/object.rs +++ b/src/lib/js/object.rs @@ -13,7 +13,38 @@ pub static PROTOTYPE: &'static str = "prototype"; /// As this string will be used a lot throughout the program, its best being a static global string which will be referenced pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; -pub type ObjectData = HashMap; +/// `ObjectData` is the representation of an object in JavaScript +#[derive(Trace, Finalize, Debug, Clone)] +pub struct ObjectData { + /// Kind + pub kind: ObjectKind, + /// Internal Slots + pub internal_slots: Box>, + /// Properties + pub properties: Box>, + /// Symbol Properties + pub sym_properties: Box>, +} + +impl ObjectData { + /// Return a new ObjectData struct, with `kind` set to Ordinary + pub fn default() -> ObjectData { + Self { + kind: ObjectKind::Ordinary, + internal_slots: Box::new(HashMap::new()), + properties: Box::new(HashMap::new()), + sym_properties: Box::new(HashMap::new()), + } + } +} +#[derive(Trace, Finalize, Clone, Debug)] +pub enum ObjectKind { + Function, + Array, + Symbol, + Error, + Ordinary, +} /// A Javascript Property AKA The Property Descriptor /// [[SPEC] - The Property Descriptor Specification Type](https://tc39.github.io/ecma262/#sec-property-descriptor-specification-type) diff --git a/src/lib/js/string.rs b/src/lib/js/string.rs index 16697f2ec4..ea3cd13651 100644 --- a/src/lib/js/string.rs +++ b/src/lib/js/string.rs @@ -18,20 +18,20 @@ pub fn make_string(this: Value, _: Value, args: Vec) -> ResultValue { // let a: String = from_value(args[0].clone()).unwrap(); // this.set_field_slice("length", to_value(a.len() as i32)); - this.set_private_field_slice("PrimitiveValue", args[0].clone()); + this.set_internal_slot("PrimitiveValue", args[0].clone()); Ok(this) } /// Get a string's length pub fn get_string_length(this: Value, _: Value, _: Vec) -> ResultValue { - let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let this_str: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); Ok(to_value::(this_str.len() as i32)) } /// Get the string value to a primitive string pub fn to_string(this: Value, _: Value, _: Vec) -> ResultValue { // Get String from String Object and send it back as a new value - let primitive_val = this.get_private_field("PrimitiveValue"); + let primitive_val = this.get_internal_slot("PrimitiveValue"); Ok(to_value(format!("{}", primitive_val).to_string())) } @@ -43,7 +43,7 @@ pub fn char_at(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments (we only care about the first one in this case) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); let pos: i32 = from_value(args[0].clone()).unwrap(); // Calling .len() on a string would give the wrong result, as they are bytes not the number of @@ -70,7 +70,7 @@ pub fn char_code_at(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments (we only care about the first one in this case) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. @@ -94,7 +94,7 @@ pub fn concat(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); let mut new_str = primitive_val.clone(); @@ -113,7 +113,7 @@ pub fn repeat(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments (only care about the first one in this case) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); let repeat_times: usize = from_value(args[0].clone()).unwrap(); Ok(to_value(primitive_val.repeat(repeat_times))) @@ -126,7 +126,7 @@ pub fn slice(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); let start: i32 = from_value(args[0].clone()).unwrap(); let end: i32 = from_value(args[1].clone()).unwrap(); @@ -163,7 +163,7 @@ pub fn starts_with(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); // TODO: Should throw TypeError if pattern is regular expression let search_string: String = from_value(args[0].clone()).unwrap(); @@ -198,7 +198,7 @@ pub fn ends_with(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); // TODO: Should throw TypeError if search_string is regular expression let search_string: String = from_value(args[0].clone()).unwrap(); @@ -235,7 +235,7 @@ pub fn includes(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); // TODO: Should throw TypeError if search_string is regular expression let search_string: String = from_value(args[0].clone()).unwrap(); @@ -267,7 +267,7 @@ pub fn index_of(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); // TODO: Should throw TypeError if search_string is regular expression let search_string: String = from_value(args[0].clone()).unwrap(); @@ -308,7 +308,7 @@ pub fn last_index_of(this: Value, _: Value, args: Vec) -> ResultValue { // ^^ represents instance ^^ represents arguments) // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value - let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let primitive_val: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); // TODO: Should throw TypeError if search_string is regular expression let search_string: String = from_value(args[0].clone()).unwrap(); @@ -439,19 +439,19 @@ fn is_trimmable_whitespace(c: char) -> bool { } pub fn trim(this: Value, _: Value, _: Vec) -> ResultValue { - let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let this_str: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); Ok(to_value(this_str.trim_matches(is_trimmable_whitespace))) } pub fn trim_start(this: Value, _: Value, _: Vec) -> ResultValue { - let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let this_str: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); Ok(to_value( this_str.trim_start_matches(is_trimmable_whitespace), )) } pub fn trim_end(this: Value, _: Value, _: Vec) -> ResultValue { - let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap(); + let this_str: String = from_value(this.get_internal_slot("PrimitiveValue")).unwrap(); Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace))) } diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index e5e8aea715..cc46e6af12 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -5,10 +5,8 @@ use crate::js::{ use gc::{Gc, GcCell}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use std::{ - collections::HashMap, f64::NAN, fmt::{self, Display}, - iter::FromIterator, ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Not, Rem, Shl, Shr, Sub}, str::FromStr, }; @@ -35,40 +33,34 @@ pub enum ValueData { /// `Number` - A 32-bit integer, such as `42` Integer(i32), /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values - /// Some Objects will need an internal slot to hold private values, so our second ObjectData is for that - /// The second object storage is optional for now - Object(GcCell, GcCell), + Object(GcCell), /// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object - Function(GcCell), + Function(Box>), } impl ValueData { /// Returns a new empty object pub fn new_obj(global: Option<&Value>) -> Value { - let mut obj: ObjectData = HashMap::new(); - let private_obj: ObjectData = HashMap::new(); + let mut obj = ObjectData::default(); + if global.is_some() { let obj_proto = global .unwrap() .get_field_slice("Object") .get_field_slice(PROTOTYPE); - obj.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(obj_proto)); + obj.properties + .insert(INSTANCE_PROTOTYPE.to_string(), Property::new(obj_proto)); } - Gc::new(ValueData::Object( - GcCell::new(obj), - GcCell::new(private_obj), - )) + Gc::new(ValueData::Object(GcCell::new(obj))) } /// Similar to `new_obj`, but you can pass a prototype to create from pub fn new_obj_from_prototype(proto: Value) -> Value { - let mut obj: ObjectData = HashMap::new(); - let private_obj: ObjectData = HashMap::new(); - obj.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(proto)); - Gc::new(ValueData::Object( - GcCell::new(obj), - GcCell::new(private_obj), - )) + let mut obj = ObjectData::default(); + + obj.internal_slots + .insert(INSTANCE_PROTOTYPE.to_string(), proto); + Gc::new(ValueData::Object(GcCell::new(obj))) } /// This will tell us if we can exten an object or not, not properly implemented yet, for now always returns true @@ -82,7 +74,7 @@ impl ValueData { /// Returns true if the value is an object pub fn is_object(&self) -> bool { match *self { - ValueData::Object(_, _) => true, + ValueData::Object(_) => true, _ => false, } } @@ -139,7 +131,7 @@ impl ValueData { /// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) pub fn is_true(&self) -> bool { match *self { - ValueData::Object(_, _) => true, + ValueData::Object(_) => true, ValueData::String(ref s) if !s.is_empty() => true, ValueData::Number(n) if n != 0.0 && !n.is_nan() => true, ValueData::Integer(n) if n != 0 => true, @@ -151,7 +143,7 @@ impl ValueData { /// Converts the value into a 64-bit floating point number pub fn to_num(&self) -> f64 { match *self { - ValueData::Object(_, _) | ValueData::Undefined | ValueData::Function(_) => NAN, + ValueData::Object(_) | ValueData::Undefined | ValueData::Function(_) => NAN, ValueData::String(ref str) => match FromStr::from_str(str) { Ok(num) => num, Err(_) => NAN, @@ -166,7 +158,7 @@ impl ValueData { /// Converts the value into a 32-bit integer pub fn to_int(&self) -> i32 { match *self { - ValueData::Object(_, _) + ValueData::Object(_) | ValueData::Undefined | ValueData::Null | ValueData::Boolean(false) @@ -185,11 +177,11 @@ impl ValueData { /// It will return a boolean based on if the value was removed, if there was no value to remove false is returned pub fn remove_prop(&self, field: &str) { match *self { - ValueData::Object(ref obj, _) => obj.borrow_mut().deref_mut().remove(field), + ValueData::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field), // Accesing .object on borrow() seems to automatically dereference it, so we don't need the * ValueData::Function(ref func) => match func.borrow_mut().deref_mut() { - Function::NativeFunc(ref mut func) => func.object.remove(field), - Function::RegularFunc(ref mut func) => func.object.remove(field), + Function::NativeFunc(ref mut func) => func.object.properties.remove(field), + Function::RegularFunc(ref mut func) => func.object.properties.remove(field), }, _ => None, }; @@ -207,7 +199,7 @@ impl ValueData { } let obj: ObjectData = match *self { - ValueData::Object(ref obj, _) => { + ValueData::Object(ref obj) => { let hash = obj.clone(); // TODO: This will break, we should return a GcCellRefMut instead // into_inner will consume the wrapped value and remove it from the hashmap @@ -221,9 +213,9 @@ impl ValueData { _ => return None, }; - match obj.get(field) { + match obj.properties.get(field) { Some(val) => Some(val.clone()), - None => match obj.get(&INSTANCE_PROTOTYPE.to_string()) { + None => match obj.properties.get(&INSTANCE_PROTOTYPE.to_string()) { Some(prop) => prop.value.get_prop(field), None => None, }, @@ -242,7 +234,7 @@ impl ValueData { configurable: Option, ) { let obj: Option = match self { - ValueData::Object(ref obj, _) => Some(obj.borrow_mut().deref_mut().clone()), + ValueData::Object(ref obj) => Some(obj.borrow_mut().deref_mut().clone()), // Accesing .object on borrow() seems to automatically dereference it, so we don't need the * ValueData::Function(ref func) => match func.borrow_mut().deref_mut() { Function::NativeFunc(ref mut func) => Some(func.object.clone()), @@ -251,9 +243,9 @@ impl ValueData { _ => None, }; - if let Some(mut hashmap) = obj { + if let Some(mut obj_data) = obj { // Use value, or walk up the prototype chain - if let Some(ref mut prop) = hashmap.get_mut(field) { + if let Some(ref mut prop) = obj_data.properties.get_mut(field) { prop.value = value.unwrap_or_else(|| prop.value.clone()); prop.enumerable = enumerable.unwrap_or(prop.enumerable); prop.writable = writable.unwrap_or(prop.writable); @@ -264,29 +256,17 @@ impl ValueData { /// Resolve the property in the object /// Returns a copy of the Property - pub fn get_private_prop(&self, field: &str) -> Option { + pub fn get_internal_slot(&self, field: &str) -> Value { let obj: ObjectData = match *self { - ValueData::Object(_, ref obj) => { + ValueData::Object(ref obj) => { let hash = obj.clone(); hash.into_inner() } - _ => return None, + _ => return Gc::new(ValueData::Undefined), }; - match obj.get(field) { - Some(val) => Some(val.clone()), - None => match obj.get(&INSTANCE_PROTOTYPE.to_string()) { - Some(prop) => prop.value.get_prop(field), - None => None, - }, - } - } - - /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist - /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] - pub fn get_private_field(&self, field: &str) -> Value { - match self.get_private_prop(field) { - Some(prop) => prop.value.clone(), + match obj.internal_slots.get(field) { + Some(val) => val.clone(), None => Gc::new(ValueData::Undefined), } } @@ -338,17 +318,21 @@ impl ValueData { /// Set the field in the value pub fn set_field(&self, field: String, val: Value) -> Value { match *self { - ValueData::Object(ref obj, _) => { - obj.borrow_mut().insert(field, Property::new(val.clone())); + ValueData::Object(ref obj) => { + obj.borrow_mut() + .properties + .insert(field, Property::new(val.clone())); } ValueData::Function(ref func) => { match *func.borrow_mut().deref_mut() { - Function::NativeFunc(ref mut f) => { - f.object.insert(field, Property::new(val.clone())) - } - Function::RegularFunc(ref mut f) => { - f.object.insert(field, Property::new(val.clone())) - } + Function::NativeFunc(ref mut f) => f + .object + .properties + .insert(field, Property::new(val.clone())), + Function::RegularFunc(ref mut f) => f + .object + .properties + .insert(field, Property::new(val.clone())), }; } _ => (), @@ -362,28 +346,29 @@ impl ValueData { } /// Set the private field in the value - pub fn set_private_field(&self, field: String, val: Value) -> Value { - if let ValueData::Object(_, ref obj) = *self { - obj.borrow_mut().insert(field, Property::new(val.clone())); + pub fn set_internal_slot(&self, field: &str, val: Value) -> Value { + if let ValueData::Object(ref obj) = *self { + obj.borrow_mut() + .internal_slots + .insert(field.to_string(), val.clone()); } val } - /// Set the private field in the value - pub fn set_private_field_slice<'a>(&self, field: &'a str, val: Value) -> Value { - self.set_private_field(field.to_string(), val) - } - /// Set the property in the value pub fn set_prop(&self, field: String, prop: Property) -> Property { match *self { - ValueData::Object(ref obj, _) => { - obj.borrow_mut().insert(field, prop.clone()); + ValueData::Object(ref obj) => { + obj.borrow_mut().properties.insert(field, prop.clone()); } ValueData::Function(ref func) => { match *func.borrow_mut().deref_mut() { - Function::NativeFunc(ref mut f) => f.object.insert(field, prop.clone()), - Function::RegularFunc(ref mut f) => f.object.insert(field, prop.clone()), + Function::NativeFunc(ref mut f) => { + f.object.properties.insert(field, prop.clone()) + } + Function::RegularFunc(ref mut f) => { + f.object.properties.insert(field, prop.clone()) + } }; } _ => (), @@ -403,28 +388,27 @@ impl ValueData { JSONValue::String(v) => ValueData::String(v), JSONValue::Bool(v) => ValueData::Boolean(v), JSONValue::Array(vs) => { - let mut i = 0; - let private_data: ObjectData = HashMap::new(); - let mut data: ObjectData = FromIterator::from_iter(vs.iter().map(|json| { - i += 1; - ( - (i - 1).to_string().to_string(), - Property::new(to_value(json.clone())), - ) - })); - data.insert( + let mut new_obj = ObjectData::default(); + for (idx, json) in vs.iter().enumerate() { + new_obj + .properties + .insert(idx.to_string(), Property::new(to_value(json.clone()))); + } + new_obj.properties.insert( "length".to_string(), Property::new(to_value(vs.len() as i32)), ); - ValueData::Object(GcCell::new(data), GcCell::new(private_data)) + ValueData::Object(GcCell::new(new_obj)) } JSONValue::Object(obj) => { - let private_data: ObjectData = HashMap::new(); - let data: ObjectData = FromIterator::from_iter( - obj.iter() - .map(|(key, json)| (key.clone(), Property::new(to_value(json.clone())))), - ); - ValueData::Object(GcCell::new(data), GcCell::new(private_data)) + let mut new_obj = ObjectData::default(); + for (key, json) in obj.iter() { + new_obj + .properties + .insert(key.clone(), Property::new(to_value(json.clone()))); + } + + ValueData::Object(GcCell::new(new_obj)) } JSONValue::Null => ValueData::Null, } @@ -434,9 +418,9 @@ impl ValueData { match *self { ValueData::Null | ValueData::Undefined => JSONValue::Null, ValueData::Boolean(b) => JSONValue::Bool(b), - ValueData::Object(ref obj, _) => { + ValueData::Object(ref obj) => { let mut new_obj = Map::new(); - for (k, v) in obj.borrow().iter() { + for (k, v) in obj.borrow().properties.iter() { if k != INSTANCE_PROTOTYPE { new_obj.insert(k.clone(), v.value.to_json()); } @@ -480,11 +464,11 @@ impl Display for ValueData { _ => v.to_string(), } ), - ValueData::Object(ref v, ref p) => { + ValueData::Object(ref v) => { write!(f, "{{")?; // Print public properties - if let Some((last_key, _)) = v.borrow().iter().last() { - for (key, val) in v.borrow().iter() { + if let Some((last_key, _)) = v.borrow().properties.iter().last() { + for (key, val) in v.borrow().properties.iter() { write!(f, "{}: {}", key, val.value.clone())?; if key != last_key { write!(f, ", ")?; @@ -492,10 +476,10 @@ impl Display for ValueData { } }; - // Print private properties - if let Some((last_key, _)) = p.borrow().iter().last() { - for (key, val) in p.borrow().iter() { - write!(f, "(Private) {}: {}", key, val.value.clone())?; + // Print internal slots + if let Some((last_key, _)) = v.borrow().internal_slots.iter().last() { + for (key, val) in v.borrow().internal_slots.iter() { + write!(f, "[[{}]]: {}", key, &val)?; if key != last_key { write!(f, ", ")?; } @@ -696,18 +680,20 @@ impl FromValue for bool { impl<'s, T: ToValue> ToValue for &'s [T] { fn to_value(&self) -> Value { - let mut arr = HashMap::new(); + let mut arr = ObjectData::default(); for (i, item) in self.iter().enumerate() { - arr.insert(i.to_string(), Property::new(item.to_value())); + arr.properties + .insert(i.to_string(), Property::new(item.to_value())); } to_value(arr) } } impl ToValue for Vec { fn to_value(&self) -> Value { - let mut arr = HashMap::new(); + let mut arr = ObjectData::default(); for (i, item) in self.iter().enumerate() { - arr.insert(i.to_string(), Property::new(item.to_value())); + arr.properties + .insert(i.to_string(), Property::new(item.to_value())); } to_value(arr) } @@ -726,18 +712,14 @@ impl FromValue for Vec { impl ToValue for ObjectData { fn to_value(&self) -> Value { - let private_obj: Self = HashMap::new(); - Gc::new(ValueData::Object( - GcCell::new(self.clone()), - GcCell::new(private_obj), - )) + Gc::new(ValueData::Object(GcCell::new(self.clone()))) } } impl FromValue for ObjectData { fn from_value(v: Value) -> Result { match *v { - ValueData::Object(ref obj, _) => Ok(obj.clone().into_inner()), + ValueData::Object(ref obj) => Ok(obj.clone().into_inner()), ValueData::Function(ref func) => Ok(match *func.borrow().deref() { Function::NativeFunc(ref data) => data.object.clone(), Function::RegularFunc(ref data) => data.object.clone(), @@ -790,8 +772,8 @@ impl FromValue for Option { impl ToValue for NativeFunctionData { fn to_value(&self) -> Value { - Gc::new(ValueData::Function(GcCell::new(Function::NativeFunc( - NativeFunction::new(*self), + Gc::new(ValueData::Function(Box::new(GcCell::new( + Function::NativeFunc(NativeFunction::new(*self)), )))) } } diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index 8b5847aef7..f7616332d4 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -239,8 +239,8 @@ impl Parser { loop { match self.get_token(self.pos)?.data { TokenData::Keyword(Keyword::Case) - | TokenData::Keyword(Keyword::Default) => break, - TokenData::Punctuator(Punctuator::CloseBlock) => break, + | TokenData::Keyword(Keyword::Default) + | TokenData::Punctuator(Punctuator::CloseBlock) => break, _ => block.push(self.parse()?), } } @@ -252,8 +252,8 @@ impl Parser { loop { match self.get_token(self.pos)?.data { TokenData::Keyword(Keyword::Case) - | TokenData::Keyword(Keyword::Default) => break, - TokenData::Punctuator(Punctuator::CloseBlock) => break, + | TokenData::Keyword(Keyword::Default) + | TokenData::Punctuator(Punctuator::CloseBlock) => break, _ => block.push(self.parse()?), } } diff --git a/tests/js/test.js b/tests/js/test.js index cd873a69ab..ef6239f657 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -1,4 +1,2 @@ -let a = "b"; -let c = 5; - -c + a; \ No newline at end of file +let a = new String("iisiojdou"); +a;