Skip to content

Commit

Permalink
Implement this expression
Browse files Browse the repository at this point in the history
  • Loading branch information
sanxiyn committed Oct 23, 2019
1 parent 6dff219 commit a1cf1d8
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 5 deletions.
6 changes: 6 additions & 0 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 Down
36 changes: 31 additions & 5 deletions src/lib/exec.rs
Original file line number Diff line number Diff line change
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 @@ -345,7 +350,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 @@ -359,11 +367,12 @@ impl Executor for Interpreter {
Function::RegularFunc(ref data) => {
// Create new scope
let env = &mut self.realm.environment;
let func_rec = new_function_environment_record(
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() {
Expand All @@ -376,9 +385,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 @@ -509,11 +518,12 @@ 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);
let func_rec = new_function_environment_record(
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");
Expand Down Expand Up @@ -753,4 +763,20 @@ mod tests {
"#;
assert_eq!(exec(non_num_key_wont_affect_length), String::from("3"));
}

#[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,
/// 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 @@ -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

0 comments on commit a1cf1d8

Please sign in to comment.