Browse Source

Fixed function object constructable/callable (#409)

- Maded arrow functions non-constructable
 - Simplified Function object and removed FunctionKind
 - Rnamed create_ordinary -> ordinary, create_builtin -> builtin
 - Added name and length properties in global objects
pull/424/head
HalidOdat 4 years ago committed by GitHub
parent
commit
d42e413f8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa/src/builtins/array/mod.rs
  2. 18
      boa/src/builtins/bigint/mod.rs
  3. 9
      boa/src/builtins/boolean/mod.rs
  4. 5
      boa/src/builtins/error/mod.rs
  5. 5
      boa/src/builtins/error/range.rs
  6. 304
      boa/src/builtins/function/mod.rs
  7. 2
      boa/src/builtins/mod.rs
  8. 17
      boa/src/builtins/number/mod.rs
  9. 14
      boa/src/builtins/object/mod.rs
  10. 2
      boa/src/builtins/regexp/mod.rs
  11. 2
      boa/src/builtins/string/mod.rs
  12. 3
      boa/src/builtins/symbol/mod.rs
  13. 2
      boa/src/builtins/value/mod.rs
  14. 6
      boa/src/exec/declaration/mod.rs
  15. 17
      boa/src/exec/mod.rs
  16. 7
      boa/src/realm.rs

2
boa/src/builtins/array/mod.rs

@ -1031,7 +1031,7 @@ impl Array {
make_builtin_fn(Self::slice, "slice", &prototype, 2);
make_builtin_fn(Self::some, "some", &prototype, 2);
let array = make_constructor_fn(Self::make_array, global, prototype);
let array = make_constructor_fn("Array", 1, Self::make_array, global, prototype, true);
// Static Methods
make_builtin_fn(Self::is_array, "isArray", &array, 1);

18
boa/src/builtins/bigint/mod.rs

@ -96,14 +96,14 @@ impl BigInt {
))
}
// /// `BigInt.prototype.valueOf()`
// ///
// /// The `valueOf()` method returns the wrapped primitive value of a Number object.
// ///
// /// More information:
// /// - [ECMAScript reference][spec]
// /// - [MDN documentation][mdn]
// ///
/// `BigInt.prototype.valueOf()`
///
/// The `valueOf()` method returns the wrapped primitive value of a Number object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf
pub(crate) fn value_of(
@ -124,7 +124,7 @@ impl BigInt {
make_builtin_fn(Self::to_string, "toString", &prototype, 1);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
make_constructor_fn(Self::make_bigint, global, prototype)
make_constructor_fn("BigInt", 1, Self::make_bigint, global, prototype, false)
}
/// Initialise the `BigInt` object on the global object.

9
boa/src/builtins/boolean/mod.rs

@ -115,7 +115,14 @@ impl Boolean {
make_builtin_fn(Self::to_string, "toString", &prototype, 0);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
make_constructor_fn(Self::construct_boolean, global, prototype)
make_constructor_fn(
"Boolean",
1,
Self::construct_boolean,
global,
prototype,
true,
)
}
/// Initialise the `Boolean` object on the global object.

5
boa/src/builtins/error/mod.rs

@ -72,9 +72,10 @@ impl Error {
pub(crate) fn create(global: &Value) -> Value {
let prototype = Value::new_object(Some(global));
prototype.set_field("message", Value::from(""));
prototype.set_field("name", Value::from("Error"));
make_builtin_fn(Self::to_string, "toString", &prototype, 0);
make_constructor_fn(Self::make_error, global, prototype)
make_constructor_fn("Error", 1, Self::make_error, global, prototype, true)
}
/// Initialise the global object with the `Error` object.

5
boa/src/builtins/error/range.rs

@ -63,9 +63,10 @@ impl RangeError {
pub(crate) fn create(global: &Value) -> Value {
let prototype = Value::new_object(Some(global));
prototype.set_field("message", Value::from(""));
prototype.set_field("name", Value::from("RangeError"));
make_builtin_fn(Self::to_string, "toString", &prototype, 0);
make_constructor_fn(Self::make_error, global, prototype)
make_constructor_fn("RangeError", 1, Self::make_error, global, prototype, true)
}
/// Runs a `new RangeError(message)`.

304
boa/src/builtins/function/mod.rs

@ -14,7 +14,7 @@
use crate::{
builtins::{
array::Array,
object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE},
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{ResultValue, Value},
},
@ -72,18 +72,6 @@ unsafe impl Trace for FunctionBody {
unsafe_empty_trace!();
}
/// Signal what sort of function this is
#[derive(Clone, Debug, Copy, Finalize)]
pub enum FunctionKind {
BuiltIn,
Ordinary,
}
/// Waiting on <https://github.com/Manishearth/rust-gc/issues/87> until we can derive Copy
unsafe impl Trace for FunctionKind {
unsafe_empty_trace!();
}
/// Boa representation of a Function Object.
///
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
@ -95,48 +83,73 @@ pub struct Function {
pub params: Box<[FormalParameter]>,
/// This Mode
pub this_mode: ThisMode,
/// Function kind
pub kind: FunctionKind,
// Environment, built-in functions don't need Environments
pub environment: Option<Environment>,
/// Is it constructable
constructable: bool,
/// Is it callable.
callable: bool,
}
impl Function {
/// This will create an ordinary function object
///
/// <https://tc39.es/ecma262/#sec-ordinaryfunctioncreate>
pub fn create_ordinary<P>(
pub fn new<P>(
parameter_list: P,
scope: Environment,
scope: Option<Environment>,
body: FunctionBody,
this_mode: ThisMode,
constructable: bool,
callable: bool,
) -> Self
where
P: Into<Box<[FormalParameter]>>,
{
Self {
body,
environment: Some(scope),
environment: scope,
params: parameter_list.into(),
kind: FunctionKind::Ordinary,
this_mode,
constructable,
callable,
}
}
/// This will create an ordinary function object
///
/// <https://tc39.es/ecma262/#sec-ordinaryfunctioncreate>
pub fn ordinary<P>(
parameter_list: P,
scope: Environment,
body: StatementList,
this_mode: ThisMode,
) -> Self
where
P: Into<Box<[FormalParameter]>>,
{
Self::new(
parameter_list.into(),
Some(scope),
FunctionBody::Ordinary(body),
this_mode,
true,
true,
)
}
/// This will create a built-in function object
///
/// <https://tc39.es/ecma262/#sec-createbuiltinfunction>
pub fn create_builtin<P>(parameter_list: P, body: FunctionBody) -> Self
pub fn builtin<P>(parameter_list: P, body: NativeFunctionData) -> Self
where
P: Into<Box<[FormalParameter]>>,
{
Self {
body,
params: parameter_list.into(),
this_mode: ThisMode::NonLexical,
kind: FunctionKind::BuiltIn,
environment: None,
}
Self::new(
parameter_list.into(),
None,
FunctionBody::BuiltIn(body),
ThisMode::NonLexical,
false,
true,
)
}
/// This will handle calls for both ordinary and built-in functions
@ -150,57 +163,53 @@ impl Function {
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
match self.kind {
FunctionKind::BuiltIn => match &self.body {
if self.callable {
match self.body {
FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
FunctionBody::Ordinary(_) => {
panic!("Builtin function should not have Ordinary Function body")
}
},
FunctionKind::Ordinary => {
// 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.clone(),
None,
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Uninitialized,
);
// Add argument bindings to the function environment
for i in 0..self.params.len() {
let param = self.params.get(i).expect("Could not get param");
// Rest Parameters
if param.is_rest_param() {
self.add_rest_param(param, i, args_list, interpreter, &local_env);
break;
FunctionBody::Ordinary(ref body) => {
// 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.clone(),
None,
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Uninitialized,
);
// Add argument bindings to the function environment
for i in 0..self.params.len() {
let param = self.params.get(i).expect("Could not get param");
// Rest Parameters
if param.is_rest_param() {
self.add_rest_param(param, i, args_list, interpreter, &local_env);
break;
}
let value = args_list.get(i).expect("Could not get value");
self.add_arguments_to_environment(param, value.clone(), &local_env);
}
let value = args_list.get(i).expect("Could not get value");
self.add_arguments_to_environment(param, value.clone(), &local_env);
}
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args_list);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
interpreter.realm.environment.push(local_env);
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args_list);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
interpreter.realm.environment.push(local_env);
// Call body should be set before reaching here
let result = match &self.body {
FunctionBody::Ordinary(ref body) => body.run(interpreter),
_ => panic!("Ordinary function should not have BuiltIn Function body"),
};
// local_env gets dropped here, its no longer needed
interpreter.realm.environment.pop();
result
// Call body should be set before reaching here
let result = body.run(interpreter);
// local_env gets dropped here, its no longer needed
interpreter.realm.environment.pop();
result
}
}
} else {
panic!("TypeError: class constructors must be invoked with 'new'");
}
}
@ -212,59 +221,56 @@ impl Function {
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
match self.kind {
FunctionKind::BuiltIn => match &self.body {
if self.constructable {
match self.body {
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")
}
},
FunctionKind::Ordinary => {
// 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.clone(),
Some(this_obj.clone()),
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Initialized,
);
// Add argument bindings to the function environment
for (i, param) in self.params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param() {
self.add_rest_param(param, i, args_list, interpreter, &local_env);
break;
FunctionBody::Ordinary(ref body) => {
// 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.clone(),
Some(this_obj.clone()),
Some(self.environment.as_ref().unwrap().clone()),
BindingStatus::Initialized,
);
// Add argument bindings to the function environment
for (i, param) in self.params.iter().enumerate() {
// Rest Parameters
if param.is_rest_param() {
self.add_rest_param(param, i, args_list, interpreter, &local_env);
break;
}
let value = args_list.get(i).expect("Could not get value");
self.add_arguments_to_environment(param, value.clone(), &local_env);
}
let value = args_list.get(i).expect("Could not get value");
self.add_arguments_to_environment(param, value.clone(), &local_env);
}
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args_list);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
interpreter.realm.environment.push(local_env);
// Call body should be set before reaching here
let _ = body.run(interpreter);
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args_list);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false);
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
interpreter.realm.environment.push(local_env);
// Call body should be set before reaching here
let _ = match &self.body {
FunctionBody::Ordinary(ref body) => body.run(interpreter),
_ => panic!("Ordinary function should not have BuiltIn Function body"),
};
// local_env gets dropped here, its no longer needed
let binding = interpreter.realm.environment.get_this_binding();
Ok(binding)
// local_env gets dropped here, its no longer needed
let binding = interpreter.realm.environment.get_this_binding();
Ok(binding)
}
}
} else {
let name = this.get_field("name").to_string();
panic!("TypeError: {} is not a constructor", name);
}
}
@ -309,6 +315,16 @@ impl Function {
.borrow_mut()
.initialize_binding(param.name(), value);
}
/// Returns true if the function object is callable.
pub fn is_callable(&self) -> bool {
self.callable
}
/// Returns true if the function object is constructable.
pub fn is_constructable(&self) -> bool {
self.constructable
}
}
impl Debug for Function {
@ -319,16 +335,6 @@ impl Debug for Function {
}
}
/// Function Prototype.
///
/// <https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object>
pub fn create_function_prototype() {
let mut function_prototype: Object = Object::default();
// Set Kind to function (for historical & compatibility reasons)
// <https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object>
function_prototype.kind = ObjectKind::Function;
}
/// Arguments.
///
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
@ -369,19 +375,25 @@ 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, global, prototype)
make_constructor_fn("Function", 1, make_function, global, prototype, true)
}
/// 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 {
pub fn make_constructor_fn(
name: &str,
length: i32,
body: NativeFunctionData,
global: &Value,
proto: Value,
constructable: bool,
) -> Value {
// Create the native function
let constructor_fn = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn(body),
);
let mut constructor_fn = Function::builtin(Vec::new(), body);
constructor_fn.constructable = constructable;
// Get reference to Function.prototype
let func_prototype = global.get_field("Function").get_field(PROTOTYPE);
@ -390,13 +402,27 @@ pub fn make_constructor_fn(body: NativeFunctionData, global: &Value, proto: Valu
let mut constructor_obj = Object::function();
constructor_obj.set_func(constructor_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype);
constructor_obj.set_internal_slot(INSTANCE_PROTOTYPE, func_prototype);
let constructor_val = Value::from(constructor_obj);
// Set proto.constructor -> constructor_obj
proto.set_field("constructor", constructor_val.clone());
constructor_val.set_field(PROTOTYPE, proto);
let length = Property::new()
.value(Value::from(length))
.writable(false)
.configurable(false)
.enumerable(false);
constructor_val.set_property_slice("length", length);
let name = Property::new()
.value(Value::from(name))
.writable(false)
.configurable(false)
.enumerable(false);
constructor_val.set_property_slice("name", name);
constructor_val
}
@ -407,7 +433,7 @@ pub fn make_builtin_fn<N>(function: NativeFunctionData, name: N, parent: &Value,
where
N: Into<String>,
{
let func = Function::create_builtin(vec![], FunctionBody::BuiltIn(function));
let func = Function::builtin(Vec::new(), function);
let mut new_func = Object::function();
new_func.set_func(func);

2
boa/src/builtins/mod.rs

@ -1,4 +1,4 @@
//! Builtins live here, such as Object, String, Math etc
//! Builtins live here, such as Object, String, Math, etc.
pub mod array;
pub mod bigint;

17
boa/src/builtins/number/mod.rs

@ -89,21 +89,6 @@ impl Number {
Ok(data)
}
/// `Number()` function.
///
/// More Information https://tc39.es/ecma262/#sec-number-constructor-number-value
pub(crate) fn call_number(
_this: &mut Value,
args: &[Value],
_ctx: &mut Interpreter,
) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => Self::to_number(value),
None => Self::to_number(&Value::from(0)),
};
Ok(data)
}
/// `Number.prototype.toExponential( [fractionDigits] )`
///
/// The `toExponential()` method returns a string representing the Number object in exponential notation.
@ -417,7 +402,7 @@ impl Number {
make_builtin_fn(Self::to_string, "toString", &prototype, 1);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
make_constructor_fn(Self::make_number, global, prototype)
make_constructor_fn("Number", 1, Self::make_number, global, prototype, true)
}
/// Initialise the `Number` object on the global object.

14
boa/src/builtins/object/mod.rs

@ -471,7 +471,10 @@ impl Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-iscallable
pub fn is_callable(&self) -> bool {
self.func.is_some()
match self.func {
Some(ref function) => function.is_callable(),
None => false,
}
}
/// It determines if Object is a function object with a [[Construct]] internal method.
@ -480,8 +483,11 @@ impl Object {
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isconstructor
pub fn is_constructor(&self) -> bool {
self.func.is_some()
pub fn is_constructable(&self) -> bool {
match self.func {
Some(ref function) => function.is_constructable(),
None => false,
}
}
}
@ -614,7 +620,7 @@ pub fn create(global: &Value) -> Value {
make_builtin_fn(has_own_property, "hasOwnProperty", &prototype, 0);
make_builtin_fn(to_string, "toString", &prototype, 0);
let object = make_constructor_fn(make_object, global, prototype);
let object = make_constructor_fn("Object", 1, make_object, global, prototype, true);
object.set_field("length", Value::from(1));
make_builtin_fn(set_prototype_of, "setPrototypeOf", &object, 2);

2
boa/src/builtins/regexp/mod.rs

@ -485,7 +485,7 @@ impl RegExp {
make_builtin_fn(Self::get_sticky, "sticky", &prototype, 0);
make_builtin_fn(Self::get_unicode, "unicode", &prototype, 0);
make_constructor_fn(Self::make_regexp, global, prototype)
make_constructor_fn("RegExp", 1, Self::make_regexp, global, prototype, true)
}
/// Initialise the `RegExp` object on the global object.

2
boa/src/builtins/string/mod.rs

@ -1071,7 +1071,7 @@ impl String {
make_builtin_fn(Self::match_all, "matchAll", &prototype, 1);
make_builtin_fn(Self::replace, "replace", &prototype, 2);
make_constructor_fn(Self::make_string, global, prototype)
make_constructor_fn("String", 1, Self::make_string, global, prototype, true)
}
/// Initialise the `String` object on the global object.

3
boa/src/builtins/symbol/mod.rs

@ -92,8 +92,9 @@ pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa
pub fn create(global: &Value) -> Value {
// Create prototype object
let prototype = Value::new_object(Some(global));
make_builtin_fn(to_string, "toString", &prototype, 0);
make_constructor_fn(call_symbol, global, prototype)
make_constructor_fn("Symbol", 1, call_symbol, global, prototype, false)
}
/// Initialise the `Symbol` object on the global object.

2
boa/src/builtins/value/mod.rs

@ -217,7 +217,7 @@ impl ValueData {
match *self {
Self::Object(ref o) => {
let borrowed_obj = o.borrow();
borrowed_obj.is_callable() || borrowed_obj.is_constructor()
borrowed_obj.is_callable() || borrowed_obj.is_constructable()
}
_ => false,
}

6
boa/src/exec/declaration/mod.rs

@ -18,6 +18,8 @@ impl Executable for FunctionDecl {
self.parameters().to_vec(),
self.body().to_vec(),
ThisMode::NonLexical,
true,
true,
);
// Set the name and assign it in the current environment
@ -43,6 +45,8 @@ impl Executable for FunctionExpr {
self.parameters().to_vec(),
self.body().to_vec(),
ThisMode::NonLexical,
true,
true,
);
if let Some(name) = self.name() {
@ -125,6 +129,8 @@ impl Executable for ArrowFunctionDecl {
self.params().to_vec(),
self.body().to_vec(),
ThisMode::Lexical,
false,
true,
))
}
}

17
boa/src/exec/mod.rs

@ -58,7 +58,14 @@ impl Interpreter {
}
/// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions
pub(crate) fn create_function<P, B>(&mut self, params: P, body: B, this_mode: ThisMode) -> Value
pub(crate) fn create_function<P, B>(
&mut self,
params: P,
body: B,
this_mode: ThisMode,
constructable: bool,
callable: bool,
) -> Value
where
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
@ -69,7 +76,7 @@ impl Interpreter {
.get_global_object()
.expect("Could not get the global object")
.get_field("Function")
.get_field("Prototype");
.get_field(PROTOTYPE);
// Every new function has a prototype property pre-made
let global_val = &self
@ -81,11 +88,13 @@ impl Interpreter {
let params = params.into();
let params_len = params.len();
let func = FunctionObject::create_ordinary(
let func = FunctionObject::new(
params,
self.realm.environment.get_current_environment().clone(),
Some(self.realm.environment.get_current_environment().clone()),
FunctionBody::Ordinary(body.into()),
this_mode,
constructable,
callable,
);
let mut new_func = Object::function();

7
boa/src/realm.rs

@ -7,7 +7,7 @@
use crate::{
builtins::{
self,
function::NativeFunctionData,
function::{Function, NativeFunctionData},
value::{Value, ValueData},
},
environment::{
@ -60,10 +60,7 @@ impl Realm {
/// Utility to add a function to the global object
pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self {
let func = crate::builtins::function::Function::create_builtin(
vec![],
crate::builtins::function::FunctionBody::BuiltIn(func),
);
let func = Function::builtin(Vec::new(), func);
self.global_obj
.set_field(Value::from(func_name), ValueData::from_func(func));

Loading…
Cancel
Save