Browse Source

updates

pull/5/head
Jason Williams 6 years ago
parent
commit
75a4502a36
  1. 110
      src/lib/js/function.rs
  2. 4
      src/lib/js/mod.rs
  3. 36
      src/lib/js/object.rs
  4. 78
      src/lib/js/value.rs

110
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, 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<Value>,
) -> 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<String>,
}
impl RegularFunction {
/// Make a new regular function
pub fn new(expr: Expr, args: Vec<String>) -> 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<Value>) -> 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));
}

4
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

36
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<String, Property>;
/// 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(),
}
}
}

78
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<Value, Value>;
/// 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<ValueData>,
@ -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<ObjectData>),
/// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object
Function(GcCell<Function>),
}
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<Property> {
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),
}
}
}

Loading…
Cancel
Save