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]] |
[[package]] |
||||||
name = "Boa" |
name = "Boa" |
||||||
version = "0.1.3" |
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::{ |
use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData}; |
||||||
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 |
|
||||||
|
|
||||||
|
|
||||||
/// An execution engine
|
/// An execution engine
|
||||||
pub trait Executor { |
pub trait Executor { |
||||||
/// Make a new execution engine
|
/// Make a new execution engine
|
||||||
fn new() -> Self; |
fn new() -> Self; |
||||||
/// Set a global variable called `name` with the value `val`
|
/// 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`
|
/// 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
|
/// 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
|
/// Destroy the current scope
|
||||||
fn destroy_scope(&mut self) -> (); |
fn destroy_scope(&mut self) -> (); |
||||||
/// Run an expression
|
/// Run an expression
|
||||||
fn run(&mut self, expr: &Expr) -> ResultValue; |
// 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", |
|
||||||
})) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
} |
||||||
|
@ -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; |
pub mod syntax; |
||||||
|
Loading…
Reference in new issue