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" #320

Merged
merged 6 commits into from
May 16, 2020
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"type": "process",
"label": "Cargo Run",
"command": "cargo",
"args": ["run", "./tests/js/test.js"],
"args": ["run", "--bin", "boa", "./tests/js/test.js"],
"problemMatcher": ["$rustc"],
"group": {
"kind": "build",
Expand Down
5 changes: 3 additions & 2 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
#[cfg(test)]
mod tests;

use super::function::make_constructor_fn;
use crate::{
builtins::{
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{ResultValue, Value, ValueData},
},
Expand Down Expand Up @@ -977,7 +978,7 @@ pub fn create(global: &Value) -> Value {
make_builtin_fn!(slice, named "slice", with length 2, of prototype);
make_builtin_fn!(some, named "some", with length 2, of prototype);

let array = make_constructor_fn!(make_array, make_array, global, prototype);
let array = make_constructor_fn(make_array, global, prototype);

// Static Methods
make_builtin_fn!(is_array, named "isArray", with length 1, of array);
Expand Down
16 changes: 6 additions & 10 deletions boa/src/builtins/boolean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
#[cfg(test)]
mod tests;

use super::function::make_constructor_fn;
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
object::{internal_methods_trait::ObjectInternalMethods, ObjectKind},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use std::{borrow::Borrow, ops::Deref};

/// Create a new boolean object - [[Construct]]
/// `[[Construct]]` Create a new boolean object
///
/// `[[Call]]` Creates a new boolean primitive
pub fn construct_boolean(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.set_kind(ObjectKind::Boolean);

Expand All @@ -32,13 +35,6 @@ pub fn construct_boolean(this: &mut Value, args: &[Value], _: &mut Interpreter)
this.set_internal_slot("BooleanData", to_boolean(&Value::from(false)));
}

// no need to return `this` as its passed by reference
Ok(this.clone())
}

/// Return a boolean literal [[Call]]
pub fn call_boolean(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// Get the argument, if any
match args.get(0) {
Some(ref value) => Ok(to_boolean(value)),
None => Ok(to_boolean(&Value::from(false))),
Expand Down Expand Up @@ -108,7 +104,7 @@ pub fn create(global: &Value) -> Value {
make_builtin_fn!(to_string, named "toString", of prototype);
make_builtin_fn!(value_of, named "valueOf", of prototype);

make_constructor_fn!(construct_boolean, call_boolean, global, prototype)
make_constructor_fn(construct_boolean, global, prototype)
}

/// Initialise the `Boolean` object on the global object.
Expand Down
5 changes: 3 additions & 2 deletions boa/src/builtins/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
//! [spec]: https://tc39.es/ecma262/#sec-error-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error

use super::function::make_constructor_fn;
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
object::ObjectKind,
value::{ResultValue, Value},
},
exec::Interpreter,
Expand Down Expand Up @@ -58,7 +59,7 @@ pub fn create(global: &Value) -> Value {
prototype.set_field_slice("message", Value::from(""));
prototype.set_field_slice("name", Value::from("Error"));
make_builtin_fn!(to_string, named "toString", of prototype);
make_constructor_fn!(make_error, global, prototype)
make_constructor_fn(make_error, global, prototype)
}

/// Initialise the global object with the `Error` object.
Expand Down
55 changes: 47 additions & 8 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ use crate::{
property::Property,
value::{ResultValue, Value},
},
environment::lexical_environment::{new_function_environment, Environment},
environment::{
function_environment_record::BindingStatus,
lexical_environment::{new_function_environment, Environment},
},
exec::Executor,
syntax::ast::node::{FormalParameter, Node},
Interpreter,
Expand Down Expand Up @@ -162,8 +165,9 @@ impl Function {
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this.clone(),
this_obj.clone(),
None,
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Uninitialized,
);

// Add argument bindings to the function environment
Expand Down Expand Up @@ -203,6 +207,7 @@ impl Function {
}
}

/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
pub fn construct(
&self,
this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
Expand All @@ -212,7 +217,10 @@ impl Function {
) -> ResultValue {
match self.kind {
FunctionKind::BuiltIn => match &self.body {
FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
FunctionBody::BuiltIn(func) => {
func(this_obj, args_list, interpreter).unwrap();
Ok(this_obj.clone())
}
FunctionBody::Ordinary(_) => {
panic!("Builtin function should not have Ordinary Function body")
}
Expand All @@ -222,8 +230,9 @@ impl Function {
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this.clone(),
this_obj.clone(),
Some(this_obj.clone()),
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Initialized,
);

// Add argument bindings to the function environment
Expand All @@ -250,14 +259,14 @@ impl Function {
interpreter.realm.environment.push(local_env);

// Call body should be set before reaching here
let result = match &self.body {
let _ = match &self.body {
FunctionBody::Ordinary(ref body) => interpreter.run(body),
_ => panic!("Ordinary function should not have BuiltIn Function body"),
};

// local_env gets dropped here, its no longer needed
interpreter.realm.environment.pop();
result
let binding = interpreter.realm.environment.get_this_binding();
Ok(binding)
}
}
}
Expand Down Expand Up @@ -363,7 +372,37 @@ pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> Resu
pub fn create(global: &Value) -> Value {
let prototype = Value::new_object(Some(global));

make_constructor_fn!(make_function, make_function, global, prototype)
make_constructor_fn(make_function, global, prototype)
}

/// Creates a new constructor function
///
/// This utility function handling linking the new Constructor to the prototype.
/// So far this is only used by internal functions
pub fn make_constructor_fn(body: NativeFunctionData, global: &Value, proto: Value) -> Value {
// Create the native function
let constructor_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn(body),
);

// Get reference to Function.prototype
let func_prototype = global
.get_field_slice("Function")
.get_field_slice(PROTOTYPE);

// Create the function object and point its instance prototype to Function.prototype
let mut constructor_obj = Object::function();
constructor_obj.set_func(constructor_fn);

constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = Value::from(constructor_obj);

// Set proto.constructor -> constructor_obj
proto.set_field_slice("constructor", constructor_val.clone());
constructor_val.set_field_slice(PROTOTYPE, proto);

constructor_val
}

/// Initialise the `Function` object on the global object.
Expand Down
62 changes: 1 addition & 61 deletions boa/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ macro_rules! make_builtin_fn {
);

let mut new_func = crate::builtins::object::Object::function();
new_func.set_call(func);
new_func.set_func(func);
let new_func_obj = Value::from(new_func);
new_func_obj.set_field_slice("length", Value::from($l));
$p.set_field_slice($name, new_func_obj);
Expand All @@ -21,66 +21,6 @@ macro_rules! make_builtin_fn {
};
}

/// Macro to create a new constructor function
///
/// Either (construct_body, global, prototype)
macro_rules! make_constructor_fn {
($body:ident, $global:ident, $proto:ident) => {{
// Create the native function
let constructor_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($body),
);

// Get reference to Function.prototype
let func_prototype = $global
.get_field_slice("Function")
.get_field_slice(PROTOTYPE);

// Create the function object and point its instance prototype to Function.prototype
let mut constructor_obj = Object::function();
constructor_obj.set_construct(constructor_fn);

constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = Value::from(constructor_obj);

// Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone());
constructor_val.set_field_slice(PROTOTYPE, $proto);

constructor_val
}};
($construct_body:ident, $call_body:ident, $global:ident, $proto:ident) => {{
// Create the native functions
let construct_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($construct_body),
);
let call_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn($call_body),
);

// Get reference to Function.prototype
let func_prototype = $global
.get_field_slice("Function")
.get_field_slice(PROTOTYPE);

// Create the function object and point its instance prototype to Function.prototype
let mut constructor_obj = Object::function();
constructor_obj.set_construct(construct_fn);
constructor_obj.set_call(call_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = Value::from(constructor_obj);

// Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone());
constructor_val.set_field_slice(PROTOTYPE, $proto);

constructor_val
}};
}

pub mod array;
pub mod boolean;
pub mod console;
Expand Down
15 changes: 10 additions & 5 deletions boa/src/builtins/number/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
#[cfg(test)]
mod tests;

use super::{function::make_constructor_fn, object::ObjectKind};
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, PROTOTYPE},
object::internal_methods_trait::ObjectInternalMethods,
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
Expand Down Expand Up @@ -57,14 +58,18 @@ fn num_to_exponential(n: f64) -> String {
}
}

/// Create a new number `[[Construct]]`
/// `[[Construct]]` - Creates a Number instance
///
/// `[[Call]]` - Creates a number primitive
pub fn make_number(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => to_number(value),
None => to_number(&Value::from(0)),
};
this.set_internal_slot("NumberData", data);
Ok(this.clone())
this.set_kind(ObjectKind::Number);
this.set_internal_slot("NumberData", data.clone());

Ok(data)
}

/// `Number()` function.
Expand Down Expand Up @@ -360,7 +365,7 @@ pub fn create(global: &Value) -> Value {
make_builtin_fn!(to_string, named "toString", with length 1, of prototype);
make_builtin_fn!(value_of, named "valueOf", of prototype);

make_constructor_fn!(make_number, call_number, global, prototype)
make_constructor_fn(make_number, global, prototype)
}

/// Initialise the `Number` object on the global object.
Expand Down
Loading