Browse Source

Refactor objects (#69)

Refactoring Objects into Structs. Changing the way objects are accessed.
New `internal_slots` and `sym_properties` fields
pull/70/head
Jason Williams 6 years ago committed by GitHub
parent
commit
0e92d37756
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      src/lib/exec.rs
  2. 6
      src/lib/js/console.rs
  3. 9
      src/lib/js/function.rs
  4. 33
      src/lib/js/object.rs
  5. 32
      src/lib/js/string.rs
  6. 198
      src/lib/js/value.rs
  7. 8
      src/lib/syntax/parser.rs
  8. 6
      tests/js/test.js

16
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",

6
src/lib/js/console.rs

@ -14,13 +14,13 @@ pub fn log(_: Value, _: Value, args: Vec<Value>) -> 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;

9
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<Value>) -> ResultValue;
@ -35,8 +34,8 @@ pub struct RegularFunction {
impl RegularFunction {
/// Make a new regular function
pub fn new(expr: Expr, args: Vec<String>) -> 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

33
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<String, Property>;
/// `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<HashMap<String, Value>>,
/// Properties
pub properties: Box<HashMap<String, Property>>,
/// Symbol Properties
pub sym_properties: Box<HashMap<usize, Property>>,
}
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)

32
src/lib/js/string.rs

@ -18,20 +18,20 @@ pub fn make_string(this: Value, _: Value, args: Vec<Value>) -> 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<Value>) -> 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::<i32>(this_str.len() as i32))
}
/// Get the string value to a primitive string
pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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<Value>) -> 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)))
}

198
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<ObjectData>, GcCell<ObjectData>),
Object(GcCell<ObjectData>),
/// `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>),
Function(Box<GcCell<Function>>),
}
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<bool>,
) {
let obj: Option<ObjectData> = 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<Property> {
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<T: ToValue> ToValue for Vec<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)
}
@ -726,18 +712,14 @@ impl<T: FromValue> FromValue for Vec<T> {
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<Self, &'static str> {
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<T: FromValue> FromValue for Option<T> {
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)),
))))
}
}

8
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()?),
}
}

6
tests/js/test.js

@ -1,4 +1,2 @@
let a = "b";
let c = 5;
c + a;
let a = new String("iisiojdou");
a;

Loading…
Cancel
Save