|
|
|
@ -3,7 +3,7 @@
|
|
|
|
|
use crate::{ |
|
|
|
|
builtins::{ |
|
|
|
|
array::array_iterator::ArrayIterator, |
|
|
|
|
function::{BuiltInFunction, Function, FunctionFlags, NativeFunction}, |
|
|
|
|
function::{Function, NativeFunction}, |
|
|
|
|
map::map_iterator::MapIterator, |
|
|
|
|
map::ordered_map::OrderedMap, |
|
|
|
|
regexp::regexp_string_iterator::RegExpStringIterator, |
|
|
|
@ -22,6 +22,7 @@ use std::{
|
|
|
|
|
any::Any, |
|
|
|
|
fmt::{self, Debug, Display}, |
|
|
|
|
ops::{Deref, DerefMut}, |
|
|
|
|
rc::Rc, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
@ -264,7 +265,7 @@ impl Object {
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-iscallable
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_callable(&self) -> bool { |
|
|
|
|
matches!(self.data, ObjectData::Function(ref f) if f.is_callable()) |
|
|
|
|
matches!(self.data, ObjectData::Function(_)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// It determines if Object is a function object with a `[[Construct]]` internal method.
|
|
|
|
@ -682,24 +683,40 @@ where
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
pub struct FunctionBuilder<'context> { |
|
|
|
|
context: &'context mut Context, |
|
|
|
|
function: BuiltInFunction, |
|
|
|
|
name: Option<String>, |
|
|
|
|
function: Option<Function>, |
|
|
|
|
name: JsString, |
|
|
|
|
length: usize, |
|
|
|
|
callable: bool, |
|
|
|
|
constructable: bool, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<'context> FunctionBuilder<'context> { |
|
|
|
|
/// Create a new `FunctionBuilder`
|
|
|
|
|
/// Create a new `FunctionBuilder` for creating a native function.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn new(context: &'context mut Context, function: NativeFunction) -> Self { |
|
|
|
|
pub fn native(context: &'context mut Context, function: NativeFunction) -> Self { |
|
|
|
|
Self { |
|
|
|
|
context, |
|
|
|
|
function: function.into(), |
|
|
|
|
name: None, |
|
|
|
|
function: Some(Function::Native { |
|
|
|
|
function: function.into(), |
|
|
|
|
constructable: false, |
|
|
|
|
}), |
|
|
|
|
name: JsString::default(), |
|
|
|
|
length: 0, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create a new `FunctionBuilder` for creating a closure function.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn closure<F>(context: &'context mut Context, function: F) -> Self |
|
|
|
|
where |
|
|
|
|
F: Fn(&Value, &[Value], &mut Context) -> Result<Value, Value> + 'static, |
|
|
|
|
{ |
|
|
|
|
Self { |
|
|
|
|
context, |
|
|
|
|
function: Some(Function::Closure { |
|
|
|
|
function: Rc::new(function), |
|
|
|
|
constructable: false, |
|
|
|
|
}), |
|
|
|
|
name: JsString::default(), |
|
|
|
|
length: 0, |
|
|
|
|
callable: true, |
|
|
|
|
constructable: false, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -711,7 +728,7 @@ impl<'context> FunctionBuilder<'context> {
|
|
|
|
|
where |
|
|
|
|
N: AsRef<str>, |
|
|
|
|
{ |
|
|
|
|
self.name = Some(name.as_ref().into()); |
|
|
|
|
self.name = name.as_ref().into(); |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -726,21 +743,16 @@ impl<'context> FunctionBuilder<'context> {
|
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Specify the whether the object function object can be called.
|
|
|
|
|
///
|
|
|
|
|
/// The default is `true`.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn callable(&mut self, yes: bool) -> &mut Self { |
|
|
|
|
self.callable = yes; |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Specify the whether the object function object can be called with `new` keyword.
|
|
|
|
|
///
|
|
|
|
|
/// The default is `false`.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn constructable(&mut self, yes: bool) -> &mut Self { |
|
|
|
|
self.constructable = yes; |
|
|
|
|
match self.function.as_mut() { |
|
|
|
|
Some(Function::Native { constructable, .. }) => *constructable = yes, |
|
|
|
|
Some(Function::Closure { constructable, .. }) => *constructable = yes, |
|
|
|
|
_ => unreachable!(), |
|
|
|
|
} |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -748,10 +760,7 @@ impl<'context> FunctionBuilder<'context> {
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn build(&mut self) -> GcObject { |
|
|
|
|
let mut function = Object::function( |
|
|
|
|
Function::BuiltIn( |
|
|
|
|
self.function, |
|
|
|
|
FunctionFlags::from_parameters(self.callable, self.constructable), |
|
|
|
|
), |
|
|
|
|
self.function.take().unwrap(), |
|
|
|
|
self.context |
|
|
|
|
.standard_objects() |
|
|
|
|
.function_object() |
|
|
|
@ -759,11 +768,7 @@ impl<'context> FunctionBuilder<'context> {
|
|
|
|
|
.into(), |
|
|
|
|
); |
|
|
|
|
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; |
|
|
|
|
if let Some(name) = self.name.take() { |
|
|
|
|
function.insert_property("name", name, attribute); |
|
|
|
|
} else { |
|
|
|
|
function.insert_property("name", "", attribute); |
|
|
|
|
} |
|
|
|
|
function.insert_property("name", self.name.clone(), attribute); |
|
|
|
|
function.insert_property("length", self.length, attribute); |
|
|
|
|
|
|
|
|
|
GcObject::new(function) |
|
|
|
@ -772,10 +777,7 @@ impl<'context> FunctionBuilder<'context> {
|
|
|
|
|
/// Initializes the `Function.prototype` function object.
|
|
|
|
|
pub(crate) fn build_function_prototype(&mut self, object: &GcObject) { |
|
|
|
|
let mut object = object.borrow_mut(); |
|
|
|
|
object.data = ObjectData::Function(Function::BuiltIn( |
|
|
|
|
self.function, |
|
|
|
|
FunctionFlags::from_parameters(self.callable, self.constructable), |
|
|
|
|
)); |
|
|
|
|
object.data = ObjectData::Function(self.function.take().unwrap()); |
|
|
|
|
object.set_prototype_instance( |
|
|
|
|
self.context |
|
|
|
|
.standard_objects() |
|
|
|
@ -783,12 +785,8 @@ impl<'context> FunctionBuilder<'context> {
|
|
|
|
|
.prototype() |
|
|
|
|
.into(), |
|
|
|
|
); |
|
|
|
|
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; |
|
|
|
|
if let Some(name) = self.name.take() { |
|
|
|
|
object.insert_property("name", name, attribute); |
|
|
|
|
} else { |
|
|
|
|
object.insert_property("name", "", attribute); |
|
|
|
|
} |
|
|
|
|
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; |
|
|
|
|
object.insert_property("name", self.name.clone(), attribute); |
|
|
|
|
object.insert_property("length", self.length, attribute); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -836,10 +834,9 @@ impl<'context> ObjectInitializer<'context> {
|
|
|
|
|
B: Into<FunctionBinding>, |
|
|
|
|
{ |
|
|
|
|
let binding = binding.into(); |
|
|
|
|
let function = FunctionBuilder::new(self.context, function) |
|
|
|
|
let function = FunctionBuilder::native(self.context, function) |
|
|
|
|
.name(binding.name) |
|
|
|
|
.length(length) |
|
|
|
|
.callable(true) |
|
|
|
|
.constructable(false) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
@ -876,7 +873,7 @@ pub struct ConstructorBuilder<'context> {
|
|
|
|
|
constructor_function: NativeFunction, |
|
|
|
|
constructor_object: GcObject, |
|
|
|
|
prototype: GcObject, |
|
|
|
|
name: Option<String>, |
|
|
|
|
name: JsString, |
|
|
|
|
length: usize, |
|
|
|
|
callable: bool, |
|
|
|
|
constructable: bool, |
|
|
|
@ -907,7 +904,7 @@ impl<'context> ConstructorBuilder<'context> {
|
|
|
|
|
constructor_object: GcObject::new(Object::default()), |
|
|
|
|
prototype: GcObject::new(Object::default()), |
|
|
|
|
length: 0, |
|
|
|
|
name: None, |
|
|
|
|
name: JsString::default(), |
|
|
|
|
callable: true, |
|
|
|
|
constructable: true, |
|
|
|
|
inherit: None, |
|
|
|
@ -926,7 +923,7 @@ impl<'context> ConstructorBuilder<'context> {
|
|
|
|
|
constructor_object: object.constructor, |
|
|
|
|
prototype: object.prototype, |
|
|
|
|
length: 0, |
|
|
|
|
name: None, |
|
|
|
|
name: JsString::default(), |
|
|
|
|
callable: true, |
|
|
|
|
constructable: true, |
|
|
|
|
inherit: None, |
|
|
|
@ -940,10 +937,9 @@ impl<'context> ConstructorBuilder<'context> {
|
|
|
|
|
B: Into<FunctionBinding>, |
|
|
|
|
{ |
|
|
|
|
let binding = binding.into(); |
|
|
|
|
let function = FunctionBuilder::new(self.context, function) |
|
|
|
|
let function = FunctionBuilder::native(self.context, function) |
|
|
|
|
.name(binding.name) |
|
|
|
|
.length(length) |
|
|
|
|
.callable(true) |
|
|
|
|
.constructable(false) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
@ -967,10 +963,9 @@ impl<'context> ConstructorBuilder<'context> {
|
|
|
|
|
B: Into<FunctionBinding>, |
|
|
|
|
{ |
|
|
|
|
let binding = binding.into(); |
|
|
|
|
let function = FunctionBuilder::new(self.context, function) |
|
|
|
|
let function = FunctionBuilder::native(self.context, function) |
|
|
|
|
.name(binding.name) |
|
|
|
|
.length(length) |
|
|
|
|
.callable(true) |
|
|
|
|
.constructable(false) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
@ -1081,7 +1076,7 @@ impl<'context> ConstructorBuilder<'context> {
|
|
|
|
|
where |
|
|
|
|
N: AsRef<str>, |
|
|
|
|
{ |
|
|
|
|
self.name = Some(name.as_ref().into()); |
|
|
|
|
self.name = name.as_ref().into(); |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1122,17 +1117,17 @@ impl<'context> ConstructorBuilder<'context> {
|
|
|
|
|
/// Build the constructor function object.
|
|
|
|
|
pub fn build(&mut self) -> GcObject { |
|
|
|
|
// Create the native function
|
|
|
|
|
let function = Function::BuiltIn( |
|
|
|
|
self.constructor_function.into(), |
|
|
|
|
FunctionFlags::from_parameters(self.callable, self.constructable), |
|
|
|
|
); |
|
|
|
|
let function = Function::Native { |
|
|
|
|
function: self.constructor_function.into(), |
|
|
|
|
constructable: self.constructable, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let length = DataDescriptor::new( |
|
|
|
|
self.length, |
|
|
|
|
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
|
|
|
|
); |
|
|
|
|
let name = DataDescriptor::new( |
|
|
|
|
self.name.take().unwrap_or_else(|| String::from("[object]")), |
|
|
|
|
self.name.clone(), |
|
|
|
|
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|