mirror of https://github.com/boa-dev/boa.git
jedel1043
3 years ago
committed by
GitHub
11 changed files with 668 additions and 59 deletions
@ -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 |
||||
} |
||||
} |
@ -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 |
||||
} |
@ -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) |
||||
} |
Loading…
Reference in new issue