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 #150

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 @@ -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;
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());
Copy link
Member

Choose a reason for hiding this comment

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

not sure if the assert is needed?
It won't even compile if a has_this_binding() isn't implemented

Copy link
Author

Choose a reason for hiding this comment

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

has_this_binding can return false for some environment records, although the interpreter should never call get_this_binding for them. I am asserting that invariant here.

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 @@ -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)
}
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
17 changes: 13 additions & 4 deletions src/lib/environment/lexical_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,22 +164,31 @@ 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_function_environment(
f: Value,
new_target: Value,
outer: Option<Environment>,
) -> 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>) -> Environment {
Expand Down
30 changes: 27 additions & 3 deletions src/lib/exec.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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");
}
}
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,
/// Gets the constant field of a value
GetConstField(Box<Expr>, String),
/// Gets the field of a value
Expand Down Expand Up @@ -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) => {
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 @@ -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()))),
Expand Down