Skip to content

Commit 2c382c9

Browse files
committed
Refactor Function
- Added call to js object - Removed Function::call - Removed `Function::construct` and added `GcObject::construct` - Made Function an enum - Made the Function into an enum - Removed `InternalState` - Removed `internal_state.rs` file
1 parent c5b708b commit 2c382c9

File tree

13 files changed

+284
-544
lines changed

13 files changed

+284
-544
lines changed

boa/src/builtins/function/mod.rs

+52-286
Large diffs are not rendered by default.

boa/src/builtins/map/tests.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,18 @@ fn recursive_display() {
228228
}
229229

230230
#[test]
231-
#[should_panic]
232231
fn not_a_function() {
233232
let realm = Realm::create();
234233
let mut engine = Interpreter::new(realm);
235-
let init = "let map = Map()";
236-
forward(&mut engine, init);
234+
let init = r"
235+
try {
236+
let map = Map()
237+
} catch(e) {
238+
e.toString()
239+
}
240+
";
241+
assert_eq!(
242+
forward(&mut engine, init),
243+
"\"TypeError: function object is not callable\""
244+
);
237245
}

boa/src/builtins/object/gcobject.rs

+155
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
//! The `GcObject` is a garbage collected Object.
44
55
use super::Object;
6+
use crate::{
7+
builtins::{
8+
function::{create_unmapped_arguments_object, BuiltInFunction, Function},
9+
ResultValue, Value,
10+
},
11+
environment::{
12+
function_environment_record::BindingStatus, lexical_environment::new_function_environment,
13+
},
14+
Executable, Interpreter,
15+
};
616
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace};
717
use std::fmt::{self, Display};
818

@@ -41,6 +51,151 @@ impl GcObject {
4151
pub fn equals(lhs: &Self, rhs: &Self) -> bool {
4252
std::ptr::eq(lhs.as_ref(), rhs.as_ref())
4353
}
54+
55+
/// This will handle calls for both ordinary and built-in functions
56+
///
57+
/// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
58+
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
59+
pub fn call(&self, this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
60+
let this_function_object = self.clone();
61+
let object = self.borrow();
62+
if let Some(function) = object.as_function() {
63+
if function.is_callable() {
64+
match function {
65+
Function::BuiltIn(BuiltInFunction(function), _) => function(this, args, ctx),
66+
Function::Ordinary {
67+
body,
68+
params,
69+
environment,
70+
flags,
71+
} => {
72+
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
73+
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
74+
let local_env = new_function_environment(
75+
this_function_object,
76+
if flags.is_lexical_this_mode() {
77+
None
78+
} else {
79+
Some(this.clone())
80+
},
81+
Some(environment.clone()),
82+
// Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records
83+
if flags.is_lexical_this_mode() {
84+
BindingStatus::Lexical
85+
} else {
86+
BindingStatus::Uninitialized
87+
},
88+
);
89+
90+
// Add argument bindings to the function environment
91+
for (i, param) in params.iter().enumerate() {
92+
// Rest Parameters
93+
if param.is_rest_param() {
94+
function.add_rest_param(param, i, args, ctx, &local_env);
95+
break;
96+
}
97+
98+
let value = args.get(i).cloned().unwrap_or_else(Value::undefined);
99+
function.add_arguments_to_environment(param, value, &local_env);
100+
}
101+
102+
// Add arguments object
103+
let arguments_obj = create_unmapped_arguments_object(args);
104+
local_env
105+
.borrow_mut()
106+
.create_mutable_binding("arguments".to_string(), false);
107+
local_env
108+
.borrow_mut()
109+
.initialize_binding("arguments", arguments_obj);
110+
111+
ctx.realm.environment.push(local_env);
112+
113+
// Call body should be set before reaching here
114+
let result = body.run(ctx);
115+
116+
// local_env gets dropped here, its no longer needed
117+
ctx.realm.environment.pop();
118+
result
119+
}
120+
}
121+
} else {
122+
ctx.throw_type_error("function object is not callable")
123+
}
124+
} else {
125+
ctx.throw_type_error("not a function")
126+
}
127+
}
128+
129+
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
130+
pub fn construct(&self, this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
131+
let this_function_object = self.clone();
132+
let object = self.borrow();
133+
if let Some(function) = object.as_function() {
134+
if function.is_constructable() {
135+
match function {
136+
Function::BuiltIn(BuiltInFunction(function), _) => {
137+
function(this, args, ctx)?;
138+
Ok(this.clone())
139+
}
140+
Function::Ordinary {
141+
body,
142+
params,
143+
environment,
144+
flags,
145+
} => {
146+
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
147+
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
148+
let local_env = new_function_environment(
149+
this_function_object,
150+
Some(this.clone()),
151+
Some(environment.clone()),
152+
// Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records
153+
if flags.is_lexical_this_mode() {
154+
BindingStatus::Lexical
155+
} else {
156+
BindingStatus::Uninitialized
157+
},
158+
);
159+
160+
// Add argument bindings to the function environment
161+
for (i, param) in params.iter().enumerate() {
162+
// Rest Parameters
163+
if param.is_rest_param() {
164+
function.add_rest_param(param, i, args, ctx, &local_env);
165+
break;
166+
}
167+
168+
let value = args.get(i).cloned().unwrap_or_else(Value::undefined);
169+
function.add_arguments_to_environment(param, value, &local_env);
170+
}
171+
172+
// Add arguments object
173+
let arguments_obj = create_unmapped_arguments_object(args);
174+
local_env
175+
.borrow_mut()
176+
.create_mutable_binding("arguments".to_string(), false);
177+
local_env
178+
.borrow_mut()
179+
.initialize_binding("arguments", arguments_obj);
180+
181+
ctx.realm.environment.push(local_env);
182+
183+
// Call body should be set before reaching here
184+
let _ = body.run(ctx);
185+
186+
// local_env gets dropped here, its no longer needed
187+
let binding = ctx.realm.environment.get_this_binding();
188+
Ok(binding)
189+
}
190+
}
191+
} else {
192+
let name = this.get_field("name").display().to_string();
193+
ctx.throw_type_error(format!("{} is not a constructor", name))
194+
}
195+
} else {
196+
ctx.throw_type_error("not a function")
197+
}
198+
}
44199
}
45200

46201
impl AsRef<GcCell<Object>> for GcObject {

boa/src/builtins/object/internal_state.rs

-64
This file was deleted.

boa/src/builtins/object/mod.rs

+4-26
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,19 @@ use std::fmt::{Debug, Display, Error, Formatter};
3030

3131
use super::function::{make_builtin_fn, make_constructor_fn};
3232
use crate::builtins::value::same_value;
33-
pub use internal_state::{InternalState, InternalStateCell};
3433

35-
pub mod gcobject;
36-
pub mod internal_methods;
37-
mod internal_state;
34+
mod gcobject;
35+
mod internal_methods;
3836

3937
pub use gcobject::GcObject;
38+
pub use internal_methods::*;
4039

4140
#[cfg(test)]
4241
mod tests;
4342

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

47-
// /// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object.
48-
// pub static INSTANCE_PROTOTYPE: &str = "__proto__";
49-
5046
/// The internal representation of an JavaScript object.
5147
#[derive(Debug, Trace, Finalize, Clone)]
5248
pub struct Object {
@@ -58,8 +54,6 @@ pub struct Object {
5854
symbol_properties: FxHashMap<u32, Property>,
5955
/// Instance prototype `__proto__`.
6056
prototype: Value,
61-
/// Some rust object that stores internal state
62-
state: Option<InternalStateCell>,
6357
/// Whether it can have new properties added to it.
6458
extensible: bool,
6559
}
@@ -69,7 +63,7 @@ pub struct Object {
6963
pub enum ObjectData {
7064
Array,
7165
Map(OrderedMap<Value, Value>),
72-
RegExp(RegExp),
66+
RegExp(Box<RegExp>),
7367
BigInt(RcBigInt),
7468
Boolean(bool),
7569
Function(Function),
@@ -115,7 +109,6 @@ impl Default for Object {
115109
properties: FxHashMap::default(),
116110
symbol_properties: FxHashMap::default(),
117111
prototype: Value::null(),
118-
state: None,
119112
extensible: true,
120113
}
121114
}
@@ -136,7 +129,6 @@ impl Object {
136129
properties: FxHashMap::default(),
137130
symbol_properties: FxHashMap::default(),
138131
prototype,
139-
state: None,
140132
extensible: true,
141133
}
142134
}
@@ -161,7 +153,6 @@ impl Object {
161153
properties: FxHashMap::default(),
162154
symbol_properties: FxHashMap::default(),
163155
prototype: Value::null(),
164-
state: None,
165156
extensible: true,
166157
}
167158
}
@@ -173,7 +164,6 @@ impl Object {
173164
properties: FxHashMap::default(),
174165
symbol_properties: FxHashMap::default(),
175166
prototype: Value::null(),
176-
state: None,
177167
extensible: true,
178168
}
179169
}
@@ -188,7 +178,6 @@ impl Object {
188178
properties: FxHashMap::default(),
189179
symbol_properties: FxHashMap::default(),
190180
prototype: Value::null(),
191-
state: None,
192181
extensible: true,
193182
}
194183
}
@@ -200,7 +189,6 @@ impl Object {
200189
properties: FxHashMap::default(),
201190
symbol_properties: FxHashMap::default(),
202191
prototype: Value::null(),
203-
state: None,
204192
extensible: true,
205193
}
206194
}
@@ -419,16 +407,6 @@ impl Object {
419407
&mut self.symbol_properties
420408
}
421409

422-
#[inline]
423-
pub fn state(&self) -> &Option<InternalStateCell> {
424-
&self.state
425-
}
426-
427-
#[inline]
428-
pub fn state_mut(&mut self) -> &mut Option<InternalStateCell> {
429-
&mut self.state
430-
}
431-
432410
pub fn prototype(&self) -> &Value {
433411
&self.prototype
434412
}

boa/src/builtins/regexp/mod.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use regex::Regex;
1414
use super::function::{make_builtin_fn, make_constructor_fn};
1515
use crate::{
1616
builtins::{
17-
object::{InternalState, ObjectData},
17+
object::ObjectData,
1818
property::Property,
1919
value::{RcString, ResultValue, Value},
2020
},
@@ -64,8 +64,6 @@ unsafe impl Trace for RegExp {
6464
unsafe_empty_trace!();
6565
}
6666

67-
impl InternalState for RegExp {}
68-
6967
impl RegExp {
7068
/// The name of the object.
7169
pub(crate) const NAME: &'static str = "RegExp";
@@ -156,7 +154,7 @@ impl RegExp {
156154
original_flags: regex_flags,
157155
};
158156

159-
this.set_data(ObjectData::RegExp(regexp));
157+
this.set_data(ObjectData::RegExp(Box::new(regexp)));
160158

161159
Ok(this.clone())
162160
}

0 commit comments

Comments
 (0)