Browse Source

Implement arguments exotic objects (#1522)

pull/1635/head
jedel1043 3 years ago committed by GitHub
parent
commit
df836f1e2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      boa/src/builtins/array/mod.rs
  2. 272
      boa/src/builtins/function/arguments.rs
  3. 40
      boa/src/builtins/function/mod.rs
  4. 46
      boa/src/builtins/intrinsics.rs
  5. 1
      boa/src/builtins/mod.rs
  6. 2
      boa/src/builtins/object/mod.rs
  7. 18
      boa/src/context.rs
  8. 260
      boa/src/object/internal_methods/arguments.rs
  9. 1
      boa/src/object/internal_methods/mod.rs
  10. 33
      boa/src/object/jsobject.rs
  11. 40
      boa/src/object/mod.rs

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

@ -52,11 +52,7 @@ impl BuiltIn for Array {
.constructable(false)
.build();
let values_function = FunctionBuilder::native(context, Self::values)
.name("values")
.length(0)
.constructable(false)
.build();
let values_function = Self::values_intrinsic(context);
let array = ConstructorBuilder::with_standard_object(
context,
@ -2660,4 +2656,12 @@ impl Array {
}
}
}
pub(crate) fn values_intrinsic(context: &mut Context) -> JsObject {
FunctionBuilder::native(context, Self::values)
.name("values")
.length(0)
.constructable(false)
.build()
}
}

272
boa/src/builtins/function/arguments.rs

@ -0,0 +1,272 @@
use crate::{
builtins::Array,
environment::lexical_environment::Environment,
object::{FunctionBuilder, JsObject, ObjectData},
property::PropertyDescriptor,
symbol::{self, WellKnownSymbols},
syntax::ast::node::FormalParameter,
Context, JsValue,
};
use gc::{Finalize, Trace};
use rustc_hash::FxHashSet;
#[derive(Debug, Clone, Trace, Finalize)]
pub struct MappedArguments(JsObject);
impl MappedArguments {
pub(crate) fn parameter_map(&self) -> JsObject {
self.0.clone()
}
}
#[derive(Debug, Clone, Trace, Finalize)]
pub enum Arguments {
Unmapped,
Mapped(MappedArguments),
}
impl Arguments {
/// Creates a new unmapped Arguments ordinary object.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject
pub(crate) fn create_unmapped_arguments_object(
arguments_list: &[JsValue],
context: &mut Context,
) -> JsObject {
// 1. Let len be the number of elements in argumentsList.
let len = arguments_list.len();
// 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »).
let obj = context.construct_object();
// 3. Set obj.[[ParameterMap]] to undefined.
// skipped because the `Arguments` enum ensures ordinary argument objects don't have a `[[ParameterMap]]`
obj.borrow_mut().data = ObjectData::arguments(Arguments::Unmapped);
// 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len),
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
obj.define_property_or_throw(
"length",
PropertyDescriptor::builder()
.value(len)
.writable(true)
.enumerable(false)
.configurable(true),
context,
)
.expect("DefinePropertyOrThrow must not fail per the spec");
// 5. Let index be 0.
// 6. Repeat, while index < len,
for (index, value) in arguments_list.iter().cloned().enumerate() {
// a. Let val be argumentsList[index].
// b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).
obj.create_data_property_or_throw(index, value, context)
.expect("CreateDataPropertyOrThrow must not fail per the spec");
// c. Set index to index + 1.
}
// 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {
// [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false,
// [[Configurable]]: true }).
obj.define_property_or_throw(
symbol::WellKnownSymbols::iterator(),
PropertyDescriptor::builder()
.value(Array::values_intrinsic(context))
.writable(true)
.enumerable(false)
.configurable(true),
context,
)
.expect("DefinePropertyOrThrow must not fail per the spec");
let throw_type_error = context.intrinsics().throw_type_error();
// 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {
// [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false,
// [[Configurable]]: false }).
obj.define_property_or_throw(
"callee",
PropertyDescriptor::builder()
.get(throw_type_error.clone())
.set(throw_type_error)
.enumerable(false)
.configurable(false),
context,
)
.expect("DefinePropertyOrThrow must not fail per the spec");
// 9. Return obj.
obj
}
/// Creates a new mapped Arguments exotic object.
///
/// <https://tc39.es/ecma262/#sec-createmappedargumentsobject>
pub(crate) fn create_mapped_arguments_object(
func: &JsObject,
formals: &[FormalParameter],
arguments_list: &[JsValue],
env: &Environment,
context: &mut Context,
) -> JsObject {
// 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers.
// It may contain duplicate identifiers.
// 2. Let len be the number of elements in argumentsList.
let len = arguments_list.len();
// 3. Let obj be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »).
// 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1.
// 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2.
// 6. Set obj.[[Get]] as specified in 10.4.4.3.
// 7. Set obj.[[Set]] as specified in 10.4.4.4.
// 8. Set obj.[[Delete]] as specified in 10.4.4.5.
// 9. Set obj.[[Prototype]] to %Object.prototype%.
// 10. Let map be ! OrdinaryObjectCreate(null).
let map = JsObject::empty();
// 11. Set obj.[[ParameterMap]] to map.
let obj = JsObject::from_proto_and_data(
context.standard_objects().object_object().prototype(),
ObjectData::arguments(Arguments::Mapped(MappedArguments(map.clone()))),
);
// 14. Let index be 0.
// 15. Repeat, while index < len,
for (index, val) in arguments_list.iter().cloned().enumerate() {
// a. Let val be argumentsList[index].
// b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).
obj.create_data_property_or_throw(index, val, context)
.expect("CreateDataPropertyOrThrow must not fail per the spec");
// c. Set index to index + 1.
}
// 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len),
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
obj.define_property_or_throw(
"length",
PropertyDescriptor::builder()
.value(len)
.writable(true)
.enumerable(false)
.configurable(true),
context,
)
.expect("DefinePropertyOrThrow must not fail per the spec");
// 17. Let mappedNames be a new empty List.
// using a set to optimize `contains`
let mut mapped_names = FxHashSet::default();
// 12. Let parameterNames be the BoundNames of formals.
// 13. Let numberOfParameters be the number of elements in parameterNames.
// 18. Set index to numberOfParameters - 1.
// 19. Repeat, while index ≥ 0,
// a. Let name be parameterNames[index].
for (index, parameter_name) in formals.iter().map(|fp| fp.name()).enumerate().rev() {
// b. If name is not an element of mappedNames, then
if !mapped_names.contains(parameter_name) {
// i. Add name as an element of the list mappedNames.
mapped_names.insert(parameter_name);
// ii. If index < len, then
if index < len {
// 1. Let g be MakeArgGetter(name, env).
// https://tc39.es/ecma262/#sec-makearggetter
let g = {
// 2. Let getter be ! CreateBuiltinFunction(getterClosure, 0, "", « »).
// 3. NOTE: getter is never directly accessible to ECMAScript code.
// 4. Return getter.
FunctionBuilder::closure_with_captures(
context,
// 1. Let getterClosure be a new Abstract Closure with no parameters that captures
// name and env and performs the following steps when called:
|_, _, captures, context| {
captures.0.get_binding_value(&captures.1, false, context)
},
(env.clone(), parameter_name.to_owned()),
)
.length(0)
.name("")
.build()
};
// 2. Let p be MakeArgSetter(name, env).
// https://tc39.es/ecma262/#sec-makeargsetter
let p = {
// 2. Let setter be ! CreateBuiltinFunction(setterClosure, 1, "", « »).
// 3. NOTE: setter is never directly accessible to ECMAScript code.
// 4. Return setter.
FunctionBuilder::closure_with_captures(
context,
// 1. Let setterClosure be a new Abstract Closure with parameters (value) that captures
// name and env and performs the following steps when called:
|_, args, captures, context| {
let value = args.get(0).cloned().unwrap_or_default();
// a. Return env.SetMutableBinding(name, value, false).
captures
.0
.set_mutable_binding(&captures.1, value, false, context)
.map(|_| JsValue::Undefined)
// Ok(JsValue::Undefined)
},
(env.clone(), parameter_name.to_owned()),
)
.length(1)
.name("")
.build()
};
// 3. Perform map.[[DefineOwnProperty]](! ToString(𝔽(index)), PropertyDescriptor {
// [[Set]]: p, [[Get]]: g, [[Enumerable]]: false, [[Configurable]]: true }).
map.__define_own_property__(
index.into(),
PropertyDescriptor::builder()
.set(p)
.get(g)
.enumerable(false)
.configurable(true)
.build(),
context,
)
.expect("[[DefineOwnProperty]] must not fail per the spec");
}
}
}
// 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {
// [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false,
// [[Configurable]]: true }).
obj.define_property_or_throw(
WellKnownSymbols::iterator(),
PropertyDescriptor::builder()
.value(Array::values_intrinsic(context))
.writable(true)
.enumerable(false)
.configurable(true),
context,
)
.expect("DefinePropertyOrThrow must not fail per the spec");
// 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {
// [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
obj.define_property_or_throw(
"callee",
PropertyDescriptor::builder()
.value(func.clone())
.writable(true)
.enumerable(false)
.configurable(true),
context,
)
.expect("DefinePropertyOrThrow must not fail per the spec");
// 22. Return obj.
obj
}
}

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

@ -36,6 +36,7 @@ use crate::{
use super::JsArgs;
pub(crate) mod arguments;
#[cfg(test)]
mod tests;
@ -288,45 +289,6 @@ impl Function {
}
}
/// Arguments.
///
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
pub fn create_unmapped_arguments_object(
arguments_list: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let len = arguments_list.len();
let obj = JsObject::empty();
// Set length
let length = PropertyDescriptor::builder()
.value(len)
.writable(true)
.enumerable(false)
.configurable(true)
.build();
// Define length as a property
crate::object::internal_methods::ordinary_define_own_property(
&obj,
"length".into(),
length,
context,
)?;
let mut index: usize = 0;
while index < len {
let val = arguments_list.get(index).expect("Could not get argument");
let prop = PropertyDescriptor::builder()
.value(val.clone())
.writable(true)
.enumerable(true)
.configurable(true);
obj.insert(index, prop);
index += 1;
}
Ok(JsValue::new(obj))
}
/// Creates a new member function of a `Object` or `prototype`.
///
/// A function registered using this macro can then be called from Javascript using:

46
boa/src/builtins/intrinsics.rs

@ -0,0 +1,46 @@
use crate::{
builtins::function::Function,
object::{JsObject, ObjectData},
property::PropertyDescriptor,
Context, JsResult, JsValue,
};
#[derive(Debug, Default)]
pub struct IntrinsicObjects {
throw_type_error: JsObject,
}
impl IntrinsicObjects {
pub fn init(context: &mut Context) -> IntrinsicObjects {
Self {
throw_type_error: create_throw_type_error(context),
}
}
pub fn throw_type_error(&self) -> JsObject {
self.throw_type_error.clone()
}
}
fn create_throw_type_error(context: &mut Context) -> JsObject {
fn throw_type_error(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
context.throw_type_error("invalid type")
}
let function = JsObject::from_proto_and_data(
context.standard_objects().function_object().prototype(),
ObjectData::function(Function::Native {
function: throw_type_error,
constructable: false,
}),
);
let property = PropertyDescriptor::builder()
.writable(false)
.enumerable(false)
.configurable(false);
function.insert_property("name", property.clone().value("ThrowTypeError"));
function.insert_property("length", property.value(0));
function
}

1
boa/src/builtins/mod.rs

@ -11,6 +11,7 @@ pub mod error;
pub mod function;
pub mod global_this;
pub mod infinity;
pub mod intrinsics;
pub mod iterable;
pub mod json;
pub mod map;

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

@ -471,7 +471,7 @@ impl Object {
let o = o.borrow();
match o.kind() {
ObjectKind::Array => "Array",
// TODO: Arguments Exotic Objects are currently not supported
ObjectKind::Arguments(_) => "Arguments",
ObjectKind::Function(_) => "Function",
ObjectKind::Error => "Error",
ObjectKind::Boolean(_) => "Boolean",

18
boa/src/context.rs

@ -4,6 +4,7 @@ use crate::{
builtins::{
self,
function::{Function, NativeFunctionSignature, ThisMode},
intrinsics::IntrinsicObjects,
iterable::IteratorPrototypes,
typed_array::TypedArray,
},
@ -88,7 +89,7 @@ pub struct StandardObjects {
symbol: StandardConstructor,
error: StandardConstructor,
type_error: StandardConstructor,
referece_error: StandardConstructor,
reference_error: StandardConstructor,
range_error: StandardConstructor,
syntax_error: StandardConstructor,
eval_error: StandardConstructor,
@ -133,7 +134,7 @@ impl Default for StandardObjects {
symbol: StandardConstructor::default(),
error: StandardConstructor::default(),
type_error: StandardConstructor::default(),
referece_error: StandardConstructor::default(),
reference_error: StandardConstructor::default(),
range_error: StandardConstructor::default(),
syntax_error: StandardConstructor::default(),
eval_error: StandardConstructor::default(),
@ -210,7 +211,7 @@ impl StandardObjects {
#[inline]
pub fn reference_error_object(&self) -> &StandardConstructor {
&self.referece_error
&self.reference_error
}
#[inline]
@ -384,6 +385,9 @@ pub struct Context {
/// Cached standard objects and their prototypes.
standard_objects: StandardObjects,
/// Cached intrinsic objects
intrinsic_objects: IntrinsicObjects,
/// Whether or not strict mode is active.
strict: StrictType,
@ -403,6 +407,7 @@ impl Default for Context {
iterator_prototypes: IteratorPrototypes::default(),
typed_array_constructor: StandardConstructor::default(),
standard_objects: Default::default(),
intrinsic_objects: IntrinsicObjects::default(),
strict: StrictType::Off,
#[cfg(feature = "vm")]
vm: Vm {
@ -426,6 +431,7 @@ impl Default for Context {
context.typed_array_constructor.prototype = typed_array_constructor_prototype;
context.create_intrinsics();
context.iterator_prototypes = IteratorPrototypes::init(&mut context);
context.intrinsic_objects = IntrinsicObjects::init(&mut context);
context
}
}
@ -1060,6 +1066,12 @@ impl Context {
&self.standard_objects
}
/// Return the intrinsic objects.
#[inline]
pub fn intrinsics(&self) -> &IntrinsicObjects {
&self.intrinsic_objects
}
/// Set the value of trace on the context
#[cfg(feature = "vm")]
pub fn set_trace(&mut self, trace: bool) {

260
boa/src/object/internal_methods/arguments.rs

@ -0,0 +1,260 @@
use crate::{
object::JsObject,
property::{DescriptorKind, PropertyDescriptor, PropertyKey},
Context, JsResult, JsValue,
};
use super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};
pub(crate) static ARGUMENTS_EXOTIC_INTERNAL_METHODS: InternalObjectMethods =
InternalObjectMethods {
__get_own_property__: arguments_exotic_get_own_property,
__define_own_property__: arguments_exotic_define_own_property,
__get__: arguments_exotic_get,
__set__: arguments_exotic_set,
__delete__: arguments_exotic_delete,
..ORDINARY_INTERNAL_METHODS
};
/// `[[GetOwnProperty]]` for arguments exotic objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-getownproperty-p
#[inline]
pub(crate) fn arguments_exotic_get_own_property(
obj: &JsObject,
key: &PropertyKey,
context: &mut Context,
) -> JsResult<Option<PropertyDescriptor>> {
// 1. Let desc be OrdinaryGetOwnProperty(args, P).
// 2. If desc is undefined, return desc.
let desc = if let Some(desc) = super::ordinary_get_own_property(obj, key, context)? {
desc
} else {
return Ok(None);
};
// 3. Let map be args.[[ParameterMap]].
let map = obj
.borrow()
.as_mapped_arguments()
.expect("arguments exotic method must only be callable from arguments objects")
.parameter_map();
Ok(Some(
// 4. Let isMapped be ! HasOwnProperty(map, P).
// 5. If isMapped is true, then
if map
.has_own_property(key.clone(), context)
.expect("HasOwnProperty must not fail per the spec")
{
// a. Set desc.[[Value]] to Get(map, P).
PropertyDescriptor::builder()
.value(map.get(key.clone(), context)?)
.maybe_writable(desc.writable())
.maybe_enumerable(desc.enumerable())
.maybe_configurable(desc.configurable())
.build()
} else {
// 6. Return desc.
desc
},
))
}
/// `[[DefineOwnProperty]]` for arguments exotic objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-defineownproperty-p-desc
pub(crate) fn arguments_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
desc: PropertyDescriptor,
context: &mut Context,
) -> JsResult<bool> {
// 1. Let map be args.[[ParameterMap]].
let map = obj
.borrow()
.as_mapped_arguments()
.expect("arguments exotic method must only be callable from arguments objects")
.parameter_map();
// 2. Let isMapped be HasOwnProperty(map, P).
let is_mapped = map.has_own_property(key.clone(), context)?;
let new_arg_desc = match desc.kind() {
// 4. If isMapped is true and IsDataDescriptor(Desc) is true, then
// a. If Desc.[[Value]] is not present and Desc.[[Writable]] is present and its
// value is false, then
DescriptorKind::Data {
writable: Some(false),
value: None,
} if is_mapped =>
// i. Set newArgDesc to a copy of Desc.
// ii. Set newArgDesc.[[Value]] to Get(map, P).
{
PropertyDescriptor::builder()
.value(map.get(key.clone(), context)?)
.writable(false)
.maybe_enumerable(desc.enumerable())
.maybe_configurable(desc.configurable())
.build()
}
// 3. Let newArgDesc be Desc.
_ => desc.clone(),
};
// 5. Let allowed be ? OrdinaryDefineOwnProperty(args, P, newArgDesc).
// 6. If allowed is false, return false.
if !super::ordinary_define_own_property(obj, key.clone(), new_arg_desc, context)? {
return Ok(false);
}
// 7. If isMapped is true, then
if is_mapped {
// a. If IsAccessorDescriptor(Desc) is true, then
if desc.is_accessor_descriptor() {
// i. Call map.[[Delete]](P).
map.__delete__(&key, context)?;
}
// b. Else,
else {
// i. If Desc.[[Value]] is present, then
if let Some(value) = desc.value() {
// 1. Let setStatus be Set(map, P, Desc.[[Value]], false).
let set_status = map.set(key.clone(), value, false, context);
// 2. Assert: setStatus is true because formal parameters mapped by argument objects are always writable.
assert_eq!(set_status, Ok(true))
}
// ii. If Desc.[[Writable]] is present and its value is false, then
if let Some(false) = desc.writable() {
// 1. Call map.[[Delete]](P).
map.__delete__(&key, context)?;
}
}
}
// 8. Return true.
Ok(true)
}
/// `[[Get]]` for arguments exotic objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver
pub(crate) fn arguments_exotic_get(
obj: &JsObject,
key: &PropertyKey,
receiver: JsValue,
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let map be args.[[ParameterMap]].
let map = obj
.borrow()
.as_mapped_arguments()
.expect("arguments exotic method must only be callable from arguments objects")
.parameter_map();
// 2. Let isMapped be ! HasOwnProperty(map, P).
// 4. Else,
if map
.has_own_property(key.clone(), context)
.expect("HasOwnProperty must not fail per the spec")
{
// a. Assert: map contains a formal parameter mapping for P.
// b. Return Get(map, P).
map.get(key.clone(), context)
// 3. If isMapped is false, then
} else {
// a. Return ? OrdinaryGet(args, P, Receiver).
super::ordinary_get(obj, key, receiver, context)
}
}
/// `[[Set]]` for arguments exotic objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver
pub(crate) fn arguments_exotic_set(
obj: &JsObject,
key: PropertyKey,
value: JsValue,
receiver: JsValue,
context: &mut Context,
) -> JsResult<bool> {
// 1. If SameValue(args, Receiver) is false, then
// a. Let isMapped be false.
// 2. Else,
if JsValue::same_value(&obj.clone().into(), &receiver) {
// a. Let map be args.[[ParameterMap]].
let map = obj
.borrow()
.as_mapped_arguments()
.expect("arguments exotic method must only be callable from arguments objects")
.parameter_map();
// b. Let isMapped be ! HasOwnProperty(map, P).
// 3. If isMapped is true, then
if map
.has_own_property(key.clone(), context)
.expect("HasOwnProperty must not fail per the spec")
{
// a. Let setStatus be Set(map, P, V, false).
let set_status = map.set(key.clone(), value.clone(), false, context);
// b. Assert: setStatus is true because formal parameters mapped by argument objects are always writable.
assert_eq!(set_status, Ok(true));
}
}
// 4. Return ? OrdinarySet(args, P, V, Receiver).
super::ordinary_set(obj, key, value, receiver, context)
}
/// `[[Delete]]` for arguments exotic objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-delete-p
pub(crate) fn arguments_exotic_delete(
obj: &JsObject,
key: &PropertyKey,
context: &mut Context,
) -> JsResult<bool> {
// 1. Let map be args.[[ParameterMap]].
let map = obj
.borrow()
.as_mapped_arguments()
.expect("arguments exotic method must only be callable from arguments objects")
.parameter_map();
// 2. Let isMapped be ! HasOwnProperty(map, P).
let is_mapped = map
.has_own_property(key.clone(), context)
.expect("HasOwnProperty must not fail per the spec");
// 3. Let result be ? OrdinaryDelete(args, P).
let result = super::ordinary_delete(obj, key, context)?;
// 4. If result is true and isMapped is true, then
if is_mapped && result {
// a. Call map.[[Delete]](P).
map.__delete__(key, context)?;
}
// 5. Return result.
Ok(result)
}

1
boa/src/object/internal_methods/mod.rs

@ -15,6 +15,7 @@ use crate::{
use super::PROTOTYPE;
pub(super) mod arguments;
pub(super) mod array;
pub(super) mod integer_indexed;
pub(super) mod string;

33
boa/src/object/jsobject.rs

@ -20,12 +20,8 @@ use std::{
#[cfg(not(feature = "vm"))]
use crate::{
builtins::function::{
create_unmapped_arguments_object, Captures, ClosureFunctionSignature, Function,
NativeFunctionSignature,
},
builtins::function::{Captures, ClosureFunctionSignature, Function, NativeFunctionSignature},
environment::{
environment_record_trait::EnvironmentRecordTrait,
function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
lexical_environment::Environment,
},
@ -160,7 +156,7 @@ impl JsObject {
context: &mut Context,
construct: bool,
) -> JsResult<JsValue> {
use crate::context::StandardObjects;
use crate::{builtins::function::arguments::Arguments, context::StandardObjects};
use super::internal_methods::get_prototype_from_constructor;
@ -233,14 +229,21 @@ impl JsObject {
)?;
let mut arguments_in_parameter_names = false;
let mut is_simple_parameter_list = true;
for param in params.iter() {
has_parameter_expressions =
has_parameter_expressions || param.init().is_some();
arguments_in_parameter_names =
arguments_in_parameter_names || param.name() == "arguments";
is_simple_parameter_list = is_simple_parameter_list
&& !param.is_rest_param()
&& param.init().is_none()
}
// Turn local_env into Environment so it can be cloned
let local_env: Environment = local_env.into();
// An arguments object is added when all of the following conditions are met
// - If not in an arrow function (10.2.11.16)
// - If the parameter list does not contain `arguments` (10.2.11.17)
@ -254,14 +257,22 @@ impl JsObject {
&& !body.function_declared_names().contains("arguments")))
{
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args, context)?;
let arguments_obj =
if context.strict() || body.strict() || !is_simple_parameter_list {
Arguments::create_unmapped_arguments_object(args, context)
} else {
Arguments::create_mapped_arguments_object(
self, params, args, &local_env, context,
)
};
local_env.create_mutable_binding("arguments", false, true, context)?;
local_env.initialize_binding("arguments", arguments_obj, context)?;
local_env.initialize_binding(
"arguments",
arguments_obj.into(),
context,
)?;
}
// Turn local_env into Environment so it can be cloned
let local_env: Environment = local_env.into();
// Push the environment first so that it will be used by default parameters
context.push_environment(local_env.clone());

40
boa/src/object/mod.rs

@ -4,6 +4,7 @@ use crate::{
builtins::{
array::array_iterator::ArrayIterator,
array_buffer::ArrayBuffer,
function::arguments::{Arguments, MappedArguments},
function::{Captures, Function, NativeFunctionSignature},
map::map_iterator::MapIterator,
map::ordered_map::OrderedMap,
@ -16,6 +17,7 @@ use crate::{
},
context::StandardConstructor,
gc::{Finalize, Trace},
object::internal_methods::arguments::ARGUMENTS_EXOTIC_INTERNAL_METHODS,
property::{Attribute, PropertyDescriptor, PropertyKey},
Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue,
};
@ -113,6 +115,7 @@ pub enum ObjectKind {
Ordinary,
Date(Date),
Global,
Arguments(Arguments),
NativeObject(Box<dyn NativeObject>),
IntegerIndexed(IntegerIndexed),
}
@ -286,6 +289,18 @@ impl ObjectData {
}
}
/// Create the `Arguments` object data
pub fn arguments(arguments: Arguments) -> Self {
Self {
internal_methods: if matches!(arguments, Arguments::Unmapped) {
&ORDINARY_INTERNAL_METHODS
} else {
&ARGUMENTS_EXOTIC_INTERNAL_METHODS
},
kind: ObjectKind::Arguments(arguments),
}
}
/// Create the `NativeObject` object data
pub fn native_object(native_object: Box<dyn NativeObject>) -> Self {
Self {
@ -327,6 +342,7 @@ impl Display for ObjectKind {
Self::BigInt(_) => "BigInt",
Self::Date(_) => "Date",
Self::Global => "Global",
Self::Arguments(_) => "Arguments",
Self::NativeObject(_) => "NativeObject",
Self::IntegerIndexed(_) => "TypedArray",
})
@ -858,6 +874,30 @@ impl Object {
)
}
/// Checks if it is an `Arguments` object.
#[inline]
pub fn is_arguments(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Arguments(_),
..
}
)
}
/// Gets the mapped arguments data if this is a mapped arguments object.
#[inline]
pub fn as_mapped_arguments(&self) -> Option<&MappedArguments> {
match self.data {
ObjectData {
kind: ObjectKind::Arguments(Arguments::Mapped(ref args)),
..
} => Some(args),
_ => None,
}
}
/// Gets the typed array data (integer indexed object) if this is a typed array.
#[inline]
pub fn as_typed_array(&self) -> Option<&IntegerIndexed> {

Loading…
Cancel
Save