From c4a652a517d171ce6e4a226fce98d03a1e9b15b8 Mon Sep 17 00:00:00 2001 From: Jason Williams <936006+jasonwilliams@users.noreply.github.com> Date: Thu, 25 Jun 2020 15:52:42 +0100 Subject: [PATCH] Fix sending `this` value to function environments (#526) --- .editorConfig | 6 ++ .vscode/tasks.json | 60 ++++++++++++------- boa/src/builtins/function/mod.rs | 20 ++++++- .../function_environment_record.rs | 2 +- boa/src/environment/lexical_environment.rs | 11 +++- boa/src/exec/tests.rs | 16 +++++ 6 files changed, 88 insertions(+), 27 deletions(-) diff --git a/.editorConfig b/.editorConfig index f8296da124..4e2d43cc62 100644 --- a/.editorConfig +++ b/.editorConfig @@ -3,3 +3,9 @@ root = true [{Makefile,**.mk}] # Use tabs for indentation (Makefiles require tabs) indent_style = tab + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.vscode/tasks.json b/.vscode/tasks.json index aec1c2cd7b..032f530552 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,14 +7,20 @@ "type": "process", "label": "Cargo Run", "command": "cargo", - "args": ["run", "--bin", "boa", "./tests/js/test.js"], - "problemMatcher": ["$rustc"], + "args": [ + "run", + "--bin", + "boa", + "./tests/js/test.js" + ], "group": { "kind": "build", "isDefault": true }, "options": { - "env": { "RUST_BACKTRACE": "full" } + "env": { + "RUST_BACKTRACE": "full" + } }, "presentation": { "clear": true @@ -24,14 +30,17 @@ "type": "process", "label": "Cargo Run (Profiler)", "command": "cargo", - "args": ["run", "--features", "Boa/profiler", "../tests/js/test.js"], - "problemMatcher": ["$rustc"], - "group": { - "kind": "build", - "isDefault": true - }, + "args": [ + "run", + "--features", + "Boa/profiler", + "../tests/js/test.js" + ], + "group": "build", "options": { - "env": { "RUST_BACKTRACE": "full" }, + "env": { + "RUST_BACKTRACE": "full" + }, "cwd": "${workspaceFolder}/boa_cli" }, "presentation": { @@ -42,8 +51,12 @@ "type": "process", "label": "Get Tokens", "command": "cargo", - "args": ["run", "--", "-t=Debug", "./tests/js/test.js"], - "problemMatcher": ["$rustc"], + "args": [ + "run", + "--", + "-t=Debug", + "./tests/js/test.js" + ], "group": "build", "presentation": { "clear": true @@ -53,19 +66,24 @@ "type": "process", "label": "Get AST", "command": "cargo", - "args": ["run", "--", "-a=Debug", "./tests/js/test.js"], - "problemMatcher": ["$rustc"], + "args": [ + "run", + "--", + "-a=Debug", + "./tests/js/test.js" + ], "group": "build", "presentation": { "clear": true - } + }, }, { "type": "process", "label": "Cargo Test", "command": "cargo", - "args": ["test"], - "problemMatcher": ["$rustc"], + "args": [ + "test" + ], "group": { "kind": "test", "isDefault": true @@ -78,9 +96,11 @@ "type": "process", "label": "Cargo Test Build", "command": "cargo", - "args": ["test", "--no-run"], - "problemMatcher": ["$rustc"], + "args": [ + "test", + "--no-run" + ], "group": "build" } ] -} +} \ No newline at end of file diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 1e19f78afe..d98c99ad8f 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -191,9 +191,18 @@ impl Function { // let local_env = new_function_environment( function, - None, + if let ThisMode::Lexical = self.this_mode { + None + } else { + Some(this.clone()) + }, self.environment.as_ref().cloned(), - BindingStatus::Uninitialized, + // Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records + if let ThisMode::Lexical = self.this_mode { + BindingStatus::Lexical + } else { + BindingStatus::Uninitialized + }, ); // Add argument bindings to the function environment @@ -253,7 +262,12 @@ impl Function { function, Some(this.clone()), self.environment.as_ref().cloned(), - BindingStatus::Initialized, + // Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records + if let ThisMode::Lexical = self.this_mode { + BindingStatus::Lexical + } else { + BindingStatus::Uninitialized + }, ); // Add argument bindings to the function environment diff --git a/boa/src/environment/function_environment_record.rs b/boa/src/environment/function_environment_record.rs index dcd3dbee00..8a8c4b0b80 100644 --- a/boa/src/environment/function_environment_record.rs +++ b/boa/src/environment/function_environment_record.rs @@ -112,7 +112,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { } BindingStatus::Uninitialized => { // TODO: change this when error handling comes into play - panic!("Reference Error: Unitialised binding for this function"); + panic!("Reference Error: Uninitialised binding for this function"); } BindingStatus::Initialized => self.this_value.clone(), diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 9bb933c8f6..ad7e0dde19 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -235,15 +235,20 @@ pub fn new_function_environment( outer: Option, binding_status: BindingStatus, ) -> Environment { - Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord { + let mut func_env = FunctionEnvironmentRecord { env_rec: FxHashMap::default(), function: f, this_binding_status: binding_status, home_object: Value::undefined(), new_target: Value::undefined(), outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects - this_value: this.unwrap_or_else(Value::undefined), - }))) + this_value: Value::undefined(), + }; + // If a `this` value has been passed, bind it to the environment + if let Some(v) = this { + func_env.bind_this_value(v); + } + Gc::new(GcCell::new(Box::new(func_env))) } pub fn new_object_environment(object: Value, environment: Option) -> Environment { diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 79d68a82af..439bcedf56 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -939,3 +939,19 @@ fn to_object() { .is_object()); assert!(engine.to_object(&Value::null()).unwrap_err().is_object()); } + +#[test] +fn check_this_binding_in_object_literal() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var foo = { + a: 3, + bar: function () { return this.a + 5 } + }; + + foo.bar() + "#; + + assert_eq!(forward(&mut engine, init), "8"); +}