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 5 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 crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp};
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::HashMap;
/// An execution engine /// An execution engine
pub trait Executor { pub trait Executor {
@ -100,7 +99,7 @@ impl Executor for Interpreter {
v_args.push(self.run(arg)?); v_args.push(self.run(arg)?);
} }
match *func { 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) => { Function::NativeFunc(ref ntv) => {
let func = ntv.data; let func = ntv.data;
func(this, self.run(callee)?, v_args) func(this, self.run(callee)?, v_args)
@ -203,7 +202,7 @@ impl Executor for Interpreter {
ExprDef::FunctionDeclExpr(ref name, ref args, ref expr) => { ExprDef::FunctionDeclExpr(ref name, ref args, ref expr) => {
let function = let function =
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); 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() { if name.is_some() {
self.environment self.environment
.create_mutable_binding(name.clone().unwrap(), false); .create_mutable_binding(name.clone().unwrap(), false);
@ -215,7 +214,9 @@ impl Executor for Interpreter {
ExprDef::ArrowFunctionDeclExpr(ref args, ref expr) => { ExprDef::ArrowFunctionDeclExpr(ref args, ref expr) => {
let function = let function =
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); 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) => { ExprDef::BinOpExpr(BinOp::Num(ref op), ref a, ref b) => {
let v_r_a = self.run(a)?; let v_r_a = self.run(a)?;
@ -287,10 +288,7 @@ impl Executor for Interpreter {
for arg in args.iter() { for arg in args.iter() {
v_args.push(self.run(arg)?); v_args.push(self.run(arg)?);
} }
let this = Gc::new(ValueData::Object( let this = ValueData::new_obj(None);
GcCell::new(HashMap::new()),
GcCell::new(HashMap::new()),
));
// Create a blank object, then set its __proto__ property to the [Constructor].prototype // Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.borrow() this.borrow()
.set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE)); .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)?; let val = self.run(val_e)?;
Ok(to_value(match *val { Ok(to_value(match *val {
ValueData::Undefined => "undefined", ValueData::Undefined => "undefined",
ValueData::Null | ValueData::Object(_, _) => "object", ValueData::Null | ValueData::Object(_) => "object",
ValueData::Boolean(_) => "boolean", ValueData::Boolean(_) => "boolean",
ValueData::Number(_) | ValueData::Integer(_) => "number", ValueData::Number(_) | ValueData::Integer(_) => "number",
ValueData::String(_) => "string", 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! // The input is a vector of Values, we generate a vector of strings then pass them to println!
match *x.clone() { match *x.clone() {
// We don't want to print private (compiler) or prototype properties // 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 // Create empty formatted string to start writing to
// TODO: once constructor is set on objects, we can do specific output for Strings, Numbers etc // TODO: once constructor is set on objects, we can do specific output for Strings, Numbers etc
let mut s = String::new(); let mut s = String::new();
write!(s, "{{").unwrap(); write!(s, "{{").unwrap();
if let Some((last_key, _)) = v.borrow().iter().last() { if let Some((last_key, _)) = v.borrow().properties.iter().last() {
for (key, val) in v.borrow().iter() { for (key, val) in v.borrow().properties.iter() {
// Don't print prototype properties // Don't print prototype properties
if key == INSTANCE_PROTOTYPE { if key == INSTANCE_PROTOTYPE {
continue; 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::js::value::{to_value, ResultValue, Value, ValueData};
use crate::syntax::ast::expr::Expr; use crate::syntax::ast::expr::Expr;
use gc::Gc; use gc::Gc;
use std::collections::HashMap;
/// fn(this, callee, arguments) /// fn(this, callee, arguments)
pub type NativeFunctionData = fn(Value, Value, Vec<Value>) -> ResultValue; pub type NativeFunctionData = fn(Value, Value, Vec<Value>) -> ResultValue;
@ -35,8 +34,8 @@ pub struct RegularFunction {
impl RegularFunction { impl RegularFunction {
/// Make a new regular function /// Make a new regular function
pub fn new(expr: Expr, args: Vec<String>) -> Self { pub fn new(expr: Expr, args: Vec<String>) -> Self {
let mut object = HashMap::new(); let mut object = ObjectData::default();
object.insert( object.properties.insert(
"arguments".to_string(), "arguments".to_string(),
Property::new(Gc::new(ValueData::Integer(args.len() as i32))), Property::new(Gc::new(ValueData::Integer(args.len() as i32))),
); );
@ -55,14 +54,14 @@ pub struct NativeFunction {
impl NativeFunction { impl NativeFunction {
/// Make a new native function with the given function data /// Make a new native function with the given function data
pub fn new(data: NativeFunctionData) -> Self { pub fn new(data: NativeFunctionData) -> Self {
let object = HashMap::new(); let object = ObjectData::default();
Self { object, data } Self { object, data }
} }
} }
/// Create a new `Function` object /// Create a new `Function` object
pub fn _create() -> Value { pub fn _create() -> Value {
let function: ObjectData = HashMap::new(); let function: ObjectData = ObjectData::default();
to_value(function) to_value(function)
} }
/// Initialise the global object with the `Function` object /// 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 /// 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 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 /// A Javascript Property AKA The Property Descriptor
/// [[SPEC] - The Property Descriptor Specification Type](https://tc39.github.io/ecma262/#sec-property-descriptor-specification-type) /// [[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(); // let a: String = from_value(args[0].clone()).unwrap();
// this.set_field_slice("length", to_value(a.len() as i32)); // 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) Ok(this)
} }
/// Get a string's length /// Get a string's length
pub fn get_string_length(this: Value, _: Value, _: Vec<Value>) -> ResultValue { 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)) Ok(to_value::<i32>(this_str.len() as i32))
} }
/// Get the string value to a primitive string /// Get the string value to a primitive string
pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
// Get String from String Object and send it back as a new value // 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())) 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) // ^^ 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. // 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 // 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(); 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 // 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) // ^^ 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. // 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 // 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 // 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. // 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 // ^^ represents instance ^^ represents arguments
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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(); 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) // ^^ 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. // 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 // 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(); let repeat_times: usize = from_value(args[0].clone()).unwrap();
Ok(to_value(primitive_val.repeat(repeat_times))) 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) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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 start: i32 = from_value(args[0].clone()).unwrap();
let end: i32 = from_value(args[1].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) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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 // TODO: Should throw TypeError if pattern is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); 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) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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 // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); 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) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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 // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); 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) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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 // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); 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) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // 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 // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); 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 { 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))) Ok(to_value(this_str.trim_matches(is_trimmable_whitespace)))
} }
pub fn trim_start(this: Value, _: Value, _: Vec<Value>) -> ResultValue { 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( Ok(to_value(
this_str.trim_start_matches(is_trimmable_whitespace), this_str.trim_start_matches(is_trimmable_whitespace),
)) ))
} }
pub fn trim_end(this: Value, _: Value, _: Vec<Value>) -> ResultValue { 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))) 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 gc::{Gc, GcCell};
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use std::{ use std::{
collections::HashMap,
f64::NAN, f64::NAN,
fmt::{self, Display}, fmt::{self, Display},
iter::FromIterator,
ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Not, Rem, Shl, Shr, Sub}, ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Not, Rem, Shl, Shr, Sub},
str::FromStr, str::FromStr,
}; };
@ -35,40 +33,34 @@ pub enum ValueData {
/// `Number` - A 32-bit integer, such as `42` /// `Number` - A 32-bit integer, such as `42`
Integer(i32), Integer(i32),
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values /// `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 Object(GcCell<ObjectData>),
/// The second object storage is optional for now
Object(GcCell<ObjectData>, 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` - 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 { impl ValueData {
/// Returns a new empty object /// Returns a new empty object
pub fn new_obj(global: Option<&Value>) -> Value { pub fn new_obj(global: Option<&Value>) -> Value {
let mut obj: ObjectData = HashMap::new(); let mut obj = ObjectData::default();
let private_obj: ObjectData = HashMap::new();
if global.is_some() { if global.is_some() {
let obj_proto = global let obj_proto = global
.unwrap() .unwrap()
.get_field_slice("Object") .get_field_slice("Object")
.get_field_slice(PROTOTYPE); .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( Gc::new(ValueData::Object(GcCell::new(obj)))
GcCell::new(obj),
GcCell::new(private_obj),
))
} }
/// Similar to `new_obj`, but you can pass a prototype to create from /// Similar to `new_obj`, but you can pass a prototype to create from
pub fn new_obj_from_prototype(proto: Value) -> Value { pub fn new_obj_from_prototype(proto: Value) -> Value {
let mut obj: ObjectData = HashMap::new(); let mut obj = ObjectData::default();
let private_obj: ObjectData = HashMap::new();
obj.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(proto)); obj.internal_slots
Gc::new(ValueData::Object( .insert(INSTANCE_PROTOTYPE.to_string(), proto);
GcCell::new(obj), Gc::new(ValueData::Object(GcCell::new(obj)))
GcCell::new(private_obj),
))
} }
/// This will tell us if we can exten an object or not, not properly implemented yet, for now always returns true /// 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 /// Returns true if the value is an object
pub fn is_object(&self) -> bool { pub fn is_object(&self) -> bool {
match *self { match *self {
ValueData::Object(_, _) => true, ValueData::Object(_) => true,
_ => false, _ => false,
} }
} }
@ -139,7 +131,7 @@ impl ValueData {
/// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) /// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean)
pub fn is_true(&self) -> bool { pub fn is_true(&self) -> bool {
match *self { match *self {
ValueData::Object(_, _) => true, ValueData::Object(_) => true,
ValueData::String(ref s) if !s.is_empty() => true, ValueData::String(ref s) if !s.is_empty() => true,
ValueData::Number(n) if n != 0.0 && !n.is_nan() => true, ValueData::Number(n) if n != 0.0 && !n.is_nan() => true,
ValueData::Integer(n) if n != 0 => true, ValueData::Integer(n) if n != 0 => true,
@ -151,7 +143,7 @@ impl ValueData {
/// Converts the value into a 64-bit floating point number /// Converts the value into a 64-bit floating point number
pub fn to_num(&self) -> f64 { pub fn to_num(&self) -> f64 {
match *self { 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) { ValueData::String(ref str) => match FromStr::from_str(str) {
Ok(num) => num, Ok(num) => num,
Err(_) => NAN, Err(_) => NAN,
@ -166,7 +158,7 @@ impl ValueData {
/// Converts the value into a 32-bit integer /// Converts the value into a 32-bit integer
pub fn to_int(&self) -> i32 { pub fn to_int(&self) -> i32 {
match *self { match *self {
ValueData::Object(_, _) ValueData::Object(_)
| ValueData::Undefined | ValueData::Undefined
| ValueData::Null | ValueData::Null
| ValueData::Boolean(false) | 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 /// 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) { pub fn remove_prop(&self, field: &str) {
match *self { 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 * // 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() { ValueData::Function(ref func) => match func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut func) => func.object.remove(field), Function::NativeFunc(ref mut func) => func.object.properties.remove(field),
Function::RegularFunc(ref mut func) => func.object.remove(field), Function::RegularFunc(ref mut func) => func.object.properties.remove(field),
}, },
_ => None, _ => None,
}; };
@ -207,7 +199,7 @@ impl ValueData {
} }
let obj: ObjectData = match *self { let obj: ObjectData = match *self {
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj) => {
let hash = obj.clone(); let hash = obj.clone();
// TODO: This will break, we should return a GcCellRefMut instead // TODO: This will break, we should return a GcCellRefMut instead
// into_inner will consume the wrapped value and remove it from the hashmap // into_inner will consume the wrapped value and remove it from the hashmap
@ -221,9 +213,9 @@ impl ValueData {
_ => return None, _ => return None,
}; };
match obj.get(field) { match obj.properties.get(field) {
Some(val) => Some(val.clone()), 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), Some(prop) => prop.value.get_prop(field),
None => None, None => None,
}, },
@ -242,7 +234,7 @@ impl ValueData {
configurable: Option<bool>, configurable: Option<bool>,
) { ) {
let obj: Option<ObjectData> = match self { 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 * // 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() { ValueData::Function(ref func) => match func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut func) => Some(func.object.clone()), Function::NativeFunc(ref mut func) => Some(func.object.clone()),
@ -251,9 +243,9 @@ impl ValueData {
_ => None, _ => None,
}; };
if let Some(mut hashmap) = obj { if let Some(mut obj_data) = obj {
// Use value, or walk up the prototype chain // 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.value = value.unwrap_or_else(|| prop.value.clone());
prop.enumerable = enumerable.unwrap_or(prop.enumerable); prop.enumerable = enumerable.unwrap_or(prop.enumerable);
prop.writable = writable.unwrap_or(prop.writable); prop.writable = writable.unwrap_or(prop.writable);
@ -264,29 +256,17 @@ impl ValueData {
/// Resolve the property in the object /// Resolve the property in the object
/// Returns a copy of the Property /// 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 { let obj: ObjectData = match *self {
ValueData::Object(_, ref obj) => { ValueData::Object(ref obj) => {
let hash = obj.clone(); let hash = obj.clone();
hash.into_inner() hash.into_inner()
} }
_ => return None, _ => return Gc::new(ValueData::Undefined),
}; };
match obj.get(field) { match obj.internal_slots.get(field) {
Some(val) => Some(val.clone()), Some(val) => 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(),
None => Gc::new(ValueData::Undefined), None => Gc::new(ValueData::Undefined),
} }
} }
@ -338,17 +318,21 @@ impl ValueData {
/// Set the field in the value /// Set the field in the value
pub fn set_field(&self, field: String, val: Value) -> Value { pub fn set_field(&self, field: String, val: Value) -> Value {
match *self { match *self {
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj) => {
obj.borrow_mut().insert(field, Property::new(val.clone())); obj.borrow_mut()
.properties
.insert(field, Property::new(val.clone()));
} }
ValueData::Function(ref func) => { ValueData::Function(ref func) => {
match *func.borrow_mut().deref_mut() { match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => { Function::NativeFunc(ref mut f) => f
f.object.insert(field, Property::new(val.clone())) .object
} .properties
Function::RegularFunc(ref mut f) => { .insert(field, Property::new(val.clone())),
f.object.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 /// Set the private field in the value
pub fn set_private_field(&self, field: String, val: Value) -> Value { pub fn set_internal_slot(&self, field: &str, val: Value) -> Value {
if let ValueData::Object(_, ref obj) = *self { if let ValueData::Object(ref obj) = *self {
obj.borrow_mut().insert(field, Property::new(val.clone())); obj.borrow_mut()
.internal_slots
.insert(field.to_string(), val.clone());
} }
val 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 /// Set the property in the value
pub fn set_prop(&self, field: String, prop: Property) -> Property { pub fn set_prop(&self, field: String, prop: Property) -> Property {
match *self { match *self {
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj) => {
obj.borrow_mut().insert(field, prop.clone()); obj.borrow_mut().properties.insert(field, prop.clone());
} }
ValueData::Function(ref func) => { ValueData::Function(ref func) => {
match *func.borrow_mut().deref_mut() { match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => f.object.insert(field, prop.clone()), Function::NativeFunc(ref mut f) => {
Function::RegularFunc(ref mut f) => f.object.insert(field, prop.clone()), 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::String(v) => ValueData::String(v),
JSONValue::Bool(v) => ValueData::Boolean(v), JSONValue::Bool(v) => ValueData::Boolean(v),
JSONValue::Array(vs) => { JSONValue::Array(vs) => {
let mut i = 0; let mut new_obj = ObjectData::default();
let private_data: ObjectData = HashMap::new(); for (idx, json) in vs.iter().enumerate() {
let mut data: ObjectData = FromIterator::from_iter(vs.iter().map(|json| { new_obj
i += 1; .properties
( .insert(idx.to_string(), Property::new(to_value(json.clone())));
(i - 1).to_string().to_string(), }
Property::new(to_value(json.clone())), new_obj.properties.insert(
)
}));
data.insert(
"length".to_string(), "length".to_string(),
Property::new(to_value(vs.len() as i32)), 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) => { JSONValue::Object(obj) => {
let private_data: ObjectData = HashMap::new(); let mut new_obj = ObjectData::default();
let data: ObjectData = FromIterator::from_iter( for (key, json) in obj.iter() {
obj.iter() new_obj
.map(|(key, json)| (key.clone(), Property::new(to_value(json.clone())))), .properties
); .insert(key.clone(), Property::new(to_value(json.clone())));
ValueData::Object(GcCell::new(data), GcCell::new(private_data)) }
ValueData::Object(GcCell::new(new_obj))
} }
JSONValue::Null => ValueData::Null, JSONValue::Null => ValueData::Null,
} }
@ -434,9 +418,9 @@ impl ValueData {
match *self { match *self {
ValueData::Null | ValueData::Undefined => JSONValue::Null, ValueData::Null | ValueData::Undefined => JSONValue::Null,
ValueData::Boolean(b) => JSONValue::Bool(b), ValueData::Boolean(b) => JSONValue::Bool(b),
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj) => {
let mut new_obj = Map::new(); 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 { if k != INSTANCE_PROTOTYPE {
new_obj.insert(k.clone(), v.value.to_json()); new_obj.insert(k.clone(), v.value.to_json());
} }
@ -480,11 +464,11 @@ impl Display for ValueData {
_ => v.to_string(), _ => v.to_string(),
} }
), ),
ValueData::Object(ref v, ref p) => { ValueData::Object(ref v) => {
write!(f, "{{")?; write!(f, "{{")?;
// Print public properties // Print public properties
if let Some((last_key, _)) = v.borrow().iter().last() { if let Some((last_key, _)) = v.borrow().properties.iter().last() {
for (key, val) in v.borrow().iter() { for (key, val) in v.borrow().properties.iter() {
write!(f, "{}: {}", key, val.value.clone())?; write!(f, "{}: {}", key, val.value.clone())?;
if key != last_key { if key != last_key {
write!(f, ", ")?; write!(f, ", ")?;
@ -492,10 +476,10 @@ impl Display for ValueData {
} }
}; };
// Print private properties // Print internal slots
if let Some((last_key, _)) = p.borrow().iter().last() { if let Some((last_key, _)) = v.borrow().internal_slots.iter().last() {
for (key, val) in p.borrow().iter() { for (key, val) in v.borrow().internal_slots.iter() {
write!(f, "(Private) {}: {}", key, val.value.clone())?; write!(f, "[[{}]]: {}", key, &val)?;
if key != last_key { if key != last_key {
write!(f, ", ")?; write!(f, ", ")?;
} }
@ -696,18 +680,20 @@ impl FromValue for bool {
impl<'s, T: ToValue> ToValue for &'s [T] { impl<'s, T: ToValue> ToValue for &'s [T] {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
let mut arr = HashMap::new(); let mut arr = ObjectData::default();
for (i, item) in self.iter().enumerate() { 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) to_value(arr)
} }
} }
impl<T: ToValue> ToValue for Vec<T> { impl<T: ToValue> ToValue for Vec<T> {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
let mut arr = HashMap::new(); let mut arr = ObjectData::default();
for (i, item) in self.iter().enumerate() { 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) to_value(arr)
} }
@ -726,18 +712,14 @@ impl<T: FromValue> FromValue for Vec<T> {
impl ToValue for ObjectData { impl ToValue for ObjectData {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
let private_obj: Self = HashMap::new(); Gc::new(ValueData::Object(GcCell::new(self.clone())))
Gc::new(ValueData::Object(
GcCell::new(self.clone()),
GcCell::new(private_obj),
))
} }
} }
impl FromValue for ObjectData { impl FromValue for ObjectData {
fn from_value(v: Value) -> Result<Self, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
match *v { 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() { ValueData::Function(ref func) => Ok(match *func.borrow().deref() {
Function::NativeFunc(ref data) => data.object.clone(), Function::NativeFunc(ref data) => data.object.clone(),
Function::RegularFunc(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 { impl ToValue for NativeFunctionData {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
Gc::new(ValueData::Function(GcCell::new(Function::NativeFunc( Gc::new(ValueData::Function(Box::new(GcCell::new(
NativeFunction::new(*self), Function::NativeFunc(NativeFunction::new(*self)),
)))) ))))
} }
} }

8
src/lib/syntax/parser.rs

@ -239,8 +239,8 @@ impl Parser {
loop { loop {
match self.get_token(self.pos)?.data { match self.get_token(self.pos)?.data {
TokenData::Keyword(Keyword::Case) TokenData::Keyword(Keyword::Case)
| TokenData::Keyword(Keyword::Default) => break, | TokenData::Keyword(Keyword::Default)
TokenData::Punctuator(Punctuator::CloseBlock) => break, | TokenData::Punctuator(Punctuator::CloseBlock) => break,
_ => block.push(self.parse()?), _ => block.push(self.parse()?),
} }
} }
@ -252,8 +252,8 @@ impl Parser {
loop { loop {
match self.get_token(self.pos)?.data { match self.get_token(self.pos)?.data {
TokenData::Keyword(Keyword::Case) TokenData::Keyword(Keyword::Case)
| TokenData::Keyword(Keyword::Default) => break, | TokenData::Keyword(Keyword::Default)
TokenData::Punctuator(Punctuator::CloseBlock) => break, | TokenData::Punctuator(Punctuator::CloseBlock) => break,
_ => block.push(self.parse()?), _ => block.push(self.parse()?),
} }
} }

6
tests/js/test.js

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

Loading…
Cancel
Save