diff --git a/src/lib/js/function.rs b/src/lib/js/function.rs index 1e3b5db2d5..995670b218 100644 --- a/src/lib/js/function.rs +++ b/src/lib/js/function.rs @@ -1,110 +1,18 @@ -use exec::Interpreter; -use gc::GcCell; -use js::object::{ObjectData, Property}; -use js::value::{to_value, ResultValue, Value, ValueData}; -use std::collections::HashMap; -use syntax::ast::expr::Expr; +use js::object::ObjectData; +use js::value::{ResultValue, Value}; + +pub type FunctionData = fn(Vec, Value, Value, Value) -> 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 -pub enum Function { - /// A native javascript function - NativeFunc(NativeFunction), - /// A regular javascript function - RegularFunc(RegularFunction), -} +/// In our implementation, Function is extending Object by holding an object field which some extra data -impl Function { - /// Call a function with some arguments - pub fn call( - &self, - exe: &mut Interpreter, - this: Value, - callee: Value, - args: Vec, - ) -> ResultValue { - match *self { - Function::NativeFunc(ref ntv) => { - let func = ntv.data; - func(this, callee, args) - } - Function::RegularFunc(ref data) => { - let scope = exe.make_scope(); - scope - .borrow() - .borrow_mut() - .insert("this".to_string(), Property::new(this)); - for i in 0..data.args.len() { - let name = data.args.get(i); - let expr = args.get(i); - scope - .borrow() - .borrow_mut() - .insert(name.to_string(), Property::new(*expr)); - } - let result = exe.run(&data.expr); - exe.destroy_scope(); - result - } - } - } -} - -/// Represents a regular javascript function in memory -/// A member of the Object type that may be invoked as a subroutine -pub struct RegularFunction { +#[derive(Trace, Finalize)] +pub struct Function { /// The fields associated with the function pub object: ObjectData, - /// This function's expression - pub expr: Expr, + /// This function's JIT representation + pub repr: FunctionData, /// The argument names of the function pub args: Vec, } - -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::new(GcCell::new(ValueData::Integer(args.len() as i32))), - ); - RegularFunction { - object: obj, - expr: expr, - args: args, - } - } -} - -pub type NativeFunctionData = fn(Value, Value, Vec) -> ResultValue; - -/// 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, - } - } -} - -/// Create a new `Function` object -pub fn _create() -> Value { - let function: ObjectData = HashMap::new(); - to_value(function) -} - -/// Initialise the global object with the `Function` object -pub fn init(global: Value) { - let global_ptr = global.borrow(); - global_ptr.set_field_slice("Function", _create(global)); -} diff --git a/src/lib/js/mod.rs b/src/lib/js/mod.rs index 7ca97bb081..2d97619a91 100644 --- a/src/lib/js/mod.rs +++ b/src/lib/js/mod.rs @@ -1,6 +1,6 @@ extern crate serde_json; -// /// The global `Function` object and function value representations -// pub mod function; +/// The global `Function` object and function value representations +pub mod function; // /// The global `JSON` object // pub mod json; /// The global `Object` object diff --git a/src/lib/js/object.rs b/src/lib/js/object.rs index 30a4c9e9dc..f7d1d0f718 100644 --- a/src/lib/js/object.rs +++ b/src/lib/js/object.rs @@ -1,16 +1,14 @@ -use js::value::Value; +use js::value::{Value, ValueData}; use std::collections::HashMap; pub static PROTOTYPE: &'static str = "prototype"; pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; pub type ObjectData = HashMap; -/// A Javascript Property -/// [Attributes of a Data Property](https://tc39.github.io/ecma262/#sec-property-attributes) -/// [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)] +/// A Javascript Property AKA The Property Descriptor +/// [[SPEC] - The Property Descriptor Specification Type](https://tc39.github.io/ecma262/#sec-property-descriptor-specification-type) +/// [[SPEC] - Default Attribute Values](https://tc39.github.io/ecma262/#table-4) +#[derive(Trace, Finalize, Clone)] pub struct Property { /// If the type of this can be changed and this can be deleted pub configurable: bool, @@ -20,20 +18,34 @@ pub struct Property { pub writable: bool, /// The value associated with the property pub value: Value, - // pub get: Value, - // pub set: Value, + /// The function serving as getter + pub get: Value, + /// The function serving as setter + pub set: Value, } impl Property { /// Make a new property with the given value - pub fn new(value: Value) -> Property { + pub fn new() -> Property { + Property { + configurable: false, + enumerable: false, + writable: false, + value: Value::undefined(), + 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(), + get: Value::undefined(), + set: Value::undefined(), } } } diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 31efa2c766..f564a145c7 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -1,11 +1,13 @@ use gc::{Gc, GcCell}; +use js::function::Function; use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; use std::collections::HashMap; +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)] +#[derive(Trace, Finalize, Clone)] pub struct Value { /// The garbage-collected pointer pub ptr: Gc, @@ -28,6 +30,8 @@ pub enum ValueData { Integer(i32), /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values Object(GcCell), + /// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object + Function(GcCell), } impl Value { @@ -39,7 +43,10 @@ impl Value { .unwrap() .get_field_slice("Object") .get_field_slice(PROTOTYPE); - obj.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(obj_proto)); + obj.insert( + INSTANCE_PROTOTYPE.to_string(), + Property::from_value(obj_proto), + ); } Value { ptr: Gc::new(ValueData::Object(GcCell::new(obj))), @@ -111,9 +118,9 @@ impl Value { pub fn to_num(&self) -> f64 { match *self.ptr { ValueData::Object(_) | ValueData::Undefined | ValueData::Function(_) => std::f64::NAN, - ValueData::String(ref str) => match from_str(str) { - Some(num) => num, - None => std::f64::NAN, + ValueData::String(ref str) => match FromStr::from_str(str) { + Ok(num) => num, + Err(_) => std::f64::NAN, }, ValueData::Number(num) => num, ValueData::Boolean(true) => 1.0, @@ -121,4 +128,65 @@ impl Value { ValueData::Integer(num) => num as f64, } } + + /// Converts the value into a 32-bit integer + pub fn to_int(&self) -> i32 { + match *self.ptr { + ValueData::Object(_) + | ValueData::Undefined + | ValueData::Null + | ValueData::Boolean(false) + | ValueData::Function(_) => 0, + ValueData::String(ref str) => match FromStr::from_str(str) { + Ok(num) => num, + Err(_) => 0, + }, + ValueData::Number(num) => num as i32, + ValueData::Boolean(true) => 1, + ValueData::Integer(num) => num, + } + } + + /// 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 { + ValueData::Object(ref obj) => { + let hash = obj.clone(); + hash.into_inner() + } + // Accesing .object on borrow() seems to automatically dereference it, so we don't need the * + // ValueData::Function(ref func) => func.clone().object, + _ => return None, + }; + match obj.get(&field) { + Some(val) => Some(val.clone()), + None => match obj.get(&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 + 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), + }, + } + } + + /// 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<'a>(&self, field: &'a str) -> Value { + self.get_field(field.to_string()) + } + + /// Get the value for undefined + pub fn undefined() -> Value { + Value { + ptr: Gc::new(ValueData::Undefined), + } + } }