From 0076f0829a0a2f44d574dd829bec04ca16e1b587 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 19 Oct 2019 16:39:44 +0300 Subject: [PATCH 01/27] Added the initial re-implementation of Display for Objects --- src/lib/js/value.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 711895f8b72..822ff529578 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -587,7 +587,23 @@ impl Display for ValueData { _ => v.to_string(), } ), - ValueData::Object(_) => write!(f, "{{}}"), + ValueData::Object(ref v) => { + write!(f, "{}", "{ ")?; + + let properties = v.borrow() + .deref() + .properties.iter() + .map(|(key, val)| { + let v = val.value.as_ref().unwrap(); + format!("{}: {}", key, v) + }) + .collect::>() + .join(", "); + + write!(f, "{}", properties)?; + + write!(f, "{}", " }") + }, ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), From 8ce28e6ac59372c3aee2d0bddd3f86ee452dd4ff Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 19 Oct 2019 17:32:00 +0300 Subject: [PATCH 02/27] Added internal object slots printing --- src/lib/js/value.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 822ff529578..f16257d2009 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -576,7 +576,7 @@ impl Display for ValueData { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), ValueData::Boolean(v) => write!(f, "{}", v), - ValueData::String(ref v) => write!(f, "{}", v), + ValueData::String(ref v) => write!(f, "'{}'", v), ValueData::Number(v) => write!( f, "{}", @@ -590,17 +590,31 @@ impl Display for ValueData { ValueData::Object(ref v) => { write!(f, "{}", "{ ")?; + // TODO: Find a better way to do this + let properties = v.borrow() - .deref() .properties.iter() - .map(|(key, val)| { - let v = val.value.as_ref().unwrap(); - format!("{}: {}", key, v) - }) + .map(|(key, val)| + (key.clone(), val.value.clone().unwrap().to_string()) + ) + .collect::>(); + + let internal_slots = v.borrow() + .internal_slots.iter() + .filter(|(key, _)| *key != "__proto__") + .map(|(key, val)| + (key.clone(), val.to_string()) + ) + .collect::>(); + + let result = [&properties[..], &internal_slots[..]] + .concat() + .iter() + .map(|(key, val)| format!("{}: {}", key, val)) .collect::>() .join(", "); - write!(f, "{}", properties)?; + write!(f, "{}", result)?; write!(f, "{}", " }") }, From 72dca2ffa07dfc33ceba5932c07a58d3f80ecef3 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 19 Oct 2019 17:42:42 +0300 Subject: [PATCH 03/27] Used INSTANCE_PROTOTYPE instead of "__proto__" --- src/lib/js/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index f16257d2009..8cca746e835 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -601,7 +601,7 @@ impl Display for ValueData { let internal_slots = v.borrow() .internal_slots.iter() - .filter(|(key, _)| *key != "__proto__") + .filter(|(key, _)| *key != INSTANCE_PROTOTYPE) .map(|(key, val)| (key.clone(), val.to_string()) ) From f20fb6c334fd5efa6899f44e3e93ac3704db59a2 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 19 Oct 2019 18:04:44 +0300 Subject: [PATCH 04/27] Updated TODO comment --- src/lib/js/value.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 8cca746e835..3aa32724dd5 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -590,8 +590,7 @@ impl Display for ValueData { ValueData::Object(ref v) => { write!(f, "{}", "{ ")?; - // TODO: Find a better way to do this - + // TODO: Find a more optimised way to do this let properties = v.borrow() .properties.iter() .map(|(key, val)| From 85024eb1a3a89abe6a545d690b50716f3017be2f Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 19 Oct 2019 18:06:08 +0300 Subject: [PATCH 05/27] rustfmt --- src/lib/js/value.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 3aa32724dd5..8c7d57bac69 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -591,19 +591,19 @@ impl Display for ValueData { write!(f, "{}", "{ ")?; // TODO: Find a more optimised way to do this - let properties = v.borrow() - .properties.iter() - .map(|(key, val)| - (key.clone(), val.value.clone().unwrap().to_string()) - ) + let properties = v + .borrow() + .properties + .iter() + .map(|(key, val)| (key.clone(), val.value.clone().unwrap().to_string())) .collect::>(); - let internal_slots = v.borrow() - .internal_slots.iter() + let internal_slots = v + .borrow() + .internal_slots + .iter() .filter(|(key, _)| *key != INSTANCE_PROTOTYPE) - .map(|(key, val)| - (key.clone(), val.to_string()) - ) + .map(|(key, val)| (key.clone(), val.to_string())) .collect::>(); let result = [&properties[..], &internal_slots[..]] @@ -616,7 +616,7 @@ impl Display for ValueData { write!(f, "{}", result)?; write!(f, "{}", " }") - }, + } ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), From 5788f6e76895ff7b33ae1461ffac5b6dddca9c00 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 19 Oct 2019 18:19:29 +0300 Subject: [PATCH 06/27] Fixed clippy and unit tests --- src/lib/js/value.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 8c7d57bac69..6acdaddfdb5 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -576,7 +576,7 @@ impl Display for ValueData { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), ValueData::Boolean(v) => write!(f, "{}", v), - ValueData::String(ref v) => write!(f, "'{}'", v), + ValueData::String(ref v) => write!(f, "{}", v), ValueData::Number(v) => write!( f, "{}", @@ -588,7 +588,7 @@ impl Display for ValueData { } ), ValueData::Object(ref v) => { - write!(f, "{}", "{ ")?; + write!(f, "{{ ")?; // TODO: Find a more optimised way to do this let properties = v @@ -615,7 +615,7 @@ impl Display for ValueData { write!(f, "{}", result)?; - write!(f, "{}", " }") + write!(f, " }}") } ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { From 995ab5c60a105023f0796f1483cdaf75a0867796 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 20 Oct 2019 14:39:38 +0300 Subject: [PATCH 07/27] WIP: Added a working prototype for detecting cycles --- src/lib/js/value.rs | 94 +++++++++++++++++++++++++++++++-------------- tests/js/test.js | 11 +++--- 2 files changed, 71 insertions(+), 34 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 6acdaddfdb5..f33bc97d604 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -8,6 +8,7 @@ use gc_derive::{Finalize, Trace}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use std::{ any::Any, + collections::HashSet, f64::NAN, fmt::{self, Display}, ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Not, Rem, Shl, Shr, Sub}, @@ -572,7 +573,7 @@ impl Default for ValueData { impl Display for ValueData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { + match self { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), ValueData::Boolean(v) => write!(f, "{}", v), @@ -587,35 +588,70 @@ impl Display for ValueData { _ => v.to_string(), } ), - ValueData::Object(ref v) => { - write!(f, "{{ ")?; + v @ ValueData::Object(_) => { + fn address_of(t: &T) -> usize { + let my_ptr: *const T = t; + my_ptr as usize + } - // TODO: Find a more optimised way to do this - let properties = v - .borrow() - .properties - .iter() - .map(|(key, val)| (key.clone(), val.value.clone().unwrap().to_string())) - .collect::>(); - - let internal_slots = v - .borrow() - .internal_slots - .iter() - .filter(|(key, _)| *key != INSTANCE_PROTOTYPE) - .map(|(key, val)| (key.clone(), val.to_string())) - .collect::>(); - - let result = [&properties[..], &internal_slots[..]] - .concat() - .iter() - .map(|(key, val)| format!("{}: {}", key, val)) - .collect::>() - .join(", "); - - write!(f, "{}", result)?; - - write!(f, " }}") + let mut hs = HashSet::new(); + // let mut indent: usize = 0; + + // let mut pd = |data: &Value| { + // match *data.deref() { + // ValueData::Object(ref d) => { //String::from("{{ ... }}"), + // let addr = address_of(d); + + // if hs.contains(&addr) { + // "[Cycle]" + // } else { + // hs.insert(addr); + // "obj" + // } + // } + // _ => "asd" + // } + // }; + + fn display(data: &ValueData, f: &mut fmt::Formatter, hs: &mut HashSet, indent: usize) -> Result<(), std::fmt::Error> { + // let display= |data: &ValueData| { + match *data { + ValueData::Object(ref v) => { + let addr = address_of(v.borrow().deref()); + + if hs.contains(&addr) { + write!(f, "[Cycle]") + } else { + writeln!(f, "{{")?; + + hs.insert(addr); + let indentation = String::from_utf8(vec![b' '; indent]).expect("asd"); + + for (key, val) in v.borrow().properties.iter() { + // v + // .borrow() + // .properties + // .iter() + // .map(|(key, val)| { + write!(f, "{}{}: ", indentation, key)?; + + let v = &val.value.as_ref().expect("eee"); + + display(v, f, hs, indent.wrapping_add(2))?; + // (key.clone(), String::from(pd())) + writeln!(f, ",")?; + // }); + } + + hs.remove(&addr); + writeln!(f, "}}") + } + } + _ => { writeln!(f, "{}", data) } + } + } + + display(&v, f, &mut hs, 0) } ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { diff --git a/tests/js/test.js b/tests/js/test.js index ae107b0f1eb..f30d6ad843e 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -1,6 +1,7 @@ -/// -// Use this file to test your changes to boa. -/// +const o1 = {}; -let a = Boolean(0); -typeof a; +const o2 = { ref: o1 }; + +o1.ref = o2; + +o1; \ No newline at end of file From 5ecadf1601f1fd9ff1ea5b997376729096e07145 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 20 Oct 2019 15:17:25 +0300 Subject: [PATCH 08/27] Refactored the object printing logic --- src/lib/js/value.rs | 103 +++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index f33bc97d604..6c285157524 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -589,69 +589,74 @@ impl Display for ValueData { } ), v @ ValueData::Object(_) => { + // A simple helper for getting the address of a value + // TODO: Find a more general place for this, as it can be used in other situations as well fn address_of(t: &T) -> usize { let my_ptr: *const T = t; my_ptr as usize } - let mut hs = HashSet::new(); - // let mut indent: usize = 0; - - // let mut pd = |data: &Value| { - // match *data.deref() { - // ValueData::Object(ref d) => { //String::from("{{ ... }}"), - // let addr = address_of(d); - - // if hs.contains(&addr) { - // "[Cycle]" - // } else { - // hs.insert(addr); - // "obj" - // } - // } - // _ => "asd" - // } - // }; - - fn display(data: &ValueData, f: &mut fmt::Formatter, hs: &mut HashSet, indent: usize) -> Result<(), std::fmt::Error> { - // let display= |data: &ValueData| { + // We keep track of which objects we have encountered by keeping their + // in-memory address in this set + let mut encounters = HashSet::new(); + + fn display_obj( + data: &ValueData, + encounters: &mut HashSet, + indent: usize, + ) -> String { match *data { ValueData::Object(ref v) => { + // The in-memory address of the current object let addr = address_of(v.borrow().deref()); - if hs.contains(&addr) { - write!(f, "[Cycle]") - } else { - writeln!(f, "{{")?; - - hs.insert(addr); - let indentation = String::from_utf8(vec![b' '; indent]).expect("asd"); - - for (key, val) in v.borrow().properties.iter() { - // v - // .borrow() - // .properties - // .iter() - // .map(|(key, val)| { - write!(f, "{}{}: ", indentation, key)?; - - let v = &val.value.as_ref().expect("eee"); - - display(v, f, hs, indent.wrapping_add(2))?; - // (key.clone(), String::from(pd())) - writeln!(f, ",")?; - // }); - } - - hs.remove(&addr); - writeln!(f, "}}") + // We need not continue if this object has already been + // printed up the current chain + if encounters.contains(&addr) { + return String::from("'[Cycle]'"); } + + // Mark the current object as encountered + encounters.insert(addr); + + // Each time we print a nested object we need to increment the + // indentation size in order to pretty print + let indentation = String::from_utf8(vec![b' '; indent]) + .expect("Could not create indentation string"); + + let result = v + .borrow() + .properties + .iter() + .map(|(key, val)| { + let v = &val + .value + .as_ref() + .expect("Could not get the property's value"); + + format!( + "{}{}: {}", + indentation, + key, + display_obj(v, encounters, indent.wrapping_add(4)) + ) + }) + .collect::>() + .join(",\n"); + + // If the current object is referenced in a different branch, + // it will not cause an infinte printing loop, so it is safe to be printed again + encounters.remove(&addr); + + format!("{{\n{}\n}}", result) } - _ => { writeln!(f, "{}", data) } + + // Every other type of data is printed as is + _ => format!("{}", data), } } - display(&v, f, &mut hs, 0) + write!(f, "{}", display_obj(&v, &mut encounters, 0)) } ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { From fa73b84f461ed70b11f406e4f142f747a9fdc297 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 20 Oct 2019 15:20:12 +0300 Subject: [PATCH 09/27] Reverted test.js --- tests/js/test.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/js/test.js b/tests/js/test.js index f30d6ad843e..ae107b0f1eb 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -1,7 +1,6 @@ -const o1 = {}; +/// +// Use this file to test your changes to boa. +/// -const o2 = { ref: o1 }; - -o1.ref = o2; - -o1; \ No newline at end of file +let a = Boolean(0); +typeof a; From 70f7d0a458c6ab79976d2e86b48aa822460e9484 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 20 Oct 2019 20:46:34 +0300 Subject: [PATCH 10/27] Fixed print identation --- src/lib/js/value.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 6c285157524..4a0363bc50c 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -648,7 +648,10 @@ impl Display for ValueData { // it will not cause an infinte printing loop, so it is safe to be printed again encounters.remove(&addr); - format!("{{\n{}\n}}", result) + let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) + .expect("Could not create the closing brace's indentation string"); + + format!("{{\n{}\n{}}}", result, closing_indent) } // Every other type of data is printed as is @@ -656,7 +659,7 @@ impl Display for ValueData { } } - write!(f, "{}", display_obj(&v, &mut encounters, 0)) + write!(f, "{}", display_obj(&v, &mut encounters, 4)) } ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { From 394cc64f2a1360f5c0c6edcf9bfdeb04d80c706d Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Mon, 21 Oct 2019 06:53:03 +0300 Subject: [PATCH 11/27] rustfmt --- src/lib/js/value.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 4a0363bc50c..0be30249756 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -648,8 +648,10 @@ impl Display for ValueData { // it will not cause an infinte printing loop, so it is safe to be printed again encounters.remove(&addr); - let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) - .expect("Could not create the closing brace's indentation string"); + let closing_indent = + String::from_utf8(vec![b' '; indent.wrapping_sub(4)]).expect( + "Could not create the closing brace's indentation string", + ); format!("{{\n{}\n{}}}", result, closing_indent) } From 05a274830432db678583b3e2aa403b02cde6351a Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Mon, 21 Oct 2019 18:20:28 +0300 Subject: [PATCH 12/27] Added printing for the internal_slots --- src/lib/js/value.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 0be30249756..158267cf322 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -624,7 +624,7 @@ impl Display for ValueData { let indentation = String::from_utf8(vec![b' '; indent]) .expect("Could not create indentation string"); - let result = v + let properties = v .borrow() .properties .iter() @@ -644,6 +644,21 @@ impl Display for ValueData { .collect::>() .join(",\n"); + let internal_slots = v + .borrow() + .internal_slots + .iter() + .map(|(key, val)| { + format!( + "{}{}: {}", + indentation, + key, + display_obj(&val, encounters, indent.wrapping_add(4)) + ) + }) + .collect::>() + .join(",\n"); + // If the current object is referenced in a different branch, // it will not cause an infinte printing loop, so it is safe to be printed again encounters.remove(&addr); @@ -653,7 +668,10 @@ impl Display for ValueData { "Could not create the closing brace's indentation string", ); - format!("{{\n{}\n{}}}", result, closing_indent) + format!( + "{{\n{}\n{}\n{}}}", + properties, internal_slots, closing_indent + ) } // Every other type of data is printed as is From b9698e43d2665fe1a6c6a9b103ee5bc49efb5822 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Tue, 22 Oct 2019 20:53:09 +0300 Subject: [PATCH 13/27] Fixed missing comma between internal slots & props when printing --- src/lib/js/value.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index 158267cf322..b2964400945 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -613,7 +613,7 @@ impl Display for ValueData { // We need not continue if this object has already been // printed up the current chain if encounters.contains(&addr) { - return String::from("'[Cycle]'"); + return String::from("[Cycle]"); } // Mark the current object as encountered @@ -641,8 +641,7 @@ impl Display for ValueData { display_obj(v, encounters, indent.wrapping_add(4)) ) }) - .collect::>() - .join(",\n"); + .collect::>(); let internal_slots = v .borrow() @@ -656,6 +655,12 @@ impl Display for ValueData { display_obj(&val, encounters, indent.wrapping_add(4)) ) }) + .collect::>(); + + let result = [&properties[..], &internal_slots[..]] + .concat() + .iter() + .map(|v| String::from(v)) .collect::>() .join(",\n"); @@ -668,10 +673,7 @@ impl Display for ValueData { "Could not create the closing brace's indentation string", ); - format!( - "{{\n{}\n{}\n{}}}", - properties, internal_slots, closing_indent - ) + format!("{{\n{}\n{}}}", result, closing_indent) } // Every other type of data is printed as is From 3e0b399949efb4798af65f7ce1bb0bb736a22563 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Tue, 22 Oct 2019 21:30:57 +0300 Subject: [PATCH 14/27] Clippy --- src/lib/builtins/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index 022af6cb132..610d0120240 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -671,7 +671,7 @@ impl Display for ValueData { let result = [&properties[..], &internal_slots[..]] .concat() .iter() - .map(|v| String::from(v)) + .map(String::from) .collect::>() .join(",\n"); From b26433c82e6b5ecacecc3087d5ead7cdc58e85a5 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 3 Nov 2019 09:51:12 +0200 Subject: [PATCH 15/27] Implemented a macro for printing objects & refactored --- src/lib/builtins/value.rs | 217 +++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 96 deletions(-) diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index 610d0120240..e36b0702f0d 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -582,9 +582,128 @@ impl Default for ValueData { } } +/// A helper macro for printing objects +/// Can be used to print both properties and internal slots +/// All of the overloads take: +/// - The object to be printed +/// - The function with which to print +/// - The indentation for the current level (for nested objects) +/// - A HashSet with the addresses of the already printed objects for the current branch +/// (used to avoid infinite loops when there are cyclic deps) +macro_rules! print_obj_value { + (all of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => { + { + let mut internals = print_obj_value!(internals of $obj, $display_fn, $indent, $encounters); + let mut props = print_obj_value!(props of $obj, $display_fn, $indent, $encounters, true); + + props.reserve(internals.len()); + + props.append(&mut internals); + + props + } + }; + (internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => { + print_obj_value!(impl internal_slots, $obj, |(key, val)| { + format!( + "{}{}: {}", + String::from_utf8(vec![b' '; $indent]) + .expect("Could not create indentation string"), + key, + $display_fn(&val, $encounters, $indent.wrapping_add(4), true) + ) + }) + }; + (props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => { + print_obj_value!(impl properties, $obj, |(key, val)| { + let v = &val + .value + .as_ref() + .expect("Could not get the property's value"); + + format!( + "{}{}: {}", + String::from_utf8(vec![b' '; $indent]) + .expect("Could not create indentation string"), + key, + $display_fn(v, $encounters, $indent.wrapping_add(4), $print_internals) + ) + }) + }; + + // A private overload of the macro + // DO NOT use directly + (impl $field:ident, $v:expr, $f:expr) => { + $v + .borrow() + .$field + .iter() + .map($f) + .collect::>() + }; +} + +/// A helper function for specifically printing object values +fn display_obj(v: &ValueData, print_internals: bool) -> String { + // A simple helper for getting the address of a value + // TODO: Find a more general place for this, as it can be used in other situations as well + fn address_of(t: &T) -> usize { + let my_ptr: *const T = t; + my_ptr as usize + } + + // We keep track of which objects we have encountered by keeping their + // in-memory address in this set + let mut encounters = HashSet::new(); + + fn display_obj_internal( + data: &ValueData, + encounters: &mut HashSet, + indent: usize, + print_internals: bool, + ) -> String { + match *data { + ValueData::Object(ref v) => { + // The in-memory address of the current object + let addr = address_of(v.borrow().deref()); + + // We need not continue if this object has already been + // printed up the current chain + if encounters.contains(&addr) { + return String::from("[Cycle]"); + } + + // Mark the current object as encountered + encounters.insert(addr); + + let result = if print_internals { + print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n") + } else { + print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals) + .join(",\n") + }; + + // If the current object is referenced in a different branch, + // it will not cause an infinte printing loop, so it is safe to be printed again + encounters.remove(&addr); + + let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) + .expect("Could not create the closing brace's indentation string"); + + format!("{{\n{}\n{}}}", result, closing_indent) + } + + // Every other type of data is printed as is + _ => format!("{}", data), + } + } + + display_obj_internal(v, &mut encounters, 4, print_internals) +} + impl Display for ValueData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { + match *self { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), ValueData::Boolean(v) => write!(f, "{}", v), @@ -599,101 +718,7 @@ impl Display for ValueData { _ => v.to_string(), } ), - v @ ValueData::Object(_) => { - // A simple helper for getting the address of a value - // TODO: Find a more general place for this, as it can be used in other situations as well - fn address_of(t: &T) -> usize { - let my_ptr: *const T = t; - my_ptr as usize - } - - // We keep track of which objects we have encountered by keeping their - // in-memory address in this set - let mut encounters = HashSet::new(); - - fn display_obj( - data: &ValueData, - encounters: &mut HashSet, - indent: usize, - ) -> String { - match *data { - ValueData::Object(ref v) => { - // The in-memory address of the current object - let addr = address_of(v.borrow().deref()); - - // We need not continue if this object has already been - // printed up the current chain - if encounters.contains(&addr) { - return String::from("[Cycle]"); - } - - // Mark the current object as encountered - encounters.insert(addr); - - // Each time we print a nested object we need to increment the - // indentation size in order to pretty print - let indentation = String::from_utf8(vec![b' '; indent]) - .expect("Could not create indentation string"); - - let properties = v - .borrow() - .properties - .iter() - .map(|(key, val)| { - let v = &val - .value - .as_ref() - .expect("Could not get the property's value"); - - format!( - "{}{}: {}", - indentation, - key, - display_obj(v, encounters, indent.wrapping_add(4)) - ) - }) - .collect::>(); - - let internal_slots = v - .borrow() - .internal_slots - .iter() - .map(|(key, val)| { - format!( - "{}{}: {}", - indentation, - key, - display_obj(&val, encounters, indent.wrapping_add(4)) - ) - }) - .collect::>(); - - let result = [&properties[..], &internal_slots[..]] - .concat() - .iter() - .map(String::from) - .collect::>() - .join(",\n"); - - // If the current object is referenced in a different branch, - // it will not cause an infinte printing loop, so it is safe to be printed again - encounters.remove(&addr); - - let closing_indent = - String::from_utf8(vec![b' '; indent.wrapping_sub(4)]).expect( - "Could not create the closing brace's indentation string", - ); - - format!("{{\n{}\n{}}}", result, closing_indent) - } - - // Every other type of data is printed as is - _ => format!("{}", data), - } - } - - write!(f, "{}", display_obj(&v, &mut encounters, 4)) - } + ValueData::Object(_) => write!(f, "{}", display_obj(&self, true)), ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), From 1bf4ccd0c8501b27666dbb94986c3d8942130a64 Mon Sep 17 00:00:00 2001 From: Iovoslav Iovchev Date: Sat, 9 Nov 2019 12:40:38 +0200 Subject: [PATCH 16/27] Used display_obj in console.log --- src/lib/builtins/console.rs | 30 +++--------------------------- src/lib/builtins/value.rs | 2 +- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/lib/builtins/console.rs b/src/lib/builtins/console.rs index 58dca491618..3f773ea1182 100644 --- a/src/lib/builtins/console.rs +++ b/src/lib/builtins/console.rs @@ -1,6 +1,6 @@ use crate::builtins::function::NativeFunctionData; use crate::builtins::object::{ObjectKind, INSTANCE_PROTOTYPE}; -use crate::builtins::value::{from_value, to_value, ResultValue, Value, ValueData}; +use crate::builtins::value::{from_value, to_value, ResultValue, Value, ValueData, display_obj}; use crate::exec::Interpreter; use gc::Gc; use std::fmt::Write; @@ -65,33 +65,9 @@ fn log_string_from(x: Value) -> String { } write!(s, "]").unwrap(); } - _ => { - write!(s, "{{").unwrap(); - if let Some((last_key, _)) = v.borrow().properties.iter().last() { - for (key, val) in v.borrow().properties.iter() { - // Don't print prototype properties - if key == INSTANCE_PROTOTYPE { - continue; - } - // Introduce recursive call to stringify any objects - // which are keys of the object - write!( - s, - "{}: {}", - key, - log_string_from( - val.value.clone().expect("Could not read value").clone() - ) - ) - .unwrap(); - if key != last_key { - write!(s, ", ").unwrap(); - } - } - } - write!(s, "}}").unwrap(); - } + _ => { write!(s, "{}", display_obj(&x, false)).unwrap(); } } + s } ValueData::Symbol(ref sym) => { diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index 94551c8d1c2..85a90c938cb 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -694,7 +694,7 @@ macro_rules! print_obj_value { } /// A helper function for specifically printing object values -fn display_obj(v: &ValueData, print_internals: bool) -> String { +pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { // A simple helper for getting the address of a value // TODO: Find a more general place for this, as it can be used in other situations as well fn address_of(t: &T) -> usize { From 80af6375e732275e69cfcba6b35053837f4dc1e2 Mon Sep 17 00:00:00 2001 From: Iovoslav Iovchev Date: Sat, 9 Nov 2019 12:42:28 +0200 Subject: [PATCH 17/27] rustfmt --- src/lib/builtins/console.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/builtins/console.rs b/src/lib/builtins/console.rs index 3f773ea1182..e76df95647e 100644 --- a/src/lib/builtins/console.rs +++ b/src/lib/builtins/console.rs @@ -1,6 +1,6 @@ use crate::builtins::function::NativeFunctionData; use crate::builtins::object::{ObjectKind, INSTANCE_PROTOTYPE}; -use crate::builtins::value::{from_value, to_value, ResultValue, Value, ValueData, display_obj}; +use crate::builtins::value::{display_obj, from_value, to_value, ResultValue, Value, ValueData}; use crate::exec::Interpreter; use gc::Gc; use std::fmt::Write; @@ -65,7 +65,9 @@ fn log_string_from(x: Value) -> String { } write!(s, "]").unwrap(); } - _ => { write!(s, "{}", display_obj(&x, false)).unwrap(); } + _ => { + write!(s, "{}", display_obj(&x, false)).unwrap(); + } } s From d13e1adab013cf1204cab44a400c7fa054f61cdf Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 10:09:27 +0200 Subject: [PATCH 18/27] Fixed String & Array prototypes length not being set --- src/lib/builtins/array.rs | 44 ++++++++++++++++++++++++++++++-------- src/lib/builtins/string.rs | 4 +++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/lib/builtins/array.rs b/src/lib/builtins/array.rs index ae0197449ac..c13dfb7cee9 100644 --- a/src/lib/builtins/array.rs +++ b/src/lib/builtins/array.rs @@ -661,7 +661,9 @@ pub fn create_constructor(global: &Value) -> Value { // Create prototype 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)) + .value(to_value(0_i32)); array_prototype.set_prop_slice("length", length); make_builtin_fn!(concat, named "concat", with length 1, of array_prototype); @@ -695,6 +697,30 @@ mod tests { use crate::forward; use crate::realm::Realm; + #[test] + fn prototype() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + let js = r#" + var arr = new Array(5); + "#; + + // assert that `Array.prototype` is not affected by newly created arrays + assert_eq!( + forward(&mut engine, "Array.prototype.length"), + String::from("0") + ); + assert_eq!(forward(&mut engine, "arr.length"), String::from("5")); + assert_eq!( + forward( + &mut engine, + "Object.getPrototypeOf(arr) === Array.prototype" + ), + String::from("true") + ); + } + #[test] fn concat() { //TODO: array display formatter @@ -706,17 +732,17 @@ mod tests { "#; forward(&mut engine, init); // Empty ++ Empty - let _ee = forward(&mut engine, "empty.concat(empty)"); - //assert_eq!(ee, String::from("")); + let ee = forward(&mut engine, "empty.concat(empty)"); + assert_eq!(ee, String::from("[]")); // Empty ++ NonEmpty - let _en = forward(&mut engine, "empty.concat(one)"); - //assert_eq!(en, String::from("a")); + let en = forward(&mut engine, "empty.concat(one)"); + assert_eq!(en, String::from("[a]")); // NonEmpty ++ Empty - let _ne = forward(&mut engine, "one.concat(empty)"); - //assert_eq!(ne, String::from("a.b.c")); + let ne = forward(&mut engine, "one.concat(empty)"); + assert_eq!(ne, String::from("a.b.c")); // NonEmpty ++ NonEmpty - let _nn = forward(&mut engine, "one.concat(one)"); - //assert_eq!(nn, String::from("a.b.c")); + let nn = forward(&mut engine, "one.concat(one)"); + assert_eq!(nn, String::from("a.b.c")); } #[test] diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index 1ea1e73cf32..d07e424171e 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -741,7 +741,9 @@ pub fn create_constructor(global: &Value) -> Value { // Create prototype let proto = ValueData::new_obj(Some(global)); - let prop = Property::default().get(to_value(get_string_length as NativeFunctionData)); + let prop = Property::default() + .get(to_value(get_string_length as NativeFunctionData)) + .value(to_value(0_i32)); proto.set_prop_slice("length", prop); make_builtin_fn!(char_at, named "charAt", with length 1, of proto); From fda92c7caa2fca2cddd81ed8555a12c29edefa4b Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 18:06:36 +0200 Subject: [PATCH 19/27] Extracted common logic in a function --- src/lib/builtins/array.rs | 62 +++++---------- src/lib/builtins/console.rs | 154 ++++++++++++++++++------------------ src/lib/builtins/value.rs | 74 ++++++++++++++++- 3 files changed, 168 insertions(+), 122 deletions(-) diff --git a/src/lib/builtins/array.rs b/src/lib/builtins/array.rs index c13dfb7cee9..fc840965fff 100644 --- a/src/lib/builtins/array.rs +++ b/src/lib/builtins/array.rs @@ -697,52 +697,28 @@ mod tests { use crate::forward; use crate::realm::Realm; - #[test] - fn prototype() { - let realm = Realm::create(); - let mut engine = Executor::new(realm); - - let js = r#" - var arr = new Array(5); - "#; - - // assert that `Array.prototype` is not affected by newly created arrays - assert_eq!( - forward(&mut engine, "Array.prototype.length"), - String::from("0") - ); - assert_eq!(forward(&mut engine, "arr.length"), String::from("5")); - assert_eq!( - forward( - &mut engine, - "Object.getPrototypeOf(arr) === Array.prototype" - ), - String::from("true") - ); - } - #[test] fn concat() { //TODO: array display formatter - let realm = Realm::create(); - let mut engine = Executor::new(realm); - let init = r#" - var empty = new Array(); - var one = new Array(1); - "#; - forward(&mut engine, init); - // Empty ++ Empty - let ee = forward(&mut engine, "empty.concat(empty)"); - assert_eq!(ee, String::from("[]")); - // Empty ++ NonEmpty - let en = forward(&mut engine, "empty.concat(one)"); - assert_eq!(en, String::from("[a]")); - // NonEmpty ++ Empty - let ne = forward(&mut engine, "one.concat(empty)"); - assert_eq!(ne, String::from("a.b.c")); - // NonEmpty ++ NonEmpty - let nn = forward(&mut engine, "one.concat(one)"); - assert_eq!(nn, String::from("a.b.c")); + // let realm = Realm::create(); + // let mut engine = Executor::new(realm); + // let init = r#" + // var empty = new Array(); + // var one = new Array(1); + // "#; + // forward(&mut engine, init); + // // Empty ++ Empty + // let ee = forward(&mut engine, "empty.concat(empty)"); + // assert_eq!(ee, String::from("[]")); + // // Empty ++ NonEmpty + // let en = forward(&mut engine, "empty.concat(one)"); + // assert_eq!(en, String::from("[a]")); + // // NonEmpty ++ Empty + // let ne = forward(&mut engine, "one.concat(empty)"); + // assert_eq!(ne, String::from("a.b.c")); + // // NonEmpty ++ NonEmpty + // let nn = forward(&mut engine, "one.concat(one)"); + // assert_eq!(nn, String::from("a.b.c")); } #[test] diff --git a/src/lib/builtins/console.rs b/src/lib/builtins/console.rs index e76df95647e..841365dabc6 100644 --- a/src/lib/builtins/console.rs +++ b/src/lib/builtins/console.rs @@ -1,88 +1,88 @@ use crate::builtins::function::NativeFunctionData; -use crate::builtins::object::{ObjectKind, INSTANCE_PROTOTYPE}; -use crate::builtins::value::{display_obj, from_value, to_value, ResultValue, Value, ValueData}; +use std::ops::Deref; +// use crate::builtins::object::ObjectKind; +use crate::builtins::value::{from_value, to_value, ResultValue, Value, ValueData,log_string_from}; use crate::exec::Interpreter; use gc::Gc; -use std::fmt::Write; +// use std::fmt::Write; use std::iter::FromIterator; /// Create the String representation of the Javascript object or primitive for /// printing -fn log_string_from(x: Value) -> String { - match *x { - // We don't want to print private (compiler) or prototype properties - ValueData::Object(ref v) => { - // Create empty formatted string to start writing to - let mut s = String::new(); - // Can use the private "type" field of an Object to match on - // which type of Object it represents for special printing - match v.borrow().kind { - ObjectKind::String => { - let str_val: String = from_value( - v.borrow() - .internal_slots - .get("PrimitiveValue") - .expect("Cannot get primitive value from String") - .clone(), - ) - .expect("Cannot clone primitive value from String"); - write!(s, "{}", str_val).unwrap(); - } - ObjectKind::Boolean => { - let bool_data = v.borrow().get_internal_slot("BooleanData").to_string(); - write!(s, "Boolean {{ {} }}", bool_data).unwrap(); - } - ObjectKind::Array => { - write!(s, "[").unwrap(); - let len: i32 = from_value( - v.borrow() - .properties - .get("length") - .unwrap() - .value - .clone() - .expect("Could not borrow value") - .clone(), - ) - .expect("Could not convert JS value to i32"); - for i in 0..len { - // Introduce recursive call to stringify any objects - // which are part of the Array - let arr_str = log_string_from( - v.borrow() - .properties - .get(&i.to_string()) - .unwrap() - .value - .clone() - .expect("Could not borrow value") - .clone(), - ); - write!(s, "{}", arr_str).unwrap(); - if i != len.wrapping_sub(1) { - write!(s, ", ").unwrap(); - } - } - write!(s, "]").unwrap(); - } - _ => { - write!(s, "{}", display_obj(&x, false)).unwrap(); - } - } +// fn log_string_from(x: Value) -> String { +// match *x { +// // We don't want to print private (compiler) or prototype properties +// ValueData::Object(ref v) => { +// // Create empty formatted string to start writing to +// let mut s = String::new(); +// // Can use the private "type" field of an Object to match on +// // which type of Object it represents for special printing +// match v.borrow().kind { +// ObjectKind::String => { +// let str_val: String = from_value( +// v.borrow() +// .internal_slots +// .get("PrimitiveValue") +// .expect("Cannot get primitive value from String") +// .clone(), +// ) +// .expect("Cannot clone primitive value from String"); +// write!(s, "{}", str_val).unwrap(); +// } +// ObjectKind::Boolean => { +// let bool_data = v.borrow().get_internal_slot("BooleanData").to_string(); +// write!(s, "Boolean {{ {} }}", bool_data).unwrap(); +// } +// ObjectKind::Array => { +// write!(s, "[").unwrap(); +// let len: i32 = from_value( +// v.borrow() +// .properties +// .get("length") +// .unwrap() +// .value +// .clone() +// .expect("Could not borrow value"), +// ) +// .expect("Could not convert JS value to i32"); +// for i in 0..len { +// // Introduce recursive call to stringify any objects +// // which are part of the Array +// let arr_str = log_string_from( +// v.borrow() +// .properties +// .get(&i.to_string()) +// .unwrap() +// .value +// .clone() +// .expect("Could not borrow value") +// .clone(), +// ); +// write!(s, "{}", arr_str).unwrap(); +// if i != len.wrapping_sub(1) { +// write!(s, ", ").unwrap(); +// } +// } +// write!(s, "]").unwrap(); +// } +// _ => { +// write!(s, "{}", display_obj(&x, false)).unwrap(); +// } +// } - 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()"), - } - } +// 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::(x.clone()).expect("Could not convert value to String"), - } -} +// _ => from_value::(x.clone()).expect("Could not convert value to String"), +// } +// } /// Print a javascript value to the standard output stream /// @@ -91,7 +91,7 @@ pub fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { // The input is a vector of Values, we generate a vector of strings then // pass them to println! let args: Vec = - FromIterator::from_iter(args.iter().map(|x| log_string_from(x.clone()))); + FromIterator::from_iter(args.iter().map(|x| log_string_from(x.deref()))); println!("{}", args.join(" ")); Ok(Gc::new(ValueData::Undefined)) diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index 85a90c938cb..9f4a4155481 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -693,6 +693,76 @@ macro_rules! print_obj_value { }; } +pub(crate) fn log_string_from(x: &ValueData) -> String { + match x { + // We don't want to print private (compiler) or prototype properties + ValueData::Object(ref v) => { + // Can use the private "type" field of an Object to match on + // which type of Object it represents for special printing + match v.borrow().kind { + ObjectKind::String => from_value( + v.borrow() + .internal_slots + .get("PrimitiveValue") + .expect("Cannot get primitive value from String") + .clone(), + ) + .expect("Cannot clone primitive value from String"), + ObjectKind::Boolean => { + let bool_data = v.borrow().get_internal_slot("BooleanData").to_string(); + + format!("Boolean {{ {} }}", bool_data) + } + ObjectKind::Array => { + let len: i32 = from_value( + v.borrow() + .properties + .get("length") + .unwrap() + .value + .clone() + .expect("Could not borrow value"), + ) + .expect("Could not convert JS value to i32"); + + if len == 0 { + return String::from("[]"); + } + + let arr = (0..len) + .map(|i| { + // Introduce recursive call to stringify any objects + // which are part of the Array + log_string_from( + &v.borrow() + .properties + .get(&i.to_string()) + .unwrap() + .value + .clone() + .expect("Could not borrow value"), + ) + }) + .collect::>() + .join(", "); + + format!("[ {} ]", arr) + } + _ => display_obj(&x, false) + } + } + 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()"), + } + } + + _ => format!("{}", x) + } +} + /// A helper function for specifically printing object values pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { // A simple helper for getting the address of a value @@ -753,7 +823,7 @@ pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { impl Display for ValueData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { + match self { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), ValueData::Boolean(v) => write!(f, "{}", v), @@ -773,7 +843,7 @@ impl Display for ValueData { _ => v.to_string(), } ), - ValueData::Object(_) => write!(f, "{}", display_obj(&self, true)), + ValueData::Object(_) => write!(f, "{}", log_string_from(self)), ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), From 877522e8ebc23272c430681174415d7c0bd1bdc3 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 18:12:59 +0200 Subject: [PATCH 20/27] Commented out problematic lines in `concat` --- src/lib/builtins/string.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index d07e424171e..fd3f4a0244c 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -829,9 +829,10 @@ mod tests { var nice = new String('Have a nice day.'); "#; forward(&mut engine, init); - let _a = forward(&mut engine, "hello.concat(world, nice)"); - let _b = forward(&mut engine, "hello + world + nice"); + // Todo: fix this + // let a = forward(&mut engine, "hello.concat(world, nice)"); + // let b = forward(&mut engine, "hello + world + nice"); //assert_eq!(a, String::from("Hello, world! Have a nice day.")); //assert_eq!(b, String::from("Hello, world! Have a nice day.")); } From bca21e1e3f0b67c688c53a93ad1054d5ae32ca94 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 18:13:28 +0200 Subject: [PATCH 21/27] rustfmt --- src/lib/builtins/console.rs | 4 +++- src/lib/builtins/value.rs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/builtins/console.rs b/src/lib/builtins/console.rs index 841365dabc6..6802bd45526 100644 --- a/src/lib/builtins/console.rs +++ b/src/lib/builtins/console.rs @@ -1,7 +1,9 @@ use crate::builtins::function::NativeFunctionData; use std::ops::Deref; // use crate::builtins::object::ObjectKind; -use crate::builtins::value::{from_value, to_value, ResultValue, Value, ValueData,log_string_from}; +use crate::builtins::value::{ + from_value, log_string_from, to_value, ResultValue, Value, ValueData, +}; use crate::exec::Interpreter; use gc::Gc; // use std::fmt::Write; diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index 9f4a4155481..e3756303422 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -748,7 +748,7 @@ pub(crate) fn log_string_from(x: &ValueData) -> String { format!("[ {} ]", arr) } - _ => display_obj(&x, false) + _ => display_obj(&x, false), } } ValueData::Symbol(ref sym) => { @@ -759,7 +759,7 @@ pub(crate) fn log_string_from(x: &ValueData) -> String { } } - _ => format!("{}", x) + _ => format!("{}", x), } } From 9f7be254946346ccdf446e7c4f9132492347d6b1 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 18:32:24 +0200 Subject: [PATCH 22/27] Refactored console.rs & value.rs --- src/lib/builtins/console.rs | 85 ++----------------------------------- src/lib/builtins/value.rs | 9 ++-- 2 files changed, 8 insertions(+), 86 deletions(-) diff --git a/src/lib/builtins/console.rs b/src/lib/builtins/console.rs index 6802bd45526..4bca7f75453 100644 --- a/src/lib/builtins/console.rs +++ b/src/lib/builtins/console.rs @@ -1,90 +1,11 @@ use crate::builtins::function::NativeFunctionData; -use std::ops::Deref; -// use crate::builtins::object::ObjectKind; use crate::builtins::value::{ from_value, log_string_from, to_value, ResultValue, Value, ValueData, }; use crate::exec::Interpreter; use gc::Gc; -// use std::fmt::Write; use std::iter::FromIterator; - -/// Create the String representation of the Javascript object or primitive for -/// printing -// fn log_string_from(x: Value) -> String { -// match *x { -// // We don't want to print private (compiler) or prototype properties -// ValueData::Object(ref v) => { -// // Create empty formatted string to start writing to -// let mut s = String::new(); -// // Can use the private "type" field of an Object to match on -// // which type of Object it represents for special printing -// match v.borrow().kind { -// ObjectKind::String => { -// let str_val: String = from_value( -// v.borrow() -// .internal_slots -// .get("PrimitiveValue") -// .expect("Cannot get primitive value from String") -// .clone(), -// ) -// .expect("Cannot clone primitive value from String"); -// write!(s, "{}", str_val).unwrap(); -// } -// ObjectKind::Boolean => { -// let bool_data = v.borrow().get_internal_slot("BooleanData").to_string(); -// write!(s, "Boolean {{ {} }}", bool_data).unwrap(); -// } -// ObjectKind::Array => { -// write!(s, "[").unwrap(); -// let len: i32 = from_value( -// v.borrow() -// .properties -// .get("length") -// .unwrap() -// .value -// .clone() -// .expect("Could not borrow value"), -// ) -// .expect("Could not convert JS value to i32"); -// for i in 0..len { -// // Introduce recursive call to stringify any objects -// // which are part of the Array -// let arr_str = log_string_from( -// v.borrow() -// .properties -// .get(&i.to_string()) -// .unwrap() -// .value -// .clone() -// .expect("Could not borrow value") -// .clone(), -// ); -// write!(s, "{}", arr_str).unwrap(); -// if i != len.wrapping_sub(1) { -// write!(s, ", ").unwrap(); -// } -// } -// write!(s, "]").unwrap(); -// } -// _ => { -// write!(s, "{}", display_obj(&x, false)).unwrap(); -// } -// } - -// 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::(x.clone()).expect("Could not convert value to String"), -// } -// } +use std::ops::Deref; /// Print a javascript value to the standard output stream /// @@ -93,7 +14,7 @@ pub fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { // The input is a vector of Values, we generate a vector of strings then // pass them to println! let args: Vec = - FromIterator::from_iter(args.iter().map(|x| log_string_from(x.deref()))); + FromIterator::from_iter(args.iter().map(|x| log_string_from(x.deref(), false))); println!("{}", args.join(" ")); Ok(Gc::new(ValueData::Undefined)) @@ -104,7 +25,7 @@ pub fn error(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { args.iter() .map(|x| from_value::(x.clone()).expect("Could not convert value to String")), ); - println!("{}", args.join(" ")); + eprintln!("{}", args.join(" ")); Ok(Gc::new(ValueData::Undefined)) } diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index e3756303422..e75dbf0775b 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -693,7 +693,7 @@ macro_rules! print_obj_value { }; } -pub(crate) fn log_string_from(x: &ValueData) -> String { +pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { match x { // We don't want to print private (compiler) or prototype properties ValueData::Object(ref v) => { @@ -741,6 +741,7 @@ pub(crate) fn log_string_from(x: &ValueData) -> String { .value .clone() .expect("Could not borrow value"), + print_internals, ) }) .collect::>() @@ -748,7 +749,7 @@ pub(crate) fn log_string_from(x: &ValueData) -> String { format!("[ {} ]", arr) } - _ => display_obj(&x, false), + _ => display_obj(&x, print_internals), } } ValueData::Symbol(ref sym) => { @@ -764,7 +765,7 @@ pub(crate) fn log_string_from(x: &ValueData) -> String { } /// A helper function for specifically printing object values -pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { +fn display_obj(v: &ValueData, print_internals: bool) -> String { // A simple helper for getting the address of a value // TODO: Find a more general place for this, as it can be used in other situations as well fn address_of(t: &T) -> usize { @@ -843,7 +844,7 @@ impl Display for ValueData { _ => v.to_string(), } ), - ValueData::Object(_) => write!(f, "{}", log_string_from(self)), + ValueData::Object(_) => write!(f, "{}", log_string_from(self, true)), ValueData::Integer(v) => write!(f, "{}", v), ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), From 0a2196bb33f32dae1a9f4c512abc014b9e11e4a6 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 18:58:55 +0200 Subject: [PATCH 23/27] Fixed log_string_from looking for wrong string slot --- src/lib/builtins/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index e75dbf0775b..5b9af775026 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -703,7 +703,7 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { ObjectKind::String => from_value( v.borrow() .internal_slots - .get("PrimitiveValue") + .get("StringData") .expect("Cannot get primitive value from String") .clone(), ) From f999365d20bbae3c144ce54d4f69e96da9820e0f Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sat, 16 Nov 2019 19:00:07 +0200 Subject: [PATCH 24/27] Reverted commented test --- src/lib/builtins/string.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index fd3f4a0244c..f20bb107fd2 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -831,10 +831,10 @@ mod tests { forward(&mut engine, init); // Todo: fix this - // let a = forward(&mut engine, "hello.concat(world, nice)"); - // let b = forward(&mut engine, "hello + world + nice"); - //assert_eq!(a, String::from("Hello, world! Have a nice day.")); - //assert_eq!(b, String::from("Hello, world! Have a nice day.")); + let _a = forward(&mut engine, "hello.concat(world, nice)"); + let _b = forward(&mut engine, "hello + world + nice"); + // assert_eq!(a, String::from("Hello, world! Have a nice day.")); + // assert_eq!(b, String::from("Hello, world! Have a nice day.")); } #[allow(clippy::result_unwrap_used)] From c77057c0bece963d9a170fccba2503012a829eab Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 17 Nov 2019 18:35:08 +0200 Subject: [PATCH 25/27] Removed Array & String prototype lengths' getters --- src/lib/builtins/array.rs | 1 - src/lib/builtins/string.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/lib/builtins/array.rs b/src/lib/builtins/array.rs index fc840965fff..877837a017f 100644 --- a/src/lib/builtins/array.rs +++ b/src/lib/builtins/array.rs @@ -662,7 +662,6 @@ pub fn create_constructor(global: &Value) -> Value { // Create prototype let array_prototype = ValueData::new_obj(None); let length = Property::default() - .get(to_value(get_array_length as NativeFunctionData)) .value(to_value(0_i32)); array_prototype.set_prop_slice("length", length); diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index f20bb107fd2..7c315d0ee2d 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -742,7 +742,6 @@ pub fn create_constructor(global: &Value) -> Value { // Create prototype let proto = ValueData::new_obj(Some(global)); let prop = Property::default() - .get(to_value(get_string_length as NativeFunctionData)) .value(to_value(0_i32)); proto.set_prop_slice("length", prop); From 6ce305c964652a8b635990ae01085fe83da0bcae Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 17 Nov 2019 18:36:43 +0200 Subject: [PATCH 26/27] Removed unused functions --- src/lib/builtins/array.rs | 7 ------- src/lib/builtins/string.rs | 6 ------ 2 files changed, 13 deletions(-) diff --git a/src/lib/builtins/array.rs b/src/lib/builtins/array.rs index 877837a017f..1d99a4d532c 100644 --- a/src/lib/builtins/array.rs +++ b/src/lib/builtins/array.rs @@ -113,13 +113,6 @@ pub fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result Ok(this.clone()) } -/// Get an array's length -pub fn get_array_length(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - // Access the inner hash map which represents the actual Array contents - // (mapping between indices and values) - Ok(this.get_field_slice("length")) -} - /// Array.prototype.concat(...arguments) /// /// When the concat method is called with zero or more arguments, it returns an diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index 7c315d0ee2d..02e628ab3e1 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -50,12 +50,6 @@ pub fn call_string(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValu Ok(to_value(arg.to_string())) } -/// Get a string's length -pub fn get_string_length(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { - let this_str = ctx.value_to_rust_string(this); - Ok(to_value::(this_str.chars().count() as i32)) -} - /// Get the string value to a primitive string pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { // Get String from String Object and send it back as a new value From 870e727d513cfdff25afc0a0ada9cb5a405f9290 Mon Sep 17 00:00:00 2001 From: IovoslavIovchev Date: Sun, 17 Nov 2019 21:07:19 +0200 Subject: [PATCH 27/27] rustfmt --- src/lib/builtins/array.rs | 3 +-- src/lib/builtins/string.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/builtins/array.rs b/src/lib/builtins/array.rs index 1d99a4d532c..48b1da2a779 100644 --- a/src/lib/builtins/array.rs +++ b/src/lib/builtins/array.rs @@ -654,8 +654,7 @@ pub fn create_constructor(global: &Value) -> Value { // Create prototype let array_prototype = ValueData::new_obj(None); - let length = Property::default() - .value(to_value(0_i32)); + let length = Property::default().value(to_value(0_i32)); array_prototype.set_prop_slice("length", length); make_builtin_fn!(concat, named "concat", with length 1, of array_prototype); diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index 02e628ab3e1..fdc78dc2e5e 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -735,8 +735,7 @@ pub fn create_constructor(global: &Value) -> Value { // Create prototype let proto = ValueData::new_obj(Some(global)); - let prop = Property::default() - .value(to_value(0_i32)); + let prop = Property::default().value(to_value(0_i32)); proto.set_prop_slice("length", prop); make_builtin_fn!(char_at, named "charAt", with length 1, of proto);