Skip to content

Commit c19ef72

Browse files
authored
Added undefined property to global scope (#501)
1 parent e674da4 commit c19ef72

11 files changed

Lines changed: 125 additions & 30 deletions

File tree

boa/src/builtins/array/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl Array {
5454
.realm()
5555
.environment
5656
.get_binding_value("Array")
57+
.expect("Array was not initialized")
5758
.borrow()
5859
.get_field(PROTOTYPE),
5960
);

boa/src/builtins/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod property;
1717
pub mod regexp;
1818
pub mod string;
1919
pub mod symbol;
20+
pub mod undefined;
2021
pub mod value;
2122

2223
pub(crate) use self::{
@@ -33,6 +34,7 @@ pub(crate) use self::{
3334
regexp::RegExp,
3435
string::String,
3536
symbol::Symbol,
37+
undefined::Undefined,
3638
value::{ResultValue, Value, ValueData},
3739
};
3840

@@ -62,6 +64,7 @@ pub fn init(global: &Value) {
6264
NaN::init,
6365
Infinity::init,
6466
GlobalThis::init,
67+
Undefined::init,
6568
];
6669

6770
match global.data() {

boa/src/builtins/undefined/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! This module implements the global `undefined` property.
2+
//!
3+
//! The global undefined property represents the primitive value undefined.
4+
//!
5+
//! More information:
6+
//! - [MDN documentation][mdn]
7+
//! - [ECMAScript reference][spec]
8+
//!
9+
//! [spec]: https://tc39.es/ecma262/#sec-undefined
10+
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined
11+
12+
#[cfg(test)]
13+
mod tests;
14+
15+
use crate::{builtins::value::Value, BoaProfiler};
16+
17+
/// JavaScript global `undefined` property.
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19+
pub(crate) struct Undefined;
20+
21+
impl Undefined {
22+
/// The binding name of the property.
23+
pub(crate) const NAME: &'static str = "undefined";
24+
25+
/// Initialize the `undefined` property on the global object.
26+
#[inline]
27+
pub(crate) fn init(_: &Value) -> (&str, Value) {
28+
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
29+
30+
(Self::NAME, Value::undefined())
31+
}
32+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use crate::exec;
2+
3+
#[test]
4+
fn undefined_direct_evaluation() {
5+
let scenario = r#"
6+
undefined;
7+
"#;
8+
assert_eq!(&exec(scenario), "undefined");
9+
}
10+
11+
#[test]
12+
fn undefined_assignment() {
13+
let scenario = r#"
14+
a = undefined;
15+
a
16+
"#;
17+
assert_eq!(&exec(scenario), "undefined");
18+
}

boa/src/environment/lexical_environment.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,10 @@ impl LexicalEnvironment {
212212
.any(|env| env.borrow().has_binding(name))
213213
}
214214

215-
pub fn get_binding_value(&self, name: &str) -> Value {
215+
pub fn get_binding_value(&self, name: &str) -> Option<Value> {
216216
self.environments()
217217
.find(|env| env.borrow().has_binding(name))
218218
.map(|env| env.borrow().get_binding_value(name, false))
219-
.unwrap_or_else(Value::undefined)
220219
}
221220
}
222221

@@ -319,7 +318,7 @@ mod tests {
319318
err.message
320319
}
321320
"#;
322-
// awaiting agreement on error throw testing
321+
323322
assert_eq!(&exec(scenario), "bar is not defined");
324323
}
325324

boa/src/exec/identifier/mod.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
use super::{Executable, Interpreter};
2-
use crate::{
3-
builtins::value::{ResultValue, Value, ValueData},
4-
syntax::ast::node::identifier::Identifier,
5-
};
2+
use crate::{builtins::value::ResultValue, syntax::ast::node::identifier::Identifier};
63

74
impl Executable for Identifier {
85
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
9-
let reference = resolve_binding(interpreter, self.as_ref());
10-
match reference.data() {
11-
ValueData::Undefined => Err(interpreter
12-
.throw_reference_error(self.as_ref())
13-
.expect_err("throw_reference_error() must return an error")),
14-
_ => Ok(reference),
15-
}
6+
interpreter
7+
.realm()
8+
.environment
9+
.get_binding_value(self.as_ref())
10+
.ok_or_else(|| interpreter.construct_reference_error(self.as_ref()))
1611
}
1712
}
18-
19-
pub(crate) fn resolve_binding(interpreter: &mut Interpreter, name: &str) -> Value {
20-
interpreter.realm().environment.get_binding_value(name)
21-
}

boa/src/exec/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ impl Interpreter {
420420
.realm
421421
.environment
422422
.get_binding_value("Boolean")
423+
.expect("Boolean was not initialized")
423424
.get_field(PROTOTYPE);
424425

425426
Ok(Value::new_object_from_prototype(
@@ -432,6 +433,7 @@ impl Interpreter {
432433
.realm
433434
.environment
434435
.get_binding_value("Number")
436+
.expect("Number was not initialized")
435437
.get_field(PROTOTYPE);
436438
Ok(Value::new_object_from_prototype(
437439
proto,
@@ -443,6 +445,7 @@ impl Interpreter {
443445
.realm
444446
.environment
445447
.get_binding_value("Number")
448+
.expect("Number was not initialized")
446449
.get_field(PROTOTYPE);
447450

448451
Ok(Value::new_object_from_prototype(
@@ -455,6 +458,7 @@ impl Interpreter {
455458
.realm
456459
.environment
457460
.get_binding_value("String")
461+
.expect("String was not initialized")
458462
.get_field(PROTOTYPE);
459463

460464
Ok(Value::new_object_from_prototype(
@@ -467,6 +471,7 @@ impl Interpreter {
467471
.realm
468472
.environment
469473
.get_binding_value("Symbol")
474+
.expect("Symbol was not initialized")
470475
.get_field(PROTOTYPE);
471476

472477
Ok(Value::new_object_from_prototype(
@@ -479,6 +484,7 @@ impl Interpreter {
479484
.realm
480485
.environment
481486
.get_binding_value("BigInt")
487+
.expect("BigInt was not initialized")
482488
.get_field(PROTOTYPE);
483489
let bigint_obj =
484490
Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone()));
@@ -573,7 +579,6 @@ impl Executable for Node {
573579
let _timer = BoaProfiler::global().start_event("Executable", "exec");
574580
match *self {
575581
Node::Const(Const::Null) => Ok(Value::null()),
576-
Node::Const(Const::Undefined) => Ok(Value::undefined()),
577582
Node::Const(Const::Num(num)) => Ok(Value::rational(num)),
578583
Node::Const(Const::Int(num)) => Ok(Value::integer(num)),
579584
Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())),

boa/src/exec/operator/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
//! Operator execution.
2+
#[cfg(test)]
3+
mod tests;
24

35
use super::{Executable, Interpreter};
46
use crate::{
@@ -118,7 +120,8 @@ impl Executable for BinOp {
118120
let v_a = interpreter
119121
.realm()
120122
.environment
121-
.get_binding_value(name.as_ref());
123+
.get_binding_value(name.as_ref())
124+
.ok_or_else(|| interpreter.construct_reference_error(name.as_ref()))?;
122125
let v_b = self.rhs().run(interpreter)?;
123126
let value = Self::run_assign(op, v_a, v_b);
124127
interpreter.realm.environment.set_mutable_binding(

boa/src/exec/operator/tests.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use crate::exec;
2+
3+
#[test]
4+
fn assignmentoperator_lhs_not_defined() {
5+
let scenario = r#"
6+
try {
7+
a += 5
8+
} catch (err) {
9+
err.message
10+
}
11+
"#;
12+
13+
assert_eq!(&exec(scenario), "a is not defined");
14+
}
15+
16+
#[test]
17+
fn assignmentoperator_rhs_throws_error() {
18+
let scenario = r#"
19+
try {
20+
let a;
21+
a += b
22+
} catch (err) {
23+
err.message
24+
}
25+
"#;
26+
27+
assert_eq!(&exec(scenario), "b is not defined");
28+
}

boa/src/exec/tests.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ fn property_accessor_member_expression_bracket_notation_on_function() {
4848
}
4949

5050
#[test]
51-
#[ignore] // will be solved with undefined added to global property
5251
fn empty_let_decl_undefined() {
5352
let scenario = r#"
5453
let a;
@@ -70,7 +69,6 @@ fn semicolon_expression_stop() {
7069
}
7170

7271
#[test]
73-
#[ignore] // will be fixed with undefined added as global property
7472
fn empty_var_decl_undefined() {
7573
let scenario = r#"
7674
let b;
@@ -402,7 +400,7 @@ fn for_loop_iteration_variable_does_not_leak() {
402400
err.message
403401
}
404402
"#;
405-
// awaiting agreement on unhandled error handling
403+
406404
assert_eq!(&exec(inner_scope), "i is not defined");
407405
}
408406

@@ -477,7 +475,6 @@ fn typeof_rational() {
477475
}
478476

479477
#[test]
480-
#[ignore] // Will be fixed when global property undefined is added
481478
fn typeof_undefined() {
482479
let typeof_undefined = r#"
483480
let a = undefined;
@@ -486,6 +483,14 @@ fn typeof_undefined() {
486483
assert_eq!(&exec(typeof_undefined), "undefined");
487484
}
488485

486+
#[test]
487+
fn typeof_undefined_directly() {
488+
let typeof_undefined = r#"
489+
typeof undefined;
490+
"#;
491+
assert_eq!(&exec(typeof_undefined), "undefined");
492+
}
493+
489494
#[test]
490495
fn typeof_boolean() {
491496
let typeof_boolean = r#"
@@ -756,24 +761,30 @@ mod in_operator {
756761
}
757762

758763
#[test]
759-
#[ignore] // maybe will be solved when undefined added to global property
760-
fn var_decl_hoisting() {
764+
fn var_decl_hoisting_simple() {
761765
let scenario = r#"
762766
x = 5;
763767
764768
var x;
765769
x;
766770
"#;
767771
assert_eq!(&exec(scenario), "5");
772+
}
768773

774+
#[test]
775+
fn var_decl_hoisting_with_initialization() {
769776
let scenario = r#"
770777
x = 5;
771778
772779
var x = 10;
773780
x;
774781
"#;
775782
assert_eq!(&exec(scenario), "10");
783+
}
776784

785+
#[test]
786+
#[ignore]
787+
fn var_decl_hoisting_2_variables_hoisting() {
777788
let scenario = r#"
778789
x = y;
779790
@@ -783,15 +794,23 @@ fn var_decl_hoisting() {
783794
x;
784795
"#;
785796
assert_eq!(&exec(scenario), "10");
797+
}
786798

799+
#[test]
800+
#[ignore]
801+
fn var_decl_hoisting_2_variables_hoisting_2() {
787802
let scenario = r#"
788803
var x = y;
789804
790805
var y = 5;
791806
x;
792807
"#;
793808
assert_eq!(&exec(scenario), "undefined");
809+
}
794810

811+
#[test]
812+
#[ignore]
813+
fn var_decl_hoisting_2_variables_hoisting_3() {
795814
let scenario = r#"
796815
let y = x;
797816
x = 5;

0 commit comments

Comments
 (0)