From 9e2673ae2d90505ec8775b146773a8ccf1c7dbaf Mon Sep 17 00:00:00 2001 From: croraf Date: Fri, 12 Jun 2020 22:59:56 +0200 Subject: [PATCH 01/19] Fix identifier resolution --- boa/src/builtins/error/reference.rs | 77 ++++++++++++++++++++++ boa/src/environment/lexical_environment.rs | 1 + boa/src/exec/exception.rs | 13 ++++ boa/src/exec/identifier/mod.rs | 26 ++++++++ boa/src/exec/mod.rs | 9 +-- 5 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 boa/src/builtins/error/reference.rs create mode 100644 boa/src/exec/identifier/mod.rs diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs new file mode 100644 index 00000000000..bed7c7a7b2e --- /dev/null +++ b/boa/src/builtins/error/reference.rs @@ -0,0 +1,77 @@ +//! This module implements the global `ReferenceError` object. +//! +//! Indicates an error that occurs when de-referencing an invalid reference +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror TODO +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError + +use crate::{ + builtins::{ + function::make_builtin_fn, + function::make_constructor_fn, + object::ObjectKind, + value::{ResultValue, Value}, + }, + exec::Interpreter, + profiler::BoaProfiler, +}; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct ReferenceError; + +impl ReferenceError { + /// Create a new error object. + pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + if !args.is_empty() { + this.set_field( + "message", + Value::from( + args.get(0) + .expect("failed getting error message") + .to_string(), + ), + ); + } + // This value is used by console.log and other routines to match Object type + // to its Javascript Identifier (global constructor method name) + this.set_kind(ObjectKind::Error); + Err(this.clone()) + } + + /// `Error.prototype.toString()` + /// + /// The toString() method returns a string representing the specified Error object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [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: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + let name = this.get_field("name"); + let message = this.get_field("message"); + Ok(Value::from(format!("{}: {}", name, message))) + } + + /// Create a new `ReferenceError` object. + pub(crate) fn create(global: &Value) -> Value { + let prototype = Value::new_object(Some(global)); + prototype.set_field("message", Value::from("")); + + make_builtin_fn(Self::to_string, "toString", &prototype, 0); + + make_constructor_fn("ReferenceError", 1, Self::make_error, global, prototype, true) + } + + /// Initialise the global object with the `ReferenceError` object. + pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("referenceerror", "init"); + global.set_field("ReferenceError", Self::create(global)); + } +} diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 8e32c099457..829c168a186 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -213,6 +213,7 @@ impl LexicalEnvironment { } pub fn get_binding_value(&self, name: &str) -> Value { + dbg!(&self.environment_stack); self.environments() .find(|env| env.borrow().has_binding(name)) .map(|env| env.borrow().get_binding_value(name, false)) diff --git a/boa/src/exec/exception.rs b/boa/src/exec/exception.rs index ddef2e82ee1..b65ac99d62a 100644 --- a/boa/src/exec/exception.rs +++ b/boa/src/exec/exception.rs @@ -33,4 +33,17 @@ impl Interpreter { )) .run(self) } + + /// Throws a `Reference` with the specified message. + pub fn throw_reference_error(&mut self, message: M) -> ResultValue + where + M: Into, + { + // Runs a `new ReferenceError(message)`. + New::from(Call::new( + Identifier::from("ReferenceError"), + vec![Const::from(message.into()).into()], + )) + .run(self) + } } diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs new file mode 100644 index 00000000000..b81d2b37dca --- /dev/null +++ b/boa/src/exec/identifier/mod.rs @@ -0,0 +1,26 @@ +use super::{Executable, Interpreter}; +use crate::{ + builtins::value::{ResultValue, Value, ValueData}, + syntax::ast::node::identifier::Identifier, +}; + +impl Executable for Identifier { + fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let reference = resolve_binding(interpreter, self.as_ref()); + match reference.data() { + ValueData::Undefined => Err( + interpreter + .throw_reference_error(self.as_ref()) + .expect_err("throw_reference_error() must return an error"), + ), + _ => Ok(reference), + } + } +} + +pub(crate) fn resolve_binding(interpreter: &mut Interpreter, name: &str) -> Value { + interpreter + .realm() + .environment + .get_binding_value(name) +} diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 4d8f25ed6c9..59052958516 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -7,6 +7,7 @@ mod declaration; mod exception; mod expression; mod field; +mod identifier; mod iteration; mod object; mod operator; @@ -502,13 +503,7 @@ impl Executable for Node { Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())), Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)), Node::Block(ref block) => block.run(interpreter), - Node::Identifier(ref name) => { - let val = interpreter - .realm() - .environment - .get_binding_value(name.as_ref()); - Ok(val) - } + Node::Identifier(ref identifier) => identifier.run(interpreter), Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(interpreter), Node::GetField(ref get_field) => get_field.run(interpreter), Node::Call(ref expr) => expr.run(interpreter), From f782a4dd0530148f034fe8045237c18c313d9f4a Mon Sep 17 00:00:00 2001 From: croraf Date: Fri, 12 Jun 2020 23:06:34 +0200 Subject: [PATCH 02/19] Debug --- boa/src/environment/lexical_environment.rs | 1 - boa/src/exec/identifier/mod.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 829c168a186..8e32c099457 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -213,7 +213,6 @@ impl LexicalEnvironment { } pub fn get_binding_value(&self, name: &str) -> Value { - dbg!(&self.environment_stack); self.environments() .find(|env| env.borrow().has_binding(name)) .map(|env| env.borrow().get_binding_value(name, false)) diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index b81d2b37dca..0df2c50cf0c 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -6,6 +6,7 @@ use crate::{ impl Executable for Identifier { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + dbg!(self.as_ref()); let reference = resolve_binding(interpreter, self.as_ref()); match reference.data() { ValueData::Undefined => Err( From 7e152c65c6b146d10a99a2865eebdc42bc71fc9a Mon Sep 17 00:00:00 2001 From: croraf Date: Sat, 13 Jun 2020 14:54:37 +0200 Subject: [PATCH 03/19] Fix: define ReferenceError on global object --- boa/src/builtins/error/mod.rs | 3 ++- boa/src/builtins/mod.rs | 3 ++- boa/src/exec/identifier/mod.rs | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index a6df97025be..d77030c4532 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -22,13 +22,14 @@ use crate::{ // mod eval; pub(crate) mod range; -// mod reference; +pub(crate) mod reference; // mod syntax; pub(crate) mod r#type; // mod uri; pub(crate) use self::r#type::TypeError; pub(crate) use self::range::RangeError; +pub(crate) use self::reference::ReferenceError; /// Built-in `Error` object. #[derive(Debug, Clone, Copy)] diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 4e43b5268f7..8e8eb000a9b 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -21,7 +21,7 @@ pub(crate) use self::{ array::Array, bigint::BigInt, boolean::Boolean, - error::{Error, RangeError, TypeError}, + error::{Error, RangeError, ReferenceError, TypeError}, function::Function, number::Number, regexp::RegExp, @@ -47,5 +47,6 @@ pub fn init(global: &Value) { console::init(global); Error::init(global); RangeError::init(global); + ReferenceError::init(global); TypeError::init(global); } diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index 0df2c50cf0c..b81d2b37dca 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -6,7 +6,6 @@ use crate::{ impl Executable for Identifier { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { - dbg!(self.as_ref()); let reference = resolve_binding(interpreter, self.as_ref()); match reference.data() { ValueData::Undefined => Err( From b50538bd2fafb1814146c4fdba7966a1cefe502b Mon Sep 17 00:00:00 2001 From: croraf Date: Sat, 13 Jun 2020 14:56:28 +0200 Subject: [PATCH 04/19] Format --- boa/src/builtins/error/reference.rs | 113 +++++++++++++++------------- boa/src/exec/identifier/mod.rs | 27 +++---- 2 files changed, 71 insertions(+), 69 deletions(-) diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index bed7c7a7b2e..ff8c0aa26b5 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -10,68 +10,75 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError use crate::{ - builtins::{ - function::make_builtin_fn, - function::make_constructor_fn, - object::ObjectKind, - value::{ResultValue, Value}, - }, - exec::Interpreter, - profiler::BoaProfiler, + builtins::{ + function::make_builtin_fn, + function::make_constructor_fn, + object::ObjectKind, + value::{ResultValue, Value}, + }, + exec::Interpreter, + profiler::BoaProfiler, }; #[derive(Debug, Clone, Copy)] pub(crate) struct ReferenceError; impl ReferenceError { - /// Create a new error object. - pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - if !args.is_empty() { - this.set_field( - "message", - Value::from( - args.get(0) - .expect("failed getting error message") - .to_string(), - ), - ); - } - // This value is used by console.log and other routines to match Object type - // to its Javascript Identifier (global constructor method name) - this.set_kind(ObjectKind::Error); - Err(this.clone()) - } + /// Create a new error object. + pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + if !args.is_empty() { + this.set_field( + "message", + Value::from( + args.get(0) + .expect("failed getting error message") + .to_string(), + ), + ); + } + // This value is used by console.log and other routines to match Object type + // to its Javascript Identifier (global constructor method name) + this.set_kind(ObjectKind::Error); + Err(this.clone()) + } - /// `Error.prototype.toString()` - /// - /// The toString() method returns a string representing the specified Error object. - /// - /// More information: - /// - [MDN documentation][mdn] - /// - [ECMAScript reference][spec] - /// - /// [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: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let name = this.get_field("name"); - let message = this.get_field("message"); - Ok(Value::from(format!("{}: {}", name, message))) - } + /// `Error.prototype.toString()` + /// + /// The toString() method returns a string representing the specified Error object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [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: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + let name = this.get_field("name"); + let message = this.get_field("message"); + Ok(Value::from(format!("{}: {}", name, message))) + } - /// Create a new `ReferenceError` object. - pub(crate) fn create(global: &Value) -> Value { - let prototype = Value::new_object(Some(global)); - prototype.set_field("message", Value::from("")); + /// Create a new `ReferenceError` object. + pub(crate) fn create(global: &Value) -> Value { + let prototype = Value::new_object(Some(global)); + prototype.set_field("message", Value::from("")); - make_builtin_fn(Self::to_string, "toString", &prototype, 0); + make_builtin_fn(Self::to_string, "toString", &prototype, 0); - make_constructor_fn("ReferenceError", 1, Self::make_error, global, prototype, true) - } + make_constructor_fn( + "ReferenceError", + 1, + Self::make_error, + global, + prototype, + true, + ) + } - /// Initialise the global object with the `ReferenceError` object. - pub(crate) fn init(global: &Value) { - let _timer = BoaProfiler::global().start_event("referenceerror", "init"); - global.set_field("ReferenceError", Self::create(global)); - } + /// Initialise the global object with the `ReferenceError` object. + pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("referenceerror", "init"); + global.set_field("ReferenceError", Self::create(global)); + } } diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index b81d2b37dca..9b5033c02a7 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -1,26 +1,21 @@ use super::{Executable, Interpreter}; use crate::{ - builtins::value::{ResultValue, Value, ValueData}, - syntax::ast::node::identifier::Identifier, + builtins::value::{ResultValue, Value, ValueData}, + syntax::ast::node::identifier::Identifier, }; impl Executable for Identifier { - fn run(&self, interpreter: &mut Interpreter) -> ResultValue { - let reference = resolve_binding(interpreter, self.as_ref()); - match reference.data() { - ValueData::Undefined => Err( - interpreter - .throw_reference_error(self.as_ref()) - .expect_err("throw_reference_error() must return an error"), - ), - _ => Ok(reference), + fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let reference = resolve_binding(interpreter, self.as_ref()); + match reference.data() { + ValueData::Undefined => Err(interpreter + .throw_reference_error(self.as_ref()) + .expect_err("throw_reference_error() must return an error")), + _ => Ok(reference), + } } - } } pub(crate) fn resolve_binding(interpreter: &mut Interpreter, name: &str) -> Value { - interpreter - .realm() - .environment - .get_binding_value(name) + interpreter.realm().environment.get_binding_value(name) } From 54907c8a84d1d5e6012806aa846de37406f37e52 Mon Sep 17 00:00:00 2001 From: croraf Date: Sat, 13 Jun 2020 20:27:09 +0200 Subject: [PATCH 05/19] Temp --- boa/src/exec/tests.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index e12ffca31c7..30901309cd9 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -63,7 +63,20 @@ fn semicolon_expression_stop() { fn empty_var_decl_undefined() { let scenario = r#" let b; - b == undefined; + b === undefined; + "#; + + assert_eq!(&exec(scenario), "true"); +} + +#[test] +fn identifier_on_global_object_undefined() { + let scenario = r#" + try { + b; + } catch (err) { + err instanceof ReferenceError; + } "#; assert_eq!(&exec(scenario), "true"); From f905f1256ec602b40c30c77861b03df28b66738d Mon Sep 17 00:00:00 2001 From: croraf Date: Sat, 13 Jun 2020 23:51:35 +0200 Subject: [PATCH 06/19] Rearange some tests --- boa/src/environment/lexical_environment.rs | 6 ++-- boa/src/exec/tests.rs | 34 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 8e32c099457..a56a3c37ad1 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -290,24 +290,26 @@ mod tests { use crate::exec; #[test] + //#[ignore] fn let_is_blockscoped() { let scenario = r#" { let bar = "bar"; } - bar == undefined; + bar; "#; assert_eq!(&exec(scenario), "true"); } #[test] + //#[ignore] fn const_is_blockscoped() { let scenario = r#" { const bar = "bar"; } - bar == undefined; + bar; "#; assert_eq!(&exec(scenario), "true"); diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 30901309cd9..8b80282f8d0 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -42,7 +42,7 @@ fn property_accessor_member_expression_bracket_notation_on_function() { fn empty_let_decl_undefined() { let scenario = r#" let a; - a == undefined; + a === undefined; "#; assert_eq!(&exec(scenario), "true"); @@ -70,6 +70,7 @@ fn empty_var_decl_undefined() { } #[test] +#[ignore] fn identifier_on_global_object_undefined() { let scenario = r#" try { @@ -346,7 +347,7 @@ fn do_while_post_inc() { } #[test] -fn test_for_loop() { +fn for_loop() { let simple = r#" const a = ['h', 'e', 'l', 'l', 'o']; let b = ''; @@ -378,7 +379,10 @@ fn test_for_loop() { a "#; assert_eq!(&exec(body_should_not_execute_on_false_condition), "0"); +} +#[test] +fn for_loop_iteration_variable_does_not_leak() { let inner_scope = r#" for (let i = 0;false;) {} @@ -431,55 +435,79 @@ fn unary_pre() { } #[test] -fn unary_typeof() { +fn typeof_string() { let typeof_string = r#" const a = String(); typeof a; "#; assert_eq!(&exec(typeof_string), "string"); +} +#[test] +fn typeof_int() { let typeof_int = r#" let a = 5; typeof a; "#; assert_eq!(&exec(typeof_int), "number"); +} +#[test] +fn typeof_rational() { let typeof_rational = r#" let a = 0.5; typeof a; "#; assert_eq!(&exec(typeof_rational), "number"); +} +#[test] +fn typeof_undefined() { let typeof_undefined = r#" let a = undefined; typeof a; "#; assert_eq!(&exec(typeof_undefined), "undefined"); +} +#[test] +fn typeof_boolean() { let typeof_boolean = r#" let a = true; typeof a; "#; assert_eq!(&exec(typeof_boolean), "boolean"); +} +#[test] +fn typeof_null() { let typeof_null = r#" let a = null; typeof a; "#; assert_eq!(&exec(typeof_null), "object"); +} +#[test] +fn typeof_object() { let typeof_object = r#" let a = {}; typeof a; "#; assert_eq!(&exec(typeof_object), "object"); +} +#[test] +fn typeof_symbol() { let typeof_symbol = r#" let a = Symbol(); typeof a; "#; assert_eq!(&exec(typeof_symbol), "symbol"); +} +#[test] +fn typeof_function() { let typeof_function = r#" let a = function(){}; typeof a; From 95cd526e1a1875bc8898dea3613566552a3a1bdb Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 12:48:12 +0200 Subject: [PATCH 07/19] Ignore some tests which are awaiting other tickets --- boa/src/environment/lexical_environment.rs | 8 ++++---- boa/src/exec/tests.rs | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index a56a3c37ad1..e4fa4aa98b5 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -290,7 +290,7 @@ mod tests { use crate::exec; #[test] - //#[ignore] + #[ignore] fn let_is_blockscoped() { let scenario = r#" { @@ -298,12 +298,12 @@ mod tests { } bar; "#; - + // awaiting agreement on error throw testing assert_eq!(&exec(scenario), "true"); } #[test] - //#[ignore] + #[ignore] fn const_is_blockscoped() { let scenario = r#" { @@ -311,7 +311,7 @@ mod tests { } bar; "#; - + // awaiting agreement on error throw testing assert_eq!(&exec(scenario), "true"); } diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 8b80282f8d0..fda2d149e40 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -39,6 +39,7 @@ fn property_accessor_member_expression_bracket_notation_on_function() { } #[test] +#[ignore] // will be solved with undefined added to global property fn empty_let_decl_undefined() { let scenario = r#" let a; @@ -60,6 +61,7 @@ fn semicolon_expression_stop() { } #[test] +#[ignore] // will be fixed with undefined added as global property fn empty_var_decl_undefined() { let scenario = r#" let b; @@ -382,12 +384,14 @@ fn for_loop() { } #[test] +#[ignore] fn for_loop_iteration_variable_does_not_leak() { let inner_scope = r#" for (let i = 0;false;) {} i "#; + // awaiting agreement on unhandled error handling assert_eq!(&exec(inner_scope), "undefined"); } @@ -462,6 +466,7 @@ fn typeof_rational() { } #[test] +#[ignore] // Will be fixed when global property undefined is added fn typeof_undefined() { let typeof_undefined = r#" let a = undefined; @@ -740,6 +745,7 @@ mod in_operator { } #[test] +#[ignore] // maybe will be solved when undefined added to global property fn var_decl_hoisting() { let scenario = r#" x = 5; From 1aeb95cacee7935bab346aec2c5dcba1697d8a86 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 13:05:46 +0200 Subject: [PATCH 08/19] Merge resolution --- boa/src/builtins/error/reference.rs | 23 +++++++++++++++-------- boa/src/builtins/mod.rs | 27 +-------------------------- boa/src/exec/exception.rs | 13 ------------- boa/src/exec/tests.rs | 10 ---------- 4 files changed, 16 insertions(+), 57 deletions(-) diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index ff8c0aa26b5..fa59dc2d924 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -6,14 +6,14 @@ //! - [MDN documentation][mdn] //! - [ECMAScript reference][spec] //! -//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror TODO +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError use crate::{ builtins::{ function::make_builtin_fn, function::make_constructor_fn, - object::ObjectKind, + object::ObjectData, value::{ResultValue, Value}, }, exec::Interpreter, @@ -24,6 +24,12 @@ use crate::{ pub(crate) struct ReferenceError; impl ReferenceError { + /// The name of the object. + pub(crate) const NAME: &'static str = "ReferenceError"; + + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + /// Create a new error object. pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if !args.is_empty() { @@ -38,7 +44,7 @@ impl ReferenceError { } // This value is used by console.log and other routines to match Object type // to its Javascript Identifier (global constructor method name) - this.set_kind(ObjectKind::Error); + this.set_data(ObjectData::Error); Err(this.clone()) } @@ -67,8 +73,8 @@ impl ReferenceError { make_builtin_fn(Self::to_string, "toString", &prototype, 0); make_constructor_fn( - "ReferenceError", - 1, + Self::NAME, + Self::LENGTH, Self::make_error, global, prototype, @@ -77,8 +83,9 @@ impl ReferenceError { } /// Initialise the global object with the `ReferenceError` object. - pub(crate) fn init(global: &Value) { - let _timer = BoaProfiler::global().start_event("referenceerror", "init"); - global.set_field("ReferenceError", Self::create(global)); + pub(crate) fn init(global: &Value) -> (&str, Value) { + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + + (Self::NAME, Self::create(global)) } } diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 80abca2abbf..72e65632dae 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -23,17 +23,12 @@ pub(crate) use self::{ array::Array, bigint::BigInt, boolean::Boolean, -<<<<<<< HEAD error::{Error, RangeError, ReferenceError, TypeError}, - function::Function, -======= - error::{Error, RangeError, TypeError}, global_this::GlobalThis, infinity::Infinity, json::Json, math::Math, nan::NaN, ->>>>>>> master number::Number, regexp::RegExp, string::String, @@ -44,25 +39,6 @@ pub(crate) use self::{ /// Initializes builtin objects and functions #[inline] pub fn init(global: &Value) { -<<<<<<< HEAD - Array::init(global); - BigInt::init(global); - Boolean::init(global); - json::init(global); - math::init(global); - nan::init(global); - Number::init(global); - object::init(global); - function::init(global); - RegExp::init(global); - String::init(global); - symbol::init(global); - console::init(global); - Error::init(global); - RangeError::init(global); - ReferenceError::init(global); - TypeError::init(global); -======= let globals = vec![ // The `Function` global must be initialized before other types. function::init(global), @@ -80,7 +56,7 @@ pub fn init(global: &Value) { // Global error types. Error::init(global), RangeError::init(global), - ReferenceError::init(global); + ReferenceError::init(global), TypeError::init(global), // Global properties. NaN::init(global), @@ -92,5 +68,4 @@ pub fn init(global: &Value) { for (name, value) in globals { global_object.insert_field(name, value); } ->>>>>>> master } diff --git a/boa/src/exec/exception.rs b/boa/src/exec/exception.rs index c2c8868a40e..d227662db4f 100644 --- a/boa/src/exec/exception.rs +++ b/boa/src/exec/exception.rs @@ -67,17 +67,4 @@ impl Interpreter { { Err(self.construct_reference_error(message)) } - - /// Throws a `Reference` with the specified message. - pub fn throw_reference_error(&mut self, message: M) -> ResultValue - where - M: Into, - { - // Runs a `new ReferenceError(message)`. - New::from(Call::new( - Identifier::from("ReferenceError"), - vec![Const::from(message.into()).into()], - )) - .run(self) - } } diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 533469a1bc0..f52685659e3 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -9,16 +9,6 @@ fn function_declaration_returns_undefined() { assert_eq!(&exec(scenario), "undefined"); } -#[test] -fn empty_var_decl_undefined() { - let scenario = r#" - let b; - b === undefined; - "#; - - assert_eq!(&exec(scenario), "true"); -} - #[test] fn property_accessor_member_expression_dot_notation_on_string_literal() { let scenario = r#" From 3c62b33c7389dba8197c57643e08e0b04d8b6694 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 13:31:51 +0200 Subject: [PATCH 09/19] Fix: construct_reference_error --- boa/src/exec/exception.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/boa/src/exec/exception.rs b/boa/src/exec/exception.rs index d227662db4f..504872ed681 100644 --- a/boa/src/exec/exception.rs +++ b/boa/src/exec/exception.rs @@ -53,11 +53,16 @@ impl Interpreter { } /// Constructs a `ReferenceError` with the specified message. - pub fn construct_reference_error(&mut self, _message: M) -> Value + pub fn construct_reference_error(&mut self, message: M) -> Value where M: Into, { - unimplemented!("ReferenceError: is not implemented"); + New::from(Call::new( + Identifier::from("ReferenceError"), + vec![Const::from(message.into() + " is not defined").into()], + )) + .run(self) + .expect_err("ReferenceError should always throw") } /// Throws a `ReferenceError` with the specified message. From b34da02d2a2a601effee0f9c992c8bc5b458a5e9 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 19:45:44 +0200 Subject: [PATCH 10/19] Unignore 4 tests --- boa/src/environment/lexical_environment.rs | 22 +++++++++++++++------- boa/src/exec/tests.rs | 16 +++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 95661eef0a8..8d339514a9c 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -290,29 +290,37 @@ mod tests { use crate::exec; #[test] - #[ignore] fn let_is_blockscoped() { let scenario = r#" { let bar = "bar"; } - bar; + + try{ + bar; + } catch (err) { + err.message + } "#; - // awaiting agreement on error throw testing - assert_eq!(&exec(scenario), "true"); + + assert_eq!(&exec(scenario), "bar is not defined"); } #[test] - #[ignore] fn const_is_blockscoped() { let scenario = r#" { const bar = "bar"; } - bar; + + try{ + bar; + } catch (err) { + err.message + } "#; // awaiting agreement on error throw testing - assert_eq!(&exec(scenario), "true"); + assert_eq!(&exec(scenario), "bar is not defined"); } #[test] diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index f52685659e3..13e636800e2 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -81,17 +81,16 @@ fn empty_var_decl_undefined() { } #[test] -#[ignore] fn identifier_on_global_object_undefined() { let scenario = r#" try { - b; + bar; } catch (err) { - err instanceof ReferenceError; + err.message } "#; - assert_eq!(&exec(scenario), "true"); + assert_eq!(&exec(scenario), "bar is not defined"); } #[test] @@ -393,15 +392,18 @@ fn for_loop() { } #[test] -#[ignore] fn for_loop_iteration_variable_does_not_leak() { let inner_scope = r#" for (let i = 0;false;) {} - i + try { + i + } catch (err) { + err.message + } "#; // awaiting agreement on unhandled error handling - assert_eq!(&exec(inner_scope), "undefined"); + assert_eq!(&exec(inner_scope), "i is not defined"); } #[test] From c810be58c64a7aa25493033a34e35c249fb250a9 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 22:00:23 +0200 Subject: [PATCH 11/19] [undefined] add property to global scope --- boa/src/builtins/array/mod.rs | 1 + boa/src/builtins/mod.rs | 3 ++ boa/src/builtins/undefined/mod.rs | 32 +++++++++++++++++++ boa/src/builtins/undefined/tests.rs | 0 boa/src/environment/lexical_environment.rs | 3 +- boa/src/exec/identifier/mod.rs | 16 ++++------ boa/src/exec/mod.rs | 8 ++++- boa/src/exec/operator/mod.rs | 3 +- .../syntax/parser/expression/primary/mod.rs | 4 +-- 9 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 boa/src/builtins/undefined/mod.rs create mode 100644 boa/src/builtins/undefined/tests.rs diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 0517e0c3ccf..d3ad4a1ecba 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -54,6 +54,7 @@ impl Array { .realm() .environment .get_binding_value("Array") + .unwrap() .borrow() .get_field(PROTOTYPE), ); diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 72e65632dae..4646a3615d7 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -18,6 +18,7 @@ pub mod regexp; pub mod string; pub mod symbol; pub mod value; +pub mod undefined; pub(crate) use self::{ array::Array, @@ -34,6 +35,7 @@ pub(crate) use self::{ string::String, symbol::Symbol, value::{ResultValue, Value}, + undefined::Undefined, }; /// Initializes builtin objects and functions @@ -62,6 +64,7 @@ pub fn init(global: &Value) { NaN::init(global), Infinity::init(global), GlobalThis::init(global), + Undefined::init(global), ]; let mut global_object = global.as_object_mut().expect("global object"); diff --git a/boa/src/builtins/undefined/mod.rs b/boa/src/builtins/undefined/mod.rs new file mode 100644 index 00000000000..922c284c639 --- /dev/null +++ b/boa/src/builtins/undefined/mod.rs @@ -0,0 +1,32 @@ +//! This module implements the global `undefined` property. +//! +//! The global undefined property represents the primitive value undefined. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-undefined +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined + +#[cfg(test)] +mod tests; + +use crate::{builtins::value::Value, BoaProfiler}; + +/// JavaScript global `undefined` property. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct Undefined; + +impl Undefined { + /// The binding name of the property. + pub(crate) const NAME: &'static str = "undefined"; + + /// Initialize the `undefined` property on the global object. + #[inline] + pub(crate) fn init(_: &Value) -> (&str, Value) { + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + + (Self::NAME, Value::undefined()) + } +} diff --git a/boa/src/builtins/undefined/tests.rs b/boa/src/builtins/undefined/tests.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 8d339514a9c..4b0450f267f 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -212,11 +212,10 @@ impl LexicalEnvironment { .any(|env| env.borrow().has_binding(name)) } - pub fn get_binding_value(&self, name: &str) -> Value { + pub fn get_binding_value(&self, name: &str) -> Option { self.environments() .find(|env| env.borrow().has_binding(name)) .map(|env| env.borrow().get_binding_value(name, false)) - .unwrap_or_else(Value::undefined) } } diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index 9b5033c02a7..e0f2178498a 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -1,21 +1,19 @@ use super::{Executable, Interpreter}; use crate::{ - builtins::value::{ResultValue, Value, ValueData}, + builtins::value::ResultValue, syntax::ast::node::identifier::Identifier, }; impl Executable for Identifier { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { - let reference = resolve_binding(interpreter, self.as_ref()); - match reference.data() { - ValueData::Undefined => Err(interpreter + let reference = interpreter.realm().environment.get_binding_value(self.as_ref()); + match reference { + Some(value) => Ok(value), + None => Err(interpreter .throw_reference_error(self.as_ref()) - .expect_err("throw_reference_error() must return an error")), - _ => Ok(reference), + .expect_err("throw_reference_error() must return an error") + ) } } } -pub(crate) fn resolve_binding(interpreter: &mut Interpreter, name: &str) -> Value { - interpreter.realm().environment.get_binding_value(name) -} diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 6f97503a55f..4bdf1fb92c6 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -420,6 +420,7 @@ impl Interpreter { .realm .environment .get_binding_value("Boolean") + .unwrap() .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -432,6 +433,7 @@ impl Interpreter { .realm .environment .get_binding_value("Number") + .unwrap() .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( proto, @@ -443,6 +445,7 @@ impl Interpreter { .realm .environment .get_binding_value("Number") + .unwrap() .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -455,6 +458,7 @@ impl Interpreter { .realm .environment .get_binding_value("String") + .unwrap() .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -467,6 +471,7 @@ impl Interpreter { .realm .environment .get_binding_value("Symbol") + .unwrap() .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -479,6 +484,7 @@ impl Interpreter { .realm .environment .get_binding_value("BigInt") + .unwrap() .get_field(PROTOTYPE); let bigint_obj = Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone())); @@ -573,7 +579,7 @@ impl Executable for Node { let _timer = BoaProfiler::global().start_event("Executable", "exec"); match *self { Node::Const(Const::Null) => Ok(Value::null()), - Node::Const(Const::Undefined) => Ok(Value::undefined()), + //Node::Const(Const::Undefined) => (Identifier::from((&Box::new("asd")).as_ref()).into()).run(interpreter), Node::Const(Const::Num(num)) => Ok(Value::rational(num)), Node::Const(Const::Int(num)) => Ok(Value::integer(num)), Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())), diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index 35a099dc51b..8548b165b26 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -118,7 +118,8 @@ impl Executable for BinOp { let v_a = interpreter .realm() .environment - .get_binding_value(name.as_ref()); + .get_binding_value(name.as_ref()) + .unwrap(); let v_b = self.rhs().run(interpreter)?; let value = Self::run_assign(op, v_a, v_b); interpreter.realm.environment.set_mutable_binding( diff --git a/boa/src/syntax/parser/expression/primary/mod.rs b/boa/src/syntax/parser/expression/primary/mod.rs index 0e31e581cc5..43e796d74d1 100644 --- a/boa/src/syntax/parser/expression/primary/mod.rs +++ b/boa/src/syntax/parser/expression/primary/mod.rs @@ -86,9 +86,9 @@ impl TokenParser for PrimaryExpression { } TokenKind::BooleanLiteral(boolean) => Ok(Const::from(*boolean).into()), // TODO: ADD TokenKind::UndefinedLiteral - TokenKind::Identifier(ref i) if i.as_ref() == "undefined" => { + /* TokenKind::Identifier(ref i) if i.as_ref() == "undefined" => { Ok(Const::Undefined.into()) - } + } */ TokenKind::NullLiteral => Ok(Const::Null.into()), TokenKind::Identifier(ident) => Ok(Identifier::from(ident.as_ref()).into()), // TODO: IdentifierReference TokenKind::StringLiteral(s) => Ok(Const::from(s.as_ref()).into()), From ff079114c9decb250428f5db2919607cba24cba2 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 23:02:18 +0200 Subject: [PATCH 12/19] formatting --- boa/src/builtins/mod.rs | 4 ++-- boa/src/builtins/undefined/tests.rs | 1 + boa/src/exec/identifier/mod.rs | 13 ++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 4646a3615d7..92daa4e17f2 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -17,8 +17,8 @@ pub mod property; pub mod regexp; pub mod string; pub mod symbol; -pub mod value; pub mod undefined; +pub mod value; pub(crate) use self::{ array::Array, @@ -34,8 +34,8 @@ pub(crate) use self::{ regexp::RegExp, string::String, symbol::Symbol, - value::{ResultValue, Value}, undefined::Undefined, + value::{ResultValue, Value}, }; /// Initializes builtin objects and functions diff --git a/boa/src/builtins/undefined/tests.rs b/boa/src/builtins/undefined/tests.rs index e69de29bb2d..8b137891791 100644 --- a/boa/src/builtins/undefined/tests.rs +++ b/boa/src/builtins/undefined/tests.rs @@ -0,0 +1 @@ + diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index da150ba481d..8be55d337c5 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -1,18 +1,17 @@ use super::{Executable, Interpreter}; -use crate::{ - builtins::value::ResultValue, - syntax::ast::node::identifier::Identifier, -}; +use crate::{builtins::value::ResultValue, syntax::ast::node::identifier::Identifier}; impl Executable for Identifier { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { - let reference = interpreter.realm().environment.get_binding_value(self.as_ref()); + let reference = interpreter + .realm() + .environment + .get_binding_value(self.as_ref()); match reference { Some(value) => Ok(value), None => Err(interpreter .throw_reference_error(self.as_ref()) - .expect_err("throw_reference_error() must return an error") - ) + .expect_err("throw_reference_error() must return an error")), } } } From e879bca1f5489447a41748465d54929e0ecadfff Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 23:10:57 +0200 Subject: [PATCH 13/19] Add couple of tests --- boa/src/builtins/undefined/tests.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/boa/src/builtins/undefined/tests.rs b/boa/src/builtins/undefined/tests.rs index 8b137891791..ee8ae3394ac 100644 --- a/boa/src/builtins/undefined/tests.rs +++ b/boa/src/builtins/undefined/tests.rs @@ -1 +1,21 @@ +use crate::exec; + +#[test] +fn undefined_direct_evaluation() { + let scenario = r#" + undefined; + "#; + + assert_eq!(&exec(scenario), "undefined"); +} + +#[test] +fn undefined_assignment() { + let scenario = r#" + a = undefined; + a + "#; + + assert_eq!(&exec(scenario), "undefined"); +} From d2b0ff5e1ff429342f68274e53280b5aa986b3ae Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 23:20:27 +0200 Subject: [PATCH 14/19] Tests --- boa/src/builtins/undefined/tests.rs | 3 --- boa/src/environment/lexical_environment.rs | 2 +- boa/src/exec/tests.rs | 31 +++++++++++++++++----- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/boa/src/builtins/undefined/tests.rs b/boa/src/builtins/undefined/tests.rs index ee8ae3394ac..39e7cee8cee 100644 --- a/boa/src/builtins/undefined/tests.rs +++ b/boa/src/builtins/undefined/tests.rs @@ -1,4 +1,3 @@ - use crate::exec; #[test] @@ -6,7 +5,6 @@ fn undefined_direct_evaluation() { let scenario = r#" undefined; "#; - assert_eq!(&exec(scenario), "undefined"); } @@ -16,6 +14,5 @@ fn undefined_assignment() { a = undefined; a "#; - assert_eq!(&exec(scenario), "undefined"); } diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 4b0450f267f..57112aa3cb3 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -318,7 +318,7 @@ mod tests { err.message } "#; - // awaiting agreement on error throw testing + assert_eq!(&exec(scenario), "bar is not defined"); } diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 13e636800e2..f0b3161904d 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -48,7 +48,6 @@ fn property_accessor_member_expression_bracket_notation_on_function() { } #[test] -#[ignore] // will be solved with undefined added to global property fn empty_let_decl_undefined() { let scenario = r#" let a; @@ -70,7 +69,6 @@ fn semicolon_expression_stop() { } #[test] -#[ignore] // will be fixed with undefined added as global property fn empty_var_decl_undefined() { let scenario = r#" let b; @@ -402,7 +400,7 @@ fn for_loop_iteration_variable_does_not_leak() { err.message } "#; - // awaiting agreement on unhandled error handling + assert_eq!(&exec(inner_scope), "i is not defined"); } @@ -477,7 +475,6 @@ fn typeof_rational() { } #[test] -#[ignore] // Will be fixed when global property undefined is added fn typeof_undefined() { let typeof_undefined = r#" let a = undefined; @@ -486,6 +483,14 @@ fn typeof_undefined() { assert_eq!(&exec(typeof_undefined), "undefined"); } +#[test] +fn typeof_undefined_directly() { + let typeof_undefined = r#" + typeof undefined; + "#; + assert_eq!(&exec(typeof_undefined), "undefined"); +} + #[test] fn typeof_boolean() { let typeof_boolean = r#" @@ -756,8 +761,7 @@ mod in_operator { } #[test] -#[ignore] // maybe will be solved when undefined added to global property -fn var_decl_hoisting() { +fn var_decl_hoisting_simple() { let scenario = r#" x = 5; @@ -765,7 +769,10 @@ fn var_decl_hoisting() { x; "#; assert_eq!(&exec(scenario), "5"); +} +#[test] +fn var_decl_hoisting_with_initialization() { let scenario = r#" x = 5; @@ -773,7 +780,11 @@ fn var_decl_hoisting() { x; "#; assert_eq!(&exec(scenario), "10"); +} +#[test] +#[ignore] +fn var_decl_hoisting_2_variables_hoisting() { let scenario = r#" x = y; @@ -783,7 +794,11 @@ fn var_decl_hoisting() { x; "#; assert_eq!(&exec(scenario), "10"); +} +#[test] +#[ignore] +fn var_decl_hoisting_2_variables_hoisting_2() { let scenario = r#" var x = y; @@ -791,7 +806,11 @@ fn var_decl_hoisting() { x; "#; assert_eq!(&exec(scenario), "undefined"); +} +#[test] +#[ignore] +fn var_decl_hoisting_2_variables_hoisting_3() { let scenario = r#" let y = x; x = 5; From e40788d59892d10d05e8c7052e084f7bec4e1e5c Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 23:24:20 +0200 Subject: [PATCH 15/19] Remove 2 comments --- boa/src/exec/mod.rs | 1 - boa/src/syntax/parser/expression/primary/mod.rs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 4bdf1fb92c6..98e34a773cc 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -579,7 +579,6 @@ impl Executable for Node { let _timer = BoaProfiler::global().start_event("Executable", "exec"); match *self { Node::Const(Const::Null) => Ok(Value::null()), - //Node::Const(Const::Undefined) => (Identifier::from((&Box::new("asd")).as_ref()).into()).run(interpreter), Node::Const(Const::Num(num)) => Ok(Value::rational(num)), Node::Const(Const::Int(num)) => Ok(Value::integer(num)), Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())), diff --git a/boa/src/syntax/parser/expression/primary/mod.rs b/boa/src/syntax/parser/expression/primary/mod.rs index 43e796d74d1..5118423d56a 100644 --- a/boa/src/syntax/parser/expression/primary/mod.rs +++ b/boa/src/syntax/parser/expression/primary/mod.rs @@ -85,10 +85,6 @@ impl TokenParser for PrimaryExpression { .into()) } TokenKind::BooleanLiteral(boolean) => Ok(Const::from(*boolean).into()), - // TODO: ADD TokenKind::UndefinedLiteral - /* TokenKind::Identifier(ref i) if i.as_ref() == "undefined" => { - Ok(Const::Undefined.into()) - } */ TokenKind::NullLiteral => Ok(Const::Null.into()), TokenKind::Identifier(ident) => Ok(Identifier::from(ident.as_ref()).into()), // TODO: IdentifierReference TokenKind::StringLiteral(s) => Ok(Const::from(s.as_ref()).into()), From ada9cc96f40825d2f5c95fb59036fbd477f4103b Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 16 Jun 2020 23:44:46 +0200 Subject: [PATCH 16/19] Replace unwrap with expect --- boa/src/builtins/array/mod.rs | 2 +- boa/src/exec/mod.rs | 12 ++++++------ boa/src/exec/operator/mod.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index d3ad4a1ecba..27c445ae069 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -54,7 +54,7 @@ impl Array { .realm() .environment .get_binding_value("Array") - .unwrap() + .expect("Array was not initialized") .borrow() .get_field(PROTOTYPE), ); diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 98e34a773cc..0082422d610 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -420,7 +420,7 @@ impl Interpreter { .realm .environment .get_binding_value("Boolean") - .unwrap() + .expect("Boolean was not initialized") .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -433,7 +433,7 @@ impl Interpreter { .realm .environment .get_binding_value("Number") - .unwrap() + .expect("Number was not initialized") .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( proto, @@ -445,7 +445,7 @@ impl Interpreter { .realm .environment .get_binding_value("Number") - .unwrap() + .expect("Number was not initialized") .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -458,7 +458,7 @@ impl Interpreter { .realm .environment .get_binding_value("String") - .unwrap() + .expect("String was not initialized") .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -471,7 +471,7 @@ impl Interpreter { .realm .environment .get_binding_value("Symbol") - .unwrap() + .expect("Symbol was not initialized") .get_field(PROTOTYPE); Ok(Value::new_object_from_prototype( @@ -484,7 +484,7 @@ impl Interpreter { .realm .environment .get_binding_value("BigInt") - .unwrap() + .expect("BigInt was not initialized") .get_field(PROTOTYPE); let bigint_obj = Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone())); diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index 8548b165b26..44d612c8c1c 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -119,7 +119,7 @@ impl Executable for BinOp { .realm() .environment .get_binding_value(name.as_ref()) - .unwrap(); + .expect("Identifier was not initialized"); let v_b = self.rhs().run(interpreter)?; let value = Self::run_assign(op, v_a, v_b); interpreter.realm.environment.set_mutable_binding( From 4c1e6a646c2bb4d7cd778c7bb5c272bf259ad6fc Mon Sep 17 00:00:00 2001 From: croraf Date: Wed, 17 Jun 2020 13:42:08 +0200 Subject: [PATCH 17/19] Minor - use ok_or_else instead of match construct --- boa/src/exec/identifier/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index 8be55d337c5..c2556e7f820 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -3,15 +3,14 @@ use crate::{builtins::value::ResultValue, syntax::ast::node::identifier::Identif impl Executable for Identifier { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { - let reference = interpreter + interpreter .realm() .environment - .get_binding_value(self.as_ref()); - match reference { - Some(value) => Ok(value), - None => Err(interpreter - .throw_reference_error(self.as_ref()) - .expect_err("throw_reference_error() must return an error")), - } + .get_binding_value(self.as_ref()) + .ok_or_else(|| { + interpreter + .throw_reference_error(self.as_ref()) + .expect_err("throw_reference_error() must return an error") + }) } } From 74974e22c4e39f8d484d944ec77264ccd3e1f4dc Mon Sep 17 00:00:00 2001 From: croraf Date: Wed, 17 Jun 2020 18:29:37 +0200 Subject: [PATCH 18/19] Cover assignmetoperator undefined case and test --- boa/src/exec/operator/mod.rs | 8 +++++++- boa/src/exec/operator/tests.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 boa/src/exec/operator/tests.rs diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index 44d612c8c1c..f811f1737dc 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -1,4 +1,6 @@ //! Operator execution. +#[cfg(test)] +mod tests; use super::{Executable, Interpreter}; use crate::{ @@ -119,7 +121,11 @@ impl Executable for BinOp { .realm() .environment .get_binding_value(name.as_ref()) - .expect("Identifier was not initialized"); + .ok_or_else(|| { + interpreter + .throw_reference_error(name.as_ref()) + .expect_err("throw_reference_error() must return an error") + })?; let v_b = self.rhs().run(interpreter)?; let value = Self::run_assign(op, v_a, v_b); interpreter.realm.environment.set_mutable_binding( diff --git a/boa/src/exec/operator/tests.rs b/boa/src/exec/operator/tests.rs new file mode 100644 index 00000000000..4d4fcbe6076 --- /dev/null +++ b/boa/src/exec/operator/tests.rs @@ -0,0 +1,28 @@ +use crate::exec; + +#[test] +fn assignmentoperator_lhs_not_defined() { + let scenario = r#" + try { + a += 5 + } catch (err) { + err.message + } + "#; + + assert_eq!(&exec(scenario), "a is not defined"); +} + +#[test] +fn assignmentoperator_rhs_throws_error() { + let scenario = r#" + try { + let a; + a += b + } catch (err) { + err.message + } + "#; + + assert_eq!(&exec(scenario), "b is not defined"); +} From 50f138118851847d3d87f846b0b53706a2a611ef Mon Sep 17 00:00:00 2001 From: croraf Date: Thu, 18 Jun 2020 21:02:13 +0200 Subject: [PATCH 19/19] Replace throw_reference_eror with construct_reference_error --- boa/src/exec/identifier/mod.rs | 6 +----- boa/src/exec/operator/mod.rs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/boa/src/exec/identifier/mod.rs b/boa/src/exec/identifier/mod.rs index c2556e7f820..19bf6e203cb 100644 --- a/boa/src/exec/identifier/mod.rs +++ b/boa/src/exec/identifier/mod.rs @@ -7,10 +7,6 @@ impl Executable for Identifier { .realm() .environment .get_binding_value(self.as_ref()) - .ok_or_else(|| { - interpreter - .throw_reference_error(self.as_ref()) - .expect_err("throw_reference_error() must return an error") - }) + .ok_or_else(|| interpreter.construct_reference_error(self.as_ref())) } } diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index f811f1737dc..86953a923e5 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -121,11 +121,7 @@ impl Executable for BinOp { .realm() .environment .get_binding_value(name.as_ref()) - .ok_or_else(|| { - interpreter - .throw_reference_error(name.as_ref()) - .expect_err("throw_reference_error() must return an error") - })?; + .ok_or_else(|| interpreter.construct_reference_error(name.as_ref()))?; let v_b = self.rhs().run(interpreter)?; let value = Self::run_assign(op, v_a, v_b); interpreter.realm.environment.set_mutable_binding(