Browse Source

Introduce PropertyKey for field acces, fix #172 (quotes around displayed strings) (#373)

Co-authored-by: HalidOdat <halidodat@gmail.com>
pull/597/head
João Borges 4 years ago committed by GitHub
parent
commit
667a820dee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      boa/src/builtins/array/mod.rs
  2. 92
      boa/src/builtins/array/tests.rs
  3. 14
      boa/src/builtins/bigint/tests.rs
  4. 2
      boa/src/builtins/console/mod.rs
  5. 6
      boa/src/builtins/error/range.rs
  6. 6
      boa/src/builtins/error/reference.rs
  7. 6
      boa/src/builtins/error/type.rs
  8. 4
      boa/src/builtins/function/mod.rs
  9. 2
      boa/src/builtins/global_this/tests.rs
  10. 6
      boa/src/builtins/json/mod.rs
  11. 8
      boa/src/builtins/json/tests.rs
  12. 32
      boa/src/builtins/map/tests.rs
  13. 175
      boa/src/builtins/number/tests.rs
  14. 101
      boa/src/builtins/object/internal_methods.rs
  15. 2
      boa/src/builtins/object/mod.rs
  16. 4
      boa/src/builtins/object/tests.rs
  17. 100
      boa/src/builtins/property/mod.rs
  18. 10
      boa/src/builtins/property/tests.rs
  19. 19
      boa/src/builtins/regexp/tests.rs
  20. 5
      boa/src/builtins/string/mod.rs
  21. 118
      boa/src/builtins/string/tests.rs
  22. 2
      boa/src/builtins/symbol/tests.rs
  23. 14
      boa/src/builtins/value/conversions.rs
  24. 6
      boa/src/builtins/value/display.rs
  25. 48
      boa/src/builtins/value/mod.rs
  26. 14
      boa/src/builtins/value/rcstring.rs
  27. 109
      boa/src/builtins/value/tests.rs
  28. 4
      boa/src/environment/lexical_environment.rs
  29. 2
      boa/src/exec/field/mod.rs
  30. 28
      boa/src/exec/mod.rs
  31. 6
      boa/src/exec/object/mod.rs
  32. 19
      boa/src/exec/operator/mod.rs
  33. 4
      boa/src/exec/operator/tests.rs
  34. 12
      boa/src/exec/switch/tests.rs
  35. 128
      boa/src/exec/tests.rs
  36. 3
      boa/src/realm.rs
  37. 4
      boa_cli/src/main.rs

4
boa/src/builtins/array/mod.rs

@ -408,7 +408,7 @@ impl Array {
let len = i32::from(&this.get_field("length"));
if len == 0 {
this.set_field("length", Value::from(0));
this.set_field("length", 0);
// Since length is 0, this will be an Undefined value
return Ok(this.get_field(0.to_string()));
}
@ -1026,7 +1026,7 @@ impl Array {
///
/// The reduceRight method traverses right to left starting from the last defined value in the array,
/// accumulating a value using a given callback function. It returns the accumulated value.
///
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]

92
boa/src/builtins/array/tests.rs

@ -76,13 +76,13 @@ fn join() {
eprintln!("{}", forward(&mut engine, init));
// Empty
let empty = forward(&mut engine, "empty.join('.')");
assert_eq!(empty, String::from(""));
assert_eq!(empty, String::from("\"\""));
// One
let one = forward(&mut engine, "one.join('.')");
assert_eq!(one, String::from("a"));
assert_eq!(one, String::from("\"a\""));
// Many
let many = forward(&mut engine, "many.join('.')");
assert_eq!(many, String::from("a.b.c"));
assert_eq!(many, String::from("\"a.b.c\""));
}
#[test]
@ -97,13 +97,13 @@ fn to_string() {
eprintln!("{}", forward(&mut engine, init));
// Empty
let empty = forward(&mut engine, "empty.toString()");
assert_eq!(empty, String::from(""));
assert_eq!(empty, String::from("\"\""));
// One
let one = forward(&mut engine, "one.toString()");
assert_eq!(one, String::from("a"));
assert_eq!(one, String::from("\"a\""));
// Many
let many = forward(&mut engine, "many.toString()");
assert_eq!(many, String::from("a,b,c"));
assert_eq!(many, String::from("\"a,b,c\""));
}
#[test]
@ -163,7 +163,7 @@ fn find() {
"#;
eprintln!("{}", forward(&mut engine, init));
let found = forward(&mut engine, "many.find(comp)");
assert_eq!(found, String::from("a"));
assert_eq!(found, String::from("\"a\""));
}
#[test]
@ -417,92 +417,92 @@ fn fill() {
forward(&mut engine, "var a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4).join()"),
String::from("4,4,4")
String::from("\"4,4,4\"")
);
// make sure the array is modified
assert_eq!(forward(&mut engine, "a.join()"), String::from("4,4,4"));
assert_eq!(forward(&mut engine, "a.join()"), String::from("\"4,4,4\""));
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, '1').join()"),
String::from("1,4,4")
String::from("\"1,4,4\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, 1, 2).join()"),
String::from("1,4,3")
String::from("\"1,4,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, 1, 1).join()"),
String::from("1,2,3")
String::from("\"1,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, 3, 3).join()"),
String::from("1,2,3")
String::from("\"1,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, -3, -2).join()"),
String::from("4,2,3")
String::from("\"4,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, NaN, NaN).join()"),
String::from("1,2,3")
String::from("\"1,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, 3, 5).join()"),
String::from("1,2,3")
String::from("\"1,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, '1.2', '2.5').join()"),
String::from("1,4,3")
String::from("\"1,4,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, 'str').join()"),
String::from("4,4,4")
String::from("\"4,4,4\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, 'str', 'str').join()"),
String::from("1,2,3")
String::from("\"1,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, undefined, null).join()"),
String::from("1,2,3")
String::from("\"1,2,3\"")
);
forward(&mut engine, "a = [1, 2, 3];");
assert_eq!(
forward(&mut engine, "a.fill(4, undefined, undefined).join()"),
String::from("4,4,4")
String::from("\"4,4,4\"")
);
assert_eq!(
forward(&mut engine, "a.fill().join()"),
String::from("undefined,undefined,undefined")
String::from("\"undefined,undefined,undefined\"")
);
// test object reference
forward(&mut engine, "a = (new Array(3)).fill({});");
forward(&mut engine, "a[0].hi = 'hi';");
assert_eq!(forward(&mut engine, "a[0].hi"), String::from("hi"));
assert_eq!(forward(&mut engine, "a[0].hi"), String::from("\"hi\""));
}
#[test]
@ -569,10 +569,10 @@ fn map() {
forward(&mut engine, js);
// assert the old arrays have not been modified
assert_eq!(forward(&mut engine, "one[0]"), String::from("x"));
assert_eq!(forward(&mut engine, "one[0]"), String::from("\"x\""));
assert_eq!(
forward(&mut engine, "many[2] + many[1] + many[0]"),
String::from("zyx")
String::from("\"zyx\"")
);
// NB: These tests need to be rewritten once `Display` has been implemented for `Array`
@ -584,7 +584,10 @@ fn map() {
// One
assert_eq!(forward(&mut engine, "one_mapped.length"), String::from("1"));
assert_eq!(forward(&mut engine, "one_mapped[0]"), String::from("_x"));
assert_eq!(
forward(&mut engine, "one_mapped[0]"),
String::from("\"_x\"")
);
// Many
assert_eq!(
@ -596,7 +599,7 @@ fn map() {
&mut engine,
"many_mapped[0] + many_mapped[1] + many_mapped[2]"
),
String::from("_x__y__z_")
String::from("\"_x__y__z_\"")
);
// TODO: uncomment when `this` has been implemented
@ -619,12 +622,12 @@ fn slice() {
eprintln!("{}", forward(&mut engine, init));
assert_eq!(forward(&mut engine, "empty.length"), "0");
assert_eq!(forward(&mut engine, "one[0]"), "a");
assert_eq!(forward(&mut engine, "many1[0]"), "b");
assert_eq!(forward(&mut engine, "many1[1]"), "c");
assert_eq!(forward(&mut engine, "many1[2]"), "d");
assert_eq!(forward(&mut engine, "one[0]"), "\"a\"");
assert_eq!(forward(&mut engine, "many1[0]"), "\"b\"");
assert_eq!(forward(&mut engine, "many1[1]"), "\"c\"");
assert_eq!(forward(&mut engine, "many1[2]"), "\"d\"");
assert_eq!(forward(&mut engine, "many1.length"), "3");
assert_eq!(forward(&mut engine, "many2[0]"), "c");
assert_eq!(forward(&mut engine, "many2[0]"), "\"c\"");
assert_eq!(forward(&mut engine, "many2.length"), "1");
assert_eq!(forward(&mut engine, "many3.length"), "0");
}
@ -693,10 +696,10 @@ fn filter() {
forward(&mut engine, js);
// assert the old arrays have not been modified
assert_eq!(forward(&mut engine, "one[0]"), String::from("1"));
assert_eq!(forward(&mut engine, "one[0]"), String::from("\"1\""));
assert_eq!(
forward(&mut engine, "many[2] + many[1] + many[0]"),
String::from("101")
String::from("\"101\"")
);
// NB: These tests need to be rewritten once `Display` has been implemented for `Array`
@ -711,7 +714,10 @@ fn filter() {
forward(&mut engine, "one_filtered.length"),
String::from("1")
);
assert_eq!(forward(&mut engine, "one_filtered[0]"), String::from("1"));
assert_eq!(
forward(&mut engine, "one_filtered[0]"),
String::from("\"1\"")
);
// One filtered on "0"
assert_eq!(
@ -726,7 +732,7 @@ fn filter() {
);
assert_eq!(
forward(&mut engine, "many_one_filtered[0] + many_one_filtered[1]"),
String::from("11")
String::from("\"11\"")
);
// Many filtered on "0"
@ -736,7 +742,7 @@ fn filter() {
);
assert_eq!(
forward(&mut engine, "many_zero_filtered[0]"),
String::from("0")
String::from("\"0\"")
);
}
@ -863,7 +869,7 @@ fn reduce() {
);
assert_eq!(
result,
"Reduce was called on an empty array and with no initial value"
"\"Reduce was called on an empty array and with no initial value\""
);
// Array with no defined elements
@ -882,7 +888,7 @@ fn reduce() {
);
assert_eq!(
result,
"Reduce was called on an empty array and with no initial value"
"\"Reduce was called on an empty array and with no initial value\""
);
// No callback
@ -896,7 +902,7 @@ fn reduce() {
}
"#,
);
assert_eq!(result, "Reduce was called without a callback");
assert_eq!(result, "\"Reduce was called without a callback\"");
}
#[test]
@ -986,7 +992,7 @@ fn reduce_right() {
);
assert_eq!(
result,
"reduceRight was called on an empty array and with no initial value"
"\"reduceRight was called on an empty array and with no initial value\""
);
// Array with no defined elements
@ -1005,7 +1011,7 @@ fn reduce_right() {
);
assert_eq!(
result,
"reduceRight was called on an empty array and with no initial value"
"\"reduceRight was called on an empty array and with no initial value\""
);
// No callback
@ -1019,7 +1025,7 @@ fn reduce_right() {
}
"#,
);
assert_eq!(result, "reduceRight was called without a callback");
assert_eq!(result, "\"reduceRight was called without a callback\"");
}
#[test]

14
boa/src/builtins/bigint/tests.rs

@ -94,7 +94,7 @@ fn bigint_function_conversion_from_rational_with_fractional_part() {
"#;
assert_eq!(
forward(&mut engine, scenario),
"TypeError: The number 0.1 cannot be converted to a BigInt because it is not an integer"
"\"TypeError: The number 0.1 cannot be converted to a BigInt because it is not an integer\""
);
}
@ -112,7 +112,7 @@ fn bigint_function_conversion_from_null() {
"#;
assert_eq!(
forward(&mut engine, scenario),
"TypeError: cannot convert null to a BigInt"
"\"TypeError: cannot convert null to a BigInt\""
);
}
@ -130,7 +130,7 @@ fn bigint_function_conversion_from_undefined() {
"#;
assert_eq!(
forward(&mut engine, scenario),
"TypeError: cannot convert undefined to a BigInt"
"\"TypeError: cannot convert undefined to a BigInt\""
);
}
@ -217,10 +217,10 @@ fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "1000n.toString()"), "1000");
assert_eq!(forward(&mut engine, "1000n.toString(2)"), "1111101000");
assert_eq!(forward(&mut engine, "255n.toString(16)"), "ff");
assert_eq!(forward(&mut engine, "1000n.toString(36)"), "rs");
assert_eq!(forward(&mut engine, "1000n.toString()"), "\"1000\"");
assert_eq!(forward(&mut engine, "1000n.toString(2)"), "\"1111101000\"");
assert_eq!(forward(&mut engine, "255n.toString(16)"), "\"ff\"");
assert_eq!(forward(&mut engine, "1000n.toString(36)"), "\"rs\"");
}
#[test]

2
boa/src/builtins/console/mod.rs

@ -111,7 +111,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result<String, Value>
/* unformatted data */
for rest in data.iter().skip(arg_index) {
formatted.push_str(&format!(" {}", rest))
formatted.push_str(&format!(" {}", ctx.to_string(rest)?))
}
Ok(formatted)

6
boa/src/builtins/error/range.rs

@ -54,9 +54,9 @@ impl RangeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("message");
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let name = ctx.to_string(&this.get_field("name"))?;
let message = ctx.to_string(&this.get_field("message"))?;
Ok(Value::from(format!("{}: {}", name, message)))
}

6
boa/src/builtins/error/reference.rs

@ -53,9 +53,9 @@ impl ReferenceError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("message");
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let name = ctx.to_string(&this.get_field("name"))?;
let message = ctx.to_string(&this.get_field("message"))?;
Ok(Value::from(format!("{}: {}", name, message)))
}

6
boa/src/builtins/error/type.rs

@ -60,9 +60,9 @@ impl TypeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("message");
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let name = ctx.to_string(&this.get_field("name"))?;
let message = ctx.to_string(&this.get_field("message"))?;
Ok(Value::from(format!("{}: {}", name, message)))
}

4
boa/src/builtins/function/mod.rs

@ -14,7 +14,7 @@
use crate::{
builtins::{
object::{Object, ObjectData, PROTOTYPE},
property::{Attribute, Property},
property::{Attribute, Property, PropertyKey},
value::{RcString, ResultValue, Value},
Array,
},
@ -416,7 +416,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
// Define length as a property
obj.define_own_property("length".to_string(), length);
obj.define_own_property(&PropertyKey::from(RcString::from("length")), length);
let mut index: usize = 0;
while index < len {
let val = arguments_list.get(index).expect("Could not get argument");

2
boa/src/builtins/global_this/tests.rs

@ -6,5 +6,5 @@ fn global_this_exists_on_global_object_and_evaluates_to_an_object() {
typeof globalThis;
"#;
assert_eq!(&exec(scenario), "object");
assert_eq!(&exec(scenario), "\"object\"");
}

6
boa/src/builtins/json/mod.rs

@ -53,7 +53,7 @@ impl Json {
match args.get(1) {
Some(reviver) if reviver.is_function() => {
let mut holder = Value::new_object(None);
holder.set_field(Value::from(""), j);
holder.set_field("", j);
Self::walk(reviver, ctx, &mut holder, Value::from(""))
}
_ => Ok(j),
@ -78,7 +78,7 @@ impl Json {
let v = Self::walk(reviver, ctx, &mut value, Value::from(key.as_str()));
match v {
Ok(v) if !v.is_undefined() => {
value.set_field(Value::from(key.as_str()), v);
value.set_field(key.as_str(), v);
}
Ok(_) => {
value.remove_property(key.as_str());
@ -161,7 +161,7 @@ impl Json {
.and_then(|prop| prop.value.as_ref().map(|v| v.to_json(ctx)))
.transpose()?
{
obj_to_return.insert(field.to_string(), value);
obj_to_return.insert(ctx.to_string(&field)?.to_string(), value);
}
}
Ok(Value::from(JSONValue::Object(obj_to_return).to_string()))

8
boa/src/builtins/json/tests.rs

@ -31,7 +31,7 @@ fn json_stringify_remove_undefined_values_from_objects() {
&mut engine,
r#"JSON.stringify({ aaa: undefined, bbb: 'ccc' })"#,
);
let expected = r#"{"bbb":"ccc"}"#;
let expected = r#""{"bbb":"ccc"}""#;
assert_eq!(actual, expected);
}
@ -45,7 +45,7 @@ fn json_stringify_remove_function_values_from_objects() {
&mut engine,
r#"JSON.stringify({ aaa: () => {}, bbb: 'ccc' })"#,
);
let expected = r#"{"bbb":"ccc"}"#;
let expected = r#""{"bbb":"ccc"}""#;
assert_eq!(actual, expected);
}
@ -59,7 +59,7 @@ fn json_stringify_remove_symbols_from_objects() {
&mut engine,
r#"JSON.stringify({ aaa: Symbol(), bbb: 'ccc' })"#,
);
let expected = r#"{"bbb":"ccc"}"#;
let expected = r#""{"bbb":"ccc"}""#;
assert_eq!(actual, expected);
}
@ -264,7 +264,7 @@ fn json_parse_object_with_reviver() {
JSON.stringify(jsonObj);"#,
);
assert_eq!(result, r#"{"firstname":"boa","lastname":"interpreter"}"#);
assert_eq!(result, r#""{"firstname":"boa","lastname":"interpreter"}""#);
}
#[test]

32
boa/src/builtins/map/tests.rs

@ -61,7 +61,7 @@ fn merge() {
let result = forward(&mut engine, "merged1.size");
assert_eq!(result, "3");
let result = forward(&mut engine, "merged1.get('2')");
assert_eq!(result, "second two");
assert_eq!(result, "\"second two\"");
let result = forward(&mut engine, "merged2.size");
assert_eq!(result, "4");
}
@ -75,9 +75,9 @@ fn get() {
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.get('1')");
assert_eq!(result, "one");
assert_eq!(result, "\"one\"");
let result = forward(&mut engine, "map.get('2')");
assert_eq!(result, "two");
assert_eq!(result, "\"two\"");
let result = forward(&mut engine, "map.get('3')");
assert_eq!(result, "undefined");
let result = forward(&mut engine, "map.get()");
@ -95,11 +95,11 @@ fn set() {
let result = forward(&mut engine, "map.set()");
assert_eq!(result, "Map { undefined → undefined }");
let result = forward(&mut engine, "map.set('1', 'one')");
assert_eq!(result, "Map { undefined → undefined, 1 → one }");
assert_eq!(result, "Map { undefined → undefined, \"1\"\"one\" }");
let result = forward(&mut engine, "map.set('2')");
assert_eq!(
result,
"Map { undefined → undefined, 1 → one, 2 → undefined }"
"Map { undefined → undefined, \"1\"\"one\", \"2\" → undefined }"
);
}
@ -181,7 +181,7 @@ fn modify_key() {
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map.get(obj)");
assert_eq!(result, "one");
assert_eq!(result, "\"one\"");
}
#[test]
@ -194,15 +194,21 @@ fn order() {
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map");
assert_eq!(result, "Map { 1 → one, 2 → two }");
assert_eq!(result, "Map { 1 → \"one\", 2 → \"two\" }");
let result = forward(&mut engine, "map.set(1, \"five\");map");
assert_eq!(result, "Map { 1 → five, 2 → two }");
assert_eq!(result, "Map { 1 → \"five\", 2 → \"two\" }");
let result = forward(&mut engine, "map.set();map");
assert_eq!(result, "Map { 1 → five, 2 → two, undefined → undefined }");
assert_eq!(
result,
"Map { 1 → \"five\", 2 → \"two\", undefined → undefined }"
);
let result = forward(&mut engine, "map.delete(2);map");
assert_eq!(result, "Map { 1 → five, undefined → undefined }");
assert_eq!(result, "Map { 1 → \"five\", undefined → undefined }");
let result = forward(&mut engine, "map.set(2, \"two\");map");
assert_eq!(result, "Map { 1 → five, undefined → undefined, 2 → two }");
assert_eq!(
result,
"Map { 1 → \"five\", undefined → undefined, 2 → \"two\" }"
);
}
#[test]
@ -216,9 +222,9 @@ fn recursive_display() {
"#;
forward(&mut engine, init);
let result = forward(&mut engine, "map");
assert_eq!(result, "Map { y → Map(1) }");
assert_eq!(result, "Map { \"y\" → Map(1) }");
let result = forward(&mut engine, "map.set(\"z\", array)");
assert_eq!(result, "Map { y → Map(2), z → Array(1) }");
assert_eq!(result, "Map { \"y\" → Map(2), \"z\" → Array(1) }");
}
#[test]

175
boa/src/builtins/number/tests.rs

@ -70,12 +70,12 @@ fn to_exponential() {
let nan_exp = forward(&mut engine, "nan_exp");
let noop_exp = forward(&mut engine, "noop_exp");
assert_eq!(default_exp, "0e+0");
assert_eq!(int_exp, "5e+0");
assert_eq!(float_exp, "1.234e+0");
assert_eq!(big_exp, "1.234e+3");
assert_eq!(nan_exp, "NaN");
assert_eq!(noop_exp, "1.23e+2");
assert_eq!(default_exp, "\"0e+0\"");
assert_eq!(int_exp, "\"5e+0\"");
assert_eq!(float_exp, "\"1.234e+0\"");
assert_eq!(big_exp, "\"1.234e+3\"");
assert_eq!(nan_exp, "\"NaN\"");
assert_eq!(noop_exp, "\"1.23e+2\"");
}
#[test]
@ -97,11 +97,11 @@ fn to_fixed() {
let noop_fixed = forward(&mut engine, "noop_fixed");
let nan_fixed = forward(&mut engine, "nan_fixed");
assert_eq!(default_fixed, String::from("0"));
assert_eq!(pos_fixed, String::from("34560"));
assert_eq!(neg_fixed, String::from("0"));
assert_eq!(noop_fixed, String::from("5"));
assert_eq!(nan_fixed, String::from("NaN"));
assert_eq!(default_fixed, "\"0\"");
assert_eq!(pos_fixed, "\"34560\"");
assert_eq!(neg_fixed, "\"0\"");
assert_eq!(noop_fixed, "\"5\"");
assert_eq!(nan_fixed, "\"NaN\"");
}
#[test]
@ -124,10 +124,10 @@ fn to_locale_string() {
let big_locale = forward(&mut engine, "big_locale");
let neg_locale = forward(&mut engine, "neg_locale");
assert_eq!(default_locale, String::from("0"));
assert_eq!(small_locale, String::from("5"));
assert_eq!(big_locale, String::from("345600"));
assert_eq!(neg_locale, String::from("-25"));
assert_eq!(default_locale, "\"0\"");
assert_eq!(small_locale, "\"5\"");
assert_eq!(big_locale, "\"345600\"");
assert_eq!(neg_locale, "\"-25\"");
}
#[test]
@ -168,161 +168,170 @@ fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!("NaN", &forward(&mut engine, "Number(NaN).toString()"));
assert_eq!("Infinity", &forward(&mut engine, "Number(1/0).toString()"));
assert_eq!("\"NaN\"", &forward(&mut engine, "Number(NaN).toString()"));
assert_eq!(
"-Infinity",
"\"Infinity\"",
&forward(&mut engine, "Number(1/0).toString()")
);
assert_eq!(
"\"-Infinity\"",
&forward(&mut engine, "Number(-1/0).toString()")
);
assert_eq!("0", &forward(&mut engine, "Number(0).toString()"));
assert_eq!("9", &forward(&mut engine, "Number(9).toString()"));
assert_eq!("90", &forward(&mut engine, "Number(90).toString()"));
assert_eq!("90.12", &forward(&mut engine, "Number(90.12).toString()"));
assert_eq!("0.1", &forward(&mut engine, "Number(0.1).toString()"));
assert_eq!("0.01", &forward(&mut engine, "Number(0.01).toString()"));
assert_eq!("0.0123", &forward(&mut engine, "Number(0.0123).toString()"));
assert_eq!("\"0\"", &forward(&mut engine, "Number(0).toString()"));
assert_eq!("\"9\"", &forward(&mut engine, "Number(9).toString()"));
assert_eq!("\"90\"", &forward(&mut engine, "Number(90).toString()"));
assert_eq!(
"\"90.12\"",
&forward(&mut engine, "Number(90.12).toString()")
);
assert_eq!("\"0.1\"", &forward(&mut engine, "Number(0.1).toString()"));
assert_eq!("\"0.01\"", &forward(&mut engine, "Number(0.01).toString()"));
assert_eq!(
"0.00001",
"\"0.0123\"",
&forward(&mut engine, "Number(0.0123).toString()")
);
assert_eq!(
"\"0.00001\"",
&forward(&mut engine, "Number(0.00001).toString()")
);
assert_eq!(
"0.000001",
"\"0.000001\"",
&forward(&mut engine, "Number(0.000001).toString()")
);
assert_eq!("NaN", &forward(&mut engine, "Number(NaN).toString(16)"));
assert_eq!("\"NaN\"", &forward(&mut engine, "Number(NaN).toString(16)"));
assert_eq!(
"Infinity",
"\"Infinity\"",
&forward(&mut engine, "Number(1/0).toString(16)")
);
assert_eq!(
"-Infinity",
"\"-Infinity\"",
&forward(&mut engine, "Number(-1/0).toString(16)")
);
assert_eq!("0", &forward(&mut engine, "Number(0).toString(16)"));
assert_eq!("9", &forward(&mut engine, "Number(9).toString(16)"));
assert_eq!("5a", &forward(&mut engine, "Number(90).toString(16)"));
assert_eq!("\"0\"", &forward(&mut engine, "Number(0).toString(16)"));
assert_eq!("\"9\"", &forward(&mut engine, "Number(9).toString(16)"));
assert_eq!("\"5a\"", &forward(&mut engine, "Number(90).toString(16)"));
assert_eq!(
"5a.1eb851eb852",
"\"5a.1eb851eb852\"",
&forward(&mut engine, "Number(90.12).toString(16)")
);
assert_eq!(
"0.1999999999999a",
"\"0.1999999999999a\"",
&forward(&mut engine, "Number(0.1).toString(16)")
);
assert_eq!(
"0.028f5c28f5c28f6",
"\"0.028f5c28f5c28f6\"",
&forward(&mut engine, "Number(0.01).toString(16)")
);
assert_eq!(
"0.032617c1bda511a",
"\"0.032617c1bda511a\"",
&forward(&mut engine, "Number(0.0123).toString(16)")
);
assert_eq!(
"605f9f6dd18bc8000",
"\"605f9f6dd18bc8000\"",
&forward(&mut engine, "Number(111111111111111111111).toString(16)")
);
assert_eq!(
"3c3bc3a4a2f75c0000",
"\"3c3bc3a4a2f75c0000\"",
&forward(&mut engine, "Number(1111111111111111111111).toString(16)")
);
assert_eq!(
"25a55a46e5da9a00000",
"\"25a55a46e5da9a00000\"",
&forward(&mut engine, "Number(11111111111111111111111).toString(16)")
);
assert_eq!(
"0.0000a7c5ac471b4788",
"\"0.0000a7c5ac471b4788\"",
&forward(&mut engine, "Number(0.00001).toString(16)")
);
assert_eq!(
"0.000010c6f7a0b5ed8d",
"\"0.000010c6f7a0b5ed8d\"",
&forward(&mut engine, "Number(0.000001).toString(16)")
);
assert_eq!(
"0.000001ad7f29abcaf48",
"\"0.000001ad7f29abcaf48\"",
&forward(&mut engine, "Number(0.0000001).toString(16)")
);
assert_eq!(
"0.000002036565348d256",
"\"0.000002036565348d256\"",
&forward(&mut engine, "Number(0.00000012).toString(16)")
);
assert_eq!(
"0.0000021047ee22aa466",
"\"0.0000021047ee22aa466\"",
&forward(&mut engine, "Number(0.000000123).toString(16)")
);
assert_eq!(
"0.0000002af31dc4611874",
"\"0.0000002af31dc4611874\"",
&forward(&mut engine, "Number(0.00000001).toString(16)")
);
assert_eq!(
"0.000000338a23b87483be",
"\"0.000000338a23b87483be\"",
&forward(&mut engine, "Number(0.000000012).toString(16)")
);
assert_eq!(
"0.00000034d3fe36aaa0a2",
"\"0.00000034d3fe36aaa0a2\"",
&forward(&mut engine, "Number(0.0000000123).toString(16)")
);
assert_eq!("0", &forward(&mut engine, "Number(-0).toString(16)"));
assert_eq!("-9", &forward(&mut engine, "Number(-9).toString(16)"));
assert_eq!("-5a", &forward(&mut engine, "Number(-90).toString(16)"));
assert_eq!("\"0\"", &forward(&mut engine, "Number(-0).toString(16)"));
assert_eq!("\"-9\"", &forward(&mut engine, "Number(-9).toString(16)"));
assert_eq!("\"-5a\"", &forward(&mut engine, "Number(-90).toString(16)"));
assert_eq!(
"-5a.1eb851eb852",
"\"-5a.1eb851eb852\"",
&forward(&mut engine, "Number(-90.12).toString(16)")
);
assert_eq!(
"-0.1999999999999a",
"\"-0.1999999999999a\"",
&forward(&mut engine, "Number(-0.1).toString(16)")
);
assert_eq!(
"-0.028f5c28f5c28f6",
"\"-0.028f5c28f5c28f6\"",
&forward(&mut engine, "Number(-0.01).toString(16)")
);
assert_eq!(
"-0.032617c1bda511a",
"\"-0.032617c1bda511a\"",
&forward(&mut engine, "Number(-0.0123).toString(16)")
);
assert_eq!(
"-605f9f6dd18bc8000",
"\"-605f9f6dd18bc8000\"",
&forward(&mut engine, "Number(-111111111111111111111).toString(16)")
);
assert_eq!(
"-3c3bc3a4a2f75c0000",
"\"-3c3bc3a4a2f75c0000\"",
&forward(&mut engine, "Number(-1111111111111111111111).toString(16)")
);
assert_eq!(
"-25a55a46e5da9a00000",
"\"-25a55a46e5da9a00000\"",
&forward(&mut engine, "Number(-11111111111111111111111).toString(16)")
);
assert_eq!(
"-0.0000a7c5ac471b4788",
"\"-0.0000a7c5ac471b4788\"",
&forward(&mut engine, "Number(-0.00001).toString(16)")
);
assert_eq!(
"-0.000010c6f7a0b5ed8d",
"\"-0.000010c6f7a0b5ed8d\"",
&forward(&mut engine, "Number(-0.000001).toString(16)")
);
assert_eq!(
"-0.000001ad7f29abcaf48",
"\"-0.000001ad7f29abcaf48\"",
&forward(&mut engine, "Number(-0.0000001).toString(16)")
);
assert_eq!(
"-0.000002036565348d256",
"\"-0.000002036565348d256\"",
&forward(&mut engine, "Number(-0.00000012).toString(16)")
);
assert_eq!(
"-0.0000021047ee22aa466",
"\"-0.0000021047ee22aa466\"",
&forward(&mut engine, "Number(-0.000000123).toString(16)")
);
assert_eq!(
"-0.0000002af31dc4611874",
"\"-0.0000002af31dc4611874\"",
&forward(&mut engine, "Number(-0.00000001).toString(16)")
);
assert_eq!(
"-0.000000338a23b87483be",
"\"-0.000000338a23b87483be\"",
&forward(&mut engine, "Number(-0.000000012).toString(16)")
);
assert_eq!(
"-0.00000034d3fe36aaa0a2",
"\"-0.00000034d3fe36aaa0a2\"",
&forward(&mut engine, "Number(-0.0000000123).toString(16)")
);
}
@ -332,26 +341,38 @@ fn num_to_string_exponential() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!("0", forward(&mut engine, "(0).toString()"));
assert_eq!("0", forward(&mut engine, "(-0).toString()"));
assert_eq!("\"0\"", forward(&mut engine, "(0).toString()"));
assert_eq!("\"0\"", forward(&mut engine, "(-0).toString()"));
assert_eq!(
"111111111111111110000",
"\"111111111111111110000\"",
forward(&mut engine, "(111111111111111111111).toString()")
);
assert_eq!(
"1.1111111111111111e+21",
"\"1.1111111111111111e+21\"",
forward(&mut engine, "(1111111111111111111111).toString()")
);
assert_eq!(
"1.1111111111111111e+22",
"\"1.1111111111111111e+22\"",
forward(&mut engine, "(11111111111111111111111).toString()")
);
assert_eq!("1e-7", forward(&mut engine, "(0.0000001).toString()"));
assert_eq!("1.2e-7", forward(&mut engine, "(0.00000012).toString()"));
assert_eq!("1.23e-7", forward(&mut engine, "(0.000000123).toString()"));
assert_eq!("1e-8", forward(&mut engine, "(0.00000001).toString()"));
assert_eq!("1.2e-8", forward(&mut engine, "(0.000000012).toString()"));
assert_eq!("1.23e-8", forward(&mut engine, "(0.0000000123).toString()"));
assert_eq!("\"1e-7\"", forward(&mut engine, "(0.0000001).toString()"));
assert_eq!(
"\"1.2e-7\"",
forward(&mut engine, "(0.00000012).toString()")
);
assert_eq!(
"\"1.23e-7\"",
forward(&mut engine, "(0.000000123).toString()")
);
assert_eq!("\"1e-8\"", forward(&mut engine, "(0.00000001).toString()"));
assert_eq!(
"\"1.2e-8\"",
forward(&mut engine, "(0.000000012).toString()")
);
assert_eq!(
"\"1.23e-8\"",
forward(&mut engine, "(0.0000000123).toString()")
);
}
#[test]

101
boa/src/builtins/object/internal_methods.rs

@ -7,7 +7,7 @@
use crate::builtins::{
object::{Object, PROTOTYPE},
property::{Attribute, Property},
property::{Attribute, Property, PropertyKey},
value::{same_value, RcString, Value},
};
use crate::BoaProfiler;
@ -19,16 +19,15 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
pub fn has_property(&self, val: &Value) -> bool {
debug_assert!(Property::is_property_key(val));
let prop = self.get_own_property(val);
pub fn has_property(&self, property_key: &PropertyKey) -> bool {
let prop = self.get_own_property(property_key);
if prop.value.is_none() {
let parent: Value = self.get_prototype_of();
if !parent.is_null() {
// the parent value variant should be an object
// In the unlikely event it isn't return false
return match parent {
Value::Object(ref obj) => obj.borrow().has_property(val),
Value::Object(ref obj) => obj.borrow().has_property(property_key),
_ => false,
};
}
@ -62,9 +61,8 @@ impl Object {
}
/// Delete property.
pub fn delete(&mut self, prop_key: &Value) -> bool {
debug_assert!(Property::is_property_key(prop_key));
let desc = self.get_own_property(prop_key);
pub fn delete(&mut self, property_key: &PropertyKey) -> bool {
let desc = self.get_own_property(property_key);
if desc
.value
.clone()
@ -74,17 +72,17 @@ impl Object {
return true;
}
if desc.configurable_or(false) {
self.remove_property(&prop_key.to_string());
self.remove_property(&property_key.to_string());
return true;
}
false
}
// [[Get]]
pub fn get(&self, val: &Value) -> Value {
debug_assert!(Property::is_property_key(val));
let desc = self.get_own_property(val);
/// [[Get]]
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver
pub fn get(&self, property_key: &PropertyKey) -> Value {
let desc = self.get_own_property(property_key);
if desc.value.clone().is_none()
|| desc
.value
@ -100,7 +98,7 @@ impl Object {
let parent_obj = Object::from(&parent).expect("Failed to get object");
return parent_obj.get(val);
return parent_obj.get(property_key);
}
if desc.is_data_descriptor() {
@ -118,13 +116,11 @@ impl Object {
/// [[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 {
pub fn set(&mut self, property_key: &PropertyKey, val: Value) -> bool {
let _timer = BoaProfiler::global().start_event("Object::set", "object");
// [1]
debug_assert!(Property::is_property_key(&field));
// Fetch property key
let mut own_desc = self.get_own_property(&field);
let mut own_desc = self.get_own_property(property_key);
// [2]
if own_desc.is_none() {
let parent = self.get_prototype_of();
@ -144,7 +140,7 @@ impl Object {
// Change value on the current descriptor
own_desc = own_desc.value(val);
return self.define_own_property(field.to_string(), own_desc);
return self.define_own_property(property_key, own_desc);
}
// [4]
debug_assert!(own_desc.is_accessor_descriptor());
@ -162,10 +158,10 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
pub fn define_own_property(&mut self, property_key: String, desc: Property) -> bool {
pub fn define_own_property(&mut self, property_key: &PropertyKey, desc: Property) -> bool {
let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object");
let mut current = self.get_own_property(&Value::from(property_key.to_string()));
let mut current = self.get_own_property(property_key);
let extensible = self.is_extensible();
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
@ -214,7 +210,7 @@ impl Object {
current.set = None;
}
self.insert_property(property_key.clone(), current);
self.insert_property(property_key, current);
// 7
} else if current.is_data_descriptor() && desc.is_data_descriptor() {
// a
@ -265,41 +261,40 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
pub fn get_own_property(&self, prop: &Value) -> Property {
pub fn get_own_property(&self, property_key: &PropertyKey) -> Property {
let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object");
debug_assert!(Property::is_property_key(prop));
// Prop could either be a String or Symbol
match *prop {
Value::String(ref st) => self.properties().get(st).map_or_else(Property::empty, |v| {
let mut d = Property::empty();
if v.is_data_descriptor() {
d.value = v.value.clone();
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.attribute = v.attribute;
d
}),
Value::Symbol(ref symbol) => {
self.symbol_properties()
.get(&symbol.hash())
.map_or_else(Property::empty, |v| {
let mut d = Property::empty();
if v.is_data_descriptor() {
d.value = v.value.clone();
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.attribute = v.attribute;
d
})
match property_key {
PropertyKey::String(ref st) => {
self.properties().get(st).map_or_else(Property::empty, |v| {
let mut d = Property::empty();
if v.is_data_descriptor() {
d.value = v.value.clone();
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.attribute = v.attribute;
d
})
}
_ => unreachable!("the field can only be of type string or symbol"),
PropertyKey::Symbol(ref symbol) => self
.symbol_properties()
.get(&symbol.hash())
.map_or_else(Property::empty, |v| {
let mut d = Property::empty();
if v.is_data_descriptor() {
d.value = v.value.clone();
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.attribute = v.attribute;
d
}),
}
}

2
boa/src/builtins/object/mod.rs

@ -549,7 +549,7 @@ pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Interpreter) ->
.as_object()
.as_deref()
.expect("Cannot get THIS object")
.get_own_property(&Value::string(prop.expect("cannot get prop")));
.get_own_property(&prop.expect("cannot get prop").into());
if own_property.is_none() {
Ok(Value::from(false))
} else {

4
boa/src/builtins/object/tests.rs

@ -32,7 +32,7 @@ fn object_create_with_undefined() {
let result = forward(&mut engine, init);
assert_eq!(
result,
"TypeError: Object prototype may only be an Object or null: undefined"
"\"TypeError: Object prototype may only be an Object or null: undefined\""
);
}
@ -52,7 +52,7 @@ fn object_create_with_number() {
let result = forward(&mut engine, init);
assert_eq!(
result,
"TypeError: Object prototype may only be an Object or null: 5"
"\"TypeError: Object prototype may only be an Object or null: 5\""
);
}

100
boa/src/builtins/property/mod.rs

@ -14,15 +14,15 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
//! [section]: https://tc39.es/ecma262/#sec-property-attributes
use crate::builtins::value::rcstring::RcString;
use crate::builtins::value::rcsymbol::RcSymbol;
use crate::builtins::Value;
use gc::{Finalize, Trace};
use std::fmt;
pub mod attribute;
pub use attribute::Attribute;
#[cfg(test)]
mod tests;
/// This represents a Javascript Property AKA The Property Descriptor.
///
/// Property descriptors present in objects come in two main flavors:
@ -53,12 +53,6 @@ pub struct Property {
}
impl Property {
/// Checks if the provided Value can be used as a property key.
#[inline]
pub fn is_property_key(value: &Value) -> bool {
value.is_string() || value.is_symbol()
}
/// Make a new property with the given value
/// The difference between New and Default:
///
@ -284,3 +278,91 @@ impl<'a> From<&'a Value> for Property {
}
}
}
/// This abstracts away the need for IsPropertyKey by transforming the PropertyKey
/// values into an enum with both valid types: String and Symbol
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ispropertykey
#[derive(Trace, Finalize, Debug, Clone)]
pub enum PropertyKey {
String(RcString),
Symbol(RcSymbol),
}
impl From<RcString> for PropertyKey {
#[inline]
fn from(string: RcString) -> PropertyKey {
PropertyKey::String(string)
}
}
impl From<&str> for PropertyKey {
#[inline]
fn from(string: &str) -> PropertyKey {
PropertyKey::String(string.into())
}
}
impl From<String> for PropertyKey {
#[inline]
fn from(string: String) -> PropertyKey {
PropertyKey::String(string.into())
}
}
impl From<Box<str>> for PropertyKey {
#[inline]
fn from(string: Box<str>) -> PropertyKey {
PropertyKey::String(string.into())
}
}
impl From<RcSymbol> for PropertyKey {
#[inline]
fn from(symbol: RcSymbol) -> PropertyKey {
PropertyKey::Symbol(symbol)
}
}
impl fmt::Display for PropertyKey {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PropertyKey::String(ref string) => string.fmt(f),
PropertyKey::Symbol(ref symbol) => symbol.fmt(f),
}
}
}
impl From<&PropertyKey> for RcString {
#[inline]
fn from(property_key: &PropertyKey) -> RcString {
match property_key {
PropertyKey::String(ref string) => string.clone(),
PropertyKey::Symbol(ref symbol) => symbol.to_string().into(),
}
}
}
impl From<&PropertyKey> for Value {
#[inline]
fn from(property_key: &PropertyKey) -> Value {
match property_key {
PropertyKey::String(ref string) => string.clone().into(),
PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
}
}
}
impl From<PropertyKey> for Value {
#[inline]
fn from(property_key: PropertyKey) -> Value {
match property_key {
PropertyKey::String(ref string) => string.clone().into(),
PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
}
}
}

10
boa/src/builtins/property/tests.rs

@ -1,10 +0,0 @@
use super::*;
#[test]
fn is_property_key_test() {
let v = Value::string("Boop");
assert!(Property::is_property_key(&v));
let v = Value::boolean(true);
assert!(!Property::is_property_key(&v));
}

19
boa/src/builtins/regexp/tests.rs

@ -78,13 +78,16 @@ fn exec() {
"#;
eprintln!("{}", forward(&mut engine, init));
assert_eq!(forward(&mut engine, "result[0]"), "Quick Brown Fox Jumps");
assert_eq!(forward(&mut engine, "result[1]"), "Brown");
assert_eq!(forward(&mut engine, "result[2]"), "Jumps");
assert_eq!(
forward(&mut engine, "result[0]"),
"\"Quick Brown Fox Jumps\""
);
assert_eq!(forward(&mut engine, "result[1]"), "\"Brown\"");
assert_eq!(forward(&mut engine, "result[2]"), "\"Jumps\"");
assert_eq!(forward(&mut engine, "result.index"), "4");
assert_eq!(
forward(&mut engine, "result.input"),
"The Quick Brown Fox Jumps Over The Lazy Dog"
"\"The Quick Brown Fox Jumps Over The Lazy Dog\""
);
}
@ -95,15 +98,15 @@ fn to_string() {
assert_eq!(
forward(&mut engine, "(new RegExp('a+b+c')).toString()"),
"/a+b+c/"
"\"/a+b+c/\""
);
assert_eq!(
forward(&mut engine, "(new RegExp('bar', 'g')).toString()"),
"/bar/g"
"\"/bar/g\""
);
assert_eq!(
forward(&mut engine, "(new RegExp('\\\\n', 'g')).toString()"),
"/\\n/g"
"\"/\\n/g\""
);
assert_eq!(forward(&mut engine, "/\\n/g.toString()"), "/\\n/g");
assert_eq!(forward(&mut engine, "/\\n/g.toString()"), "\"/\\n/g\"");
}

5
boa/src/builtins/string/mod.rs

@ -418,7 +418,10 @@ impl String {
if obj.internal_slots().get("RegExpMatcher").is_some() {
// first argument is another `RegExp` object, so copy its pattern and flags
if let Some(body) = obj.internal_slots().get("OriginalSource") {
return body.to_string();
return body
.as_string()
.expect("OriginalSource should be a string")
.into();
}
}
"undefined".to_string()

118
boa/src/builtins/string/tests.rs

@ -66,10 +66,10 @@ fn concat() {
eprintln!("{}", forward(&mut engine, init));
let a = forward(&mut engine, "hello.concat(world, nice)");
assert_eq!(a, "Hello, world! Have a nice day.");
assert_eq!(a, "\"Hello, world! Have a nice day.\"");
let b = forward(&mut engine, "hello + world + nice");
assert_eq!(b, "Hello, world! Have a nice day.");
assert_eq!(b, "\"Hello, world! Have a nice day.\"");
}
#[test]
@ -83,7 +83,7 @@ fn generic_concat() {
eprintln!("{}", forward(&mut engine, init));
let a = forward(&mut engine, "number.concat(' - 50', ' = 50')");
assert_eq!(a, "100 - 50 = 50");
assert_eq!(a, "\"100 - 50 = 50\"");
}
#[allow(clippy::unwrap_used)]
@ -118,14 +118,14 @@ fn repeat() {
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "empty.repeat(0)"), "");
assert_eq!(forward(&mut engine, "empty.repeat(1)"), "");
assert_eq!(forward(&mut engine, "empty.repeat(0)"), "\"\"");
assert_eq!(forward(&mut engine, "empty.repeat(1)"), "\"\"");
assert_eq!(forward(&mut engine, "en.repeat(0)"), "");
assert_eq!(forward(&mut engine, "zh.repeat(0)"), "");
assert_eq!(forward(&mut engine, "en.repeat(0)"), "\"\"");
assert_eq!(forward(&mut engine, "zh.repeat(0)"), "\"\"");
assert_eq!(forward(&mut engine, "en.repeat(1)"), "english");
assert_eq!(forward(&mut engine, "zh.repeat(2)"), "中文中文");
assert_eq!(forward(&mut engine, "en.repeat(1)"), "\"english\"");
assert_eq!(forward(&mut engine, "zh.repeat(2)"), "\"中文中文\"");
}
#[test]
@ -144,7 +144,7 @@ fn repeat_throws_when_count_is_negative() {
}
"#
),
"RangeError: repeat count cannot be a negative number"
"\"RangeError: repeat count cannot be a negative number\""
);
}
@ -164,7 +164,7 @@ fn repeat_throws_when_count_is_infinity() {
}
"#
),
"RangeError: repeat count cannot be infinity"
"\"RangeError: repeat count cannot be infinity\""
);
}
@ -184,7 +184,7 @@ fn repeat_throws_when_count_overflows_max_length() {
}
"#
),
"RangeError: repeat count must not overflow maximum string length"
"\"RangeError: repeat count must not overflow maximum string length\""
);
}
@ -196,11 +196,11 @@ fn repeat_generic() {
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "(0).repeat(0)"), "");
assert_eq!(forward(&mut engine, "(1).repeat(1)"), "1");
assert_eq!(forward(&mut engine, "(0).repeat(0)"), "\"\"");
assert_eq!(forward(&mut engine, "(1).repeat(1)"), "\"1\"");
assert_eq!(forward(&mut engine, "(1).repeat(5)"), "11111");
assert_eq!(forward(&mut engine, "(12).repeat(3)"), "121212");
assert_eq!(forward(&mut engine, "(1).repeat(5)"), "\"11111\"");
assert_eq!(forward(&mut engine, "(12).repeat(3)"), "\"121212\"");
}
#[test]
@ -215,7 +215,7 @@ fn replace() {
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "a"), "2bc");
assert_eq!(forward(&mut engine, "a"), "\"2bc\"");
}
#[test]
@ -238,11 +238,11 @@ fn replace_with_function() {
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "a"), "ecmascript is awesome!");
assert_eq!(forward(&mut engine, "a"), "\"ecmascript is awesome!\"");
assert_eq!(forward(&mut engine, "p1"), "o");
assert_eq!(forward(&mut engine, "p2"), "o");
assert_eq!(forward(&mut engine, "p3"), "l");
assert_eq!(forward(&mut engine, "p1"), "\"o\"");
assert_eq!(forward(&mut engine, "p2"), "\"o\"");
assert_eq!(forward(&mut engine, "p3"), "\"l\"");
}
#[test]
@ -311,10 +311,10 @@ fn match_all() {
);
assert_eq!(forward(&mut engine, "groupMatches.length"), "2");
assert_eq!(forward(&mut engine, "groupMatches[0][1]"), "e");
assert_eq!(forward(&mut engine, "groupMatches[0][2]"), "st1");
assert_eq!(forward(&mut engine, "groupMatches[0][3]"), "1");
assert_eq!(forward(&mut engine, "groupMatches[1][3]"), "2");
assert_eq!(forward(&mut engine, "groupMatches[0][1]"), "\"e\"");
assert_eq!(forward(&mut engine, "groupMatches[0][2]"), "\"st1\"");
assert_eq!(forward(&mut engine, "groupMatches[0][3]"), "\"1\"");
assert_eq!(forward(&mut engine, "groupMatches[1][3]"), "\"2\"");
assert_eq!(
forward(
@ -332,9 +332,9 @@ fn match_all() {
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "matches[0][0]"), "football");
assert_eq!(forward(&mut engine, "matches[0][0]"), "\"football\"");
assert_eq!(forward(&mut engine, "matches[0].index"), "6");
assert_eq!(forward(&mut engine, "matches[1][0]"), "foosball");
assert_eq!(forward(&mut engine, "matches[1][0]"), "\"foosball\"");
assert_eq!(forward(&mut engine, "matches[1].index"), "16");
}
@ -352,60 +352,66 @@ fn test_match() {
forward(&mut engine, init);
assert_eq!(forward(&mut engine, "result1[0]"), "Quick Brown Fox Jumps");
assert_eq!(forward(&mut engine, "result1[1]"), "Brown");
assert_eq!(forward(&mut engine, "result1[2]"), "Jumps");
assert_eq!(
forward(&mut engine, "result1[0]"),
"\"Quick Brown Fox Jumps\""
);
assert_eq!(forward(&mut engine, "result1[1]"), "\"Brown\"");
assert_eq!(forward(&mut engine, "result1[2]"), "\"Jumps\"");
assert_eq!(forward(&mut engine, "result1.index"), "4");
assert_eq!(
forward(&mut engine, "result1.input"),
"The Quick Brown Fox Jumps Over The Lazy Dog"
"\"The Quick Brown Fox Jumps Over The Lazy Dog\""
);
assert_eq!(forward(&mut engine, "result2[0]"), "T");
assert_eq!(forward(&mut engine, "result2[1]"), "Q");
assert_eq!(forward(&mut engine, "result2[2]"), "B");
assert_eq!(forward(&mut engine, "result2[3]"), "F");
assert_eq!(forward(&mut engine, "result2[4]"), "J");
assert_eq!(forward(&mut engine, "result2[5]"), "O");
assert_eq!(forward(&mut engine, "result2[6]"), "T");
assert_eq!(forward(&mut engine, "result2[7]"), "L");
assert_eq!(forward(&mut engine, "result2[8]"), "D");
assert_eq!(forward(&mut engine, "result3[0]"), "T");
assert_eq!(forward(&mut engine, "result2[0]"), "\"T\"");
assert_eq!(forward(&mut engine, "result2[1]"), "\"Q\"");
assert_eq!(forward(&mut engine, "result2[2]"), "\"B\"");
assert_eq!(forward(&mut engine, "result2[3]"), "\"F\"");
assert_eq!(forward(&mut engine, "result2[4]"), "\"J\"");
assert_eq!(forward(&mut engine, "result2[5]"), "\"O\"");
assert_eq!(forward(&mut engine, "result2[6]"), "\"T\"");
assert_eq!(forward(&mut engine, "result2[7]"), "\"L\"");
assert_eq!(forward(&mut engine, "result2[8]"), "\"D\"");
assert_eq!(forward(&mut engine, "result3[0]"), "\"T\"");
assert_eq!(forward(&mut engine, "result3.index"), "0");
assert_eq!(
forward(&mut engine, "result3.input"),
"The Quick Brown Fox Jumps Over The Lazy Dog"
"\"The Quick Brown Fox Jumps Over The Lazy Dog\""
);
assert_eq!(forward(&mut engine, "result4[0]"), "B");
assert_eq!(forward(&mut engine, "result4[0]"), "\"B\"");
}
#[test]
fn trim() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'Hello'.trim()"), "Hello");
assert_eq!(forward(&mut engine, "' \nHello'.trim()"), "Hello");
assert_eq!(forward(&mut engine, "'Hello \n\r'.trim()"), "Hello");
assert_eq!(forward(&mut engine, "' Hello '.trim()"), "Hello");
assert_eq!(forward(&mut engine, "'Hello'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "'Hello \n\r'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' Hello '.trim()"), "\"Hello\"");
}
#[test]
fn trim_start() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'Hello'.trimStart()"), "Hello");
assert_eq!(forward(&mut engine, "' \nHello'.trimStart()"), "Hello");
assert_eq!(forward(&mut engine, "'Hello \n'.trimStart()"), "Hello \n");
assert_eq!(forward(&mut engine, "' Hello '.trimStart()"), "Hello ");
assert_eq!(forward(&mut engine, "'Hello'.trimStart()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trimStart()"), "\"Hello\"");
assert_eq!(
forward(&mut engine, "'Hello \n'.trimStart()"),
"\"Hello \n\""
);
assert_eq!(forward(&mut engine, "' Hello '.trimStart()"), "\"Hello \"");
}
#[test]
fn trim_end() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'Hello'.trimEnd()"), "Hello");
assert_eq!(forward(&mut engine, "' \nHello'.trimEnd()"), " \nHello");
assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "Hello");
assert_eq!(forward(&mut engine, "' Hello '.trimEnd()"), " Hello");
assert_eq!(forward(&mut engine, "'Hello'.trimEnd()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trimEnd()"), "\" \nHello\"");
assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' Hello '.trimEnd()"), "\" Hello\"");
}

2
boa/src/builtins/symbol/tests.rs

@ -21,5 +21,5 @@ fn print_symbol_expect_description() {
"#;
eprintln!("{}", forward(&mut engine, init));
let sym = forward_val(&mut engine, "sym.toString()").unwrap();
assert_eq!(sym.to_string(), "Symbol(Hello)");
assert_eq!(sym.to_string(), "\"Symbol(Hello)\"");
}

14
boa/src/builtins/value/conversions.rs

@ -2,12 +2,14 @@ use super::*;
use std::convert::TryFrom;
impl From<&Value> for Value {
#[inline]
fn from(value: &Value) -> Self {
value.clone()
}
}
impl From<String> for Value {
#[inline]
fn from(value: String) -> Self {
let _timer = BoaProfiler::global().start_event("From<String>", "value");
Self::string(value)
@ -15,35 +17,47 @@ impl From<String> for Value {
}
impl From<Box<str>> for Value {
#[inline]
fn from(value: Box<str>) -> Self {
Self::string(String::from(value))
}
}
impl From<&str> for Value {
#[inline]
fn from(value: &str) -> Value {
Value::string(value)
}
}
impl From<&Box<str>> for Value {
#[inline]
fn from(value: &Box<str>) -> Self {
Self::string(value.as_ref())
}
}
impl From<char> for Value {
#[inline]
fn from(value: char) -> Self {
Value::string(value.to_string())
}
}
impl From<RcString> for Value {
#[inline]
fn from(value: RcString) -> Self {
Value::String(value)
}
}
impl From<RcSymbol> for Value {
#[inline]
fn from(value: RcSymbol) -> Self {
Value::Symbol(value)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TryFromCharError;

6
boa/src/builtins/value/display.rs

@ -90,7 +90,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children:
&v.borrow()
.properties()
.get("length")
.unwrap()
.expect("Could not get Array's length property")
.value
.clone()
.expect("Could not borrow value"),
@ -218,7 +218,7 @@ pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String {
format!("{{\n{}\n{}}}", result, closing_indent)
} else {
// Every other type of data is printed as is
// Every other type of data is printed with the display method
format!("{}", data)
}
}
@ -236,7 +236,7 @@ impl Display for Value {
Some(description) => write!(f, "Symbol({})", description),
None => write!(f, "Symbol()"),
},
Self::String(ref v) => write!(f, "{}", v),
Self::String(ref v) => write!(f, "\"{}\"", v),
Self::Rational(v) => format_rational(*v, f),
Self::Object(_) => write!(f, "{}", log_string_from(self, true, true)),
Self::Integer(v) => write!(f, "{}", v),

48
boa/src/builtins/value/mod.rs

@ -12,7 +12,7 @@ pub use crate::builtins::value::val_type::Type;
use crate::builtins::{
function::Function,
object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE},
property::{Attribute, Property},
property::{Attribute, Property, PropertyKey},
BigInt, Symbol,
};
use crate::exec::Interpreter;
@ -374,6 +374,15 @@ impl Value {
matches!(self, Self::String(_))
}
/// Returns the string if the values is a string, otherwise `None`.
#[inline]
pub fn as_string(&self) -> Option<&RcString> {
match self {
Self::String(ref string) => Some(string),
_ => None,
}
}
/// Returns true if the value is a boolean.
#[inline]
pub fn is_boolean(&self) -> bool {
@ -624,38 +633,31 @@ impl Value {
}
/// Set the field in the value
/// Field could be a Symbol, so we need to accept a Value (not a string)
pub fn set_field<F, V>(&self, field: F, val: V) -> Value
#[inline]
pub fn set_field<F, V>(&self, field: F, value: V) -> Value
where
F: Into<Value>,
F: Into<PropertyKey>,
V: Into<Value>,
{
let _timer = BoaProfiler::global().start_event("Value::set_field", "value");
let field = field.into();
let val = val.into();
let value = value.into();
let _timer = BoaProfiler::global().start_event("Value::set_field", "value");
if let Self::Object(ref obj) = *self {
if obj.borrow().is_array() {
if let Ok(num) = field.to_string().parse::<usize>() {
if num > 0 {
let len = i32::from(&self.get_field("length"));
if len < (num + 1) as i32 {
self.set_field("length", Value::from(num + 1));
if let PropertyKey::String(ref string) = field {
if obj.borrow().is_array() {
if let Ok(num) = string.parse::<usize>() {
if num > 0 {
let len = i32::from(&self.get_field("length"));
if len < (num + 1) as i32 {
self.set_field("length", num + 1);
}
}
}
}
}
// Symbols get saved into a different bucket to general properties
if field.is_symbol() {
obj.borrow_mut().set(field, val.clone());
} else {
obj.borrow_mut()
.set(Value::from(field.to_string()), val.clone());
}
obj.borrow_mut().set(&field, value.clone());
}
val
value
}
/// Set the private field in the value

14
boa/src/builtins/value/rcstring.rs

@ -79,6 +79,20 @@ impl From<String> for RcString {
}
}
impl From<&RcString> for String {
#[inline]
fn from(string: &RcString) -> Self {
string.to_string()
}
}
impl From<Box<str>> for RcString {
#[inline]
fn from(string: Box<str>) -> Self {
Self(Rc::from(string))
}
}
impl From<&str> for RcString {
#[inline]
fn from(string: &str) -> Self {

109
boa/src/builtins/value/tests.rs

@ -31,7 +31,7 @@ fn get_set_field() {
// Create string and convert it to a Value
let s = Value::from("bar");
obj.set_field("foo", s);
assert_eq!(obj.get_field("foo").to_string(), "bar");
assert_eq!(obj.get_field("foo").to_string(), "\"bar\"");
}
#[test]
@ -357,3 +357,110 @@ fn bitand_rational_and_rational() {
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, 255);
}
#[test]
fn display_string() {
let s = String::from("Hello");
let v = Value::from(s);
assert_eq!(v.to_string(), "\"Hello\"");
}
#[test]
fn display_array_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "[\"Hello\"]").unwrap();
assert_eq!(value.to_string(), "[ \"Hello\" ]");
}
#[test]
fn display_boolean_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let d_obj = r#"
let bool = new Boolean(0);
bool
"#;
let value = forward_val(&mut engine, d_obj).unwrap();
assert_eq!(value.to_string(), "Boolean { false }")
}
#[test]
fn display_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let d_obj = r#"
let num = new Number(3.14);
num
"#;
let value = forward_val(&mut engine, d_obj).unwrap();
assert_eq!(value.to_string(), "Number { 3.14 }")
}
#[test]
fn display_negative_zero_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let d_obj = r#"
let num = new Number(-0);
num
"#;
let value = forward_val(&mut engine, d_obj).unwrap();
assert_eq!(value.to_string(), "Number { -0 }")
}
#[test]
#[ignore] // TODO: Once objects are printed in a simpler way this test can be simplified and used
fn display_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let d_obj = r#"
let o = {a: 'a'};
o
"#;
let value = forward_val(&mut engine, d_obj).unwrap();
assert_eq!(
value.to_string(),
r#"{
a: "a",
__proto__: {
constructor: {
setPrototypeOf: {
length: 2
},
prototype: [Cycle],
name: "Object",
length: 1,
defineProperty: {
length: 3
},
getPrototypeOf: {
length: 1
},
is: {
length: 2
},
__proto__: {
constructor: {
name: "Function",
prototype: [Cycle],
length: 1,
__proto__: undefined
},
__proto__: undefined
}
},
hasOwnProperty: {
length: 0
},
propertyIsEnumerable: {
length: 0
},
toString: {
length: 0
}
}
}"#
);
}

4
boa/src/environment/lexical_environment.rs

@ -307,7 +307,7 @@ mod tests {
}
"#;
assert_eq!(&exec(scenario), "bar is not defined");
assert_eq!(&exec(scenario), "\"bar is not defined\"");
}
#[test]
@ -324,7 +324,7 @@ mod tests {
}
"#;
assert_eq!(&exec(scenario), "bar is not defined");
assert_eq!(&exec(scenario), "\"bar is not defined\"");
}
#[test]

2
boa/src/exec/field/mod.rs

@ -23,6 +23,6 @@ impl Executable for GetField {
}
let field = self.field().run(interpreter)?;
Ok(obj.get_field(field.to_string()))
Ok(obj.get_field(interpreter.to_string(&field)?))
}
}

28
boa/src/exec/mod.rs

@ -27,7 +27,7 @@ use crate::{
function::{Function as FunctionObject, FunctionBody, ThisMode},
number::{f64_to_int32, f64_to_uint32},
object::{Object, ObjectData, PROTOTYPE},
property::Property,
property::PropertyKey,
value::{RcBigInt, RcString, ResultValue, Type, Value},
BigInt, Console, Number,
},
@ -489,23 +489,20 @@ impl Interpreter {
///
/// https://tc39.es/ecma262/#sec-topropertykey
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_property_key(&mut self, value: &Value) -> ResultValue {
pub(crate) fn to_property_key(&mut self, value: &Value) -> Result<PropertyKey, Value> {
let key = self.to_primitive(value, PreferredType::String)?;
if key.is_symbol() {
Ok(key)
if let Value::Symbol(ref symbol) = key {
Ok(PropertyKey::from(symbol.clone()))
} else {
self.to_string(&key).map(Value::from)
let string = self.to_string(&key)?;
Ok(PropertyKey::from(string))
}
}
/// https://tc39.es/ecma262/#sec-hasproperty
pub(crate) fn has_property(&self, obj: &Value, key: &Value) -> bool {
pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool {
if let Some(obj) = obj.as_object() {
if !Property::is_property_key(key) {
false
} else {
obj.has_property(key)
}
obj.has_property(key)
} else {
false
}
@ -610,10 +607,11 @@ impl Interpreter {
.obj()
.run(self)?
.set_field(get_const_field_node.field(), value)),
Node::GetField(ref get_field) => Ok(get_field
.obj()
.run(self)?
.set_field(get_field.field().run(self)?, value)),
Node::GetField(ref get_field) => {
let field = get_field.field().run(self)?;
let key = self.to_property_key(&field)?;
Ok(get_field.obj().run(self)?.set_field(key, value))
}
_ => panic!("TypeError: invalid assignment to {}", node),
}
}

6
boa/src/exec/object/mod.rs

@ -22,13 +22,11 @@ impl Executable for Object {
for property in self.properties().iter() {
match property {
PropertyDefinition::Property(key, value) => {
obj.borrow()
.set_field(&key.clone(), value.run(interpreter)?);
obj.borrow().set_field(key.clone(), value.run(interpreter)?);
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
if let MethodDefinitionKind::Ordinary = kind {
obj.borrow()
.set_field(&name.clone(), func.run(interpreter)?);
obj.borrow().set_field(name.clone(), func.run(interpreter)?);
} else {
// TODO: Implement other types of MethodDefinitionKinds.
unimplemented!("other types of property method definitions.");

19
boa/src/exec/operator/mod.rs

@ -38,9 +38,10 @@ impl Executable for Assign {
val_obj.set_field(get_const_field.field(), val.clone());
}
Node::GetField(ref get_field) => {
let val_obj = get_field.obj().run(interpreter)?;
let val_field = get_field.field().run(interpreter)?;
val_obj.set_field(val_field, val.clone());
let object = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
let key = interpreter.to_property_key(&field)?;
object.set_field(key, val.clone());
}
_ => (),
}
@ -203,12 +204,12 @@ impl Executable for UnaryOp {
.run(interpreter)?
.remove_property(get_const_field.field()),
),
Node::GetField(ref get_field) => Value::boolean(
get_field
.obj()
.run(interpreter)?
.remove_property(&get_field.field().run(interpreter)?.to_string()),
),
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = &get_field.field().run(interpreter)?;
let res = obj.remove_property(interpreter.to_string(field)?.as_str());
return Ok(Value::boolean(res));
}
Node::Identifier(_) => Value::boolean(false),
Node::ArrayDecl(_)
| Node::Block(_)

4
boa/src/exec/operator/tests.rs

@ -10,7 +10,7 @@ fn assignmentoperator_lhs_not_defined() {
}
"#;
assert_eq!(&exec(scenario), "ReferenceError: a is not defined");
assert_eq!(&exec(scenario), "\"ReferenceError: a is not defined\"");
}
#[test]
@ -24,5 +24,5 @@ fn assignmentoperator_rhs_throws_error() {
}
"#;
assert_eq!(&exec(scenario), "ReferenceError: b is not defined");
assert_eq!(&exec(scenario), "\"ReferenceError: b is not defined\"");
}

12
boa/src/exec/switch/tests.rs

@ -152,12 +152,20 @@ fn string_switch() {
a;
"#;
assert_eq!(&exec(scenario), "world");
assert_eq!(&exec(scenario), "\"world\"");
}
#[test]
fn bigger_switch_example() {
let expected = ["Mon", "Tue", "Wed", "Thurs", "Fri", "Sat", "Sun"];
let expected = [
"\"Mon\"",
"\"Tue\"",
"\"Wed\"",
"\"Thurs\"",
"\"Fri\"",
"\"Sat\"",
"\"Sun\"",
];
for (i, val) in expected.iter().enumerate() {
let scenario = format!(

128
boa/src/exec/tests.rs

@ -21,7 +21,7 @@ fn property_accessor_member_expression_dot_notation_on_string_literal() {
typeof 'asd'.matchAll;
"#;
assert_eq!(&exec(scenario), "function");
assert_eq!(&exec(scenario), "\"function\"");
}
#[test]
@ -30,7 +30,7 @@ fn property_accessor_member_expression_bracket_notation_on_string_literal() {
typeof 'asd'['matchAll'];
"#;
assert_eq!(&exec(scenario), "function");
assert_eq!(&exec(scenario), "\"function\"");
}
#[test]
@ -40,7 +40,7 @@ fn property_accessor_member_expression_dot_notation_on_function() {
asd.name;
"#;
assert_eq!(&exec(scenario), "asd");
assert_eq!(&exec(scenario), "\"asd\"");
}
#[test]
@ -50,7 +50,7 @@ fn property_accessor_member_expression_bracket_notation_on_function() {
asd['name'];
"#;
assert_eq!(&exec(scenario), "asd");
assert_eq!(&exec(scenario), "\"asd\"");
}
#[test]
@ -94,7 +94,7 @@ fn identifier_on_global_object_undefined() {
}
"#;
assert_eq!(&exec(scenario), "bar is not defined");
assert_eq!(&exec(scenario), "\"bar is not defined\"");
}
#[test]
@ -125,7 +125,7 @@ fn spread_with_arguments() {
assert_eq!(one, String::from("1"));
let two = forward(&mut engine, "result[1]");
assert_eq!(two, String::from("test"));
assert_eq!(two, String::from("\"test\""));
let three = forward(&mut engine, "result[2]");
assert_eq!(three, String::from("3"));
@ -140,9 +140,9 @@ fn array_rest_with_arguments() {
let mut engine = Interpreter::new(realm);
let scenario = r#"
var b = [4, 5, 6]
var a = [1, 2, 3, ...b];
"#;
var b = [4, 5, 6]
var a = [1, 2, 3, ...b];
"#;
forward(&mut engine, scenario);
let one = forward(&mut engine, "a");
assert_eq!(one, String::from("[ 1, 2, 3, 4, 5, 6 ]"));
@ -151,54 +151,53 @@ fn array_rest_with_arguments() {
#[test]
fn array_field_set() {
let element_changes = r#"
let m = [1, 2, 3];
m[1] = 5;
m[1]
"#;
let m = [1, 2, 3];
m[1] = 5;
m[1]
"#;
assert_eq!(&exec(element_changes), "5");
let length_changes = r#"
let m = [1, 2, 3];
m[10] = 52;
m.length
"#;
let m = [1, 2, 3];
m[10] = 52;
m.length
"#;
assert_eq!(&exec(length_changes), "11");
let negative_index_wont_affect_length = r#"
let m = [1, 2, 3];
m[-11] = 5;
m.length
"#;
let m = [1, 2, 3];
m[-11] = 5;
m.length
"#;
assert_eq!(&exec(negative_index_wont_affect_length), "3");
let non_num_key_wont_affect_length = r#"
let m = [1, 2, 3];
m["magic"] = 5;
m.length
"#;
let m = [1, 2, 3];
m["magic"] = 5;
m.length
"#;
assert_eq!(&exec(non_num_key_wont_affect_length), "3");
}
#[test]
fn tilde_operator() {
let float = r#"
let f = -1.2;
~f
"#;
let f = -1.2;
~f
"#;
assert_eq!(&exec(float), "0");
let numeric = r#"
let f = 1789;
~f
"#;
let f = 1789;
~f
"#;
assert_eq!(&exec(numeric), "-1790");
// TODO: enable test after we have NaN
// let nan = r#"
// var m = NaN;
// ~m
// "#;
// assert_eq!(&exec(nan), "-1");
let nan = r#"
var m = NaN;
~m
"#;
assert_eq!(&exec(nan), "-1");
let object = r#"
let m = {};
@ -240,7 +239,7 @@ fn early_return() {
}
outer_fnct()
"#;
assert_eq!(&exec(early_return), "outer");
assert_eq!(&exec(early_return), "\"outer\"");
}
#[test]
@ -370,7 +369,7 @@ fn for_loop() {
}
b
"#;
assert_eq!(&exec(simple), "hello");
assert_eq!(&exec(simple), "\"hello\"");
let without_init_and_inc_step = r#"
let a = 0;
@ -407,7 +406,7 @@ fn for_loop_iteration_variable_does_not_leak() {
}
"#;
assert_eq!(&exec(inner_scope), "i is not defined");
assert_eq!(&exec(inner_scope), "\"i is not defined\"");
}
#[test]
@ -459,7 +458,7 @@ fn typeof_string() {
const a = String();
typeof a;
"#;
assert_eq!(&exec(typeof_string), "string");
assert_eq!(&exec(typeof_string), "\"string\"");
}
#[test]
@ -468,7 +467,7 @@ fn typeof_int() {
let a = 5;
typeof a;
"#;
assert_eq!(&exec(typeof_int), "number");
assert_eq!(&exec(typeof_int), "\"number\"");
}
#[test]
@ -477,7 +476,7 @@ fn typeof_rational() {
let a = 0.5;
typeof a;
"#;
assert_eq!(&exec(typeof_rational), "number");
assert_eq!(&exec(typeof_rational), "\"number\"");
}
#[test]
@ -486,7 +485,7 @@ fn typeof_undefined() {
let a = undefined;
typeof a;
"#;
assert_eq!(&exec(typeof_undefined), "undefined");
assert_eq!(&exec(typeof_undefined), "\"undefined\"");
}
#[test]
@ -494,7 +493,7 @@ fn typeof_undefined_directly() {
let typeof_undefined = r#"
typeof undefined;
"#;
assert_eq!(&exec(typeof_undefined), "undefined");
assert_eq!(&exec(typeof_undefined), "\"undefined\"");
}
#[test]
@ -503,7 +502,7 @@ fn typeof_boolean() {
let a = true;
typeof a;
"#;
assert_eq!(&exec(typeof_boolean), "boolean");
assert_eq!(&exec(typeof_boolean), "\"boolean\"");
}
#[test]
@ -512,7 +511,7 @@ fn typeof_null() {
let a = null;
typeof a;
"#;
assert_eq!(&exec(typeof_null), "object");
assert_eq!(&exec(typeof_null), "\"object\"");
}
#[test]
@ -521,7 +520,7 @@ fn typeof_object() {
let a = {};
typeof a;
"#;
assert_eq!(&exec(typeof_object), "object");
assert_eq!(&exec(typeof_object), "\"object\"");
}
#[test]
@ -530,7 +529,7 @@ fn typeof_symbol() {
let a = Symbol();
typeof a;
"#;
assert_eq!(&exec(typeof_symbol), "symbol");
assert_eq!(&exec(typeof_symbol), "\"symbol\"");
}
#[test]
@ -539,7 +538,7 @@ fn typeof_function() {
let a = function(){};
typeof a;
"#;
assert_eq!(&exec(typeof_function), "function");
assert_eq!(&exec(typeof_function), "\"function\"");
}
#[test]
@ -599,7 +598,7 @@ fn unary_void() {
const b = void test() + '';
a + b
"#;
assert_eq!(&exec(void_invocation), "42undefined");
assert_eq!(&exec(void_invocation), "\"42undefined\"");
}
#[test]
@ -609,28 +608,28 @@ fn unary_delete() {
const b = delete a + '';
a + b
"#;
assert_eq!(&exec(delete_var), "5false");
assert_eq!(&exec(delete_var), "\"5false\"");
let delete_prop = r#"
const a = { b: 5 };
const c = delete a.b + '';
a.b + c
"#;
assert_eq!(&exec(delete_prop), "undefinedtrue");
assert_eq!(&exec(delete_prop), "\"undefinedtrue\"");
let delete_not_existing_prop = r#"
const a = { b: 5 };
const c = delete a.c + '';
a.b + c
"#;
assert_eq!(&exec(delete_not_existing_prop), "5false");
assert_eq!(&exec(delete_not_existing_prop), "\"5false\"");
let delete_field = r#"
const a = { b: 5 };
const c = delete a['b'] + '';
a.b + c
"#;
assert_eq!(&exec(delete_field), "undefinedtrue");
assert_eq!(&exec(delete_field), "\"undefinedtrue\"");
let delete_object = r#"
const a = { b: 5 };
@ -746,8 +745,8 @@ mod in_operator {
var bar = new Foo();
"#;
forward(&mut engine, scenario);
assert_eq!(forward(&mut engine, "bar.a"), "a");
assert_eq!(forward(&mut engine, "bar.b"), "b");
assert_eq!(forward(&mut engine, "bar.a"), "\"a\"");
assert_eq!(forward(&mut engine, "bar.b"), "\"b\"");
}
#[test]
@ -1145,7 +1144,10 @@ fn not_a_function() {
e.toString()
}
"#;
assert_eq!(forward(&mut engine, scenario), "TypeError: not a function");
assert_eq!(
forward(&mut engine, scenario),
"\"TypeError: not a function\""
);
let scenario = r#"
try {
a.a();
@ -1153,7 +1155,10 @@ fn not_a_function() {
e.toString()
}
"#;
assert_eq!(forward(&mut engine, scenario), "TypeError: not a function");
assert_eq!(
forward(&mut engine, scenario),
"\"TypeError: not a function\""
);
let scenario = r#"
try {
b();
@ -1161,5 +1166,8 @@ fn not_a_function() {
e.toString()
}
"#;
assert_eq!(forward(&mut engine, scenario), "TypeError: not a function");
assert_eq!(
forward(&mut engine, scenario),
"\"TypeError: not a function\""
);
}

3
boa/src/realm.rs

@ -64,8 +64,7 @@ impl Realm {
/// Utility to add a function to the global object
pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self {
let func = Function::builtin(Vec::new(), func);
self.global_obj
.set_field(Value::from(func_name), Value::from_func(func));
self.global_obj.set_field(func_name, Value::from_func(func));
self
}

4
boa_cli/src/main.rs

@ -201,8 +201,8 @@ pub fn main() -> Result<(), std::io::Error> {
}
} else {
match forward_val(&mut engine, &buffer) {
Ok(v) => print!("{}", v.to_string()),
Err(v) => eprint!("{}", v.to_string()),
Ok(v) => print!("{}", v),
Err(v) => eprint!("{}", v),
}
}
}

Loading…
Cancel
Save