Browse Source

[WIP] - addition of Symbols (#191)

* Addition of Symbols
* Addition of Symbol benchmark
pull/212/head
Jason Williams 5 years ago committed by GitHub
parent
commit
d4791837db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      benches/exec.rs
  2. 113
      src/lib/builtins/array.rs
  3. 7
      src/lib/builtins/console.rs
  4. 6
      src/lib/builtins/mod.rs
  5. 2
      src/lib/builtins/number.rs
  6. 183
      src/lib/builtins/object.rs
  7. 50
      src/lib/builtins/property.rs
  8. 6
      src/lib/builtins/regexp.rs
  9. 113
      src/lib/builtins/symbol.rs
  10. 112
      src/lib/builtins/value.rs
  11. 2
      src/lib/environment/object_environment_record.rs
  12. 35
      src/lib/exec.rs
  13. 3
      src/lib/realm.rs
  14. 1
      tests/js/test.js

15
benches/exec.rs

@ -1,12 +1,23 @@
#[macro_use] #[macro_use]
extern crate criterion; extern crate criterion;
use boa::exec;
use boa::realm::Realm; use boa::realm::Realm;
use criterion::Criterion; use criterion::{black_box, Criterion};
static SRC: &str = r#"
let a = Symbol();
let b = Symbol();
let c = Symbol();
"#;
fn symbol_creation(c: &mut Criterion) {
c.bench_function("Symbol Creation", move |b| b.iter(|| exec(black_box(SRC))));
}
fn create_realm(c: &mut Criterion) { fn create_realm(c: &mut Criterion) {
c.bench_function("Create Realm", move |b| b.iter(|| Realm::create())); c.bench_function("Create Realm", move |b| b.iter(|| Realm::create()));
} }
criterion_group!(benches, create_realm); criterion_group!(benches, create_realm, symbol_creation);
criterion_main!(benches); criterion_main!(benches);

113
src/lib/builtins/array.rs

@ -35,7 +35,7 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
/// Utility function for creating array objects: `array_obj` can be any array with /// Utility function for creating array objects: `array_obj` can be any array with
/// prototype already set (it will be wiped and recreated from `array_contents`) /// prototype already set (it will be wiped and recreated from `array_contents`)
fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultValue { pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultValue {
let array_obj_ptr = array_obj.clone(); let array_obj_ptr = array_obj.clone();
// Wipe existing contents of the array object // Wipe existing contents of the array object
@ -45,9 +45,17 @@ fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultValue {
array_obj_ptr.remove_prop(&n.to_string()); array_obj_ptr.remove_prop(&n.to_string());
} }
array_obj_ptr.set_field_slice("length", to_value(array_contents.len() as i32)); // Create length
let length = Property::new()
.value(to_value(array_contents.len() as i32))
.writable(true)
.configurable(false)
.enumerable(false);
array_obj_ptr.set_prop("length".to_string(), length);
for (n, value) in array_contents.iter().enumerate() { for (n, value) in array_contents.iter().enumerate() {
array_obj_ptr.set_field(n.to_string(), value.clone()); array_obj_ptr.set_field_slice(&n.to_string(), value.clone());
} }
Ok(array_obj_ptr) Ok(array_obj_ptr)
} }
@ -60,7 +68,7 @@ pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> Re
for (n, value) in add_values.iter().enumerate() { for (n, value) in add_values.iter().enumerate() {
let new_index = orig_length.wrapping_add(n as i32); let new_index = orig_length.wrapping_add(n as i32);
array_ptr.set_field(new_index.to_string(), value.clone()); array_ptr.set_field_slice(&new_index.to_string(), value.clone());
} }
array_ptr.set_field_slice( array_ptr.set_field_slice(
@ -72,24 +80,37 @@ pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> Re
} }
/// Create a new array /// Create a new array
pub fn make_array(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// Make a new Object which will internally represent the Array (mapping // Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype // between indices and values): this creates an Object with no prototype
this.set_field_slice("length", to_value(0_i32));
// Create length
let length = Property::new()
.value(to_value(args.len() as i32))
.writable(true)
.configurable(false)
.enumerable(false);
this.set_prop("length".to_string(), length);
// Set Prototype
let array_prototype = ctx
.realm
.global_obj
.get_field_slice("Array")
.get_field_slice(PROTOTYPE);
this.set_internal_slot(INSTANCE_PROTOTYPE, array_prototype);
// This value is used by console.log and other routines to match Object type // This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name) // to its Javascript Identifier (global constructor method name)
this.set_kind(ObjectKind::Array); this.set_kind(ObjectKind::Array);
match args.len() {
0 => construct_array(this, &[]), // And finally add our arguments in
1 => { for (n, value) in args.iter().enumerate() {
let array = construct_array(this, &[]).expect("Could not construct array"); this.set_field_slice(&n.to_string(), value.clone());
let size: i32 = from_value(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to i32");
array.set_field_slice("length", to_value(size));
Ok(array)
}
_ => construct_array(this, args),
} }
Ok(this.clone())
} }
/// Get an array's length /// Get an array's length
@ -118,14 +139,14 @@ pub fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
let this_length: i32 = let this_length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
for n in 0..this_length { for n in 0..this_length {
new_values.push(this.get_field(&n.to_string())); new_values.push(this.get_field_slice(&n.to_string()));
} }
for concat_array in args { for concat_array in args {
let concat_length: i32 = from_value(concat_array.get_field_slice("length")) let concat_length: i32 = from_value(concat_array.get_field_slice("length"))
.expect("Could not convert argument to i32"); .expect("Could not convert argument to i32");
for n in 0..concat_length { for n in 0..concat_length {
new_values.push(concat_array.get_field(&n.to_string())); new_values.push(concat_array.get_field_slice(&n.to_string()));
} }
} }
@ -154,7 +175,7 @@ pub fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
return Ok(Gc::new(ValueData::Undefined)); return Ok(Gc::new(ValueData::Undefined));
} }
let pop_index = curr_length.wrapping_sub(1); let pop_index = curr_length.wrapping_sub(1);
let pop_value: Value = this.get_field(&pop_index.to_string()); let pop_value: Value = this.get_field_slice(&pop_index.to_string());
this.remove_prop(&pop_index.to_string()); this.remove_prop(&pop_index.to_string());
this.set_field_slice("length", to_value(pop_index)); this.set_field_slice("length", to_value(pop_index));
Ok(pop_value) Ok(pop_value)
@ -177,7 +198,7 @@ pub fn join(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let length: i32 = let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
for n in 0..length { for n in 0..length {
let elem_str: String = this.get_field(&n.to_string()).to_string(); let elem_str: String = this.get_field_slice(&n.to_string()).to_string();
elem_strs.push(elem_str); elem_strs.push(elem_str);
} }
@ -201,17 +222,17 @@ pub fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let upper_exists = this.has_field(&upper.to_string()); let upper_exists = this.has_field(&upper.to_string());
let lower_exists = this.has_field(&lower.to_string()); let lower_exists = this.has_field(&lower.to_string());
let upper_value = this.get_field(&upper.to_string()); let upper_value = this.get_field_slice(&upper.to_string());
let lower_value = this.get_field(&lower.to_string()); let lower_value = this.get_field_slice(&lower.to_string());
if upper_exists && lower_exists { if upper_exists && lower_exists {
this.set_field(upper.to_string(), lower_value); this.set_field_slice(&upper.to_string(), lower_value);
this.set_field(lower.to_string(), upper_value); this.set_field_slice(&lower.to_string(), upper_value);
} else if upper_exists { } else if upper_exists {
this.set_field(lower.to_string(), upper_value); this.set_field_slice(&lower.to_string(), upper_value);
this.remove_prop(&upper.to_string()); this.remove_prop(&upper.to_string());
} else if lower_exists { } else if lower_exists {
this.set_field(upper.to_string(), lower_value); this.set_field_slice(&upper.to_string(), lower_value);
this.remove_prop(&lower.to_string()); this.remove_prop(&lower.to_string());
} }
} }
@ -230,20 +251,20 @@ pub fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
if len == 0 { if len == 0 {
this.set_field_slice("length", to_value(0_i32)); this.set_field_slice("length", to_value(0_i32));
// Since length is 0, this will be an Undefined value // Since length is 0, this will be an Undefined value
return Ok(this.get_field(&0.to_string())); return Ok(this.get_field_slice(&0.to_string()));
} }
let first: Value = this.get_field(&0.to_string()); let first: Value = this.get_field_slice(&0.to_string());
for k in 1..len { for k in 1..len {
let from = k.to_string(); let from = k.to_string();
let to = (k.wrapping_sub(1)).to_string(); let to = (k.wrapping_sub(1)).to_string();
let from_value = this.get_field(&from); let from_value = this.get_field_slice(&from);
if from_value == Gc::new(ValueData::Undefined) { if from_value == Gc::new(ValueData::Undefined) {
this.remove_prop(&to); this.remove_prop(&to);
} else { } else {
this.set_field(to, from_value); this.set_field_slice(&to, from_value);
} }
} }
@ -270,11 +291,11 @@ pub fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
let from = (k.wrapping_sub(1)).to_string(); let from = (k.wrapping_sub(1)).to_string();
let to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string(); let to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string();
let from_value = this.get_field(&from); let from_value = this.get_field_slice(&from);
if from_value == Gc::new(ValueData::Undefined) { if from_value == Gc::new(ValueData::Undefined) {
this.remove_prop(&to); this.remove_prop(&to);
} else { } else {
this.set_field(to, from_value); this.set_field_slice(&to, from_value);
} }
} }
for j in 0..arg_c { for j in 0..arg_c {
@ -315,7 +336,7 @@ pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
let max_len: i32 = from_value(this.get_field_slice("length")).unwrap(); let max_len: i32 = from_value(this.get_field_slice("length")).unwrap();
let mut len = max_len; let mut len = max_len;
while i < len { while i < len {
let element = this.get_field(&i.to_string()); let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()]; let arguments = vec![element.clone(), to_value(i), this.clone()];
let result = interpreter.call(callback, &this_arg, arguments)?.is_true(); let result = interpreter.call(callback, &this_arg, arguments)?.is_true();
if !result { if !result {
@ -349,7 +370,7 @@ pub fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
let values = (0..length) let values = (0..length)
.map(|idx| { .map(|idx| {
let element = this.get_field(&idx.to_string()); let element = this.get_field_slice(&idx.to_string());
let args = vec![element, to_value(idx), new.clone()]; let args = vec![element, to_value(idx), new.clone()];
@ -400,7 +421,7 @@ pub fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu
}; };
while idx < len { while idx < len {
let check_element = this.get_field(&idx.to_string()).clone(); let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element { if check_element == search_element {
return Ok(to_value(idx)); return Ok(to_value(idx));
@ -449,7 +470,7 @@ pub fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> Resul
}; };
while idx >= 0 { while idx >= 0 {
let check_element = this.get_field(&idx.to_string()).clone(); let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element { if check_element == search_element {
return Ok(to_value(idx)); return Ok(to_value(idx));
@ -481,7 +502,7 @@ pub fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resu
}; };
let len: i32 = from_value(this.get_field_slice("length")).unwrap(); let len: i32 = from_value(this.get_field_slice("length")).unwrap();
for i in 0..len { for i in 0..len {
let element = this.get_field(&i.to_string()); let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()]; let arguments = vec![element.clone(), to_value(i), this.clone()];
let result = interpreter.call(callback, &this_arg, arguments)?; let result = interpreter.call(callback, &this_arg, arguments)?;
if result.is_true() { if result.is_true() {
@ -515,7 +536,7 @@ pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) -
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
for i in 0..length { for i in 0..length {
let element = this.get_field(&i.to_string()); let element = this.get_field_slice(&i.to_string());
let arguments = vec![element.clone(), to_value(i), this.clone()]; let arguments = vec![element.clone(), to_value(i), this.clone()];
let result = interpreter.call(predicate_arg, &this_arg, arguments)?; let result = interpreter.call(predicate_arg, &this_arg, arguments)?;
@ -556,7 +577,7 @@ pub fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
}; };
for i in start..fin { for i in start..fin {
this.set_field(i.to_string(), value.clone()); this.set_field_slice(&i.to_string(), value.clone());
} }
Ok(this.clone()) Ok(this.clone())
@ -572,7 +593,7 @@ pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> Resu
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
for idx in 0..length { for idx in 0..length {
let check_element = this.get_field(&idx.to_string()).clone(); let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element { if check_element == search_element {
return Ok(to_value(true)); return Ok(to_value(true));
@ -618,7 +639,10 @@ pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
let span = max(to.wrapping_sub(from), 0); let span = max(to.wrapping_sub(from), 0);
let mut new_array_len: i32 = 0; let mut new_array_len: i32 = 0;
for i in from..from.wrapping_add(span) { for i in from..from.wrapping_add(span) {
new_array.set_field(new_array_len.to_string(), this.get_field(&i.to_string())); new_array.set_field_slice(
&new_array_len.to_string(),
this.get_field_slice(&i.to_string()),
);
new_array_len = new_array_len.wrapping_add(1); new_array_len = new_array_len.wrapping_add(1);
} }
new_array.set_field_slice("length", to_value(new_array_len)); new_array.set_field_slice("length", to_value(new_array_len));
@ -628,14 +652,15 @@ pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
/// Create a new `Array` object /// Create a new `Array` object
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(global: &Value) -> Value {
// Create Constructor // Create Constructor
let mut array_constructor = Object::default(); let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);
let mut array_constructor = Object::create(object_prototype);
array_constructor.kind = ObjectKind::Function; array_constructor.kind = ObjectKind::Function;
array_constructor.set_internal_method("construct", make_array); array_constructor.set_internal_method("construct", make_array);
// Todo: add call function // Todo: add call function
array_constructor.set_internal_method("call", make_array); array_constructor.set_internal_method("call", make_array);
// Create prototype // Create prototype
let array_prototype = ValueData::new_obj(Some(global)); let array_prototype = ValueData::new_obj(None);
let length = Property::default().get(to_value(get_array_length as NativeFunctionData)); let length = Property::default().get(to_value(get_array_length as NativeFunctionData));
@ -1127,7 +1152,7 @@ mod tests {
// test object reference // test object reference
forward(&mut engine, "a = (new Array(3)).fill({});"); forward(&mut engine, "a = (new Array(3)).fill({});");
forward(&mut engine, "a[0].hi = 'hi';"); forward(&mut engine, "a[0].hi = 'hi';");
assert_eq!(forward(&mut engine, "a[1].hi"), String::from("hi")); assert_eq!(forward(&mut engine, "a[0].hi"), String::from("hi"));
} }
#[test] #[test]

7
src/lib/builtins/console.rs

@ -94,6 +94,13 @@ fn log_string_from(x: Value) -> String {
} }
s s
} }
ValueData::Symbol(ref sym) => {
let desc: Value = sym.borrow().get_internal_slot("Description");
match *desc {
ValueData::String(ref st) => format!("Symbol(\"{}\")", st.to_string()),
_ => String::from("Symbol()"),
}
}
_ => from_value::<String>(x.clone()).expect("Could not convert value to String"), _ => from_value::<String>(x.clone()).expect("Could not convert value to String"),
} }

6
src/lib/builtins/mod.rs

@ -1,5 +1,7 @@
/// The global `Array` object /// The global `Array` object
pub mod array; pub mod array;
/// the global `Symbol` Object
pub mod symbol;
// The global `Boolean` object // The global `Boolean` object
pub mod boolean; pub mod boolean;
/// The global `console` object /// The global `console` object
@ -16,11 +18,11 @@ pub mod math;
pub mod number; pub mod number;
/// The global `Object` object /// The global `Object` object
pub mod object; pub mod object;
/// Property, used by `Object`
pub mod property;
/// The global 'RegExp' object /// The global 'RegExp' object
pub mod regexp; pub mod regexp;
/// The global `String` object /// The global `String` object
pub mod string; pub mod string;
/// Javascript values, utility methods and conversion between Javascript values and Rust values /// Javascript values, utility methods and conversion between Javascript values and Rust values
pub mod value; pub mod value;
// Property, used by `Object`
pub mod property;

2
src/lib/builtins/number.rs

@ -20,7 +20,7 @@ fn to_number(value: &Value) -> Value {
to_value(0) to_value(0)
} }
} }
ValueData::Function(_) | ValueData::Undefined => to_value(f64::NAN), ValueData::Function(_) | ValueData::Symbol(_) | ValueData::Undefined => to_value(f64::NAN),
ValueData::Integer(i) => to_value(f64::from(i)), ValueData::Integer(i) => to_value(f64::from(i)),
ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"), ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"),
ValueData::Null => to_value(0), ValueData::Null => to_value(0),

183
src/lib/builtins/object.rs

@ -30,7 +30,7 @@ pub struct Object {
/// Properties /// Properties
pub properties: Box<HashMap<String, Property>>, pub properties: Box<HashMap<String, Property>>,
/// Symbol Properties /// Symbol Properties
pub sym_properties: Box<HashMap<usize, Property>>, pub sym_properties: Box<HashMap<i32, Property>>,
/// Some rust object that stores internal state /// Some rust object that stores internal state
pub state: Option<Box<InternalStateCell>>, pub state: Option<Box<InternalStateCell>>,
} }
@ -38,18 +38,22 @@ pub struct Object {
impl Object { impl Object {
/// Return a new ObjectData struct, with `kind` set to Ordinary /// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn default() -> Self { pub fn default() -> Self {
Object { let mut object = Object {
kind: ObjectKind::Ordinary, kind: ObjectKind::Ordinary,
internal_slots: Box::new(HashMap::new()), internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()), properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()), sym_properties: Box::new(HashMap::new()),
state: None, state: None,
} };
object.set_internal_slot("extensible", to_value(true));
object
} }
/// ObjectCreate is used to specify the runtime creation of new ordinary objects /// ObjectCreate is used to specify the runtime creation of new ordinary objects
/// ///
/// https://tc39.es/ecma262/#sec-objectcreate /// https://tc39.es/ecma262/#sec-objectcreate
// TODO: proto should be a &Value here
pub fn create(proto: Value) -> Object { pub fn create(proto: Value) -> Object {
let mut obj = Object::default(); let mut obj = Object::default();
obj.internal_slots obj.internal_slots
@ -77,6 +81,13 @@ impl Object {
self.internal_slots.insert(name.to_string(), to_value(val)); self.internal_slots.insert(name.to_string(), to_value(val));
} }
/// Utility function to set a method on this object
/// The native function will live in the `properties` field of the Object
pub fn set_method(&mut self, name: &str, val: NativeFunctionData) {
self.properties
.insert(name.to_string(), Property::default().value(to_value(val)));
}
/// Return a new Boolean object whose [[BooleanData]] internal slot is set to argument. /// Return a new Boolean object whose [[BooleanData]] internal slot is set to argument.
fn from_boolean(argument: &Value) -> Self { fn from_boolean(argument: &Value) -> Self {
let mut obj = Object { let mut obj = Object {
@ -184,28 +195,62 @@ impl Object {
self.set_internal_slot("extensible", to_value(false)); self.set_internal_slot("extensible", to_value(false));
true true
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
/// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here. /// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here.
pub fn get_own_property(&self, prop: &Value) -> Property { pub fn get_own_property(&self, prop: &Value) -> Property {
debug_assert!(Property::is_property_key(prop)); debug_assert!(Property::is_property_key(prop));
match self.properties.get(&prop.to_string()) { // Prop could either be a String or Symbol
// If O does not have an own property with key P, return undefined. match *(*prop) {
// In this case we return a new empty Property ValueData::String(ref st) => {
None => Property::default(), match self.properties.get(st) {
Some(ref v) => { // If O does not have an own property with key P, return undefined.
let mut d = Property::default(); // In this case we return a new empty Property
if v.is_data_descriptor() { None => Property::default(),
d.value = v.value.clone(); Some(ref v) => {
d.writable = v.writable; let mut d = Property::default();
} else { if v.is_data_descriptor() {
debug_assert!(v.is_accessor_descriptor()); d.value = v.value.clone();
d.get = v.get.clone(); d.writable = v.writable;
d.set = v.set.clone(); } else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.enumerable = v.enumerable;
d.configurable = v.configurable;
d
}
}
}
ValueData::Symbol(ref sym) => {
let sym_id = sym
.borrow()
.get_internal_slot("SymbolData")
.to_string()
.parse::<i32>()
.expect("Could not get Symbol ID");
match self.sym_properties.get(&sym_id) {
// If O does not have an own property with key P, return undefined.
// In this case we return a new empty Property
None => Property::default(),
Some(ref v) => {
let mut d = Property::default();
if v.is_data_descriptor() {
d.value = v.value.clone();
d.writable = v.writable;
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.enumerable = v.enumerable;
d.configurable = v.configurable;
d
}
} }
d.enumerable = v.enumerable;
d.configurable = v.configurable;
d
} }
_ => Property::default(),
} }
} }
@ -240,20 +285,18 @@ impl Object {
if !extensible { if !extensible {
return false; return false;
} }
if desc.value.is_some() && desc.value.clone().unwrap().is_symbol() {
let mut p = Property::new(); let sym_id = desc
if desc.is_generic_descriptor() || desc.is_data_descriptor() { .value
p.value = Some(desc.value.clone().unwrap_or_default()); .clone()
p.writable = Some(desc.writable.unwrap_or_default()); .unwrap()
p.configurable = Some(desc.configurable.unwrap_or_default()); .to_string()
p.enumerable = Some(desc.enumerable.unwrap_or_default()); .parse::<i32>()
.expect("parsing failed");
self.sym_properties.insert(sym_id, desc);
} else { } else {
p.get = Some(desc.get.clone().unwrap_or_default()); self.properties.insert(property_key, desc);
p.set = Some(desc.set.clone().unwrap_or_default()); }
p.configurable = Some(desc.configurable.unwrap_or_default());
p.enumerable = Some(desc.enumerable.unwrap_or_default());
};
self.properties.insert(property_key, p);
return true; return true;
} }
// If every field is absent we don't need to set anything // If every field is absent we don't need to set anything
@ -262,13 +305,13 @@ impl Object {
} }
// 4 // 4
if current.configurable.unwrap_or(false) { if !current.configurable.unwrap_or(false) {
if desc.configurable.is_some() && desc.configurable.unwrap() { if desc.configurable.is_some() && desc.configurable.unwrap() {
return false; return false;
} }
if desc.enumerable.is_some() if desc.enumerable.is_some()
&& (desc.enumerable.as_ref().unwrap() == current.enumerable.as_ref().unwrap()) && (desc.enumerable.as_ref().unwrap() != current.enumerable.as_ref().unwrap())
{ {
return false; return false;
} }
@ -276,7 +319,6 @@ impl Object {
// 5 // 5
if desc.is_generic_descriptor() { if desc.is_generic_descriptor() {
// 6 // 6
} else if current.is_data_descriptor() != desc.is_data_descriptor() { } else if current.is_data_descriptor() != desc.is_data_descriptor() {
// a // a
@ -294,7 +336,20 @@ impl Object {
current.get = None; current.get = None;
current.set = None; current.set = None;
} }
self.properties.insert(property_key, current.clone());
if current.value.is_some() && current.value.clone().unwrap().is_symbol() {
let sym_id = current
.value
.clone()
.unwrap()
.to_string()
.parse::<i32>()
.expect("parsing failed");
self.sym_properties.insert(sym_id, current.clone());
} else {
self.properties
.insert(property_key.clone(), current.clone());
}
// 7 // 7
} else if current.is_data_descriptor() && desc.is_data_descriptor() { } else if current.is_data_descriptor() && desc.is_data_descriptor() {
// a // a
@ -342,7 +397,7 @@ impl Object {
return true; return true;
} }
// 9 // 9
Property::assign(&mut current, &desc); self.properties.insert(property_key, desc);
true true
} }
@ -400,6 +455,45 @@ impl Object {
// TODO!!!!! Call getter from here // TODO!!!!! Call getter from here
Gc::new(ValueData::Undefined) Gc::new(ValueData::Undefined)
} }
/// [[Set]]
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
pub fn set(&mut self, field: Value, val: Value) -> bool {
// [1]
debug_assert!(Property::is_property_key(&field));
// Fetch property key
let mut own_desc = self.get_own_property(&field);
// [2]
if own_desc.is_none() {
let parent = self.get_prototype_of();
if !parent.is_null() {
// TODO: come back to this
}
own_desc = Property::new()
.writable(true)
.enumerable(true)
.configurable(true);
}
// [3]
if own_desc.is_data_descriptor() {
if !own_desc.writable.unwrap() {
return false;
}
// Change value on the current descriptor
own_desc = own_desc.value(val);
return self.define_own_property(field.to_string(), own_desc);
}
// [4]
debug_assert!(own_desc.is_accessor_descriptor());
match own_desc.set {
None => false,
Some(_) => {
unimplemented!();
}
}
}
} }
#[derive(Trace, Finalize, Clone, Debug, Eq, PartialEq)] #[derive(Trace, Finalize, Clone, Debug, Eq, PartialEq)]
@ -462,16 +556,15 @@ pub fn has_own_prop(this: &Value, args: &[Value], _: &mut Interpreter) -> Result
} }
/// Create a new `Object` object /// Create a new `Object` object
pub fn create_constructor(global: &Value) -> Value { pub fn create_constructor(_: &Value) -> Value {
let object = to_value(make_object as NativeFunctionData); let object = to_value(make_object as NativeFunctionData);
let prototype = ValueData::new_obj(Some(global)); // Prototype chain ends here VV
prototype.set_field_slice( let mut prototype = Object::default();
"hasOwnProperty", prototype.set_method("hasOwnProperty", has_own_prop);
to_value(has_own_prop as NativeFunctionData), prototype.set_method("toString", to_string);
);
prototype.set_field_slice("toString", to_value(to_string as NativeFunctionData));
object.set_field_slice("length", to_value(1_i32)); object.set_field_slice("length", to_value(1_i32));
object.set_field_slice(PROTOTYPE, prototype); object.set_field_slice(PROTOTYPE, to_value(prototype));
object.set_field_slice( object.set_field_slice(
"setPrototypeOf", "setPrototypeOf",
to_value(set_proto_of as NativeFunctionData), to_value(set_proto_of as NativeFunctionData),

50
src/lib/builtins/property.rs

@ -1,5 +1,4 @@
use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, ValueData}; use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, ValueData};
use gc::Gc;
use gc_derive::{Finalize, Trace}; use gc_derive::{Finalize, Trace};
/// A Javascript Property AKA The Property Descriptor /// A Javascript Property AKA The Property Descriptor
@ -26,7 +25,7 @@ pub struct Property {
impl Property { impl Property {
/// Checks if the provided Value can be used as a property key. /// Checks if the provided Value can be used as a property key.
pub fn is_property_key(value: &Value) -> bool { pub fn is_property_key(value: &Value) -> bool {
value.is_string() // || value.is_symbol() // Uncomment this when we are handeling symbols. value.is_string() || value.is_symbol() // Uncomment this when we are handeling symbols.
} }
/// Make a new property with the given value /// Make a new property with the given value
@ -92,43 +91,22 @@ impl Property {
&& self.enumerable.is_none() && self.enumerable.is_none()
} }
// https://tc39.es/ecma262/#sec-isaccessordescriptor /// An accessor Property Descriptor is one that includes any fields named either [[Get]] or [[Set]].
/// <https://tc39.es/ecma262/#sec-isaccessordescriptor>
pub fn is_accessor_descriptor(&self) -> bool { pub fn is_accessor_descriptor(&self) -> bool {
self.get.is_some() && self.set.is_some() self.get.is_some() || self.set.is_some()
} }
// https://tc39.es/ecma262/#sec-isdatadescriptor /// A data Property Descriptor is one that includes any fields named either [[Value]] or [[Writable]].
/// https://tc39.es/ecma262/#sec-isdatadescriptor
pub fn is_data_descriptor(&self) -> bool { pub fn is_data_descriptor(&self) -> bool {
self.value.is_some() && self.writable.is_some() self.value.is_some() || self.writable.is_some()
} }
// https://tc39.es/ecma262/#sec-isgenericdescriptor /// https://tc39.es/ecma262/#sec-isgenericdescriptor
pub fn is_generic_descriptor(&self) -> bool { pub fn is_generic_descriptor(&self) -> bool {
!self.is_accessor_descriptor() && !self.is_data_descriptor() !self.is_accessor_descriptor() && !self.is_data_descriptor()
} }
/// This copies only present property fields from B to A
pub fn assign(a: &mut Property, b: &Property) {
if b.get.is_some() {
a.get = b.get.clone();
}
if b.set.is_some() {
a.set = b.set.clone();
}
if b.configurable.is_some() {
a.configurable = b.configurable;
}
if b.writable.is_some() {
a.writable = b.writable;
}
if b.enumerable.is_some() {
a.enumerable = b.enumerable;
}
}
} }
impl Default for Property { impl Default for Property {
@ -136,12 +114,12 @@ impl Default for Property {
/// https://tc39.es/ecma262/#table-default-attribute-values /// https://tc39.es/ecma262/#table-default-attribute-values
fn default() -> Self { fn default() -> Self {
Self { Self {
configurable: Some(false), configurable: None,
enumerable: Some(false), enumerable: None,
writable: Some(false), writable: None,
value: Some(Gc::new(ValueData::Undefined)), value: None,
get: Some(Gc::new(ValueData::Undefined)), get: None,
set: Some(Gc::new(ValueData::Undefined)), set: None,
} }
} }
} }

6
src/lib/builtins/regexp.rs

@ -189,7 +189,8 @@ fn _make_prop(getter: NativeFunctionData) -> Property {
/// Search for a match between this regex and a specified string /// Search for a match between this regex and a specified string
pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?; let arg_str = get_argument::<String>(args, 0)?;
let mut last_index = from_value::<usize>(this.get_field("lastIndex")).map_err(to_value)?; let mut last_index =
from_value::<usize>(this.get_field_slice("lastIndex")).map_err(to_value)?;
let result = this.with_internal_state_ref(|regex: &RegExp| { let result = this.with_internal_state_ref(|regex: &RegExp| {
let result = match regex.matcher.find_at(arg_str.as_str(), last_index) { let result = match regex.matcher.find_at(arg_str.as_str(), last_index) {
Some(m) => { Some(m) => {
@ -214,7 +215,8 @@ pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// Search for a match between this regex and a specified string /// Search for a match between this regex and a specified string
pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?; let arg_str = get_argument::<String>(args, 0)?;
let mut last_index = from_value::<usize>(this.get_field("lastIndex")).map_err(to_value)?; let mut last_index =
from_value::<usize>(this.get_field_slice("lastIndex")).map_err(to_value)?;
let result = this.with_internal_state_ref(|regex: &RegExp| { let result = this.with_internal_state_ref(|regex: &RegExp| {
let mut locations = regex.matcher.capture_locations(); let mut locations = regex.matcher.capture_locations();
let result = let result =

113
src/lib/builtins/symbol.rs

@ -0,0 +1,113 @@
use crate::{
builtins::{
object::{Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use gc::{Gc, GcCell};
use rand::random;
/// https://tc39.es/ecma262/#sec-symbol-description
/// Creates Symbol instances.
///
/// Symbol instances are ordinary objects that inherit properties from the Symbol prototype object.
/// Symbol instances have a [[SymbolData]] internal slot.
/// The [[SymbolData]] internal slot is the Symbol value represented by this Symbol object.
pub fn call_symbol(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// From an implementation and specificaition perspective Symbols are similar to Objects.
// They have internal slots to hold the SymbolData and Description, they also have methods and a prototype.
// So we start by creating an Object
// TODO: Set prototype to Symbol.prototype (by changing to Object::create(), use interpreter to get Symbol.prototype)
let mut sym_instance = Object::default();
sym_instance.kind = ObjectKind::Symbol;
// Set description which should either be undefined or a string
let desc_string = match args.get(0) {
Some(value) => to_value(value.to_string()),
None => Gc::new(ValueData::Undefined),
};
sym_instance.set_internal_slot("Description", desc_string);
sym_instance.set_internal_slot("SymbolData", to_value(random::<i32>()));
// Set __proto__ internal slot
let proto = ctx
.realm
.global_obj
.get_field_slice("Symbol")
.get_field_slice(PROTOTYPE);
sym_instance.set_internal_slot(INSTANCE_PROTOTYPE, proto);
Ok(Gc::new(ValueData::Symbol(GcCell::new(sym_instance))))
}
/// <https://tc39.es/ecma262/#sec-symbol.prototype.tostring>
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let s: Value = this.get_internal_slot("Description");
let full_string = format!(r#"Symbol({})"#, s.to_string());
Ok(to_value(full_string))
}
/// <https://tc39.es/ecma262/#sec-symbol-constructor>
pub fn create_constructor(global: &Value) -> Value {
// Create Symbol constructor (or function in Symbol's case)
let mut symbol_constructor = Object::default();
symbol_constructor.set_internal_method("call", call_symbol);
// Create prototype
let mut symbol_prototype = Object::default();
// Symbol.prototype[[Prototype]] points to Object.prototype
// Symbol Constructor -> Symbol Prototype -> Object Prototype
let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);
symbol_prototype.set_internal_slot(INSTANCE_PROTOTYPE, object_prototype.clone());
symbol_prototype.set_method("toString", to_string);
let symbol_prototype_val = to_value(symbol_prototype);
let symbol_constructor_value = to_value(symbol_constructor);
symbol_prototype_val.set_field_slice("construcotor", symbol_constructor_value.clone());
symbol_constructor_value.set_field_slice(PROTOTYPE, symbol_prototype_val.clone());
symbol_constructor_value
}
#[cfg(test)]
mod tests {
use super::*;
use crate::exec::Executor;
use crate::realm::Realm;
use crate::{forward, forward_val};
#[test]
fn check_symbol_constructor_is_function() {
let global: Gc<ValueData> = ValueData::new_obj(None);
let symbol_constructor = create_constructor(&global);
assert_eq!(symbol_constructor.is_function(), true);
}
#[test]
fn call_symbol_and_check_return_type() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
var sym = Symbol();
"#;
forward(&mut engine, init);
let sym = forward_val(&mut engine, "sym").unwrap();
assert_eq!(sym.is_symbol(), true);
}
#[test]
fn print_symbol_expect_description() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
var sym = Symbol("Hello");
"#;
forward(&mut engine, init);
let sym = forward_val(&mut engine, "sym.toString()").unwrap();
assert_eq!(sym.to_string(), "Symbol(Hello)");
}
}

112
src/lib/builtins/value.rs

@ -43,6 +43,8 @@ pub enum ValueData {
Object(GcCell<Object>), Object(GcCell<Object>),
/// `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` - 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(Box<GcCell<Function>>), Function(Box<GcCell<Function>>),
/// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots
Symbol(GcCell<Object>),
} }
impl ValueData { impl ValueData {
@ -90,6 +92,14 @@ impl ValueData {
} }
} }
/// Returns true if the value is a symbol
pub fn is_symbol(&self) -> bool {
match *self {
ValueData::Symbol(_) => true,
_ => false,
}
}
/// Returns true if the value is a function /// Returns true if the value is a function
pub fn is_function(&self) -> bool { pub fn is_function(&self) -> bool {
match *self { match *self {
@ -168,7 +178,10 @@ impl ValueData {
/// Converts the value into a 64-bit floating point number /// Converts the value into a 64-bit floating point number
pub fn to_num(&self) -> f64 { pub fn to_num(&self) -> f64 {
match *self { match *self {
ValueData::Object(_) | ValueData::Undefined | ValueData::Function(_) => NAN, ValueData::Object(_)
| ValueData::Symbol(_)
| ValueData::Undefined
| ValueData::Function(_) => NAN,
ValueData::String(ref str) => match FromStr::from_str(str) { ValueData::String(ref str) => match FromStr::from_str(str) {
Ok(num) => num, Ok(num) => num,
Err(_) => NAN, Err(_) => NAN,
@ -185,6 +198,7 @@ impl ValueData {
match *self { match *self {
ValueData::Object(_) ValueData::Object(_)
| ValueData::Undefined | ValueData::Undefined
| ValueData::Symbol(_)
| ValueData::Null | ValueData::Null
| ValueData::Boolean(false) | ValueData::Boolean(false)
| ValueData::Function(_) => 0, | ValueData::Function(_) => 0,
@ -235,6 +249,10 @@ impl ValueData {
Function::NativeFunc(ref func) => func.object.clone(), Function::NativeFunc(ref func) => func.object.clone(),
Function::RegularFunc(ref func) => func.object.clone(), Function::RegularFunc(ref func) => func.object.clone(),
}, },
ValueData::Symbol(ref obj) => {
let hash = obj.clone();
hash.into_inner()
}
_ => return None, _ => return None,
}; };
@ -287,6 +305,10 @@ impl ValueData {
let hash = obj.clone(); let hash = obj.clone();
hash.into_inner() hash.into_inner()
} }
ValueData::Symbol(ref obj) => {
let hash = obj.clone();
hash.into_inner()
}
_ => return Gc::new(ValueData::Undefined), _ => return Gc::new(ValueData::Undefined),
}; };
@ -299,28 +321,35 @@ impl ValueData {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
/// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]]
/// TODO: this function should use the get Value if its set /// TODO: this function should use the get Value if its set
pub fn get_field(&self, field: &str) -> Value { pub fn get_field(&self, field: Value) -> Value {
match self.get_prop(field) { match *field {
Some(prop) => { // Our field will either be a String or a Symbol
// If the Property has [[Get]] set to a function, we should run that and return the Value ValueData::String(ref s) => {
let prop_getter = match prop.get { match self.get_prop(s) {
Some(_) => None, Some(prop) => {
None => None, // If the Property has [[Get]] set to a function, we should run that and return the Value
}; let prop_getter = match prop.get {
Some(_) => None,
// If the getter is populated, use that. If not use [[Value]] instead None => None,
match prop_getter { };
Some(val) => val,
None => { // If the getter is populated, use that. If not use [[Value]] instead
let val = prop match prop_getter {
.value Some(val) => val,
.as_ref() None => {
.expect("Could not get property as reference"); let val = prop
val.clone() .value
.as_ref()
.expect("Could not get property as reference");
val.clone()
}
}
} }
None => Gc::new(ValueData::Undefined),
} }
} }
None => Gc::new(ValueData::Undefined), ValueData::Symbol(_) => unimplemented!(),
_ => Gc::new(ValueData::Undefined),
} }
} }
@ -400,15 +429,19 @@ impl ValueData {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// 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(&self, field: &str) -> Value { pub fn get_field_slice(&self, field: &str) -> Value {
self.get_field(field) // get_field used to accept strings, but now Symbols accept it needs to accept a value
// So this function will now need to Box strings back into values (at least for now)
let f = Gc::new(ValueData::String(field.to_string()));
self.get_field(f)
} }
/// Set the field in the value /// Set the field in the value
pub fn set_field(&self, field: String, val: Value) -> Value { /// Field could be a Symbol, so we need to accept a Value (not a string)
pub fn set_field(&self, field: Value, val: Value) -> Value {
match *self { match *self {
ValueData::Object(ref obj) => { ValueData::Object(ref obj) => {
if obj.borrow().kind == ObjectKind::Array { if obj.borrow().kind == ObjectKind::Array {
if let Ok(num) = field.parse::<usize>() { if let Ok(num) = field.to_string().parse::<usize>() {
if num > 0 { if num > 0 {
let len: i32 = from_value(self.get_field_slice("length")) let len: i32 = from_value(self.get_field_slice("length"))
.expect("Could not convert argument to i32"); .expect("Could not convert argument to i32");
@ -418,20 +451,25 @@ impl ValueData {
} }
} }
} }
obj.borrow_mut()
.properties // Symbols get saved into a different bucket to general properties
.insert(field, Property::default().value(val.clone())); if field.is_symbol() {
obj.borrow_mut().set(field.clone(), val.clone());
} else {
obj.borrow_mut()
.set(to_value(field.to_string()), val.clone());
}
} }
ValueData::Function(ref func) => { ValueData::Function(ref func) => {
match *func.borrow_mut().deref_mut() { match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => f Function::NativeFunc(ref mut f) => f
.object .object
.properties .properties
.insert(field, Property::default().value(val.clone())), .insert(field.to_string(), Property::default().value(val.clone())),
Function::RegularFunc(ref mut f) => f Function::RegularFunc(ref mut f) => f
.object .object
.properties .properties
.insert(field, Property::default().value(val.clone())), .insert(field.to_string(), Property::default().value(val.clone())),
}; };
} }
_ => (), _ => (),
@ -441,7 +479,10 @@ impl ValueData {
/// Set the field in the value /// Set the field in the value
pub fn set_field_slice<'a>(&self, field: &'a str, val: Value) -> Value { pub fn set_field_slice<'a>(&self, field: &'a str, val: Value) -> Value {
self.set_field(field.to_string(), val) // set_field used to accept strings, but now Symbols accept it needs to accept a value
// So this function will now need to Box strings back into values (at least for now)
let f = Gc::new(ValueData::String(field.to_string()));
self.set_field(f, val)
} }
/// Set the private field in the value /// Set the private field in the value
@ -536,7 +577,10 @@ impl ValueData {
pub fn to_json(&self) -> JSONValue { pub fn to_json(&self) -> JSONValue {
match *self { match *self {
ValueData::Null | ValueData::Undefined | ValueData::Function(_) => JSONValue::Null, ValueData::Null
| ValueData::Symbol(_)
| ValueData::Undefined
| ValueData::Function(_) => JSONValue::Null,
ValueData::Boolean(b) => JSONValue::Bool(b), ValueData::Boolean(b) => JSONValue::Bool(b),
ValueData::Object(ref obj) => { ValueData::Object(ref obj) => {
let mut new_obj = Map::new(); let mut new_obj = Map::new();
@ -562,6 +606,7 @@ impl ValueData {
ValueData::Number(_) | ValueData::Integer(_) => "number", ValueData::Number(_) | ValueData::Integer(_) => "number",
ValueData::String(_) => "string", ValueData::String(_) => "string",
ValueData::Boolean(_) => "boolean", ValueData::Boolean(_) => "boolean",
ValueData::Symbol(_) => "symbol",
ValueData::Null => "null", ValueData::Null => "null",
ValueData::Undefined => "undefined", ValueData::Undefined => "undefined",
ValueData::Function(_) => "function", ValueData::Function(_) => "function",
@ -592,6 +637,11 @@ impl Display for ValueData {
ValueData::Null => write!(f, "null"), ValueData::Null => write!(f, "null"),
ValueData::Undefined => write!(f, "undefined"), ValueData::Undefined => write!(f, "undefined"),
ValueData::Boolean(v) => write!(f, "{}", v), ValueData::Boolean(v) => write!(f, "{}", v),
ValueData::Symbol(ref v) => match *v.borrow().get_internal_slot("Description") {
// If a description exists use it
ValueData::String(ref v) => write!(f, "{}", format!("Symbol({})", v)),
_ => write!(f, "Symbol()"),
},
ValueData::String(ref v) => write!(f, "{}", v), ValueData::String(ref v) => write!(f, "{}", v),
ValueData::Number(v) => write!( ValueData::Number(v) => write!(
f, f,
@ -842,7 +892,7 @@ impl<T: FromValue> FromValue for Vec<T> {
let len = v.get_field_slice("length").to_int(); let len = v.get_field_slice("length").to_int();
let mut vec = Self::with_capacity(len as usize); let mut vec = Self::with_capacity(len as usize);
for i in 0..len { for i in 0..len {
vec.push(from_value(v.get_field(&i.to_string()))?) vec.push(from_value(v.get_field_slice(&i.to_string()))?)
} }
Ok(vec) Ok(vec)
} }

2
src/lib/environment/object_environment_record.rs

@ -72,7 +72,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn get_binding_value(&self, name: &str, strict: bool) -> Value { fn get_binding_value(&self, name: &str, strict: bool) -> Value {
if self.bindings.has_field(name) { if self.bindings.has_field(name) {
self.bindings.get_field(name) self.bindings.get_field_slice(name)
} else { } else {
if strict { if strict {
// TODO: throw error here // TODO: throw error here

35
src/lib/exec.rs

@ -34,7 +34,7 @@ pub trait Executor {
pub struct Interpreter { pub struct Interpreter {
is_return: bool, is_return: bool,
/// realm holds both the global object and the environment /// realm holds both the global object and the environment
realm: Realm, pub realm: Realm,
} }
fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
@ -105,28 +105,30 @@ impl Executor for Interpreter {
} }
ExprDef::GetConstField(ref obj, ref field) => { ExprDef::GetConstField(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
Ok(val_obj.borrow().get_field(field)) Ok(val_obj.borrow().get_field_slice(field))
} }
ExprDef::GetField(ref obj, ref field) => { ExprDef::GetField(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
let val_field = self.run(field)?; let val_field = self.run(field)?;
Ok(val_obj.borrow().get_field(&val_field.borrow().to_string())) Ok(val_obj
.borrow()
.get_field_slice(&val_field.borrow().to_string()))
} }
ExprDef::Call(ref callee, ref args) => { ExprDef::Call(ref callee, ref args) => {
let (this, func) = match callee.def { let (this, func) = match callee.def {
ExprDef::GetConstField(ref obj, ref field) => { ExprDef::GetConstField(ref obj, ref field) => {
let mut obj = self.run(obj)?; let mut obj = self.run(obj)?;
if obj.get_type() != "object" { if obj.get_type() != "object" || obj.get_type() != "symbol" {
obj = self.to_object(&obj).expect("failed to convert to object"); obj = self.to_object(&obj).expect("failed to convert to object");
} }
(obj.clone(), obj.borrow().get_field(field)) (obj.clone(), obj.borrow().get_field_slice(field))
} }
ExprDef::GetField(ref obj, ref field) => { ExprDef::GetField(ref obj, ref field) => {
let obj = self.run(obj)?; let obj = self.run(obj)?;
let field = self.run(field)?; let field = self.run(field)?;
( (
obj.clone(), obj.clone(),
obj.borrow().get_field(&field.borrow().to_string()), obj.borrow().get_field_slice(&field.borrow().to_string()),
) )
} }
_ => (self.realm.global_obj.clone(), self.run(&callee.clone())?), // 'this' binding should come from the function's self-contained environment _ => (self.realm.global_obj.clone(), self.run(&callee.clone())?), // 'this' binding should come from the function's self-contained environment
@ -199,7 +201,7 @@ impl Executor for Interpreter {
.expect("Could not get the global object"); .expect("Could not get the global object");
let obj = ValueData::new_obj(Some(global_val)); let obj = ValueData::new_obj(Some(global_val));
for (key, val) in map.iter() { for (key, val) in map.iter() {
obj.borrow().set_field(key.clone(), self.run(val)?); obj.borrow().set_field_slice(&key.clone(), self.run(val)?);
} }
Ok(obj) Ok(obj)
} }
@ -320,10 +322,12 @@ impl Executor for Interpreter {
} }
ExprDef::GetConstField(ref obj, ref field) => { ExprDef::GetConstField(ref obj, ref field) => {
let v_r_a = self.run(obj)?; let v_r_a = self.run(obj)?;
let v_a = (*v_r_a.borrow().get_field(field)).clone(); let v_a = (*v_r_a.borrow().get_field_slice(field)).clone();
let v_b = (*self.run(b)?).clone(); let v_b = (*self.run(b)?).clone();
let value = exec_assign_op(op, v_a, v_b.clone()); let value = exec_assign_op(op, v_a, v_b.clone());
v_r_a.borrow().set_field(field.clone(), value.clone()); v_r_a
.borrow()
.set_field_slice(&field.clone(), value.clone());
Ok(value) Ok(value)
} }
_ => Ok(Gc::new(ValueData::Undefined)), _ => Ok(Gc::new(ValueData::Undefined)),
@ -409,14 +413,14 @@ impl Executor for Interpreter {
} }
ExprDef::GetConstField(ref obj, ref field) => { ExprDef::GetConstField(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
val_obj.borrow().set_field(field.clone(), val.clone()); val_obj
.borrow()
.set_field_slice(&field.clone(), val.clone());
} }
ExprDef::GetField(ref obj, ref field) => { ExprDef::GetField(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
let val_field = self.run(field)?; let val_field = self.run(field)?;
val_obj val_obj.borrow().set_field(val_field, val.clone());
.borrow()
.set_field(val_field.to_string(), val.clone());
} }
_ => (), _ => (),
} }
@ -470,6 +474,7 @@ impl Executor for Interpreter {
let val = self.run(val_e)?; let val = self.run(val_e)?;
Ok(to_value(match *val { Ok(to_value(match *val {
ValueData::Undefined => "undefined", ValueData::Undefined => "undefined",
ValueData::Symbol(_) => "symbol",
ValueData::Null | ValueData::Object(_) => "object", ValueData::Null | ValueData::Object(_) => "object",
ValueData::Boolean(_) => "boolean", ValueData::Boolean(_) => "boolean",
ValueData::Number(_) | ValueData::Integer(_) => "number", ValueData::Number(_) | ValueData::Integer(_) => "number",
@ -663,7 +668,7 @@ impl Interpreter {
string_obj.set_internal_slot("StringData", value.clone()); string_obj.set_internal_slot("StringData", value.clone());
Ok(string_obj) Ok(string_obj)
} }
ValueData::Object(_) => Ok(value.clone()), ValueData::Object(_) | ValueData::Symbol(_) => Ok(value.clone()),
} }
} }
@ -701,7 +706,7 @@ impl Interpreter {
self.to_string(&prim_value) self.to_string(&prim_value)
.to_string() .to_string()
.parse::<f64>() .parse::<f64>()
.unwrap() .expect("cannot parse valur to x64")
} }
_ => { _ => {
// TODO: Make undefined? // TODO: Make undefined?

3
src/lib/realm.rs

@ -5,7 +5,7 @@
//!A realm is represented in this implementation as a Realm struct with the fields specified from the spec //!A realm is represented in this implementation as a Realm struct with the fields specified from the spec
use crate::{ use crate::{
builtins::{ builtins::{
array, boolean, console, function, json, math, number, object, regexp, string, array, boolean, console, function, json, math, number, object, regexp, string, symbol,
value::{Value, ValueData}, value::{Value, ValueData},
}, },
environment::{ environment::{
@ -62,6 +62,7 @@ impl Realm {
global.set_field_slice("Object", object::create_constructor(global)); global.set_field_slice("Object", object::create_constructor(global));
global.set_field_slice("RegExp", regexp::create_constructor(global)); global.set_field_slice("RegExp", regexp::create_constructor(global));
global.set_field_slice("String", string::create_constructor(global)); global.set_field_slice("String", string::create_constructor(global));
global.set_field_slice("Symbol", symbol::create_constructor(global));
global.set_field_slice("console", console::create_constructor(global)); global.set_field_slice("console", console::create_constructor(global));
} }
} }

1
tests/js/test.js

@ -0,0 +1 @@
// Test your JS here
Loading…
Cancel
Save