diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 0ab02e1357..5ac3e71b7a 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -1,11 +1,16 @@ -use gc::GcCell; -use js::object::ObjectData; -use js::value::{ResultValue, Value, ValueData}; -use js::{function, json, object}; +use gc::Gc; +use js::value::{to_value, ResultValue, Value, ValueData}; +use js::{array, console, function, json, math, object, string}; use std::borrow::Borrow; -use std::cell::RefCell; -use std::collections::HashMap; -use syntax::ast::expr::Expr; +use syntax::ast::constant::Const; +use syntax::ast::expr::{Expr, ExprDef}; +/// A variable scope +pub struct Scope { + /// The value of `this` in the scope + pub this: Value, + /// The variables declared in the scope + pub vars: Value, +} /// An execution engine pub trait Executor { @@ -16,29 +21,37 @@ pub trait Executor { /// Resolve the global variable `name` fn get_global(&self, name: String) -> Value; /// Create a new scope and return it - fn make_scope(&mut self) -> GcCell>; + fn make_scope(&mut self, this: Value) -> Scope; /// Destroy the current scope - fn destroy_scope(&mut self) -> (); - // fn run(&mut self, expr: &Expr) -> ResultValue; + fn destroy_scope(&mut self) -> Scope; + /// Run an expression + fn run(&mut self, expr: &Expr) -> ResultValue; } /// A Javascript intepreter pub struct Interpreter { /// An object representing the global object global: Value, - /// The variable scopes - scopes: Vec>>, + /// The scopes + pub scopes: Vec, } -impl Executor for Interpreter { +impl Executor for Interpreter { fn new() -> Interpreter { - let global = ValueData::new_obj(None); + let global = Value::new_obj(None); object::init(global); + console::init(global); + math::init(global); + array::init(global); function::init(global); json::init(global); + string::init(global); Interpreter { global: global, - scopes: Vec::new(), + scopes: vec![Scope { + this: global, + vars: global, + }], } } @@ -50,13 +63,326 @@ impl Executor for Interpreter { 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 make_scope(&mut self, this: Value) -> Scope { + let scope = Scope { + this: this, + vars: Value::new_obj(None), + }; + self.scopes.push(scope); + scope + } + + fn destroy_scope(&mut self) -> Scope { + self.scopes.pop().unwrap() } - fn destroy_scope(&mut self) -> () { - self.scopes.pop(); + fn compile<'a>(&self, expr: &'a Expr) -> &'a Expr { + expr + } + + fn run(&mut self, expr: &Expr) -> ResultValue { + match expr.def { + ExprDef::ConstExpr(Const::Null) => Ok(to_value(None)), + ExprDef::ConstExpr(Const::Undefined) => Ok(Value::undefined()), + ExprDef::ConstExpr(Const::Num(num)) => Ok(to_value(num)), + ExprDef::ConstExpr(Const::Int(num)) => Ok(to_value(num)), + ExprDef::ConstExpr(Const::String(str)) => Ok(to_value(str)), + ExprDef::ConstExpr(Const::Bool(val)) => Ok(to_value(val)), + ExprDef::ConstExpr(Const::RegExp(_, _, _)) => Ok(to_value(None)), + ExprDef::BlockExpr(ref es) => { + let mut obj = to_value(None); + for e in es.iter() { + let val = try!(self.run(e)); + if e == es.last().unwrap() { + obj = val; + } + } + Ok(obj) + } + ExprDef::LocalExpr(ref name) => { + let mut val = Value::undefined(); + for scope in self.scopes.iter().rev() { + let vars = scope.vars; + let vars_ptr = vars.borrow(); + match *vars_ptr.ptr { + ValueData::Object(ref obj) => match obj.borrow().get(name) { + Some(v) => { + val = v.value; + break; + } + None => (), + }, + _ => unreachable!(), + } + } + Ok(val) + } + ExprDef::GetConstFieldExpr(ref obj, ref field) => { + let val_obj = try!(self.run(obj)); + Ok(val_obj.borrow().get_field(field.clone())) + } + ExprDef::GetFieldExpr(ref obj, ref field) => { + let val_obj = try!(self.run(obj)); + let val_field = try!(self.run(field)); + Ok(val_obj.borrow().get_field(val_field.borrow().to_string())) + } + ExprDef::CallExpr(ref callee, ref args) => { + let (this, func) = match callee.def { + ExprDef::GetConstFieldExpr(ref obj, ref field) => { + let obj = try!(self.run(obj)); + (obj, obj.borrow().get_field(field.clone())) + } + ExprDef::GetFieldExpr(ref obj, ref field) => { + let obj = try!(self.run(obj)); + let field = try!(self.run(field)); + (obj, obj.borrow().get_field(field.borrow().to_string())) + } + _ => (self.global.clone(), try!(self.run(&callee.clone()))), + }; + let mut v_args = Vec::with_capacity(args.len()); + for arg in args.iter() { + v_args.push(try!(self.run(arg))); + } + match *func.borrow().ptr { + ValueData::Function(ref func) => match *func.borrow() { + NativeFunc(ref ntv) => { + let func = ntv.data; + func(this, try!(self.run(*callee)), v_args) + } + RegularFunc(ref data) => { + let scope = self.make_scope(this); + let scope_vars_ptr = scope.vars.borrow(); + for i in range(0, data.args.len()) { + let name = data.args.get(i); + let expr = v_args.get(i); + scope_vars_ptr.set_field(name.clone(), *expr); + } + let result = self.run(&data.expr); + self.destroy_scope(); + result + } + }, + _ => Err(Gc::new(VUndefined)), + } + } + WhileLoopExpr(ref cond, ref expr) => { + let mut result = Gc::new(VUndefined); + while try!(self.run(*cond)).borrow().is_true() { + result = try!(self.run(*expr)); + } + Ok(result) + } + IfExpr(ref cond, ref expr, None) => Ok(if try!(self.run(*cond)).borrow().is_true() { + try!(self.run(*expr)) + } else { + Gc::new(VUndefined) + }), + IfExpr(ref cond, ref expr, Some(ref else_e)) => { + Ok(if try!(self.run(*cond)).borrow().is_true() { + try!(self.run(*expr)) + } else { + try!(self.run(*else_e)) + }) + } + SwitchExpr(ref val_e, ref vals, ref default) => { + let val = try!(self.run(*val_e)).borrow().clone(); + let mut result = Gc::new(ValueData::Null); + let mut matched = false; + for tup in vals.iter() { + let tup: &(Expr, Vec) = tup; + match *tup { + (ref cond, ref block) if (val == *try!(self.run(cond)).borrow()) => { + matched = true; + let last_expr = block.last().unwrap(); + for expr in block.iter() { + let e_result = try!(self.run(expr)); + if expr == last_expr { + result = e_result; + } + } + } + _ => (), + } + } + if !matched && default.is_some() { + result = try!(self.run(*default.as_ref().unwrap())); + } + Ok(result) + } + ObjectDeclExpr(ref map) => { + let obj = ValueData::new_obj(Some(self.global)); + for (key, val) in map.iter() { + obj.borrow().set_field(key.clone(), try!(self.run(val))); + } + Ok(obj) + } + ArrayDeclExpr(ref arr) => { + let arr_map = ValueData::new_obj(Some(self.global)); + let mut index: i32 = 0; + for val in arr.iter() { + let val = try!(self.run(val)); + arr_map.borrow().set_field(index.to_str(), val); + index += 1; + } + arr_map.borrow().set_field_slice( + INSTANCE_PROTOTYPE, + self.get_global("Array".into_strbuf()) + .borrow() + .get_field_slice(PROTOTYPE), + ); + arr_map.borrow().set_field_slice("length", to_value(index)); + Ok(arr_map) + } + FunctionDeclExpr(ref name, ref args, ref expr) => { + let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); + let val = Gc::new(VFunction(RefCell::new(function))); + if name.is_some() { + self.global.borrow().set_field(name.clone().unwrap(), val); + } + Ok(val) + } + ArrowFunctionDeclExpr(ref args, ref expr) => { + let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); + Ok(Gc::new(VFunction(RefCell::new(function)))) + } + BinOpExpr(BinNum(ref op), ref a, ref b) => { + let v_r_a = try!(self.run(*a)); + let v_r_b = try!(self.run(*b)); + let v_a = v_r_a.borrow(); + let v_b = v_r_b.borrow(); + Ok(Gc::new(match *op { + OpAdd => *v_a + *v_b, + OpSub => *v_a - *v_b, + OpMul => *v_a * *v_b, + OpDiv => *v_a / *v_b, + OpMod => *v_a % *v_b, + })) + } + UnaryOpExpr(ref op, ref a) => { + let v_r_a = try!(self.run(*a)); + let v_a = v_r_a.borrow(); + Ok(match *op { + UnaryMinus => to_value(-v_a.to_num()), + UnaryPlus => to_value(v_a.to_num()), + UnaryNot => Gc::new(!v_a), + _ => unreachable!(), + }) + } + BinOpExpr(BinBit(ref op), ref a, ref b) => { + let v_r_a = try!(self.run(*a)); + let v_r_b = try!(self.run(*b)); + let v_a = v_r_a.borrow(); + let v_b = v_r_b.borrow(); + Ok(Gc::new(match *op { + BitAnd => *v_a & *v_b, + BitOr => *v_a | *v_b, + BitXor => *v_a ^ *v_b, + BitShl => *v_a << *v_b, + BitShr => *v_a >> *v_b, + })) + } + BinOpExpr(BinComp(ref op), ref a, ref b) => { + let v_r_a = try!(self.run(*a)); + let v_r_b = try!(self.run(*b)); + let v_a = v_r_a.borrow(); + let v_b = v_r_b.borrow(); + Ok(to_value(match *op { + CompEqual if v_a.is_object() => v_r_a.ptr_eq(&v_r_b), + CompEqual => v_a == v_b, + CompNotEqual if v_a.is_object() => !v_r_a.ptr_eq(&v_r_b), + CompNotEqual => v_a != v_b, + CompStrictEqual if v_a.is_object() => v_r_a.ptr_eq(&v_r_b), + CompStrictEqual => v_a == v_b, + CompStrictNotEqual if v_a.is_object() => !v_r_a.ptr_eq(&v_r_b), + CompStrictNotEqual => v_a != v_b, + CompGreaterThan => v_a.to_num() > v_b.to_num(), + CompGreaterThanOrEqual => v_a.to_num() >= v_b.to_num(), + CompLessThan => v_a.to_num() < v_b.to_num(), + CompLessThanOrEqual => v_a.to_num() <= v_b.to_num(), + })) + } + BinOpExpr(BinLog(ref op), ref a, ref b) => { + let v_a = from_value::(try!(self.run(*a))).unwrap(); + let v_b = from_value::(try!(self.run(*b))).unwrap(); + Ok(match *op { + LogAnd => to_value(v_a && v_b), + LogOr => to_value(v_a || v_b), + }) + } + ConstructExpr(ref callee, ref args) => { + let func = try!(self.run(*callee)); + let mut v_args = Vec::with_capacity(args.len()); + for arg in args.iter() { + v_args.push(try!(self.run(arg))); + } + let this = Gc::new(VObject(RefCell::new(TreeMap::new()))); + this.borrow() + .set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE)); + match *func.borrow() { + VFunction(ref func) => match *func.borrow() { + NativeFunc(ref ntv) => { + let func = ntv.data; + func(this, try!(self.run(*callee)), v_args) + } + RegularFunc(ref data) => { + let scope = self.make_scope(this); + let scope_vars_ptr = scope.vars.borrow(); + for i in range(0, data.args.len()) { + let name = data.args.get(i); + let expr = v_args.get(i); + scope_vars_ptr.set_field(name.clone(), *expr); + } + let result = self.run(&data.expr); + self.destroy_scope(); + result + } + }, + _ => Ok(Gc::new(VUndefined)), + } + } + ReturnExpr(ref ret) => match *ret { + Some(ref v) => self.run(*v), + None => Ok(Gc::new(VUndefined)), + }, + ThrowExpr(ref ex) => Err(try!(self.run(*ex))), + AssignExpr(ref ref_e, ref val_e) => { + let val = try!(self.run(*val_e)); + match ref_e.def { + LocalExpr(ref name) => { + self.scope().vars.borrow().set_field(name.clone(), val); + } + GetConstFieldExpr(ref obj, ref field) => { + let val_obj = try!(self.run(*obj)); + val_obj.borrow().set_field(field.clone(), val); + } + _ => (), + } + Ok(val) + } + VarDeclExpr(ref vars) => { + let scope_vars = self.scope().vars; + let scope_vars_ptr = scope_vars.borrow(); + for var in vars.iter() { + let (name, value) = var.clone(); + let val = match value { + Some(v) => try!(self.run(&v)), + None => Gc::new(ValueData::Null), + }; + scope_vars_ptr.set_field(name.clone(), val); + } + Ok(Gc::new(VUndefined)) + } + TypeOfExpr(ref val_e) => { + let val = try!(self.run(*val_e)); + Ok(to_value(match *val.borrow() { + VUndefined => "undefined", + ValueData::Null | VObject(_) => "object", + VBoolean(_) => "boolean", + VNumber(_) | VInteger(_) => "number", + VString(_) => "string", + VFunction(_) => "function", + })) + } + } } } diff --git a/src/lib/js/function.rs b/src/lib/js/function.rs index 52662cb55b..46c3707146 100644 --- a/src/lib/js/function.rs +++ b/src/lib/js/function.rs @@ -1,52 +1,69 @@ -use gc::{Gc, GcCell}; +use gc::Gc; use js::object::{ObjectData, Property}; use js::value::{to_value, ResultValue, Value, ValueData}; use std::collections::HashMap; -use std::iter::FromIterator; +use syntax::ast::expr::Expr; pub type FunctionData = fn(Vec, Value, Value, Value) -> ResultValue; +pub type NativeFunctionData = fn(Value, Value, Vec) -> ResultValue; /// A Javascript function /// A member of the Object type that may be invoked as a subroutine /// https://tc39.github.io/ecma262/#sec-terms-and-definitions-function /// In our implementation, Function is extending Object by holding an object field which some extra data +/// A Javascript function +#[derive(Trace, Finalize, Debug)] +pub enum Function { + /// A native javascript function + NativeFunc(NativeFunction), + /// A regular javascript function + RegularFunc(RegularFunction), +} + +/// Represents a regular javascript function in memory #[derive(Trace, Finalize, Debug)] -pub struct Function { +pub struct RegularFunction { /// The fields associated with the function pub object: ObjectData, - /// This function's JIT representation - pub repr: FunctionData, + /// This function's expression + pub expr: Expr, /// The argument names of the function pub args: Vec, } -impl Function { - /// Make a new function - pub fn new(repr: FunctionData, args: Vec) -> Function { +impl RegularFunction { + /// Make a new regular function + pub fn new(expr: Expr, args: Vec) -> RegularFunction { let mut obj = HashMap::new(); obj.insert( "arguments".to_string(), - Property::from_value(to_value(args.len() as i32)), + Property::new(Gc::new(ValueData::Integer(args.len() as i32))), ); - Function { + RegularFunction { object: obj, - repr: repr, + expr: expr, args: args, } } - /// Create a function from function data and arguments - pub fn make(repr: FunctionData, args: &[&'static str]) -> Value { - Value { - ptr: Gc::new(ValueData::Function(GcCell::new(Function::new( - repr, - FromIterator::from_iter(args.iter().map(|arg| arg.to_string())), - )))), +} + +#[derive(Trace, Finalize, Debug)] +/// Represents a native javascript function in memory +pub struct NativeFunction { + /// The fields associated with the function + pub object: ObjectData, + /// The callable function data + pub data: NativeFunctionData, +} +impl NativeFunction { + /// Make a new native function with the given function data + pub fn new(data: NativeFunctionData) -> NativeFunction { + let obj = HashMap::new(); + NativeFunction { + object: obj, + data: data, } } - /// Call with some args - pub fn call(&self, args: Vec, global: Value, scope: Value, this: Value) -> ResultValue { - (self.repr)(args, global, scope, this) - } } /// Create a new `Function` object @@ -56,5 +73,6 @@ pub fn _create() -> Value { } /// Initialise the global object with the `Function` object pub fn init(global: Value) { - global.set_field_slice("Function", _create()); + let global_ptr = global.borrow(); + global_ptr.set_field_slice("Function", _create(global)); } diff --git a/src/lib/js/math.rs b/src/lib/js/math.rs index ea5d2fc6bf..c00b7728e8 100644 --- a/src/lib/js/math.rs +++ b/src/lib/js/math.rs @@ -6,7 +6,9 @@ use std::f64; /// Get the absolute value of a number pub fn abs(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().abs() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .abs() } else { f64::NAN })) @@ -14,7 +16,9 @@ pub fn abs(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the arccos of a number pub fn acos(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().acos() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .acos() } else { f64::NAN })) @@ -22,7 +26,9 @@ pub fn acos(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the arcsine of a number pub fn asin(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().asin() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .asin() } else { f64::NAN })) @@ -30,7 +36,9 @@ pub fn asin(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the arctangent of a number pub fn atan(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().atan() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .atan() } else { f64::NAN })) @@ -38,9 +46,9 @@ pub fn atan(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the arctangent of a numbers pub fn atan2(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)) + from_value::(args.get(0).unwrap().clone()) .unwrap() - .atan2(args.get(1).to_num()) + .atan2(args.get(1).unwrap().to_num()) } else { f64::NAN })) @@ -48,7 +56,9 @@ pub fn atan2(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the cubic root of a number pub fn cbrt(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().cbrt() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .cbrt() } else { f64::NAN })) @@ -56,7 +66,9 @@ pub fn cbrt(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get lowest integer above a number pub fn ceil(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().ceil() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .ceil() } else { f64::NAN })) @@ -64,7 +76,9 @@ pub fn ceil(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the cosine of a number pub fn cos(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().cos() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .cos() } else { f64::NAN })) @@ -72,7 +86,9 @@ pub fn cos(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the power to raise the natural logarithm to get the number pub fn exp(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().exp() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .exp() } else { f64::NAN })) @@ -80,7 +96,9 @@ pub fn exp(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the highest integer below a number pub fn floor(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().floor() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .floor() } else { f64::NAN })) @@ -88,7 +106,9 @@ pub fn floor(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the natural logarithm of a number pub fn log(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().log(f64::consts::E) + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .log(f64::consts::E) } else { f64::NAN })) @@ -114,8 +134,8 @@ pub fn min(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Raise a number to a power pub fn pow(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 2 { - let num: f64 = from_value(*args.get(0)).unwrap(); - let power: f64 = from_value(*args.get(1)).unwrap(); + let num: f64 = from_value(args.get(0).unwrap().clone()).unwrap(); + let power: f64 = from_value(args.get(1).unwrap().clone()).unwrap(); num.powf(power) } else { f64::NAN @@ -128,7 +148,9 @@ pub fn _random(_: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Round a number to the nearest integer pub fn round(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().round() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .round() } else { f64::NAN })) @@ -136,7 +158,9 @@ pub fn round(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the sine of a number pub fn sin(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().sin() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .sin() } else { f64::NAN })) @@ -144,7 +168,9 @@ pub fn sin(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the square root of a number pub fn sqrt(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().sqrt() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .sqrt() } else { f64::NAN })) @@ -152,7 +178,9 @@ pub fn sqrt(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { /// Get the tangent of a number pub fn tan(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { Ok(to_value(if args.len() >= 1 { - from_value::(*args.get(0)).unwrap().tan() + from_value::(args.get(0).unwrap().clone()) + .unwrap() + .tan() } else { f64::NAN })) @@ -166,30 +194,30 @@ pub fn _create(global: Value) -> Value { math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E)); math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E)); math.set_field_slice("SQRT1_2", to_value(0.5f64.sqrt())); - math.set_field_slice("SQRT2", to_value(f64::consts::SQRT2)); + math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2)); math.set_field_slice("PI", to_value(f64::consts::PI)); - math.set_field_slice("abs", Function::make(abs, ["num1", "num2"])); - math.set_field_slice("acos", Function::make(acos, ["num1", "num2"])); - math.set_field_slice("asin", Function::make(asin, ["num1", "num2"])); - math.set_field_slice("atan", Function::make(atan, ["num1", "num2"])); - math.set_field_slice("atan2", Function::make(atan2, ["num1", "num2"])); - math.set_field_slice("cbrt", Function::make(cbrt, ["num1", "num2"])); - math.set_field_slice("ceil", Function::make(ceil, ["num1", "num2"])); - math.set_field_slice("cos", Function::make(cos, ["num1", "num2"])); - math.set_field_slice("exp", Function::make(exp, ["num1", "num2"])); - math.set_field_slice("floor", Function::make(floor, ["num"])); - math.set_field_slice("log", Function::make(log, ["num1", "num2"])); - math.set_field_slice("max", Function::make(max, ["num1", "num2"])); - math.set_field_slice("min", Function::make(min, ["num1", "num2"])); - math.set_field_slice("pow", Function::make(pow, ["num1", "num2"])); - math.set_field_slice("random", Function::make(_random, [])); - math.set_field_slice("round", Function::make(round, ["num"])); - math.set_field_slice("sin", Function::make(sin, ["num"])); - math.set_field_slice("sqrt", Function::make(sqrt, ["num"])); - math.set_field_slice("tan", Function::make(tan, ["num"])); + math.set_field_slice("abs", Function::make(abs, &["num1", "num2"])); + math.set_field_slice("acos", Function::make(acos, &["num1", "num2"])); + math.set_field_slice("asin", Function::make(asin, &["num1", "num2"])); + math.set_field_slice("atan", Function::make(atan, &["num1", "num2"])); + math.set_field_slice("atan2", Function::make(atan2, &["num1", "num2"])); + math.set_field_slice("cbrt", Function::make(cbrt, &["num1", "num2"])); + math.set_field_slice("ceil", Function::make(ceil, &["num1", "num2"])); + math.set_field_slice("cos", Function::make(cos, &["num1", "num2"])); + math.set_field_slice("exp", Function::make(exp, &["num1", "num2"])); + math.set_field_slice("floor", Function::make(floor, &["num"])); + math.set_field_slice("log", Function::make(log, &["num1", "num2"])); + math.set_field_slice("max", Function::make(max, &["num1", "num2"])); + math.set_field_slice("min", Function::make(min, &["num1", "num2"])); + math.set_field_slice("pow", Function::make(pow, &["num1", "num2"])); + math.set_field_slice("random", Function::make(_random, &[])); + math.set_field_slice("round", Function::make(round, &["num"])); + math.set_field_slice("sin", Function::make(sin, &["num"])); + math.set_field_slice("sqrt", Function::make(sqrt, &["num"])); + math.set_field_slice("tan", Function::make(tan, &["num"])); math } /// Initialise the `Math` object on the global object pub fn init(global: Value) { - global.set_field_slice("Math", _create(global)); + global.set_field_slice("Math", _create(global.clone())); } diff --git a/src/lib/js/mod.rs b/src/lib/js/mod.rs index 4dd2a8347d..95a998b1d7 100644 --- a/src/lib/js/mod.rs +++ b/src/lib/js/mod.rs @@ -8,7 +8,11 @@ pub mod error; pub mod function; /// The global `JSON` object pub mod json; +/// The global `Math` object +pub mod math; /// The global `Object` object pub mod object; +/// The global `String` object +pub mod string; /// Javascript values, utility methods and conversion between Javascript values and Rust values pub mod value; diff --git a/src/lib/js/object.rs b/src/lib/js/object.rs index 776d2695a9..e78f22b2d9 100644 --- a/src/lib/js/object.rs +++ b/src/lib/js/object.rs @@ -1,4 +1,5 @@ -use js::value::Value; +use js::function::Function; +use js::value::{from_value, to_value, FromValue, ResultValue, ToValue, Value}; use std::collections::HashMap; pub static PROTOTYPE: &'static str = "prototype"; pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; @@ -26,26 +27,113 @@ pub struct Property { impl Property { /// Make a new property with the given value - pub fn new() -> Property { + pub fn new(value: Value) -> Property { Property { configurable: false, enumerable: false, writable: false, - value: Value::undefined(), + value: value, get: Value::undefined(), set: Value::undefined(), } } +} - /// Make a new property with the given value - pub fn from_value(value: Value) -> Property { - Property { - configurable: false, - enumerable: false, - writable: false, - value: value, - get: Value::undefined(), - set: Value::undefined(), - } +impl ToValue for Property { + fn to_value(&self) -> Value { + let prop = Value::new_obj(None); + prop.set_field_slice("configurable", to_value(self.configurable)); + prop.set_field_slice("enumerable", to_value(self.enumerable)); + prop.set_field_slice("writable", to_value(self.writable)); + prop.set_field_slice("value", self.value.clone()); + prop.set_field_slice("get", self.get.clone()); + prop.set_field_slice("set", self.set.clone()); + prop } } + +impl FromValue for Property { + fn from_value(v: Value) -> Result { + Ok(Property { + configurable: from_value(v.get_field_slice("configurable")).unwrap(), + enumerable: from_value(v.get_field_slice("enumerable")).unwrap(), + writable: from_value(v.get_field_slice("writable")).unwrap(), + value: v.get_field_slice("value"), + get: v.get_field_slice("get"), + set: v.get_field_slice("set"), + }) + } +} + +/// Create a new object +pub fn make_object(_: Vec, _: Value, _: Value, _: Value) -> ResultValue { + Ok(Value::undefined()) +} + +/// Get the prototype of an object +pub fn get_proto_of(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { + let obj = args.get(0).unwrap(); + Ok(obj.get_field_slice(INSTANCE_PROTOTYPE)) +} + +/// Set the prototype of an object +pub fn set_proto_of(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { + let obj = args.get(0).unwrap().clone(); + let proto = args.get(1).unwrap().clone(); + obj.set_field_slice(INSTANCE_PROTOTYPE, proto); + Ok(obj) +} + +/// Define a property in an object +pub fn define_prop(args: Vec, _: Value, _: Value, _: Value) -> ResultValue { + let obj = args.get(0).unwrap(); + let prop = from_value::(args.get(1).unwrap().clone()).unwrap(); + let desc = from_value::(args.get(2).unwrap().clone()).unwrap(); + obj.set_prop(prop, desc); + Ok(Value::undefined()) +} + +/// To string +pub fn to_string(_: Vec, _: Value, _: Value, this: Value) -> ResultValue { + Ok(to_value(this.to_string())) +} + +/// Check if it has a property +pub fn has_own_prop(args: Vec, _: Value, _: Value, this: Value) -> ResultValue { + let prop = if args.len() == 0 { + None + } else { + from_value::(args.get(0).unwrap().clone()).ok() + }; + Ok(to_value( + prop.is_some() && this.get_prop(prop.unwrap()).is_some(), + )) +} + +/// Create a new `Object` object +pub fn _create(global: Value) -> Value { + let object = Function::make(make_object, &[]); + let prototype = Value::new_obj(Some(global)); + prototype.set_field_slice( + "hasOwnProperty", + Function::make(has_own_prop, &["property"]), + ); + prototype.set_field_slice("toString", Function::make(to_string, &[])); + object.set_field_slice("length", to_value(1i32)); + object.set_field_slice(PROTOTYPE, prototype); + object.set_field_slice( + "setPrototypeOf", + Function::make(get_proto_of, &["object", "prototype"]), + ); + object.set_field_slice("getPrototypeOf", Function::make(get_proto_of, &["object"])); + object.set_field_slice( + "defineProperty", + Function::make(define_prop, &["object", "property"]), + ); + object +} + +/// Initialise the `Object` object on the global object +pub fn init(global: Value) { + global.set_field_slice("Object", _create(global.clone())); +} diff --git a/src/lib/js/string.rs b/src/lib/js/string.rs index f52b77d050..3c9eff16d9 100644 --- a/src/lib/js/string.rs +++ b/src/lib/js/string.rs @@ -14,14 +14,14 @@ pub fn get_string_length(_: Vec, _: Value, _: Value, this: Value) -> Resu } /// Create a new `String` object pub fn _create(global: Value) -> Value { - let string = Function::make(make_string, ["string"]); + let string = Function::make(make_string, &["string"]); let proto = Value::new_obj(Some(global)); let prop = Property { configurable: false, enumerable: false, writable: false, value: Value::undefined(), - get: Function::make(get_string_length, []), + get: Function::make(get_string_length, &[]), set: Value::undefined(), }; proto.set_prop_slice("length", prop); diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 2cecc847aa..e1e8fc6e4d 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -9,16 +9,13 @@ use std::f64::NAN; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::iter::FromIterator; use std::ops::Deref; +use std::ops::DerefMut; 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 -#[derive(Trace, Finalize, Clone, Debug)] -pub struct Value { - /// The garbage-collected pointer - pub ptr: Gc, -} +pub type Value = Gc; /// A Javascript value #[derive(Trace, Finalize, Debug)] @@ -41,7 +38,7 @@ pub enum ValueData { Function(GcCell), } -impl Value { +impl ValueData { /// Returns a new empty object pub fn new_obj(global: Option) -> Value { let mut obj: ObjectData = HashMap::new(); @@ -50,19 +47,14 @@ impl Value { .unwrap() .get_field_slice("Object") .get_field_slice(PROTOTYPE); - obj.insert( - INSTANCE_PROTOTYPE.to_string(), - Property::from_value(obj_proto), - ); - } - Value { - ptr: Gc::new(ValueData::Object(GcCell::new(obj))), + obj.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(obj_proto)); } + Gc::new(ValueData::Object(GcCell::new(obj))) } /// Returns true if the value is an object pub fn is_object(&self) -> bool { - match *self.ptr { + match *self { ValueData::Object(_) => true, _ => false, } @@ -70,7 +62,7 @@ impl Value { /// Returns true if the value is undefined pub fn is_undefined(&self) -> bool { - match *self.ptr { + match *self { ValueData::Undefined => true, _ => false, } @@ -78,7 +70,7 @@ impl Value { /// Returns true if the value is null pub fn is_null(&self) -> bool { - match *self.ptr { + match *self { ValueData::Null => true, _ => false, } @@ -86,7 +78,7 @@ impl Value { /// Returns true if the value is null or undefined pub fn is_null_or_undefined(&self) -> bool { - match *self.ptr { + match *self { ValueData::Null | ValueData::Undefined => true, _ => false, } @@ -94,7 +86,7 @@ impl Value { /// Returns true if the value is a 64-bit floating-point number pub fn is_double(&self) -> bool { - match *self.ptr { + match *self { ValueData::Number(_) => true, _ => false, } @@ -102,7 +94,7 @@ impl Value { /// Returns true if the value is a string pub fn is_string(&self) -> bool { - match *self.ptr { + match *self { ValueData::String(_) => true, _ => false, } @@ -111,7 +103,7 @@ impl Value { /// Returns true if the value is true /// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) pub fn is_true(&self) -> bool { - match *self.ptr { + match *self { ValueData::Object(_) => true, ValueData::String(ref s) if !s.is_empty() => true, ValueData::Number(n) if n >= 1.0 && n % 1.0 == 0.0 => true, @@ -123,7 +115,7 @@ impl Value { /// Converts the value into a 64-bit floating point number pub fn to_num(&self) -> f64 { - match *self.ptr { + match *self { ValueData::Object(_) | ValueData::Undefined | ValueData::Function(_) => NAN, ValueData::String(ref str) => match FromStr::from_str(str) { Ok(num) => num, @@ -138,7 +130,7 @@ impl Value { /// Converts the value into a 32-bit integer pub fn to_int(&self) -> i32 { - match *self.ptr { + match *self { ValueData::Object(_) | ValueData::Undefined | ValueData::Null @@ -157,7 +149,7 @@ impl Value { /// Resolve the property in the object /// Returns a copy of the Property pub fn get_prop(&self, field: String) -> Option { - let obj: ObjectData = match *self.ptr { + let obj: ObjectData = match *self { ValueData::Object(ref obj) => { let hash = obj.clone(); hash.into_inner() @@ -179,9 +171,7 @@ impl Value { pub fn get_field(&self, field: String) -> Value { match self.get_prop(field) { Some(prop) => prop.value.clone(), - None => Value { - ptr: Gc::new(ValueData::Undefined), - }, + None => Gc::new(ValueData::Undefined), } } @@ -192,15 +182,20 @@ impl Value { /// Set the field in the value pub fn set_field(&self, field: String, val: Value) -> Value { - match *self.ptr { + match *self { ValueData::Object(ref obj) => { obj.borrow_mut() - .insert(field.clone(), Property::from_value(val.clone())); + .insert(field.clone(), Property::new(val.clone())); } ValueData::Function(ref func) => { - func.borrow_mut() - .object - .insert(field.clone(), Property::from_value(val.clone())); + 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)) + } + }; } _ => (), } @@ -214,18 +209,26 @@ impl Value { /// Set the property in the value pub fn set_prop(&self, field: String, prop: Property) -> Property { - match *self.ptr { + match *self { ValueData::Object(ref obj) => { - obj.borrow_mut().insert(field.clone(), prop.clone()); + obj.borrow_mut().insert(field.clone(), prop); } ValueData::Function(ref func) => { - func.borrow_mut().object.insert(field.clone(), prop.clone()); + 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.to_string(), prop) + } + /// Convert from a JSON value to a JS value pub fn from_json(json: JSONValue) -> ValueData { match json { @@ -238,19 +241,20 @@ impl Value { i += 1; ( (i - 1).to_string().to_string(), - Property::from_value(to_value(json.clone())), + Property::new(to_value(json.clone())), ) })); data.insert( "length".to_string(), - Property::from_value(to_value(vs.len() as i32)), + Property::new(to_value(vs.len() as i32)), ); ValueData::Object(GcCell::new(data)) } JSONValue::Object(obj) => { - let data: ObjectData = FromIterator::from_iter(obj.iter().map(|(key, json)| { - (key.clone(), Property::from_value(to_value(json.clone()))) - })); + let data: ObjectData = FromIterator::from_iter( + obj.iter() + .map(|(key, json)| (key.clone(), Property::new(to_value(json.clone())))), + ); ValueData::Object(GcCell::new(data)) } JSONValue::Null => ValueData::Null, @@ -258,7 +262,7 @@ impl Value { } pub fn to_json(&self) -> JSONValue { - match *self.ptr { + match *self { ValueData::Null | ValueData::Undefined => JSONValue::Null, ValueData::Boolean(b) => JSONValue::Bool(b), ValueData::Object(ref obj) => { @@ -279,7 +283,7 @@ impl Value { /// Get the type of the value pub fn get_type(&self) -> &'static str { - match *self.ptr { + match *self { ValueData::Number(_) | ValueData::Integer(_) => "number", ValueData::String(_) => "string", ValueData::Boolean(_) => "boolean", @@ -288,17 +292,11 @@ impl Value { _ => "object", } } - /// Get the value for undefined - pub fn undefined() -> Value { - Value { - ptr: Gc::new(ValueData::Undefined), - } - } } -impl Display for Value { +impl Display for ValueData { fn fmt(&self, f: &mut Formatter) -> FmtResult { - match *self.ptr { + match *self { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), ValueData::Boolean(v) => write!(f, "{}", v), @@ -337,7 +335,7 @@ impl Display for Value { impl PartialEq for Value { fn eq(&self, other: &Value) -> bool { - match (self.ptr.clone().deref(), other.ptr.clone().deref()) { + match (self.clone().deref(), other.clone().deref()) { // TODO: fix this // _ if self.ptr.to_inner() == &other.ptr.to_inner() => true, _ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, @@ -373,9 +371,7 @@ pub trait FromValue { impl ToValue for String { fn to_value(&self) -> Value { - Value { - ptr: Gc::new(ValueData::String(self.clone())), - } + Gc::new(ValueData::String(self.clone())), } } @@ -450,7 +446,7 @@ impl<'s, T: ToValue> ToValue for &'s [T] { let mut arr = HashMap::new(); let mut i = 0; for item in self.iter() { - arr.insert(i.to_string(), Property::from_value(item.to_value())); + arr.insert(i.to_string(), Property::new(item.to_value())); i += 1; } to_value(arr) @@ -461,7 +457,7 @@ impl ToValue for Vec { let mut arr = HashMap::new(); let mut i = 0; for item in self.iter() { - arr.insert(i.to_string(), Property::from_value(item.to_value())); + arr.insert(i.to_string(), Property::new(item.to_value())); i += 1; } to_value(arr) diff --git a/src/lib/lib.rs b/src/lib/lib.rs index e00417dc90..f93c8b94d0 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -6,6 +6,6 @@ extern crate time; #[macro_use] extern crate gc_derive; -// pub mod exec; +pub mod exec; pub mod js; pub mod syntax; diff --git a/src/lib/syntax/ast/expr.rs b/src/lib/syntax/ast/expr.rs index 53fce62f3b..e686cdd480 100644 --- a/src/lib/syntax/ast/expr.rs +++ b/src/lib/syntax/ast/expr.rs @@ -3,7 +3,7 @@ use std::fmt::{Display, Formatter, Result}; use syntax::ast::constant::Const; use syntax::ast::op::{BinOp, Operator, UnaryOp}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Trace, Debug, PartialEq)] pub struct Expr { /// The expression definition pub def: ExprDef, @@ -12,9 +12,7 @@ pub struct Expr { impl Expr { /// Create a new expression with a starting and ending position pub fn new(def: ExprDef) -> Expr { - Expr { - def: def, - } + Expr { def: def } } }