Skip to content

Commit

Permalink
Refactor Function (#626)
Browse files Browse the repository at this point in the history
Co-authored-by: Iban Eguia <[email protected]>
  • Loading branch information
HalidOdat and Razican authored Aug 18, 2020
1 parent d025207 commit c9afbf4
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 553 deletions.
338 changes: 52 additions & 286 deletions boa/src/builtins/function/mod.rs

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions boa/src/builtins/map/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,18 @@ fn recursive_display() {
}

#[test]
#[should_panic]
fn not_a_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = "let map = Map()";
forward(&mut engine, init);
let init = r"
try {
let map = Map()
} catch(e) {
e.toString()
}
";
assert_eq!(
forward(&mut engine, init),
"\"TypeError: function object is not callable\""
);
}
160 changes: 158 additions & 2 deletions boa/src/builtins/object/gcobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
//! The `GcObject` is a garbage collected Object.

use super::Object;
use crate::{
builtins::{
function::{create_unmapped_arguments_object, BuiltInFunction, Function},
Value,
},
environment::{
function_environment_record::BindingStatus, lexical_environment::new_function_environment,
},
Executable, Interpreter, Result,
};
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace};
use std::result::Result as StdResult;
use std::{
cell::RefCell,
collections::HashSet,
Expand Down Expand Up @@ -31,12 +42,12 @@ impl GcObject {
}

#[inline]
pub fn try_borrow(&self) -> Result<GcCellRef<'_, Object>, BorrowError> {
pub fn try_borrow(&self) -> StdResult<GcCellRef<'_, Object>, BorrowError> {
self.0.try_borrow().map_err(|_| BorrowError)
}

#[inline]
pub fn try_borrow_mut(&self) -> Result<GcCellRefMut<'_, Object>, BorrowMutError> {
pub fn try_borrow_mut(&self) -> StdResult<GcCellRefMut<'_, Object>, BorrowMutError> {
self.0.try_borrow_mut().map_err(|_| BorrowMutError)
}

Expand All @@ -45,6 +56,151 @@ impl GcObject {
pub fn equals(lhs: &Self, rhs: &Self) -> bool {
std::ptr::eq(lhs.as_ref(), rhs.as_ref())
}

/// This will handle calls for both ordinary and built-in functions
///
/// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
pub fn call(&self, this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
let this_function_object = self.clone();
let object = self.borrow();
if let Some(function) = object.as_function() {
if function.is_callable() {
match function {
Function::BuiltIn(BuiltInFunction(function), _) => function(this, args, ctx),
Function::Ordinary {
body,
params,
environment,
flags,
} => {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this_function_object,
if flags.is_lexical_this_mode() {
None
} else {
Some(this.clone())
},
Some(environment.clone()),
// Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records
if flags.is_lexical_this_mode() {
BindingStatus::Lexical
} else {
BindingStatus::Uninitialized
},
);

// Add argument bindings to the function environment
for (i, param) in params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param() {
function.add_rest_param(param, i, args, ctx, &local_env);
break;
}

let value = args.get(i).cloned().unwrap_or_else(Value::undefined);
function.add_arguments_to_environment(param, value, &local_env);
}

// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);

ctx.realm.environment.push(local_env);

// Call body should be set before reaching here
let result = body.run(ctx);

// local_env gets dropped here, its no longer needed
ctx.realm.environment.pop();
result
}
}
} else {
ctx.throw_type_error("function object is not callable")
}
} else {
ctx.throw_type_error("not a function")
}
}

/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
pub fn construct(&self, this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
let this_function_object = self.clone();
let object = self.borrow();
if let Some(function) = object.as_function() {
if function.is_constructable() {
match function {
Function::BuiltIn(BuiltInFunction(function), _) => {
function(this, args, ctx)?;
Ok(this.clone())
}
Function::Ordinary {
body,
params,
environment,
flags,
} => {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this_function_object,
Some(this.clone()),
Some(environment.clone()),
// Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records
if flags.is_lexical_this_mode() {
BindingStatus::Lexical
} else {
BindingStatus::Uninitialized
},
);

// Add argument bindings to the function environment
for (i, param) in params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param() {
function.add_rest_param(param, i, args, ctx, &local_env);
break;
}

let value = args.get(i).cloned().unwrap_or_else(Value::undefined);
function.add_arguments_to_environment(param, value, &local_env);
}

// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);

ctx.realm.environment.push(local_env);

// Call body should be set before reaching here
let _ = body.run(ctx);

// local_env gets dropped here, its no longer needed
let binding = ctx.realm.environment.get_this_binding();
Ok(binding)
}
}
} else {
let name = this.get_field("name").display().to_string();
ctx.throw_type_error(format!("{} is not a constructor", name))
}
} else {
ctx.throw_type_error("not a function")
}
}
}

impl AsRef<GcCell<Object>> for GcObject {
Expand Down
64 changes: 0 additions & 64 deletions boa/src/builtins/object/internal_state.rs

This file was deleted.

30 changes: 4 additions & 26 deletions boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,19 @@ use std::result::Result as StdResult;

use super::function::{make_builtin_fn, make_constructor_fn};
use crate::builtins::value::same_value;
pub use internal_state::{InternalState, InternalStateCell};

pub mod gcobject;
pub mod internal_methods;
mod internal_state;
mod gcobject;
mod internal_methods;

pub use gcobject::GcObject;
pub use internal_methods::*;

#[cfg(test)]
mod tests;

/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub static PROTOTYPE: &str = "prototype";

// /// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object.
// pub static INSTANCE_PROTOTYPE: &str = "__proto__";

/// The internal representation of an JavaScript object.
#[derive(Debug, Trace, Finalize, Clone)]
pub struct Object {
Expand All @@ -59,8 +55,6 @@ pub struct Object {
symbol_properties: FxHashMap<u32, Property>,
/// Instance prototype `__proto__`.
prototype: Value,
/// Some rust object that stores internal state
state: Option<InternalStateCell>,
/// Whether it can have new properties added to it.
extensible: bool,
}
Expand All @@ -70,7 +64,7 @@ pub struct Object {
pub enum ObjectData {
Array,
Map(OrderedMap<Value, Value>),
RegExp(RegExp),
RegExp(Box<RegExp>),
BigInt(RcBigInt),
Boolean(bool),
Function(Function),
Expand Down Expand Up @@ -116,7 +110,6 @@ impl Default for Object {
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
}
Expand All @@ -137,7 +130,6 @@ impl Object {
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype,
state: None,
extensible: true,
}
}
Expand All @@ -162,7 +154,6 @@ impl Object {
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
}
Expand All @@ -174,7 +165,6 @@ impl Object {
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
}
Expand All @@ -189,7 +179,6 @@ impl Object {
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
}
Expand All @@ -201,7 +190,6 @@ impl Object {
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
}
Expand Down Expand Up @@ -420,16 +408,6 @@ impl Object {
&mut self.symbol_properties
}

#[inline]
pub fn state(&self) -> &Option<InternalStateCell> {
&self.state
}

#[inline]
pub fn state_mut(&mut self) -> &mut Option<InternalStateCell> {
&mut self.state
}

pub fn prototype(&self) -> &Value {
&self.prototype
}
Expand Down
Loading

0 comments on commit c9afbf4

Please sign in to comment.