mirror of https://github.com/boa-dev/boa.git
Jason Williams
6 years ago
9 changed files with 666 additions and 341 deletions
@ -1,4 +1,68 @@
|
||||
[[package]] |
||||
name = "Boa" |
||||
version = "0.1.3" |
||||
dependencies = [ |
||||
"gc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
"gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "gc" |
||||
version = "0.3.2" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
|
||||
[[package]] |
||||
name = "gc_derive" |
||||
version = "0.3.2" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
dependencies = [ |
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
"synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "quote" |
||||
version = "0.3.15" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
|
||||
[[package]] |
||||
name = "syn" |
||||
version = "0.11.11" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
dependencies = [ |
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "synom" |
||||
version = "0.11.3" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
dependencies = [ |
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "synstructure" |
||||
version = "0.6.1" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
dependencies = [ |
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "unicode-xid" |
||||
version = "0.0.4" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
|
||||
[metadata] |
||||
"checksum gc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "630b15fafc2270fc89de904da9ebb8b950642a5ed7bd99ec3f95558b0831ea9a" |
||||
"checksum gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2501c15cbaf28a0c2214617aa85351982a933161d7937fe6cd71c855364e0ea6" |
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" |
||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" |
||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" |
||||
"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" |
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" |
||||
|
@ -1,339 +1,17 @@
|
||||
use ast::{ |
||||
CompEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual, |
||||
CompNotEqual, CompStrictEqual, CompStrictNotEqual, |
||||
}; |
||||
use ast::{LogAnd, LogOr}; |
||||
use ast::{OpAdd, OpDiv, OpMod, OpMul, OpSub}; |
||||
use ast::{UnaryMinus, UnaryNot}; |
||||
use collections::treemap::TreeMap; |
||||
use js::function::{RegularFunc, RegularFunction}; |
||||
use js::object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}; |
||||
use js::value::{ |
||||
from_value, to_value, ResultValue, VBoolean, VFunction, VInteger, VNull, VNumber, VObject, |
||||
VString, VUndefined, Value, ValueData, |
||||
}; |
||||
use js::{array, console, error, function, json, math, number, object, string, uri}; |
||||
use std::cell::RefCell; |
||||
use std::gc::Gc; |
||||
use std::vec::Vec; |
||||
use syntax::ast::constant::Const; |
||||
use syntax::ast::expr::{Expr, ExprDef}; |
||||
use syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, Operator, UnaryOp}; |
||||
use syntax |
||||
|
||||
use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData}; |
||||
|
||||
/// An execution engine
|
||||
pub trait Executor { |
||||
/// Make a new execution engine
|
||||
fn new() -> Self; |
||||
/// Set a global variable called `name` with the value `val`
|
||||
fn set_global(&mut self, name: StrBuf, val: Value) -> Value; |
||||
// fn set_global(&mut self, name: String, val: Value) -> Value;
|
||||
/// Resolve the global variable `name`
|
||||
fn get_global(&self, name: StrBuf) -> Value; |
||||
// fn get_global(&self, name: String) -> Value;
|
||||
/// Create a new scope and return it
|
||||
fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>; |
||||
// fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>;
|
||||
/// Destroy the current scope
|
||||
fn destroy_scope(&mut self) -> (); |
||||
/// Run an expression
|
||||
fn run(&mut self, expr: &Expr) -> ResultValue; |
||||
} |
||||
#[deriving(Clone)] |
||||
/// A Javascript intepreter
|
||||
pub struct Interpreter { |
||||
/// An object representing the global object
|
||||
global: Value, |
||||
/// The variable scopes
|
||||
scopes: Vec<Gc<RefCell<ObjectData>>>, |
||||
} |
||||
impl Executor for Interpreter { |
||||
fn new() -> Interpreter { |
||||
let global = ValueData::new_obj(None); |
||||
object::init(global); |
||||
console::init(global); |
||||
math::init(global); |
||||
array::init(global); |
||||
function::init(global); |
||||
json::init(global); |
||||
number::init(global); |
||||
error::init(global); |
||||
string::init(global); |
||||
uri::init(global); |
||||
Interpreter { |
||||
global: global, |
||||
scopes: Vec::new(), |
||||
} |
||||
} |
||||
fn set_global(&mut self, name: StrBuf, val: Value) -> Value { |
||||
self.global.borrow().set_field(name, val) |
||||
} |
||||
fn get_global(&self, name: StrBuf) -> Value { |
||||
self.global.borrow().get_field(name) |
||||
} |
||||
fn make_scope(&mut self) -> Gc<RefCell<ObjectData>> { |
||||
let value = Gc::new(RefCell::new(TreeMap::new())); |
||||
self.scopes.push(value.clone()); |
||||
value |
||||
} |
||||
fn destroy_scope(&mut self) -> () { |
||||
self.scopes.pop(); |
||||
} |
||||
fn run(&mut self, expr: &Expr) -> ResultValue { |
||||
match expr.def { |
||||
ConstExpr(CNull) => Ok(Gc::new(VNull)), |
||||
ConstExpr(CUndefined) => Ok(Gc::new(VUndefined)), |
||||
ConstExpr(CNum(num)) => Ok(to_value(num)), |
||||
ConstExpr(CInt(num)) => Ok(to_value(num)), |
||||
ConstExpr(CString(ref str)) => Ok(Gc::new(VString(StrBuf::from_str(str.as_slice())))), |
||||
ConstExpr(CBool(val)) => Ok(Gc::new(VBoolean(val))), |
||||
ConstExpr(CRegExp(_, _, _)) => Ok(Gc::new(VNull)), |
||||
BlockExpr(ref es) => { |
||||
let mut obj = Gc::new(VNull); |
||||
for e in es.iter() { |
||||
let val = try!(self.run(e)); |
||||
if e == es.last().unwrap() { |
||||
obj = val; |
||||
} |
||||
} |
||||
Ok(obj) |
||||
} |
||||
LocalExpr(ref name) => { |
||||
let mut value = Gc::new(VUndefined); |
||||
for scope in self.scopes.iter().rev() { |
||||
match scope.borrow().borrow().find(name) { |
||||
Some(v) => { |
||||
value = v.value.clone(); |
||||
break; |
||||
} |
||||
None => (), |
||||
} |
||||
} |
||||
Ok(if value.borrow().is_undefined() { |
||||
self.global.borrow().get_field(name.clone()) |
||||
} else { |
||||
value |
||||
}) |
||||
} |
||||
GetConstFieldExpr(ref obj, ref field) => { |
||||
let val_obj = try!(self.run(*obj)); |
||||
Ok(val_obj.borrow().get_field(field.clone())) |
||||
} |
||||
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_str())) |
||||
} |
||||
CallExpr(ref callee, ref args) => { |
||||
let (this, func) = match callee.def { |
||||
GetConstFieldExpr(ref obj, ref field) => { |
||||
let obj = try!(self.run(*obj)); |
||||
(obj, obj.borrow().get_field(field.clone())) |
||||
} |
||||
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_str())) |
||||
} |
||||
_ => (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() { |
||||
VFunction(ref func) => func.borrow().call(self, this, Gc::new(VNull), v_args), |
||||
_ => 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(VNull); |
||||
let mut matched = false; |
||||
for tup in vals.iter() { |
||||
let tup: &(Expr, Vec<Expr>) = 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()), |
||||
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::<bool>(try!(self.run(*a))).unwrap(); |
||||
let v_b = from_value::<bool>(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)); |
||||
Ok(match *func.borrow() { |
||||
VFunction(ref func) => { |
||||
try!(func.borrow().call(self, this, Gc::new(VNull), v_args)); |
||||
this |
||||
} |
||||
_ => 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.global.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) |
||||
} |
||||
TypeOfExpr(ref val_e) => { |
||||
let val = try!(self.run(*val_e)); |
||||
Ok(to_value(match *val.borrow() { |
||||
VUndefined => "undefined", |
||||
VNull | VObject(_) => "object", |
||||
VBoolean(_) => "boolean", |
||||
VNumber(_) | VInteger(_) => "number", |
||||
VString(_) => "string", |
||||
VFunction(_) => "function", |
||||
})) |
||||
} |
||||
} |
||||
} |
||||
// fn run(&mut self, expr: &Expr) -> ResultValue;
|
||||
} |
||||
|
@ -0,0 +1,6 @@
|
||||
extern crate gc; |
||||
|
||||
/// The global `Object` object
|
||||
pub mod object; |
||||
/// Javascript values, utility methods and conversion between Javascript values and Rust values
|
||||
pub mod value; |
@ -0,0 +1,37 @@
|
||||
use gc::Gc; |
||||
use js::value::Value; |
||||
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
|
||||
pub struct Property { |
||||
/// If the type of this can be changed and this can be deleted
|
||||
pub configurable: bool, |
||||
/// If the property shows up in enumeration of the object
|
||||
pub enumerable: bool, |
||||
/// If this property can be changed with an assignment
|
||||
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, |
||||
} |
||||
|
||||
impl Property { |
||||
/// Make a new property with the given value
|
||||
pub fn new(value: Value) -> Property { |
||||
Property { |
||||
configurable: false, |
||||
enumerable: false, |
||||
writable: false, |
||||
value: value, |
||||
// get: Gc::new(VUndefined),
|
||||
// set: Gc::new(VUndefined),
|
||||
} |
||||
} |
||||
} |
@ -0,0 +1,533 @@
|
||||
use gc::Gc; |
||||
use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; |
||||
use std::cell::RefCell; |
||||
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
|
||||
pub type Value = Gc<ValueData>; |
||||
|
||||
/// A Javascript value
|
||||
pub enum ValueData { |
||||
/// `null` - A null value, for when a value doesn't exist
|
||||
Null, |
||||
/// `undefined` - An undefined value, for when a field or index doesn't exist
|
||||
Undefined, |
||||
/// `boolean` - A `true` / `false` value, for if a certain criteria is met
|
||||
Boolean(bool), |
||||
/// `String` - A UTF-8 string, such as `"Hello, world"`
|
||||
String(String), |
||||
/// `Number` - A 64-bit floating point number, such as `3.1415`
|
||||
Number(f64), |
||||
/// `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<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(RefCell<Function>), |
||||
} |
||||
|
||||
impl ValueData { |
||||
/// Returns a new empty object
|
||||
pub fn new_obj(global: Option<Value>) -> 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)); |
||||
} |
||||
Gc::new(ValueData::Object(RefCell::new(obj))) |
||||
} |
||||
/// Returns true if the value is an object
|
||||
pub fn is_object(&self) -> bool { |
||||
return match *self { |
||||
ValueData::Object(_) => true, |
||||
_ => false, |
||||
}; |
||||
} |
||||
/// Returns true if the value is undefined
|
||||
pub fn is_undefined(&self) -> bool { |
||||
return match *self { |
||||
ValueData::Undefined => true, |
||||
_ => false, |
||||
}; |
||||
} |
||||
/// Returns true if the value is null
|
||||
pub fn is_null(&self) -> bool { |
||||
return match *self { |
||||
ValueData::Null => true, |
||||
_ => false, |
||||
}; |
||||
} |
||||
/// Returns true if the value is null or undefined
|
||||
pub fn is_null_or_undefined(&self) -> bool { |
||||
return match *self { |
||||
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 { |
||||
ValueData::Number(_) => true, |
||||
_ => false, |
||||
}; |
||||
} |
||||
/// Returns true if the value is true
|
||||
pub fn is_true(&self) -> bool { |
||||
return match *self { |
||||
ValueData::Object(_) => true, |
||||
ValueData::String(ref s) if s.as_slice() == "1" => 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<Property> { |
||||
let obj: ObjectData = match *self { |
||||
ValueData::Object(ref obj) => obj.borrow().clone(), |
||||
ValueData::Function(ref func) => { |
||||
let func = func.borrow().clone(); |
||||
match func { |
||||
NativeFunc(f) => f.object.clone(), |
||||
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 => 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<'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() { |
||||
NativeFunc(ref mut f) => f.object.insert(field.clone(), Property::new(val)), |
||||
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() { |
||||
NativeFunc(ref mut f) => f.object.insert(field.clone(), prop), |
||||
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: Json) -> ValueData { |
||||
match json { |
||||
Number(v) => ValueData::Number(v), |
||||
String(v) => ValueData::String(v), |
||||
Boolean(v) => ValueData::Boolean(v), |
||||
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)) |
||||
} |
||||
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, |
||||
} |
||||
} |
||||
} |
||||
impl fmt::Show 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() { |
||||
NativeFunc(_) => write!(f, "{}", "function() { [native code] }"), |
||||
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 ToJson for ValueData { |
||||
fn to_json(&self) -> Json { |
||||
match *self { |
||||
ValueData::Null | ValueData::Undefined => Null, |
||||
ValueData::Boolean(b) => 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()); |
||||
} |
||||
} |
||||
Object(box nobj) |
||||
} |
||||
ValueData::String(ref str) => String(str.clone()), |
||||
ValueData::Number(num) => Number(num), |
||||
ValueData::Integer(val) => Number(val as f64), |
||||
ValueData::Function(_) => Null, |
||||
} |
||||
} |
||||
} |
||||
impl Add<ValueData, ValueData> 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<ValueData, ValueData> for ValueData { |
||||
fn sub(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Number(self.to_num() - other.to_num()) |
||||
} |
||||
} |
||||
impl Mul<ValueData, ValueData> for ValueData { |
||||
fn mul(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Number(self.to_num() * other.to_num()) |
||||
} |
||||
} |
||||
impl Div<ValueData, ValueData> for ValueData { |
||||
fn div(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Number(self.to_num() / other.to_num()) |
||||
} |
||||
} |
||||
impl Rem<ValueData, ValueData> for ValueData { |
||||
fn rem(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Number(self.to_num() % other.to_num()) |
||||
} |
||||
} |
||||
impl BitAnd<ValueData, ValueData> for ValueData { |
||||
fn bitand(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Integer(self.to_int() & other.to_int()) |
||||
} |
||||
} |
||||
impl BitOr<ValueData, ValueData> for ValueData { |
||||
fn bitor(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Integer(self.to_int() | other.to_int()) |
||||
} |
||||
} |
||||
impl BitXor<ValueData, ValueData> for ValueData { |
||||
fn bitxor(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Integer(self.to_int() ^ other.to_int()) |
||||
} |
||||
} |
||||
impl Shl<ValueData, ValueData> for ValueData { |
||||
fn shl(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Integer(self.to_int() << other.to_int()) |
||||
} |
||||
} |
||||
impl Shr<ValueData, ValueData> for ValueData { |
||||
fn shr(&self, other: &ValueData) -> ValueData { |
||||
ValueData::Integer(self.to_int() >> other.to_int()) |
||||
} |
||||
} |
||||
impl Not<ValueData> 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<Self, &'static str>; |
||||
} |
||||
impl ToValue for String { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::String(self.clone())) |
||||
} |
||||
} |
||||
impl FromValue for String { |
||||
fn from_value(v: Value) -> Result<String, &'static str> { |
||||
Ok(v.borrow().to_str()) |
||||
} |
||||
} |
||||
impl<'s> ToValue for &'s str { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::String(String::from_str(*self))) |
||||
} |
||||
} |
||||
impl ToValue for char { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::String(String::from_char(1, *self))) |
||||
} |
||||
} |
||||
impl FromValue for char { |
||||
fn from_value(v: Value) -> Result<char, &'static str> { |
||||
Ok(v.borrow().to_str().as_slice().char_at(0)) |
||||
} |
||||
} |
||||
impl ToValue for f64 { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::Number(self.clone())) |
||||
} |
||||
} |
||||
impl FromValue for f64 { |
||||
fn from_value(v: Value) -> Result<f64, &'static str> { |
||||
Ok(v.borrow().to_num()) |
||||
} |
||||
} |
||||
impl ToValue for i32 { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::Integer(self.clone())) |
||||
} |
||||
} |
||||
impl FromValue for i32 { |
||||
fn from_value(v: Value) -> Result<i32, &'static str> { |
||||
Ok(v.borrow().to_int()) |
||||
} |
||||
} |
||||
impl ToValue for bool { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::Boolean(self.clone())) |
||||
} |
||||
} |
||||
impl FromValue for bool { |
||||
fn from_value(v: Value) -> Result<bool, &'static str> { |
||||
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<T: ToValue> ToValue for Vec<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<T: FromValue> FromValue for Vec<T> { |
||||
fn from_value(v: Value) -> Result<Vec<T>, &'static str> { |
||||
let len = v.borrow().get_field_slice("length").borrow().to_int(); |
||||
let mut vec = Vec::with_capacity(len as uint); |
||||
for i in range(0, len) { |
||||
vec.push(try!(from_value(v.borrow().get_field(i.to_str())))) |
||||
} |
||||
Ok(vec) |
||||
} |
||||
} |
||||
impl ToValue for NativeFunctionData { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::Function(RefCell::new(NativeFunc( |
||||
NativeFunction::new(*self), |
||||
)))) |
||||
} |
||||
} |
||||
impl FromValue for NativeFunctionData { |
||||
fn from_value(v: Value) -> Result<NativeFunctionData, &'static str> { |
||||
match *v.borrow() { |
||||
ValueData::Function(ref func) => match *func.borrow() { |
||||
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 { |
||||
Gc::new(ValueData::Object(RefCell::new(self.clone()))) |
||||
} |
||||
} |
||||
impl FromValue for ObjectData { |
||||
fn from_value(v: Value) -> Result<ObjectData, &'static str> { |
||||
match *v.borrow() { |
||||
ValueData::Object(ref obj) => Ok(obj.clone().borrow().deref().clone()), |
||||
ValueData::Function(ref func) => Ok(match *func.borrow().deref() { |
||||
NativeFunc(ref data) => data.object.clone(), |
||||
RegularFunc(ref data) => data.object.clone(), |
||||
}), |
||||
_ => Err("Value is not a valid object"), |
||||
} |
||||
} |
||||
} |
||||
impl ToValue for Json { |
||||
fn to_value(&self) -> Value { |
||||
Gc::new(ValueData::from_json(self.clone())) |
||||
} |
||||
} |
||||
impl FromValue for Json { |
||||
fn from_value(v: Value) -> Result<Json, &'static str> { |
||||
Ok(v.borrow().to_json()) |
||||
} |
||||
} |
||||
impl ToValue for () { |
||||
fn to_value(&self) -> Value { |
||||
Gc::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<A: FromValue>(v: Value) -> Result<A, &'static str> { |
||||
FromValue::from_value(v) |
||||
} |
||||
|
||||
/// A utility function that just calls ToValue::to_value
|
||||
pub fn to_value<A: ToValue>(v: A) -> Value { |
||||
v.to_value() |
||||
} |
@ -1 +1,6 @@
|
||||
extern crate gc; |
||||
extern crate gc_derive; |
||||
|
||||
pub mod exec; |
||||
pub mod js; |
||||
pub mod syntax; |
||||
|
Loading…
Reference in new issue