diff --git a/src/lib/environment/environment_record_trait.rs b/src/lib/environment/environment_record_trait.rs index 9f1ead60586..eaa71618441 100644 --- a/src/lib/environment/environment_record_trait.rs +++ b/src/lib/environment/environment_record_trait.rs @@ -10,7 +10,7 @@ //! use crate::{ environment::lexical_environment::{Environment, EnvironmentType}, - js::value::Value, + js::value::{self, Value}, }; use gc::{Finalize, Trace}; use std::fmt::Debug; @@ -60,6 +60,11 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { /// Return true if it does and false if it does not. fn has_this_binding(&self) -> bool; + fn get_this_binding(&self) -> Value { + debug_assert!(self.has_this_binding()); + value::undefined() + } + /// Determine if an Environment Record establishes a super method binding. /// Return true if it does and false if it does not. fn has_super_binding(&self) -> bool; diff --git a/src/lib/environment/function_environment_record.rs b/src/lib/environment/function_environment_record.rs index 072b9b31a42..7e033944bbc 100644 --- a/src/lib/environment/function_environment_record.rs +++ b/src/lib/environment/function_environment_record.rs @@ -75,21 +75,6 @@ impl FunctionEnvironmentRecord { } } } - - pub fn get_this_binding(&self) -> Value { - match self.this_binding_status { - BindingStatus::Lexical => { - // TODO: change this when error handling comes into play - panic!("There is no this for a lexical function record"); - } - BindingStatus::Uninitialized => { - // TODO: change this when error handling comes into play - panic!("Reference Error: Unitialised binding for this function"); - } - - BindingStatus::Initialized => self.this_value.clone(), - } - } } impl EnvironmentRecordTrait for FunctionEnvironmentRecord { @@ -215,6 +200,21 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { } } + fn get_this_binding(&self) -> Value { + match self.this_binding_status { + BindingStatus::Lexical => { + // TODO: change this when error handling comes into play + panic!("There is no this for a lexical function record"); + } + BindingStatus::Uninitialized => { + // TODO: change this when error handling comes into play + panic!("Reference Error: Unitialised binding for this function"); + } + + BindingStatus::Initialized => self.this_value.clone(), + } + } + fn with_base_object(&self) -> Value { Gc::new(ValueData::Undefined) } diff --git a/src/lib/environment/global_environment_record.rs b/src/lib/environment/global_environment_record.rs index 8c3aa8a82a1..ad8d2da18c1 100644 --- a/src/lib/environment/global_environment_record.rs +++ b/src/lib/environment/global_environment_record.rs @@ -29,10 +29,6 @@ pub struct GlobalEnvironmentRecord { } impl GlobalEnvironmentRecord { - pub fn get_this_binding(&self) -> Value { - self.global_this_binding.clone() - } - pub fn has_var_declaration(&self, name: &str) -> bool { self.var_names.contains(name) } @@ -166,6 +162,10 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord { true } + fn get_this_binding(&self) -> Value { + self.global_this_binding.clone() + } + fn has_super_binding(&self) -> bool { false } diff --git a/src/lib/environment/lexical_environment.rs b/src/lib/environment/lexical_environment.rs index 4069157617b..d66807b771b 100644 --- a/src/lib/environment/lexical_environment.rs +++ b/src/lib/environment/lexical_environment.rs @@ -164,14 +164,14 @@ pub fn new_declarative_environment(env: Option) -> Environment { Gc::new(GcCell::new(boxed_env)) } -pub fn new_function_environment( +pub fn new_function_environment_record( f: Value, new_target: Value, outer: Option, -) -> Environment { +) -> FunctionEnvironmentRecord { debug_assert!(f.is_function()); debug_assert!(new_target.is_object() || new_target.is_undefined()); - Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord { + FunctionEnvironmentRecord { env_rec: HashMap::new(), function_object: f, this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported @@ -179,7 +179,16 @@ pub fn new_function_environment( new_target, outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.github.io/ecma262/#sec-ecmascript-function-objects this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with - }))) + } +} + +pub fn new_function_environment( + f: Value, + new_target: Value, + outer: Option, +) -> Environment { + let func_rec = Box::new(new_function_environment_record(f, new_target, outer)); + Gc::new(GcCell::new(func_rec)) } pub fn new_object_environment(object: Value, environment: Option) -> Environment { diff --git a/src/lib/exec.rs b/src/lib/exec.rs index fd1382cc38b..2b2df9b2e7e 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -1,5 +1,5 @@ use crate::{ - environment::lexical_environment::new_function_environment, + environment::lexical_environment::{new_function_environment, new_function_environment_record}, js::{ function::{create_unmapped_arguments_object, Function, RegularFunction}, object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, @@ -90,6 +90,11 @@ impl Executor for Interpreter { let val = self.realm.environment.get_binding_value(name); Ok(val) } + ExprDef::This => { + let env = self.realm.environment.get_current_environment_ref(); + let val = env.deref().borrow().get_this_binding(); + Ok(val) + } ExprDef::GetConstField(ref obj, ref field) => { let val_obj = self.run(obj)?; Ok(val_obj.borrow().get_field(field)) @@ -462,11 +467,14 @@ impl Interpreter { let env = &mut self.realm.environment; // New target (second argument) is only needed for constructors, just pass undefined let undefined = Gc::new(ValueData::Undefined); - env.push(new_function_environment( + let mut func_rec = new_function_environment_record( f.clone(), undefined, Some(env.get_current_environment_ref().clone()), - )); + ); + func_rec.bind_this_value(v.clone()); + env.push(Gc::new(GcCell::new(Box::new(func_rec)))); + for i in 0..data.args.len() { let name = data.args.get(i).unwrap(); let expr: &Value = arguments_list.get(i).unwrap(); @@ -660,4 +668,20 @@ mod tests { assert_eq!(exec(scenario), pass); } + + #[test] + fn example_this() { + let scenario = r#" + let vector = { + x: 3, + y: 4, + length: function() { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }; + vector.length(); + "#; + + assert_eq!(exec(scenario), "5"); + } } diff --git a/src/lib/syntax/ast/expr.rs b/src/lib/syntax/ast/expr.rs index eac8c040aeb..be40612b6ef 100644 --- a/src/lib/syntax/ast/expr.rs +++ b/src/lib/syntax/ast/expr.rs @@ -44,6 +44,7 @@ pub enum ExprDef { Block(Vec), /// Load a reference to a value Local(String), + This, /// Gets the constant field of a value GetConstField(Box, String), /// Gets the field of a value @@ -123,6 +124,7 @@ impl Display for ExprDef { write!(f, "}}") } ExprDef::Local(ref s) => write!(f, "{}", s), + ExprDef::This => write!(f, "this"), ExprDef::GetConstField(ref ex, ref field) => write!(f, "{}.{}", ex, field), ExprDef::GetField(ref ex, ref field) => write!(f, "{}[{}]", ex, field), ExprDef::Call(ref ex, ref args) => { diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index 4c94e523c60..b065f1478cb 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -358,6 +358,7 @@ impl Parser { mk!(self, ExprDef::Const(Const::Undefined)) } TokenData::Identifier(s) => mk!(self, ExprDef::Local(s)), + TokenData::Keyword(Keyword::This) => mk!(self, ExprDef::This), TokenData::Keyword(keyword) => self.parse_struct(keyword)?, TokenData::RegularExpressionLiteral(body, flags) => Expr::new(ExprDef::Construct( Box::new(Expr::new(ExprDef::Local("RegExp".to_string()))),