Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement this expression #185

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/lib/environment/environment_record_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait`
//!
use crate::{
builtins::value::Value,
builtins::value::{self, Value},
environment::lexical_environment::{Environment, EnvironmentType},
};
use gc::{Finalize, Trace};
Expand Down Expand Up @@ -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;
Expand Down
30 changes: 15 additions & 15 deletions src/lib/environment/function_environment_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -221,6 +206,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)
}
Expand Down
8 changes: 4 additions & 4 deletions src/lib/environment/global_environment_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
}
Expand Down
14 changes: 10 additions & 4 deletions src/lib/environment/lexical_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ impl LexicalEnvironment {
.map(|env| env.borrow().get_binding_value(name, false))
.unwrap_or_else(|| Gc::new(ValueData::Undefined))
}

pub fn get_this_environment(&self) -> Environment {
self.environments()
.find(|env| env.borrow().has_this_binding())
.expect("Could not get environment for this")
}
}

pub fn new_declarative_environment(env: Option<Environment>) -> Environment {
Expand All @@ -224,22 +230,22 @@ pub fn new_declarative_environment(env: Option<Environment>) -> 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>,
) -> 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
home_object: Gc::new(ValueData::Undefined),
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_object_environment(object: Value, environment: Option<Environment>) -> Environment {
Expand Down
44 changes: 36 additions & 8 deletions src/lib/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
value::{from_value, to_value, ResultValue, Value, ValueData},
},
environment::lexical_environment::{
new_declarative_environment, new_function_environment, VariableScope,
new_declarative_environment, new_function_environment_record, VariableScope,
},
realm::Realm,
syntax::ast::{
Expand Down Expand Up @@ -101,6 +101,11 @@ impl Executor for Interpreter {
let val = self.realm.environment.get_binding_value(name);
Ok(val)
}
ExprDef::This => {
let env = self.realm.environment.get_this_environment();
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))
Expand Down Expand Up @@ -354,7 +359,10 @@ impl Executor for Interpreter {
func_object.borrow().get_field_slice(PROTOTYPE),
);

let construct = func_object.get_internal_slot("construct");
let construct = match *func_object {
ValueData::Object(ref obj) => obj.borrow().get_internal_slot("construct"),
_ => func_object,
};

match *construct {
ValueData::Function(ref inner_func) => match inner_func.clone().into_inner() {
Expand All @@ -368,11 +376,13 @@ impl Executor for Interpreter {
Function::RegularFunc(ref data) => {
// Create new scope
let env = &mut self.realm.environment;
env.push(new_function_environment(
let mut func_rec = new_function_environment_record(
construct.clone(),
this.clone(),
Some(env.get_current_environment_ref().clone()),
));
);
func_rec.bind_this_value(this.clone());
env.push(Gc::new(GcCell::new(Box::new(func_rec))));

for i in 0..data.args.len() {
let name = data.args.get(i).expect("Could not get data argument");
Expand All @@ -384,9 +394,9 @@ impl Executor for Interpreter {
);
env.initialize_binding(name, expr.to_owned());
}
let result = self.run(&data.expr);
self.run(&data.expr)?;
self.realm.environment.pop();
result
Ok(this)
}
},
_ => Ok(Gc::new(ValueData::Undefined)),
Expand Down Expand Up @@ -517,11 +527,13 @@ 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).expect("Could not get data argument");
let expr: &Value = arguments_list.get(i).expect("Could not get argument");
Expand Down Expand Up @@ -798,4 +810,20 @@ mod tests {
"#;
assert_eq!(exec(boolean_false), String::from("-1"));
}

#[test]
fn this() {
let scenario = r#"
function Vector(x, y) {
this.x = x;
this.y = y;
}
let vector = new Vector(3, 4);
vector.length = function() {
return Math.sqrt(this.x * this.x + this.y * this.y);
};
vector.length();
"#;
assert_eq!(exec(scenario), String::from("5"));
}
}
2 changes: 2 additions & 0 deletions src/lib/syntax/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub enum ExprDef {
Block(Vec<Expr>),
/// Load a reference to a value
Local(String),
This,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment above this would be nice

/// Gets the constant field of a value
GetConstField(Box<Expr>, String),
/// Gets the field of a value
Expand Down Expand Up @@ -124,6 +125,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) => {
Expand Down
1 change: 1 addition & 0 deletions src/lib/syntax/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,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()))),
Expand Down