From 538ef9791f61f3b837a03f2db32885239c549faf Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Fri, 12 Oct 2018 00:04:17 +0100 Subject: [PATCH] starting from a more recent commit --- src/lib/exec.rs | 23 +- src/lib/js/mod.rs | 9 +- src/lib/js/object.rs | 41 +--- src/lib/js/value.rs | 505 ++++--------------------------------------- src/lib/lib.rs | 3 +- 5 files changed, 73 insertions(+), 508 deletions(-) diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 7c0d3a1389..0ab02e1357 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -2,7 +2,9 @@ use gc::GcCell; use js::object::ObjectData; use js::value::{ResultValue, Value, ValueData}; use js::{function, json, object}; +use std::borrow::Borrow; use std::cell::RefCell; +use std::collections::HashMap; use syntax::ast::expr::Expr; /// An execution engine @@ -17,8 +19,7 @@ pub trait Executor { fn make_scope(&mut self) -> GcCell>; /// Destroy the current scope fn destroy_scope(&mut self) -> (); - /// Run an expression - fn run(&mut self, expr: &Expr) -> ResultValue; + // fn run(&mut self, expr: &Expr) -> ResultValue; } /// A Javascript intepreter @@ -40,4 +41,22 @@ impl Executor for Interpreter { scopes: Vec::new(), } } + + fn set_global(&mut self, name: String, val: Value) -> Value { + self.global.borrow().set_field(name, val) + } + + fn get_global(&self, name: String) -> Value { + self.global.borrow().get_field(name) + } + + fn make_scope(&mut self) -> GcCell> { + let value = GcCell::new(RefCell::new(HashMap::new())); + self.scopes.push(value.clone()); + value + } + + fn destroy_scope(&mut self) -> () { + self.scopes.pop(); + } } diff --git a/src/lib/js/mod.rs b/src/lib/js/mod.rs index 334f3f4cc9..7ca97bb081 100644 --- a/src/lib/js/mod.rs +++ b/src/lib/js/mod.rs @@ -1,9 +1,8 @@ -extern crate gc; extern crate serde_json; -/// The global `Function` object and function value representations -pub mod function; -/// The global `JSON` object -pub mod json; +// /// The global `Function` object and function value representations +// pub mod function; +// /// The global `JSON` object +// pub mod json; /// The global `Object` object pub mod object; /// Javascript values, utility methods and conversion between Javascript values and Rust values diff --git a/src/lib/js/object.rs b/src/lib/js/object.rs index 0ceb391d4c..30a4c9e9dc 100644 --- a/src/lib/js/object.rs +++ b/src/lib/js/object.rs @@ -1,5 +1,4 @@ -use gc::GcCell; -use js::value::{to_value, ResultValue, Value, ValueData}; +use js::value::Value; use std::collections::HashMap; pub static PROTOTYPE: &'static str = "prototype"; pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; @@ -11,6 +10,7 @@ pub type ObjectData = HashMap; /// [Attributes of an Accessor Property](https://tc39.github.io/ecma262/#table-3) /// A data property associates a key value with an ECMAScript language value and a set of Boolean attributes. /// An accessor property associates a key value with one or two accessor functions, and a set of Boolean attributes. +#[derive(Trace, Finalize)] pub struct Property { /// If the type of this can be changed and this can be deleted pub configurable: bool, @@ -20,49 +20,20 @@ pub struct Property { pub writable: bool, /// The value associated with the property pub value: Value, - /// The function serving as getter - pub get: Value, - /// The function serving as setter - pub set: Value, + // pub get: Value, + // pub set: Value, } impl Property { /// Make a new property with the given value - /// [Default Attributes](https://tc39.github.io/ecma262/#table-4) pub fn new(value: Value) -> Property { Property { configurable: false, enumerable: false, writable: false, value: value, - get: GcCell::new(Value::Undefined), - set: GcCell::new(Value::Undefined), + // get: Value::undefined(), + // set: Value::undefined(), } } } - -/// Create a new object -pub fn make_object() -> ResultValue { - Ok(GcCell::new(ValueData::Undefined)) -} - -/// Create a new `Object` object -pub fn _create(global: Value) -> Value { - let object = to_value(make_object); - let object_ptr = object.borrow(); - let prototype = ValueData::new_obj(Some(global)); - // prototype.borrow().set_field_slice("hasOwnProperty", to_value(has_own_prop)); - // prototype.borrow().set_field_slice("toString", to_value(to_string)); - object_ptr.set_field_slice("length", to_value(1i32)); - // object_ptr.set_field_slice(PROTOTYPE, prototype); - // object_ptr.set_field_slice("setPrototypeOf", to_value(set_proto_of)); - // object_ptr.set_field_slice("getPrototypeOf", to_value(get_proto_of)); - // object_ptr.set_field_slice("defineProperty", to_value(define_prop)); - object -} - -/// Initialise the `Object` object on the global object -pub fn init(global: Value) { - let global_ptr = global.borrow(); - global_ptr.set_field_slice("Object", _create(global)); -} diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index ae6372776a..2f63ef8408 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -1,18 +1,18 @@ -use gc::GcCell; -use js::function::Function; +use gc::{Gc, GcCell}; use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; -use std::cell::RefCell; use std::collections::HashMap; -use std::fmt; -use std::iter::FromIterator; -use std::str::FromStr; /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) pub type ResultValue = Result; /// A Garbage-collected Javascript value as represented in the interpreter -pub type Value = GcCell; +#[derive(Trace, Finalize)] +pub struct Value { + /// The garbage-collected pointer + pub ptr: Gc, +} /// A Javascript value +#[derive(Trace, Finalize)] pub enum ValueData { /// `null` - A null value, for when a value doesn't exist Null, @@ -27,508 +27,83 @@ 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 - Object(RefCell), - /// `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(RefCell), + Object(GcCell), } -impl ValueData { +impl Value { /// Returns a new empty object pub fn new_obj(global: Option) -> Value { let mut obj: ObjectData = HashMap::new(); if global.is_some() { let obj_proto = global .unwrap() - .borrow() .get_field_slice("Object") - .borrow() .get_field_slice(PROTOTYPE); - obj.insert(INSTANCE_PROTOTYPE.into_String(), Property::new(obj_proto)); + obj.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(obj_proto)); + } + Value { + ptr: Gc::new(ValueData::Object(GcCell::new(obj))), } - GcCell::new(ValueData::Object(RefCell::new(obj))) } + /// Returns true if the value is an object pub fn is_object(&self) -> bool { - return match *self { + match *self.ptr { ValueData::Object(_) => true, _ => false, - }; + } } + /// Returns true if the value is undefined pub fn is_undefined(&self) -> bool { - return match *self { + match *self.ptr { ValueData::Undefined => true, _ => false, - }; + } } + /// Returns true if the value is null pub fn is_null(&self) -> bool { - return match *self { + match *self.ptr { ValueData::Null => true, _ => false, - }; + } } + /// Returns true if the value is null or undefined pub fn is_null_or_undefined(&self) -> bool { - return match *self { + match *self.ptr { ValueData::Null | ValueData::Undefined => true, _ => false, - }; + } } + /// Returns true if the value is a 64-bit floating-point number pub fn is_double(&self) -> bool { - return match *self { + match *self.ptr { ValueData::Number(_) => true, _ => false, - }; + } + } + + /// Returns true if the value is a string + pub fn is_string(&self) -> bool { + match *self.ptr { + ValueData::String(_) => true, + _ => false, + } } + /// Returns true if the value is true + /// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) pub fn is_true(&self) -> bool { - return match *self { + match *self.ptr { ValueData::Object(_) => true, - ValueData::String(ref s) if s.as_slice() == "1" => true, + ValueData::String(ref s) if !s.is_empty() => true, ValueData::Number(n) if n >= 1.0 && n % 1.0 == 0.0 => true, ValueData::Integer(n) if n > 1 => true, ValueData::Boolean(v) => v, _ => false, - }; - } - /// Converts the value into a 64-bit floating point number - pub fn to_num(&self) -> f64 { - return match *self { - ValueData::Object(_) | ValueData::Undefined | ValueData::Function(_) => f64::NAN, - ValueData::String(ref str) => match FromStr::from_str(str.as_slice()) { - Some(num) => num, - None => f64::NAN, - }, - ValueData::Number(num) => num, - ValueData::Boolean(true) => 1.0, - ValueData::Boolean(false) | ValueData::Null => 0.0, - ValueData::Integer(num) => num as f64, - }; - } - /// Converts the value into a 32-bit integer - pub fn to_int(&self) -> i32 { - return match *self { - ValueData::Object(_) - | ValueData::Undefined - | ValueData::Null - | ValueData::Boolean(false) - | ValueData::Function(_) => 0, - ValueData::String(ref str) => match FromStr::from_str(str.as_slice()) { - Some(num) => num, - None => 0, - }, - ValueData::Number(num) => num as i32, - ValueData::Boolean(true) => 1, - ValueData::Integer(num) => num, - }; - } - /// Resolve the property in the object - pub fn get_prop(&self, field: String) -> Option { - let obj: ObjectData = match *self { - ValueData::Object(ref obj) => obj.borrow().clone(), - ValueData::Function(ref func) => { - let func = func.borrow().clone(); - match func { - Function::NativeFunc(f) => f.object.clone(), - Function::RegularFunc(f) => f.object.clone(), - } - } - _ => return None, - }; - match obj.find(&field) { - Some(val) => Some(*val), - None => match obj.find(&PROTOTYPE.into_String()) { - Some(prop) => prop.value.borrow().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 - pub fn get_field(&self, field: String) -> Value { - match self.get_prop(field) { - Some(prop) => prop.value, - None => GcCell::new(ValueData::Undefined), - } - } - /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist - pub fn get_field_slice<'t>(&self, field: &'t str) -> Value { - self.get_field(field.into_String()) - } - /// 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.clone(), Property::new(val)); - } - ValueData::Function(ref func) => { - match *func.borrow_mut().deref_mut() { - Function::NativeFunc(ref mut f) => { - f.object.insert(field.clone(), Property::new(val)) - } - Function::RegularFunc(ref mut f) => { - f.object.insert(field.clone(), Property::new(val)) - } - }; - } - _ => (), - } - val - } - /// Set the field in the value - pub fn set_field_slice<'t>(&self, field: &'t str, val: Value) -> Value { - self.set_field(field.into_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.clone(), prop); - } - ValueData::Function(ref func) => { - match *func.borrow_mut().deref_mut() { - Function::NativeFunc(ref mut f) => f.object.insert(field.clone(), prop), - Function::RegularFunc(ref mut f) => f.object.insert(field.clone(), prop), - }; - } - _ => (), - } - prop - } - /// Set the property in the value - pub fn set_prop_slice<'t>(&self, field: &'t str, prop: Property) -> Property { - self.set_prop(field.into_String(), prop) - } - /// Convert from a JSON value to a JS value - pub fn from_json(json: serde_json::Value) -> ValueData { - match json { - serde_json::Value::Number(v) => ValueData::Number(v), - serde_json::Value::String(v) => ValueData::String(v), - serde_json::Value::Boolean(v) => ValueData::Boolean(v), - serde_json::Value::List(vs) => { - let mut i = 0; - let mut data: ObjectData = FromIterator::from_iter(vs.iter().map(|json| { - i += 1; - ( - (i - 1).to_str().into_String(), - Property::new(to_value(json.clone())), - ) - })); - data.insert( - "length".into_String(), - Property::new(to_value(vs.len() as i32)), - ); - ValueData::Object(RefCell::new(data)) - } - serde_json::Value::Object(obj) => { - let data: ObjectData = FromIterator::from_iter( - obj.iter() - .map(|(key, json)| (key.clone(), Property::new(to_value(json.clone())))), - ); - ValueData::Object(RefCell::new(data)) - } - Null => ValueData::Null, - } - } - - pub fn to_json(&self) -> serde_json::Value { - match *self { - ValueData::Null | ValueData::Undefined => serde_json::Value::Null, - ValueData::Boolean(b) => serde_json::Value::Boolean(b), - ValueData::Object(ref obj) => { - let mut nobj = HashMap::new(); - for (k, v) in obj.borrow().iter() { - if k.as_slice() != INSTANCE_PROTOTYPE.as_slice() { - nobj.insert(k.clone(), v.value.borrow().to_json()); - } - } - serde_json::Value::Object(Box::new(nobj)) - } - ValueData::String(ref str) => serde_json::Value::String(str.clone()), - ValueData::Number(num) => serde_json::Value::Number(num), - ValueData::Integer(val) => serde_json::Value::Number(val as f64), - ValueData::Function(_) => serde_json::Value::Null, - } - } -} - -impl fmt::Display for ValueData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ValueData::Null => write!(f, "null"), - ValueData::Undefined => write!(f, "undefined"), - ValueData::Boolean(v) => write!(f, "{}", v), - ValueData::String(ref v) => write!(f, "{}", v), - ValueData::Number(v) => write!( - f, - "{}", - match v { - _ if v.is_nan() => "NaN".into_String(), - f64::INFINITY => "Infinity".into_String(), - f64::NEG_INFINITY => "-Infinity".into_String(), - _ => f64::to_str_digits(v, 15), - } - ), - ValueData::Object(ref v) => { - try!(write!(f, "{}", "{")); - match v.borrow().iter().last() { - Some((last_key, _)) => { - for (key, val) in v.borrow().iter() { - try!(write!(f, "{}: {}", key, val.value.borrow())); - if key != last_key { - try!(write!(f, "{}", ", ")); - } - } - } - None => (), - } - write!(f, "{}", "}") - } - ValueData::Integer(v) => write!(f, "{}", v), - ValueData::Function(ref v) => match v.borrow().clone() { - Function::NativeFunc(_) => write!(f, "{}", "function() { [native code] }"), - Function::RegularFunc(rf) => { - write!(f, "function({}){}", rf.args.connect(", "), rf.expr) - } - }, - } - } -} -impl PartialEq for ValueData { - fn eq(&self, other: &ValueData) -> bool { - match (self.clone(), other.clone()) { - (ref a, ref b) if a.is_null_or_undefined() && b.is_null_or_undefined() => true, - (ValueData::String(ref a), ValueData::String(ref b)) if a == b => true, - (ValueData::String(ref a), ref b) | (ref b, ValueData::String(ref a)) - if *a == b.to_str() => - { - true - } - (ValueData::Boolean(a), ValueData::Boolean(b)) if a == b => true, - (ValueData::Number(a), ValueData::Number(b)) - if a == b && !a.is_nan() && !b.is_nan() => - { - true - } - (ValueData::Number(a), ref b) | (ref b, ValueData::Number(a)) if a == b.to_num() => { - true - } - (ValueData::Integer(a), ValueData::Integer(b)) if a == b => true, - _ => false, - } - } -} - -// impl Add for ValueData { -// fn add(&self, other: &ValueData) -> ValueData { -// return match (self.clone(), other.clone()) { -// (ValueData::String(s), other) | (other, ValueData::String(s)) => { -// ValueData::String(s.clone().append(other.to_str().as_slice())) -// } -// (_, _) => ValueData::Number(self.to_num() + other.to_num()), -// }; -// } -// } -// impl Sub for ValueData { -// fn sub(&self, other: &ValueData) -> ValueData { -// ValueData::Number(self.to_num() - other.to_num()) -// } -// } -// impl Mul for ValueData { -// fn mul(&self, other: &ValueData) -> ValueData { -// ValueData::Number(self.to_num() * other.to_num()) -// } -// } -// impl Div for ValueData { -// fn div(&self, other: &ValueData) -> ValueData { -// ValueData::Number(self.to_num() / other.to_num()) -// } -// } -// impl Rem for ValueData { -// fn rem(&self, other: &ValueData) -> ValueData { -// ValueData::Number(self.to_num() % other.to_num()) -// } -// } -// impl BitAnd for ValueData { -// fn bitand(&self, other: &ValueData) -> ValueData { -// ValueData::Integer(self.to_int() & other.to_int()) -// } -// } -// impl BitOr for ValueData { -// fn bitor(&self, other: &ValueData) -> ValueData { -// ValueData::Integer(self.to_int() | other.to_int()) -// } -// } -// impl BitXor for ValueData { -// fn bitxor(&self, other: &ValueData) -> ValueData { -// ValueData::Integer(self.to_int() ^ other.to_int()) -// } -// } -// impl Shl for ValueData { -// fn shl(&self, other: &ValueData) -> ValueData { -// ValueData::Integer(self.to_int() << other.to_int()) -// } -// } -// impl Shr for ValueData { -// fn shr(&self, other: &ValueData) -> ValueData { -// ValueData::Integer(self.to_int() >> other.to_int()) -// } -// } -// impl Not for ValueData { -// fn not(&self) -> ValueData { -// ValueData::Boolean(!self.is_true()) -// } -// } -/// Conversion to Javascript values from Rust values -pub trait ToValue { - /// Convert this value to a Rust value - fn to_value(&self) -> Value; -} -/// Conversion to Rust values from Javascript values -pub trait FromValue { - /// Convert this value to a Javascript value - fn from_value(value: Value) -> Result; -} -impl ToValue for String { - fn to_value(&self) -> Value { - GcCell::new(ValueData::String(self.clone())) - } -} -impl FromValue for String { - fn from_value(v: Value) -> Result { - Ok(v.borrow().to_str()) - } -} -impl<'s> ToValue for &'s str { - fn to_value(&self) -> Value { - GcCell::new(ValueData::String(String::from_str(*self))) - } -} -impl ToValue for char { - fn to_value(&self) -> Value { - GcCell::new(ValueData::String(String::from_char(1, *self))) - } -} -impl FromValue for char { - fn from_value(v: Value) -> Result { - Ok(v.borrow().to_str().as_slice().char_at(0)) - } -} -impl ToValue for f64 { - fn to_value(&self) -> Value { - GcCell::new(ValueData::Number(self.clone())) - } -} -impl FromValue for f64 { - fn from_value(v: Value) -> Result { - Ok(v.borrow().to_num()) - } -} -impl ToValue for i32 { - fn to_value(&self) -> Value { - GcCell::new(ValueData::Integer(self.clone())) - } -} -impl FromValue for i32 { - fn from_value(v: Value) -> Result { - Ok(v.borrow().to_int()) - } -} -impl ToValue for bool { - fn to_value(&self) -> Value { - GcCell::new(ValueData::Boolean(self.clone())) - } -} -impl FromValue for bool { - fn from_value(v: Value) -> Result { - Ok(v.borrow().is_true()) - } -} -impl<'s, T: ToValue> ToValue for &'s [T] { - fn to_value(&self) -> Value { - let mut arr = HashMap::new(); - let mut i = 0; - for item in self.iter() { - arr.insert(i.to_str().into_String(), Property::new(item.to_value())); - i += 1; } - to_value(arr) } } -impl ToValue for Vec { - fn to_value(&self) -> Value { - let mut arr = HashMap::new(); - let mut i = 0; - for item in self.iter() { - arr.insert(i.to_str().into_String(), Property::new(item.to_value())); - i += 1; - } - to_value(arr) - } -} -impl, R> FromValue for Vec { - fn from_value(v: Value) -> Result, &'static str> { - let len = v.borrow().get_field_slice("length").borrow().to_int(); - let mut vec = Vec::with_capacity(len); - for i in 0..len { - vec.push(try!(from_value(v.borrow().get_field(i.to_str())))) - } - Ok(vec) - } -} -impl ToValue for Function { - fn to_value(&self) -> Value { - GcCell::new(ValueData::Function(RefCell::new(Function::NativeFunc( - Function::NativeFunction::new(*self), - )))) - } -} -impl FromValue for Function { - fn from_value(v: Value) -> Result { - match *v.borrow() { - ValueData::Function(ref func) => match *func.borrow() { - Function::NativeFunc(ref data) => Ok(data.data), - _ => Err("Value is not a native function"), - }, - _ => Err("Value is not a function"), - } - } -} -impl ToValue for ObjectData { - fn to_value(&self) -> Value { - GcCell::new(ValueData::Object(RefCell::new(self.clone()))) - } -} -impl FromValue for ObjectData { - fn from_value(v: Value) -> Result { - match *v.borrow() { - ValueData::Object(ref obj) => Ok(obj.clone().borrow().deref().clone()), - ValueData::Function(ref func) => Ok(match *func.borrow().deref() { - Function::NativeFunc(ref data) => data.object.clone(), - Function::RegularFunc(ref data) => data.object.clone(), - }), - _ => Err("Value is not a valid object"), - } - } -} - -impl ToValue for () { - fn to_value(&self) -> Value { - GcCell::new(ValueData::Null) - } -} -impl FromValue for () { - fn from_value(_: Value) -> Result<(), &'static str> { - Ok(()) - } -} -/// A utility function that just calls FromValue::from_value -pub fn from_value>(v: Value) -> Result { - FromValue::from_value(v) -} - -/// A utility function that just calls ToValue::to_value -pub fn to_value(v: A) -> Value { - v.to_value() -} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 8962564faf..3b71e6da3f 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -1,6 +1,7 @@ extern crate gc; +#[macro_use] extern crate gc_derive; -pub mod exec; +// pub mod exec; pub mod js; pub mod syntax;