Browse Source

Added undefined property to global scope (#501)

pull/506/head
croraf 4 years ago committed by GitHub
parent
commit
c19ef724e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      boa/src/builtins/array/mod.rs
  2. 3
      boa/src/builtins/mod.rs
  3. 32
      boa/src/builtins/undefined/mod.rs
  4. 18
      boa/src/builtins/undefined/tests.rs
  5. 5
      boa/src/environment/lexical_environment.rs
  6. 21
      boa/src/exec/identifier/mod.rs
  7. 7
      boa/src/exec/mod.rs
  8. 5
      boa/src/exec/operator/mod.rs
  9. 28
      boa/src/exec/operator/tests.rs
  10. 31
      boa/src/exec/tests.rs
  11. 4
      boa/src/syntax/parser/expression/primary/mod.rs

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

@ -54,6 +54,7 @@ impl Array {
.realm()
.environment
.get_binding_value("Array")
.expect("Array was not initialized")
.borrow()
.get_field(PROTOTYPE),
);

3
boa/src/builtins/mod.rs

@ -17,6 +17,7 @@ pub mod property;
pub mod regexp;
pub mod string;
pub mod symbol;
pub mod undefined;
pub mod value;
pub(crate) use self::{
@ -33,6 +34,7 @@ pub(crate) use self::{
regexp::RegExp,
string::String,
symbol::Symbol,
undefined::Undefined,
value::{ResultValue, Value, ValueData},
};
@ -62,6 +64,7 @@ pub fn init(global: &Value) {
NaN::init,
Infinity::init,
GlobalThis::init,
Undefined::init,
];
match global.data() {

32
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())
}
}

18
boa/src/builtins/undefined/tests.rs

@ -0,0 +1,18 @@
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");
}

5
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<Value> {
self.environments()
.find(|env| env.borrow().has_binding(name))
.map(|env| env.borrow().get_binding_value(name, false))
.unwrap_or_else(Value::undefined)
}
}
@ -319,7 +318,7 @@ mod tests {
err.message
}
"#;
// awaiting agreement on error throw testing
assert_eq!(&exec(scenario), "bar is not defined");
}

21
boa/src/exec/identifier/mod.rs

@ -1,21 +1,12 @@
use super::{Executable, Interpreter};
use crate::{
builtins::value::{ResultValue, Value, ValueData},
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 = 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),
}
interpreter
.realm()
.environment
.get_binding_value(self.as_ref())
.ok_or_else(|| interpreter.construct_reference_error(self.as_ref()))
}
}
pub(crate) fn resolve_binding(interpreter: &mut Interpreter, name: &str) -> Value {
interpreter.realm().environment.get_binding_value(name)
}

7
boa/src/exec/mod.rs

@ -420,6 +420,7 @@ impl Interpreter {
.realm
.environment
.get_binding_value("Boolean")
.expect("Boolean was not initialized")
.get_field(PROTOTYPE);
Ok(Value::new_object_from_prototype(
@ -432,6 +433,7 @@ impl Interpreter {
.realm
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
.get_field(PROTOTYPE);
Ok(Value::new_object_from_prototype(
proto,
@ -443,6 +445,7 @@ impl Interpreter {
.realm
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
.get_field(PROTOTYPE);
Ok(Value::new_object_from_prototype(
@ -455,6 +458,7 @@ impl Interpreter {
.realm
.environment
.get_binding_value("String")
.expect("String was not initialized")
.get_field(PROTOTYPE);
Ok(Value::new_object_from_prototype(
@ -467,6 +471,7 @@ impl Interpreter {
.realm
.environment
.get_binding_value("Symbol")
.expect("Symbol was not initialized")
.get_field(PROTOTYPE);
Ok(Value::new_object_from_prototype(
@ -479,6 +484,7 @@ impl Interpreter {
.realm
.environment
.get_binding_value("BigInt")
.expect("BigInt was not initialized")
.get_field(PROTOTYPE);
let bigint_obj =
Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone()));
@ -573,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) => Ok(Value::undefined()),
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())),

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

@ -1,4 +1,6 @@
//! Operator execution.
#[cfg(test)]
mod tests;
use super::{Executable, Interpreter};
use crate::{
@ -118,7 +120,8 @@ impl Executable for BinOp {
let v_a = interpreter
.realm()
.environment
.get_binding_value(name.as_ref());
.get_binding_value(name.as_ref())
.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(

28
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");
}

31
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;

4
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()),

Loading…
Cancel
Save