From 83df2181834460b250ad4c08966ec0aed4a7e4cc Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 6 Dec 2024 23:18:07 +0200 Subject: [PATCH 1/2] feat(ecmascript): Infallible internal method variants --- .../operations_on_objects.rs | 76 +- nova_vm/src/ecmascript/builtins/array.rs | 228 ++- .../builtins/array/abstract_operations.rs | 107 +- .../src/ecmascript/builtins/bound_function.rs | 85 +- .../builtins/builtin_constructor.rs | 85 +- .../ecmascript/builtins/builtin_function.rs | 73 +- .../promise_resolving_functions.rs | 75 +- .../builtins/ecmascript_function.rs | 94 +- .../ecmascript/builtins/embedder_object.rs | 104 +- nova_vm/src/ecmascript/builtins/error.rs | 167 ++- .../object_objects/object_constructor.rs | 3 + .../object_objects/object_prototype.rs | 19 +- nova_vm/src/ecmascript/builtins/module.rs | 315 ++-- nova_vm/src/ecmascript/builtins/ordinary.rs | 486 ++++-- .../ecmascript/builtins/primitive_objects.rs | 96 +- nova_vm/src/ecmascript/builtins/proxy.rs | 10 +- .../builtins/reflection/reflect_object.rs | 2 +- .../environments/function_environment.rs | 27 +- .../ecmascript/scripts_and_modules/script.rs | 4 +- nova_vm/src/ecmascript/types/language.rs | 5 +- .../src/ecmascript/types/language/function.rs | 193 ++- .../types/language/function/into_function.rs | 112 +- .../src/ecmascript/types/language/object.rs | 1297 ++++++++++++++++- .../types/language/object/internal_methods.rs | 403 +++-- nova_vm/src/engine/bytecode/iterator.rs | 8 +- nova_vm/src/engine/bytecode/vm.rs | 14 +- tests/expectations.json | 60 +- tests/metrics.json | 4 +- 28 files changed, 3270 insertions(+), 882 deletions(-) diff --git a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs index 26a40fe1..286fcd6c 100644 --- a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs +++ b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs @@ -127,6 +127,38 @@ pub(crate) fn set( Ok(()) } +/// ### [7.3.5] CreateDataProperty ( O, P, V )[https://tc39.es/ecma262/#sec-createdataproperty] +/// +/// The abstract operation CreateDataProperty takes arguments O (an Object), P +/// (a property key), and V (an ECMAScript language value) and returns either a +/// normal completion containing a Boolean or a throw completion. It is used to +/// create a new own property of an object. +/// +/// > NOTE: This abstract operation creates a property whose attributes are set +/// > to the same defaults used for properties created by the ECMAScript language +/// > assignment operator. Normally, the property will not already exist. If it +/// > does exist and is not configurable or if O is not extensible, +/// > [\[DefineOwnProperty]] will return false. +pub(crate) fn try_create_data_property( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + object: Object, + property_key: PropertyKey, + value: Value, +) -> Option { + // 1. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }. + let new_desc = PropertyDescriptor { + value: Some(value), + writable: Some(true), + get: None, + set: None, + enumerable: Some(true), + configurable: Some(true), + }; + // 2. Return ? O.[[DefineOwnProperty]](P, newDesc). + object.try_define_own_property(agent, gc, property_key, new_desc) +} + /// ### [7.3.5] CreateDataProperty ( O, P, V )[https://tc39.es/ecma262/#sec-createdataproperty] /// /// The abstract operation CreateDataProperty takes arguments O (an Object), P @@ -673,7 +705,7 @@ pub(crate) fn ordinary_has_instance( return instanceof_operator(agent, gc.reborrow(), o, bc); } // 3. If O is not an Object, return false. - let Ok(mut o) = Object::try_from(o.into_value()) else { + let Ok(o) = Object::try_from(o.into_value()) else { return Ok(false); }; // 4. Let P be ? Get(C, "prototype"). @@ -688,19 +720,43 @@ pub(crate) fn ordinary_has_instance( )); }; // 6. Repeat, + is_prototype_of_loop(agent, gc, p, o) +} + +pub(crate) fn is_prototype_of_loop( + agent: &mut Agent, + mut gc: GcScope, + o: Object, + mut v: Object, +) -> JsResult { + { + let gc = gc.nogc(); + loop { + let proto = v.try_get_prototype_of(agent, gc); + let Some(proto) = proto else { + break; + }; + if let Some(proto) = proto { + v = proto; + if o == v { + return Ok(true); + } + } else { + return Ok(false); + } + } + } + let o = o.scope(agent, gc.nogc()); loop { - // a. Set O to ? O.[[GetPrototypeOf]](). - let o_prototype = o.internal_get_prototype_of(agent, gc.reborrow())?; - if let Some(o_prototype) = o_prototype { - o = o_prototype; + let proto = v.internal_get_prototype_of(agent, gc.reborrow())?; + if let Some(proto) = proto { + v = proto; + if o.get(agent) == v { + return Ok(true); + } } else { - // b. If O is null, return false. return Ok(false); } - // c. If SameValue(P, O) is true, return true. - if same_value(agent, p, o) { - return Ok(true); - } } } diff --git a/nova_vm/src/ecmascript/builtins/array.rs b/nova_vm/src/ecmascript/builtins/array.rs index 2cb4f2da..b46a4a67 100644 --- a/nova_vm/src/ecmascript/builtins/array.rs +++ b/nova_vm/src/ecmascript/builtins/array.rs @@ -11,20 +11,23 @@ mod data; use std::ops::{Index, IndexMut, RangeInclusive}; -use super::{array_set_length, ordinary::ordinary_define_own_property}; -use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ operations_on_objects::{call_function, create_array_from_list}, testing_and_comparison::same_value, }, + builtins::{ + array::abstract_operations::{array_set_length, array_try_set_length}, + ordinary::ordinary_try_define_own_property, + }, execution::{Agent, JsResult, ProtoIntrinsics}, types::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, PropertyDescriptor, PropertyKey, Value, BUILTIN_STRING_MEMORY, }, }, + engine::context::{GcScope, NoGcScope}, heap::{ element_array::{ElementArrays, ElementDescriptor}, indexes::ArrayIndex, @@ -98,6 +101,26 @@ impl Array { Array(ArrayIndex::last(&agent.heap.arrays)) } + #[inline] + fn try_get_backing( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + if let Some(object_index) = self.get_backing_object(agent) { + // If backing object exists, then we might have properties there + object_index.try_get(agent, gc, property_key, receiver) + } else { + // If backing object doesn't exist, then we might still have + // properties in the prototype. + self.internal_prototype(agent) + .unwrap() + .try_get(agent, gc, property_key, receiver) + } + } + #[inline] fn internal_get_backing( self, @@ -222,19 +245,23 @@ impl InternalSlots for Array { } impl InternalMethods for Array { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { + ) -> Option> { if let PropertyKey::Integer(index) = property_key { let index = index.into_i64(); if !ARRAY_INDEX_RANGE.contains(&index) { if let Some(backing_object) = self.get_backing_object(agent) { - return backing_object.internal_get_own_property(agent, gc, property_key); + return Some( + backing_object + .try_get_own_property(agent, gc, property_key) + .unwrap(), + ); } else { - return Ok(None); + return Some(None); } } // ARRAY_INDEX_RANGE guarantees were in u32 area. @@ -243,7 +270,7 @@ impl InternalMethods for Array { let length = elements.len(); if index >= length { // Out of bounds - return Ok(None); + return Some(None); } let elements = elements.into(); let index = index as usize; @@ -251,9 +278,9 @@ impl InternalMethods for Array { let value = *agent.heap.elements.get(elements).get(index).unwrap(); let descriptor = agent.heap.elements.get_descriptor(elements, index); return if value.is_none() && descriptor.is_none() { - Ok(None) + Some(None) } else { - Ok(Some(ElementDescriptor::to_property_descriptor( + Some(Some(ElementDescriptor::to_property_descriptor( descriptor, value, ))) }; @@ -261,7 +288,7 @@ impl InternalMethods for Array { let length_key = PropertyKey::from(BUILTIN_STRING_MEMORY.length); let array_data = agent[self]; if property_key == length_key { - Ok(Some(PropertyDescriptor { + Some(Some(PropertyDescriptor { value: Some(array_data.elements.len().into()), writable: Some(array_data.elements.len_writable), configurable: Some(false), @@ -269,35 +296,38 @@ impl InternalMethods for Array { ..Default::default() })) } else if let Some(backing_object) = array_data.object_index { - backing_object.internal_get_own_property(agent, gc, property_key) + Some( + backing_object + .try_get_own_property(agent, gc, property_key) + .unwrap(), + ) } else { - Ok(None) + Some(None) } } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { + ) -> Option { if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { - array_set_length(agent, gc.reborrow(), self, property_descriptor) + array_try_set_length(agent, self, property_descriptor) } else if let PropertyKey::Integer(index) = property_key { let index = index.into_i64(); if !ARRAY_INDEX_RANGE.contains(&index) { let backing_object = self .get_backing_object(agent) - .unwrap_or_else(|| self.create_backing_object(agent)) - .into_object(); - return ordinary_define_own_property( + .unwrap_or_else(|| self.create_backing_object(agent)); + return Some(ordinary_try_define_own_property( agent, - gc.reborrow(), + gc, backing_object, property_key, property_descriptor, - ); + )); } // Let lengthDesc be OrdinaryGetOwnProperty(A, "length"). // b. Assert: IsDataDescriptor(lengthDesc) is true. @@ -312,7 +342,7 @@ impl InternalMethods for Array { if index >= length { // g. If index ≥ length and lengthDesc.[[Writable]] is false, return false. if !length_writable { - return Ok(false); + return Some(false); } let Heap { elements, arrays, .. @@ -335,10 +365,10 @@ impl InternalMethods for Array { // This should've already been handled by the push. debug_assert_eq!(agent[self].elements.len(), index + 1); // iii. Assert: succeeded is true. - Ok(true) + Some(true) } else { // h. Let succeeded be ! OrdinaryDefineOwnProperty(A, P, Desc). - return Ok(ordinary_define_own_property_for_array( + return Some(ordinary_define_own_property_for_array( agent, elements, index, @@ -348,16 +378,55 @@ impl InternalMethods for Array { } else { let backing_object = self .get_backing_object(agent) - .unwrap_or_else(|| self.create_backing_object(agent)) - .into_object(); - ordinary_define_own_property( + .unwrap_or_else(|| self.create_backing_object(agent)); + Some(ordinary_try_define_own_property( agent, gc, backing_object, property_key, property_descriptor, - ) + )) + } + } + + fn internal_define_own_property( + self, + agent: &mut Agent, + gc: GcScope<'_, '_>, + property_key: PropertyKey, + property_descriptor: PropertyDescriptor, + ) -> JsResult { + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { + array_set_length(agent, gc, self, property_descriptor) + } else { + Ok(self + .try_define_own_property(agent, gc.into_nogc(), property_key, property_descriptor) + .unwrap()) + } + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + let has_own = self.try_get_own_property(agent, gc, property_key).unwrap(); + if has_own.is_some() { + return Some(true); + } + + // 3. Let parent be ? O.[[GetPrototypeOf]](). + let parent = self.try_get_prototype_of(agent, gc).unwrap(); + + // 4. If parent is not null, then + if let Some(parent) = parent { + // a. Return ? parent.[[HasProperty]](P). + return parent.try_has_property(agent, gc, property_key); } + + // 5. Return false. + Some(false) } fn internal_has_property( @@ -384,6 +453,62 @@ impl InternalMethods for Array { Ok(false) } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { + Some(self.len(agent).into()) + } else if let PropertyKey::Integer(index) = property_key { + let index = index.into_i64(); + if !ARRAY_INDEX_RANGE.contains(&index) { + // Negative indexes and indexes over 2^32 - 2 go into backing store + return self.try_get_backing(agent, gc, property_key, receiver); + } + let index = index as u32; + let elements = agent[self].elements; + if index >= elements.len() { + // Indexes below 2^32 but above length are necessarily not + // defined: If they were, then the length would be larger. + // Hence, we look in the prototype. + return if let Some(prototype) = self.internal_prototype(agent) { + prototype.try_get(agent, gc, property_key, receiver) + } else { + Some(Value::Undefined) + }; + } + // Index has been checked to be between 0 <= idx < len; indexing should never fail. + let element = agent[elements][index as usize]; + if let Some(element) = element { + Some(element) + } else { + let (descriptors, _) = agent + .heap + .elements + .get_descriptors_and_slice(elements.into()); + if let Some(descriptors) = descriptors { + if let Some(descriptor) = descriptors.get(&index) { + if let Some(_getter) = descriptor.getter_function() { + // 7. Return ? Call(getter, Receiver). + // return call_function(agent, gc, getter, receiver, None); + return None; + } + } + } + if let Some(prototype) = self.internal_prototype(agent) { + prototype.try_get(agent, gc, property_key, receiver) + } else { + Some(Value::Undefined) + } + } + } else { + self.try_get_backing(agent, gc, property_key, receiver) + } + } + fn internal_get( self, agent: &mut Agent, @@ -439,27 +564,29 @@ impl InternalMethods for Array { } } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { + ) -> Option { if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { - Ok(true) + Some(true) } else if let PropertyKey::Integer(index) = property_key { let index = index.into_i64(); if !ARRAY_INDEX_RANGE.contains(&index) { - return self - .get_backing_object(agent) - .map_or(Ok(true), |object_index| { - object_index.internal_delete(agent, gc.reborrow(), property_key) - }); + return Some( + self.get_backing_object(agent) + .map(|object_index| { + object_index.try_delete(agent, gc, property_key).unwrap() + }) + .unwrap_or(true), + ); } let index = index as u32; let elements = agent[self].elements; if index >= elements.len() { - return Ok(true); + return Some(true); } let (descriptors, slice) = agent .heap @@ -469,29 +596,30 @@ impl InternalMethods for Array { if let Some(descriptor) = descriptors.get(&index) { if !descriptor.is_configurable() { // Unconfigurable property. - return Ok(false); + return Some(false); } descriptors.remove(&index); } } // Index has been checked to be between 0 <= idx < len; indexing should never fail. slice[index as usize] = None; - Ok(true) + Some(true) } else { - self.get_backing_object(agent) - .map_or(Ok(true), |object_index| { - object_index.internal_delete(agent, gc.reborrow(), property_key) - }) + Some( + self.get_backing_object(agent) + .map(|object_index| object_index.try_delete(agent, gc, property_key).unwrap()) + .unwrap_or(true), + ) } } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - gc: GcScope<'_, '_>, - ) -> JsResult> { + gc: NoGcScope<'_, '_>, + ) -> Option> { let backing_keys = if let Some(backing_object) = self.get_backing_object(agent) { - backing_object.internal_own_property_keys(agent, gc)? + backing_object.try_own_property_keys(agent, gc).unwrap() } else { Default::default() }; @@ -508,7 +636,7 @@ impl InternalMethods for Array { keys.extend(backing_keys); - Ok(keys) + Some(keys) } } diff --git a/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs b/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs index 2ac13f81..2ff7f2e5 100644 --- a/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/array/abstract_operations.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::abstract_operations::type_conversion::to_uint32_number; use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ @@ -158,7 +159,7 @@ pub(crate) fn array_species_create( /// ### [10.4.2.4 ArraySetLength ( A, Desc )](https://tc39.es/ecma262/#sec-arraysetlength) /// /// The abstract operation ArraySetLength takes arguments A (an Array) and Desc (a Property Descriptor) and returns either a normal completion containing a Boolean or a throw completion. -pub fn array_set_length( +pub(crate) fn array_set_length( agent: &mut Agent, mut gc: GcScope<'_, '_>, a: Array, @@ -264,3 +265,107 @@ pub fn array_set_length( // 19. Return true. Ok(true) } + +pub(crate) fn array_try_set_length( + agent: &mut Agent, + a: Array, + desc: PropertyDescriptor, +) -> Option { + // 1. If Desc does not have a [[Value]] field, then + let Some(desc_value) = desc.value else { + // a. Return ! OrdinaryDefineOwnProperty(A, "length", Desc). + if !desc.has_fields() { + return Some(true); + } + if desc.configurable == Some(true) || desc.enumerable == Some(true) { + return Some(false); + } + if !desc.is_generic_descriptor() && desc.is_accessor_descriptor() { + return Some(false); + } + if !agent[a].elements.len_writable { + // Length is already frozen. + if desc.writable == Some(true) { + return Some(false); + } + } else if desc.writable == Some(false) { + agent[a].elements.len_writable = false; + } + return Some(true); + }; + // 2. Let newLenDesc be a copy of Desc. + // 13. If newLenDesc does not have a [[Writable]] field or newLenDesc.[[Writable]] is true, then + // a. Let newLenDesc.[[Writable]] be true + let new_len_writable = desc.writable.unwrap_or(true); + // NOTE: Setting the [[Writable]] attribute to false is deferred in case any elements cannot be deleted. + // 3. Let newLen be ? ToUint32(Desc.[[Value]]). + // 4. Let numberLen be ? ToNumber(Desc.[[Value]]). + let Ok(number_len) = Number::try_from(desc_value) else { + return None; + }; + let new_len = to_uint32_number(agent, number_len); + // 5. If SameValueZero(newLen, numberLen) is false, throw a RangeError exception. + if !Number::same_value_zero(agent, number_len, new_len.into()) { + return None; + } + // 6. Set newLenDesc.[[Value]] to newLen. + // 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). + let Heap { + arrays, elements, .. + } = &mut agent.heap; + let array_heap_data = &mut arrays[a]; + // 10. Let oldLen be oldLenDesc.[[Value]]. + let (old_len, old_len_writable) = ( + array_heap_data.elements.len(), + array_heap_data.elements.len_writable, + ); + // 12. If oldLenDesc.[[Writable]] is false, return false. + if !old_len_writable { + return Some(false); + } + // Optimization: check OrdinaryDefineOwnProperty conditions for failing early on. + if desc.configurable == Some(true) || desc.enumerable == Some(true) { + // 16. If succeeded is false, return false. + return Some(false); + } + // 11. If newLen ≥ oldLen, then + if new_len >= old_len { + // a. Return ! OrdinaryDefineOwnProperty(A, "length", newLenDesc). + array_heap_data.elements.reserve(elements, new_len); + array_heap_data.elements.len = new_len; + array_heap_data.elements.len_writable = new_len_writable; + return Some(true); + } + // 15. Let succeeded be ! OrdinaryDefineOwnProperty(A, "length", newLenDesc). + let old_elements = array_heap_data.elements; + array_heap_data.elements.len = new_len; + // 17. For each own property key P of A such that P is an array index and ! ToUint32(P) ≥ newLen, in descending numeric index order, do + debug_assert!(old_len > new_len); + for i in new_len + 1..old_len { + // a. Let deleteSucceeded be ! A.[[Delete]](P). + let elements = &mut elements[old_elements]; + // TODO: Handle unwritable properties and property descriptors. + *elements.get_mut(i as usize).unwrap() = None; + let delete_succeeded = true; + // b. If deleteSucceeded is false, then + if !delete_succeeded { + let array_heap_data = &mut arrays[a]; + // i. Set newLenDesc.[[Value]] to ! ToUint32(P) + 1𝔽. + array_heap_data.elements.len = i + 1; + // ii. If newWritable is false, set newLenDesc.[[Writable]] to false. + array_heap_data.elements.len_writable &= new_len_writable; + // iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc). + // iv. Return false. + return Some(false); + } + } + // 18. If newWritable is false, then + if !new_len_writable { + // a. Set succeeded to ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Writable]]: false }). + // b. Assert: succeeded is true. + let array_heap_data = &mut arrays[a]; + array_heap_data.elements.len_writable &= new_len_writable; + } + // 19. Return true. + Some(true) +} diff --git a/nova_vm/src/ecmascript/builtins/bound_function.rs b/nova_vm/src/ecmascript/builtins/bound_function.rs index 8198e3c9..d29d04a7 100644 --- a/nova_vm/src/ecmascript/builtins/bound_function.rs +++ b/nova_vm/src/ecmascript/builtins/bound_function.rs @@ -4,7 +4,8 @@ use std::ops::{Index, IndexMut}; -use crate::engine::context::GcScope; +use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::{ @@ -145,76 +146,110 @@ impl InternalSlots for BoundFunction { } impl InternalMethods for BoundFunction { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + _gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { - function_internal_get_own_property(self, agent, property_key) + ) -> Option> { + Some(function_internal_get_own_property( + self, + agent, + property_key, + )) } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { - function_internal_define_own_property( + ) -> Option { + Some(function_internal_define_own_property( self, agent, - gc.reborrow(), + gc, property_key, property_descriptor, - ) + )) + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + function_try_has_property(self, agent, gc, property_key) } fn internal_has_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, ) -> JsResult { - function_internal_has_property(self, agent, gc.reborrow(), property_key) + function_internal_has_property(self, agent, gc, property_key) + } + + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + function_try_get(self, agent, gc, property_key, receiver) } fn internal_get( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, receiver: Value, ) -> JsResult { - function_internal_get(self, agent, gc.reborrow(), property_key, receiver) + function_internal_get(self, agent, gc, property_key, receiver) + } + + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + function_try_set(self, agent, gc, property_key, value, receiver) } fn internal_set( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, value: Value, receiver: Value, ) -> JsResult { - function_internal_set(self, agent, gc.reborrow(), property_key, value, receiver) + function_internal_set(self, agent, gc, property_key, value, receiver) } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { - function_internal_delete(self, agent, gc.reborrow(), property_key) + ) -> Option { + Some(function_internal_delete(self, agent, gc, property_key)) } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, - ) -> JsResult> { - function_internal_own_property_keys(self, agent, gc.reborrow()) + gc: NoGcScope<'_, '_>, + ) -> Option> { + Some(function_internal_own_property_keys(self, agent, gc)) } /// ### [10.4.1.1 \[\[Call\]\] ( thisArgument, argumentsList )](https://tc39.es/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist) diff --git a/nova_vm/src/ecmascript/builtins/builtin_constructor.rs b/nova_vm/src/ecmascript/builtins/builtin_constructor.rs index a95c65a0..d933f7ef 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_constructor.rs @@ -6,7 +6,8 @@ use std::ops::{Index, IndexMut}; use oxc_span::Span; -use crate::engine::context::GcScope; +use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{ @@ -190,76 +191,110 @@ impl InternalSlots for BuiltinConstructorFunction { } impl InternalMethods for BuiltinConstructorFunction { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + _gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { - function_internal_get_own_property(self, agent, property_key) + ) -> Option> { + Some(function_internal_get_own_property( + self, + agent, + property_key, + )) } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { - function_internal_define_own_property( + ) -> Option { + Some(function_internal_define_own_property( self, agent, - gc.reborrow(), + gc, property_key, property_descriptor, - ) + )) + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + function_try_has_property(self, agent, gc, property_key) } fn internal_has_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, ) -> JsResult { - function_internal_has_property(self, agent, gc.reborrow(), property_key) + function_internal_has_property(self, agent, gc, property_key) + } + + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + function_try_get(self, agent, gc, property_key, receiver) } fn internal_get( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, receiver: Value, ) -> JsResult { - function_internal_get(self, agent, gc.reborrow(), property_key, receiver) + function_internal_get(self, agent, gc, property_key, receiver) + } + + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + function_try_set(self, agent, gc, property_key, value, receiver) } fn internal_set( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, value: Value, receiver: Value, ) -> JsResult { - function_internal_set(self, agent, gc.reborrow(), property_key, value, receiver) + function_internal_set(self, agent, gc, property_key, value, receiver) } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { - function_internal_delete(self, agent, gc.reborrow(), property_key) + ) -> Option { + Some(function_internal_delete(self, agent, gc, property_key)) } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, - ) -> JsResult> { - function_internal_own_property_keys(self, agent, gc.reborrow()) + gc: NoGcScope<'_, '_>, + ) -> Option> { + Some(function_internal_own_property_keys(self, agent, gc)) } /// ### [10.3.1 \[\[Call\]\] ( thisArgument, argumentsList )](https://tc39.es/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist) diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 0f0a8e9a..649a01dd 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -4,6 +4,7 @@ use std::ops::{Deref, Index, IndexMut}; +use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ @@ -232,23 +233,42 @@ impl InternalSlots for BuiltinFunction { } impl InternalMethods for BuiltinFunction { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + _gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { - function_internal_get_own_property(self, agent, property_key) + ) -> Option> { + Some(function_internal_get_own_property( + self, + agent, + property_key, + )) } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { - function_internal_define_own_property(self, agent, gc, property_key, property_descriptor) + ) -> Option { + Some(function_internal_define_own_property( + self, + agent, + gc, + property_key, + property_descriptor, + )) + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + function_try_has_property(self, agent, gc, property_key) } fn internal_has_property( @@ -260,6 +280,16 @@ impl InternalMethods for BuiltinFunction { function_internal_has_property(self, agent, gc, property_key) } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + function_try_get(self, agent, gc, property_key, receiver) + } + fn internal_get( self, agent: &mut Agent, @@ -270,6 +300,17 @@ impl InternalMethods for BuiltinFunction { function_internal_get(self, agent, gc, property_key, receiver) } + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + function_try_set(self, agent, gc, property_key, value, receiver) + } + fn internal_set( self, agent: &mut Agent, @@ -281,21 +322,21 @@ impl InternalMethods for BuiltinFunction { function_internal_set(self, agent, gc, property_key, value, receiver) } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { - function_internal_delete(self, agent, gc, property_key) + ) -> Option { + Some(function_internal_delete(self, agent, gc, property_key)) } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - gc: GcScope<'_, '_>, - ) -> JsResult> { - function_internal_own_property_keys(self, agent, gc) + gc: NoGcScope<'_, '_>, + ) -> Option> { + Some(function_internal_own_property_keys(self, agent, gc)) } /// ### [10.3.1 \[\[Call\]\] ( thisArgument, argumentsList )](https://tc39.es/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist) diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs index 9f11fff6..e1ae9108 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_resolving_functions.rs @@ -4,7 +4,8 @@ use std::ops::{Index, IndexMut}; -use crate::engine::context::GcScope; +use crate::ecmascript::types::{function_try_get, function_try_has_property, function_try_set}; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builtins::{control_abstraction_objects::promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, ArgumentsList}, @@ -117,23 +118,42 @@ impl InternalSlots for BuiltinPromiseResolvingFunction { } impl InternalMethods for BuiltinPromiseResolvingFunction { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - _: GcScope<'_, '_>, + _gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { - function_internal_get_own_property(self, agent, property_key) + ) -> Option> { + Some(function_internal_get_own_property( + self, + agent, + property_key, + )) } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { - function_internal_define_own_property(self, agent, gc, property_key, property_descriptor) + ) -> Option { + Some(function_internal_define_own_property( + self, + agent, + gc, + property_key, + property_descriptor, + )) + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + function_try_has_property(self, agent, gc, property_key) } fn internal_has_property( @@ -145,6 +165,16 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { function_internal_has_property(self, agent, gc, property_key) } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + function_try_get(self, agent, gc, property_key, receiver) + } + fn internal_get( self, agent: &mut Agent, @@ -155,6 +185,17 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { function_internal_get(self, agent, gc, property_key, receiver) } + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + function_try_set(self, agent, gc, property_key, value, receiver) + } + fn internal_set( self, agent: &mut Agent, @@ -166,21 +207,21 @@ impl InternalMethods for BuiltinPromiseResolvingFunction { function_internal_set(self, agent, gc, property_key, value, receiver) } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { - function_internal_delete(self, agent, gc, property_key) + ) -> Option { + Some(function_internal_delete(self, agent, gc, property_key)) } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - gc: GcScope<'_, '_>, - ) -> JsResult> { - function_internal_own_property_keys(self, agent, gc) + gc: NoGcScope<'_, '_>, + ) -> Option> { + Some(function_internal_own_property_keys(self, agent, gc)) } fn internal_call( diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 3e901e82..aff3f0cc 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -11,7 +11,10 @@ use oxc_ast::ast::{FormalParameters, FunctionBody}; use oxc_ecmascript::IsSimpleParameterList; use oxc_span::Span; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::{ + ecmascript::types::{function_try_get, function_try_has_property, function_try_set}, + engine::context::{GcScope, NoGcScope}, +}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_object, @@ -341,23 +344,42 @@ impl FunctionInternalProperties for ECMAScriptFunction { } impl InternalMethods for ECMAScriptFunction { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + _gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { - function_internal_get_own_property(self, agent, property_key) + ) -> Option> { + Some(function_internal_get_own_property( + self, + agent, + property_key, + )) } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { - function_internal_define_own_property(self, agent, gc, property_key, property_descriptor) + ) -> Option { + Some(function_internal_define_own_property( + self, + agent, + gc, + property_key, + property_descriptor, + )) + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + function_try_has_property(self, agent, gc, property_key) } fn internal_has_property( @@ -369,6 +391,16 @@ impl InternalMethods for ECMAScriptFunction { function_internal_has_property(self, agent, gc, property_key) } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + function_try_get(self, agent, gc, property_key, receiver) + } + fn internal_get( self, agent: &mut Agent, @@ -379,6 +411,17 @@ impl InternalMethods for ECMAScriptFunction { function_internal_get(self, agent, gc, property_key, receiver) } + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + function_try_set(self, agent, gc, property_key, value, receiver) + } + fn internal_set( self, agent: &mut Agent, @@ -390,21 +433,21 @@ impl InternalMethods for ECMAScriptFunction { function_internal_set(self, agent, gc, property_key, value, receiver) } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { - function_internal_delete(self, agent, gc, property_key) + ) -> Option { + Some(function_internal_delete(self, agent, gc, property_key)) } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - gc: GcScope<'_, '_>, - ) -> JsResult> { - function_internal_own_property_keys(self, agent, gc) + gc: NoGcScope<'_, '_>, + ) -> Option> { + Some(function_internal_own_property_keys(self, agent, gc)) } /// ### [10.2.1 \[\[Call\]\] ( thisArgument, argumentsList )](https://tc39.es/ecma262/#sec-call) @@ -885,7 +928,7 @@ pub(crate) fn make_constructor( agent: &mut Agent, function: impl IntoFunction + InternalMethods, writable_prototype: Option, - prototype: Option, + prototype: Option, ) { // 4. If writablePrototype is not present, set writablePrototype to true. let writable_prototype = writable_prototype.unwrap_or(true); @@ -914,11 +957,15 @@ pub(crate) fn make_constructor( // 5. If prototype is not present, then let prototype = prototype.unwrap_or_else(|| { // a. Set prototype to OrdinaryObjectCreate(%Object.prototype%). - let prototype = - ordinary_object_create_with_intrinsics(agent, Some(ProtoIntrinsics::Object), None); + let prototype = OrdinaryObject::try_from(ordinary_object_create_with_intrinsics( + agent, + Some(ProtoIntrinsics::Object), + None, + )) + .unwrap(); // b. Perform ! DefinePropertyOrThrow(prototype, "constructor", PropertyDescriptor { [[Value]]: F, [[Writable]]: writablePrototype, [[Enumerable]]: false, [[Configurable]]: true }). let key = PropertyKey::from(BUILTIN_STRING_MEMORY.constructor); - prototype.property_storage().set( + prototype.into_object().property_storage().set( agent, key, PropertyDescriptor { @@ -958,9 +1005,10 @@ pub(crate) fn make_constructor( /// method. #[inline] pub(crate) fn make_method(agent: &mut Agent, f: ECMAScriptFunction, home_object: Object) { - // 1. Set F.[[HomeObject]] to homeObject. + // 1. Assert: homeObject is an ordinary object. + // 2. Set F.[[HomeObject]] to homeObject. agent[f].ecmascript_function.home_object = Some(home_object); - // 2. Return unused. + // 3. Return unused. } /// ### [10.2.9 SetFunctionName ( F, name \[ , prefix \] )](https://tc39.es/ecma262/#sec-setfunctionname) diff --git a/nova_vm/src/ecmascript/builtins/embedder_object.rs b/nova_vm/src/ecmascript/builtins/embedder_object.rs index 146b33b9..f81dfbb6 100644 --- a/nova_vm/src/ecmascript/builtins/embedder_object.rs +++ b/nova_vm/src/ecmascript/builtins/embedder_object.rs @@ -4,13 +4,11 @@ use std::ops::{Index, IndexMut}; -use crate::engine::context::GcScope; use crate::{ ecmascript::{ - execution::{Agent, JsResult}, + execution::Agent, types::{ - InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, - PropertyDescriptor, PropertyKey, Value, + InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, heap::{ @@ -103,103 +101,7 @@ impl InternalSlots for EmbedderObject { } } -impl InternalMethods for EmbedderObject { - fn internal_get_prototype_of( - self, - agent: &mut Agent, - _gc: GcScope<'_, '_>, - ) -> JsResult> { - Ok(self.internal_prototype(agent)) - } - - fn internal_set_prototype_of( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _prototype: Option, - ) -> JsResult { - todo!(); - } - - fn internal_is_extensible(self, agent: &mut Agent, _gc: GcScope<'_, '_>) -> JsResult { - Ok(self.internal_extensible(agent)) - } - - fn internal_prevent_extensions( - self, - agent: &mut Agent, - _gc: GcScope<'_, '_>, - ) -> JsResult { - self.internal_set_extensible(agent, false); - Ok(true) - } - - fn internal_get_own_property( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - ) -> JsResult> { - todo!(); - } - - fn internal_define_own_property( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - _property_descriptor: PropertyDescriptor, - ) -> JsResult { - todo!(); - } - - fn internal_has_property( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - ) -> JsResult { - todo!(); - } - - fn internal_get( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - _receiver: Value, - ) -> JsResult { - todo!(); - } - - fn internal_set( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - _value: Value, - _receiver: Value, - ) -> JsResult { - todo!(); - } - - fn internal_delete( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - ) -> JsResult { - todo!(); - } - - fn internal_own_property_keys( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - ) -> JsResult> { - todo!(); - } -} +impl InternalMethods for EmbedderObject {} impl Index for Agent { type Output = EmbedderObjectHeapData; diff --git a/nova_vm/src/ecmascript/builtins/error.rs b/nova_vm/src/ecmascript/builtins/error.rs index 9d664f2a..3d97ba68 100644 --- a/nova_vm/src/ecmascript/builtins/error.rs +++ b/nova_vm/src/ecmascript/builtins/error.rs @@ -8,13 +8,13 @@ use std::ops::{Index, IndexMut}; pub(crate) use data::ErrorHeapData; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{agent::ExceptionType, Agent, JsResult, ProtoIntrinsics}, types::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, ObjectHeapData, - OrdinaryObject, PropertyDescriptor, PropertyKey, Value, BUILTIN_STRING_MEMORY, + OrdinaryObject, PropertyDescriptor, PropertyKey, String, Value, BUILTIN_STRING_MEMORY, }, }, heap::{ @@ -98,19 +98,19 @@ impl InternalSlots for Error { fn create_backing_object(self, agent: &mut Agent) -> OrdinaryObject { let prototype = self.internal_prototype(agent).unwrap(); let message_entry = agent[self].message.map(|message| ObjectEntry { - key: PropertyKey::from(BUILTIN_STRING_MEMORY.length), + key: PropertyKey::from(BUILTIN_STRING_MEMORY.message), value: ObjectEntryPropertyDescriptor::Data { value: message.into_value(), - writable: false, + writable: true, enumerable: false, configurable: true, }, }); let cause_entry = agent[self].cause.map(|cause| ObjectEntry { - key: PropertyKey::from(BUILTIN_STRING_MEMORY.name), + key: PropertyKey::from(BUILTIN_STRING_MEMORY.cause), value: ObjectEntryPropertyDescriptor::Data { value: cause, - writable: false, + writable: true, enumerable: false, configurable: true, }, @@ -163,16 +163,14 @@ impl InternalSlots for Error { } impl InternalMethods for Error { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { + ) -> Option> { match self.get_backing_object(agent) { - Some(backing_object) => { - backing_object.internal_get_own_property(agent, gc, property_key) - } + Some(backing_object) => backing_object.try_get_own_property(agent, gc, property_key), None => { let property_value = if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) { @@ -182,7 +180,7 @@ impl InternalMethods for Error { } else { None }; - Ok(property_value.map(|value| PropertyDescriptor { + Some(property_value.map(|value| PropertyDescriptor { value: Some(value), writable: Some(true), get: None, @@ -194,6 +192,26 @@ impl InternalMethods for Error { } } + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + match self.get_backing_object(agent) { + Some(backing_object) => backing_object.try_has_property(agent, gc, property_key), + None => Some( + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) { + agent[self].message.is_some() + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { + agent[self].cause.is_some() + } else { + false + }, + ), + } + } + fn internal_has_property( self, agent: &mut Agent, @@ -214,10 +232,40 @@ impl InternalMethods for Error { } } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + match self.get_backing_object(agent) { + Some(backing_object) => backing_object.try_get(agent, gc, property_key, receiver), + None => { + let property_value = + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) { + agent[self].message.map(|message| message.into_value()) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { + agent[self].cause + } else { + None + }; + if let Some(property_value) = property_value { + Some(property_value) + } else if let Some(parent) = self.try_get_prototype_of(agent, gc).unwrap() { + // c. Return ? parent.[[Get]](P, Receiver). + parent.try_get(agent, gc, property_key, receiver) + } else { + Some(Value::Undefined) + } + } + } + } + fn internal_get( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -234,7 +282,9 @@ impl InternalMethods for Error { }; if let Some(property_value) = property_value { Ok(property_value) - } else if let Some(parent) = self.internal_get_prototype_of(agent, gc.reborrow())? { + } else if let Some(parent) = self.try_get_prototype_of(agent, gc.nogc()).unwrap() { + // Note: Error is never a prototype so [[GetPrototypeOf]] + // cannot call user code. // c. Return ? parent.[[Get]](P, Receiver). parent.internal_get(agent, gc, property_key, receiver) } else { @@ -244,13 +294,92 @@ impl InternalMethods for Error { } } - fn internal_own_property_keys( + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + match self.get_backing_object(agent) { + Some(backing_object) => { + backing_object.try_set(agent, gc, property_key, value, receiver) + } + None => { + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) + && value.is_string() + { + agent[self].message = Some(String::try_from(value).unwrap()); + Some(true) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { + agent[self].cause = Some(value); + Some(true) + } else { + let backing_object = self.create_backing_object(agent); + backing_object.try_set(agent, gc, property_key, value, receiver) + } + } + } + } + + fn internal_set( self, agent: &mut Agent, gc: GcScope<'_, '_>, - ) -> JsResult> { + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> JsResult { + match self.get_backing_object(agent) { + Some(backing_object) => { + backing_object.internal_set(agent, gc, property_key, value, receiver) + } + None => { + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) + && value.is_string() + { + agent[self].message = Some(String::try_from(value).unwrap()); + Ok(true) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { + agent[self].cause = Some(value); + Ok(true) + } else { + let backing_object = self.create_backing_object(agent); + backing_object.internal_set(agent, gc, property_key, value, receiver) + } + } + } + } + + fn try_delete( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + match self.get_backing_object(agent) { + Some(backing_object) => { + Some(backing_object.try_delete(agent, gc, property_key).unwrap()) + } + None => { + if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.message) { + agent[self].message = None; + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.cause) { + agent[self].cause = None; + } + Some(true) + } + } + } + + fn try_own_property_keys( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + ) -> Option> { match self.get_backing_object(agent) { - Some(backing_object) => backing_object.internal_own_property_keys(agent, gc), + Some(backing_object) => Some(backing_object.try_own_property_keys(agent, gc).unwrap()), None => { let mut property_keys = Vec::with_capacity(2); if agent[self].message.is_some() { @@ -259,7 +388,7 @@ impl InternalMethods for Error { if agent[self].cause.is_some() { property_keys.push(BUILTIN_STRING_MEMORY.cause.into()); } - Ok(property_keys) + Some(property_keys) } } } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs index 9cb3f276..e990a21c 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs @@ -681,6 +681,9 @@ impl ObjectConstructor { arguments: ArgumentsList, ) -> JsResult { let obj = to_object(agent, gc.nogc(), arguments.get(0))?; + // Note: We do not use try_get_prototype_of here as we don't need to + // protect any on-stack values from GC. We're perfectly okay with + // triggering GC here. obj.internal_get_prototype_of(agent, gc) .map(|proto| proto.map_or(Value::Null, |proto| proto.into_value())) } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs index d148a126..92813508 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_prototype.rs @@ -2,12 +2,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use crate::ecmascript::abstract_operations::operations_on_objects::is_prototype_of_loop; use crate::engine::context::GcScope; use crate::{ ecmascript::{ abstract_operations::{ operations_on_objects::{get, has_own_property, invoke}, - testing_and_comparison::same_value, type_conversion::{to_object, to_property_key}, }, builders::ordinary_object_builder::OrdinaryObjectBuilder, @@ -94,26 +94,17 @@ impl ObjectPrototype { fn is_prototype_of( agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, this_value: Value, arguments: ArgumentsList, ) -> JsResult { let v = arguments.get(0); - let Ok(mut v) = Object::try_from(v) else { + let Ok(v) = Object::try_from(v) else { return Ok(false.into()); }; let o = to_object(agent, gc.nogc(), this_value)?; - loop { - let proto = v.internal_get_prototype_of(agent, gc.reborrow())?; - if let Some(proto) = proto { - v = proto; - if same_value(agent, o, v) { - return Ok(true.into()); - } - } else { - return Ok(false.into()); - } - } + let result = is_prototype_of_loop(agent, gc, o, v)?; + Ok(result.into()) } fn property_is_enumerable( diff --git a/nova_vm/src/ecmascript/builtins/module.rs b/nova_vm/src/ecmascript/builtins/module.rs index 2de94fea..5d633483 100644 --- a/nova_vm/src/ecmascript/builtins/module.rs +++ b/nova_vm/src/ecmascript/builtins/module.rs @@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::testing_and_comparison::same_value, @@ -22,8 +22,8 @@ use crate::{ use self::data::ModuleHeapData; use super::ordinary::{ - ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_has_property, - ordinary_own_property_keys, set_immutable_prototype, + ordinary_delete, ordinary_get, ordinary_own_property_keys, ordinary_try_define_own_property, + ordinary_try_get, ordinary_try_has_property, }; pub mod data; @@ -140,36 +140,72 @@ impl InternalSlots for Module { impl InternalMethods for Module { /// ### [10.4.6.1 \[\[GetPrototypeOf\]\] ( )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getprototypeof) - fn internal_get_prototype_of( - self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - ) -> JsResult> { - Ok(None) + fn try_get_prototype_of(self, _: &mut Agent, _: NoGcScope<'_, '_>) -> Option> { + Some(None) } /// ### [10.4.6.2 \[\[SetPrototypeOf\]\] ( V )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-setprototypeof-v) - fn internal_set_prototype_of( + fn try_set_prototype_of( self, - agent: &mut Agent, - mut gc: GcScope<'_, '_>, + _: &mut Agent, + _: NoGcScope<'_, '_>, prototype: Option, - ) -> JsResult { - set_immutable_prototype(agent, gc.reborrow(), self.into_object(), prototype) + ) -> Option { + // This is what it all comes down to in the end. + Some(prototype.is_none()) } /// ### [10.4.6.3 \[\[IsExtensible\]\] ( )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-isextensible) - fn internal_is_extensible(self, _agent: &mut Agent, _gc: GcScope<'_, '_>) -> JsResult { - Ok(false) + fn try_is_extensible(self, _: &mut Agent, _: NoGcScope<'_, '_>) -> Option { + Some(false) } /// ### [10.4.6.4 \[\[PreventExtensions\]\] ( )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-preventextensions) - fn internal_prevent_extensions( + fn try_prevent_extensions(self, _: &mut Agent, _: NoGcScope<'_, '_>) -> Option { + Some(true) + } + + fn try_get_own_property( self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - ) -> JsResult { - Ok(true) + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option> { + match property_key { + PropertyKey::Symbol(_) => { + // 1. If P is a Symbol, return OrdinaryGetOwnProperty(O, P). + Some( + self.get_backing_object(agent) + .and_then(|object| ordinary_get_own_property(agent, object, property_key)), + ) + } + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { + // 2. Let exports be O.[[Exports]]. + let exports: &[String] = &agent[self].exports; + let key = match property_key { + PropertyKey::SmallString(data) => String::SmallString(data), + PropertyKey::String(data) => String::String(data), + PropertyKey::Integer(_) | PropertyKey::Symbol(_) => unreachable!(), + }; + let exports_contains_p = exports.contains(&key); + // 3. If exports does not contain P, return undefined. + if !exports_contains_p { + Some(None) + } else { + // 4. Let value be ? O.[[Get]](P, O). + let value = self.try_get(agent, gc, property_key, self.into_value())?; + // 5. Return PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }. + Some(Some(PropertyDescriptor { + value: Some(value), + writable: Some(true), + get: None, + set: None, + enumerable: Some(true), + configurable: Some(false), + })) + } + } + } } /// 10.4.6.5 \[\[GetOwnProperty\]\] ( P ) @@ -179,23 +215,22 @@ impl InternalMethods for Module { gc: GcScope<'_, '_>, property_key: PropertyKey, ) -> JsResult> { + if let Some(result) = self.try_get_own_property(agent, gc.nogc(), property_key) { + return Ok(result); + } match property_key { PropertyKey::Symbol(_) => { - // 1. If P is a Symbol, return OrdinaryGetOwnProperty(O, P). - Ok(self.get_backing_object(agent).and_then(|object| { - ordinary_get_own_property(agent, object.into_object(), property_key) - })) + // This would've returned Some from try branch. + unreachable!(); } - // TODO: Check this but it should not be possible to export any - // integer-valued names. - PropertyKey::Integer(_) => Ok(None), - PropertyKey::SmallString(_) | PropertyKey::String(_) => { + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { // 2. Let exports be O.[[Exports]]. let exports: &[String] = &agent[self].exports; let key = match property_key { PropertyKey::SmallString(data) => String::SmallString(data), PropertyKey::String(data) => String::String(data), - PropertyKey::Integer(_) | PropertyKey::Symbol(_) => unreachable!(), + PropertyKey::Integer(_) => todo!(), + PropertyKey::Symbol(_) => unreachable!(), }; let exports_contains_p = exports.contains(&key); // 3. If exports does not contain P, return undefined. @@ -218,11 +253,66 @@ impl InternalMethods for Module { } } + /// ### [10.4.6.6 \[\[DefineOwnProperty\]\] ( P, Desc )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc) + fn try_define_own_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + property_descriptor: PropertyDescriptor, + ) -> Option { + match property_key { + PropertyKey::Symbol(_) => { + // 1. If P is a Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc). + Some(self.get_backing_object(agent).map_or(false, |object| { + ordinary_try_define_own_property( + agent, + gc, + object, + property_key, + property_descriptor, + ) + })) + } + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { + // 2. Let current be ? O.[[GetOwnProperty]](P). + let current = self.try_get_own_property(agent, gc, property_key)?; + // 3. If current is undefined, return false. + let Some(current) = current else { + return Some(false); + }; + // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false. + if property_descriptor.configurable == Some(true) { + return Some(false); + } + // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false. + if property_descriptor.enumerable == Some(false) { + return Some(false); + } + // 6. If IsAccessorDescriptor(Desc) is true, return false. + if property_descriptor.is_accessor_descriptor() { + return Some(false); + } + // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false. + if property_descriptor.writable == Some(false) { + return Some(false); + } + // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]). + if let Some(value) = property_descriptor.value { + Some(same_value(agent, value, current.value.unwrap())) + } else { + // 9. Return true. + Some(true) + } + } + } + } + /// ### [10.4.6.6 \[\[DefineOwnProperty\]\] ( P, Desc )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc) fn internal_define_own_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, ) -> JsResult { @@ -230,20 +320,16 @@ impl InternalMethods for Module { PropertyKey::Symbol(_) => { // 1. If P is a Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc). Ok(self.get_backing_object(agent).map_or(false, |object| { - ordinary_define_own_property( + ordinary_try_define_own_property( agent, - gc.reborrow(), - object.into_object(), + gc.into_nogc(), + object, property_key, property_descriptor, ) - .unwrap() })) } - // TODO: Check this but it should not be possible to export any - // integer-valued names. - PropertyKey::Integer(_) => Ok(false), - PropertyKey::SmallString(_) | PropertyKey::String(_) => { + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { // 2. Let current be ? O.[[GetOwnProperty]](P). let current = self.internal_get_own_property(agent, gc, property_key)?; // 3. If current is undefined, return false. @@ -278,40 +364,113 @@ impl InternalMethods for Module { } /// ### [10.4.6.7 \[\[HasProperty\]\] ( P )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p) - fn internal_has_property( + fn try_has_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { + ) -> Option { match property_key { - PropertyKey::Integer(_) => Ok(false), - PropertyKey::SmallString(_) | PropertyKey::String(_) => { + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { let p = match property_key { PropertyKey::String(data) => String::String(data), PropertyKey::SmallString(data) => String::SmallString(data), + PropertyKey::Integer(_data) => todo!(), _ => unreachable!(), }; // 2. Let exports be O.[[Exports]]. let exports: &[String] = &agent[self].exports; // 3. If exports contains P, return true. if exports.contains(&p) { - Ok(true) + Some(true) } else { // 4. Return false. - Ok(false) + Some(false) } } PropertyKey::Symbol(_) => { // 1. If P is a Symbol, return ! OrdinaryHasProperty(O, P). - Ok(self.get_backing_object(agent).map_or(false, |object| { - ordinary_has_property(agent, gc.reborrow(), object.into_object(), property_key) - .unwrap() + Some(self.get_backing_object(agent).map_or(false, |object| { + ordinary_try_has_property(agent, gc, object, property_key).unwrap() })) } } } + /// ### [10.4.6.8 \[\[Get\]\] ( P, Receiver )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver) + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + // NOTE: ResolveExport is side-effect free. Each time this operation + // is called with a specific exportName, resolveSet pair as arguments + // it must return the same result. An implementation might choose to + // pre-compute or cache the ResolveExport results for the [[Exports]] + // of each module namespace exotic object. + + match property_key { + // 1. If P is a Symbol, then + PropertyKey::Symbol(_) => { + // a. Return ! OrdinaryGet(O, P, Receiver). + Some( + self.get_backing_object(agent) + .map_or(Value::Undefined, |object| { + ordinary_try_get(agent, gc, object, property_key, receiver).unwrap() + }), + ) + } + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { + // 2. Let exports be O.[[Exports]]. + let exports: &[String] = &agent[self].exports; + let key = match property_key { + PropertyKey::SmallString(data) => String::SmallString(data), + PropertyKey::String(data) => String::String(data), + PropertyKey::Integer(_) => todo!(), + PropertyKey::Symbol(_) => unreachable!(), + }; + let exports_contains_p = exports.contains(&key); + // 3. If exports does not contain P, return undefined. + if !exports_contains_p { + Some(Value::Undefined) + } else { + // 4. Let m be O.[[Module]]. + let m = &agent[self].module; + // 5. Let binding be m.ResolveExport(P). + let binding = m.resolve_export(property_key); + // 6. Assert: binding is a ResolvedBinding Record. + let Some(data::ResolveExportResult::Resolved(binding)) = binding else { + unreachable!(); + }; + // 7. Let targetModule be binding.[[Module]]. + // 8. Assert: targetModule is not undefined. + let target_module = binding.module.unwrap(); + // 9. If binding.[[BindingName]] is NAMESPACE, then + let _binding_name = match binding.binding_name { + data::ResolvedBindingName::Namespace => { + // a. Return GetModuleNamespace(targetModule). + todo!(); + } + data::ResolvedBindingName::String(data) => String::String(data), + data::ResolvedBindingName::SmallString(data) => String::SmallString(data), + }; + // 10. Let targetEnv be targetModule.[[Environment]]. + let target_env = agent[target_module].module.environment; + // 11. If targetEnv is EMPTY, throw a ReferenceError exception. + match target_env { + None => None, + Some(_target_env) => { + // 12. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true). + todo!() + } + } + } + } + } + } + /// ### [10.4.6.8 \[\[Get\]\] ( P, Receiver )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver) fn internal_get( self, @@ -333,24 +492,17 @@ impl InternalMethods for Module { Ok(self .get_backing_object(agent) .map_or(Value::Undefined, |object| { - ordinary_get( - agent, - gc.reborrow(), - object.into_object(), - property_key, - receiver, - ) - .unwrap() + ordinary_get(agent, gc.reborrow(), object, property_key, receiver).unwrap() })) } - PropertyKey::Integer(_) => Ok(Value::Undefined), - PropertyKey::SmallString(_) | PropertyKey::String(_) => { + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { // 2. Let exports be O.[[Exports]]. let exports: &[String] = &agent[self].exports; let key = match property_key { PropertyKey::SmallString(data) => String::SmallString(data), PropertyKey::String(data) => String::String(data), - PropertyKey::Integer(_) | PropertyKey::Symbol(_) => unreachable!(), + PropertyKey::Integer(_) => todo!(), + _ => unreachable!(), }; let exports_contains_p = exports.contains(&key); // 3. If exports does not contain P, return undefined. @@ -397,59 +549,58 @@ impl InternalMethods for Module { } /// ### [10.4.6.9 \[\[Set\]\] ( P, V, Receiver )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver) - fn internal_set( + fn try_set( self, - _agent: &mut Agent, - _gc: GcScope<'_, '_>, - _property_key: PropertyKey, - _value: Value, - _receiver: Value, - ) -> JsResult { - Ok(false) + _: &mut Agent, + _: NoGcScope<'_, '_>, + _: PropertyKey, + _: Value, + _: Value, + ) -> Option { + Some(false) } /// ### [10.4.6.10 \[\[Delete\]\] ( P )](https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-delete-p) - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { + ) -> Option { match property_key { PropertyKey::Symbol(_) => { // 1. If P is a Symbol, then // a. Return ! OrdinaryDelete(O, P). - Ok(self.get_backing_object(agent).map_or(true, |object| { - ordinary_delete(agent, gc.reborrow(), object.into_object(), property_key) - .unwrap() + Some(self.get_backing_object(agent).map_or(true, |object| { + ordinary_delete(agent, gc, object, property_key) })) } - PropertyKey::Integer(_) => Ok(false), - PropertyKey::SmallString(_) | PropertyKey::String(_) => { + PropertyKey::Integer(_) | PropertyKey::SmallString(_) | PropertyKey::String(_) => { let p = match property_key { PropertyKey::String(data) => String::String(data), PropertyKey::SmallString(data) => String::SmallString(data), + PropertyKey::Integer(_) => todo!(), _ => unreachable!(), }; // 2. Let exports be O.[[Exports]]. let exports = &agent[self].exports; // 3. If exports contains P, return false. if exports.contains(&p) { - Ok(false) + Some(false) } else { // 4. Return true. - Ok(true) + Some(true) } } } } /// ### [10.4.6.11 \[\[OwnPropertyKeys\]\] ( )])(https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-ownpropertykeys) - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, - ) -> JsResult> { + _: NoGcScope<'_, '_>, + ) -> Option> { // 1. Let exports be O.[[Exports]]. let exports = agent[self] .exports @@ -467,7 +618,7 @@ impl InternalMethods for Module { symbol_keys .iter() .for_each(|symbol_key| own_property_keys.push(*symbol_key)); - Ok(own_property_keys) + Some(own_property_keys) } } diff --git a/nova_vm/src/ecmascript/builtins/ordinary.rs b/nova_vm/src/ecmascript/builtins/ordinary.rs index e286b618..ea1663d4 100644 --- a/nova_vm/src/ecmascript/builtins/ordinary.rs +++ b/nova_vm/src/ecmascript/builtins/ordinary.rs @@ -7,7 +7,10 @@ use std::{ vec, }; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::{ + ecmascript::abstract_operations::operations_on_objects::try_create_data_property, + engine::context::{GcScope, NoGcScope}, +}; use crate::{ ecmascript::{ abstract_operations::{ @@ -41,6 +44,7 @@ use super::{ set_objects::set_iterator_objects::set_iterator::SetIteratorHeapData, }, map::data::MapHeapData, + module::Module, primitive_objects::PrimitiveObjectHeapData, promise::data::PromiseHeapData, set::data::SetHeapData, @@ -94,22 +98,40 @@ impl IndexMut for Vec> { impl InternalMethods for OrdinaryObject {} /// ### [10.1.1.1 OrdinaryGetPrototypeOf ( O )](https://tc39.es/ecma262/#sec-ordinarygetprototypeof) -pub(crate) fn ordinary_get_prototype_of(agent: &mut Agent, object: Object) -> Option { +pub(crate) fn ordinary_get_prototype_of( + agent: &mut Agent, + _: NoGcScope, + object: OrdinaryObject, +) -> Option { // 1. Return O.[[Prototype]]. object.internal_prototype(agent) } -/// Implements steps 5 through 7 of OrdinarySetPrototypeOf -/// -/// Returns false if a loop is detected, corresponding to substep 7.b.i. of the -/// abstract operation. -pub(crate) fn ordinary_set_prototype_of_check_loop( +/// ### [10.1.2.1 OrdinarySetPrototypeOf ( O, V )](https://tc39.es/ecma262/#sec-ordinarysetprototypeof) +pub(crate) fn ordinary_set_prototype_of( agent: &mut Agent, - o: Object, - v: Option, + object: Object, + prototype: Option, ) -> bool { + // 1. Let current be O.[[Prototype]]. + let current = object.internal_prototype(agent); + + // 2. If SameValue(V, current) is true, return true. + if prototype == current { + return true; + } + + // 3. Let extensible be O.[[Extensible]]. + let extensible = object.internal_extensible(agent); + + // 4. If extensible is false, return false. + if !extensible { + // 7.b.i. Return false. + return false; + } + // 5. Let p be V. - let mut p = v; + let mut p = prototype; // 6. Let done be false. let mut done = false; @@ -117,7 +139,7 @@ pub(crate) fn ordinary_set_prototype_of_check_loop( while !done { if let Some(p_inner) = p { // b. Else if SameValue(p, O) is true, then - if same_value(agent, p_inner, o) { + if p_inner == object { // i. Return false. return false; } else { @@ -139,38 +161,6 @@ pub(crate) fn ordinary_set_prototype_of_check_loop( done = true; } } - o.internal_set_prototype(agent, v); - true -} - -/// ### [10.1.2.1 OrdinarySetPrototypeOf ( O, V )](https://tc39.es/ecma262/#sec-ordinarysetprototypeof) -pub(crate) fn ordinary_set_prototype_of( - agent: &mut Agent, - object: Object, - prototype: Option, -) -> bool { - // 1. Let current be O.[[Prototype]]. - let current = object.internal_prototype(agent); - - // 2. If SameValue(V, current) is true, return true. - match (prototype, current) { - (Some(prototype), Some(current)) if same_value(agent, prototype, current) => return true, - (None, None) => return true, - _ => {} - } - - // 3. Let extensible be O.[[Extensible]]. - let extensible = object.internal_extensible(agent); - - // 4. If extensible is false, return false. - if !extensible { - // 7.b.i. Return false. - return false; - } - - if !ordinary_set_prototype_of_check_loop(agent, object, prototype) { - return false; - } // 8. Set O.[[Prototype]] to V. object.internal_set_prototype(agent, prototype); @@ -180,13 +170,13 @@ pub(crate) fn ordinary_set_prototype_of( } /// ### [10.1.3.1 OrdinaryIsExtensible ( O )](https://tc39.es/ecma262/#sec-ordinaryisextensible) -pub(crate) fn ordinary_is_extensible(agent: &mut Agent, object: Object) -> bool { +pub(crate) fn ordinary_is_extensible(agent: &mut Agent, object: OrdinaryObject) -> bool { // 1. Return O.[[Extensible]]. object.internal_extensible(agent) } /// ### [10.1.4.1 OrdinaryPreventExtensions ( O )](https://tc39.es/ecma262/#sec-ordinarypreventextensions) -pub(crate) fn ordinary_prevent_extensions(agent: &mut Agent, object: Object) -> bool { +pub(crate) fn ordinary_prevent_extensions(agent: &mut Agent, object: OrdinaryObject) -> bool { // 1. Set O.[[Extensible]] to false. object.internal_set_extensible(agent, false); @@ -197,12 +187,15 @@ pub(crate) fn ordinary_prevent_extensions(agent: &mut Agent, object: Object) -> /// ### [10.1.5.1 OrdinaryGetOwnProperty ( O, P )](https://tc39.es/ecma262/#sec-ordinarygetownproperty) pub(crate) fn ordinary_get_own_property( agent: &mut Agent, - object: Object, + object: OrdinaryObject, property_key: PropertyKey, ) -> Option { // 1. If O does not have an own property with key P, return undefined. // 3. Let X be O's own property whose key is P. - let x = object.property_storage().get(agent, property_key)?; + let x = object + .into_object() + .property_storage() + .get(agent, property_key)?; // 2. Let D be a newly created Property Descriptor with no fields. let mut descriptor = PropertyDescriptor::default(); @@ -236,16 +229,45 @@ pub(crate) fn ordinary_get_own_property( Some(descriptor) } +/// ### [10.1.6.1 OrdinaryDefineOwnProperty ( O, P, Desc )](https://tc39.es/ecma262/#sec-ordinarydefineownproperty) +pub(crate) fn ordinary_try_define_own_property( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + object: OrdinaryObject, + property_key: PropertyKey, + descriptor: PropertyDescriptor, +) -> bool { + // 1. Let current be ! O.[[GetOwnProperty]](P). + let current = object + .try_get_own_property(agent, gc, property_key) + .unwrap(); + + // 2. Let extensible be ! IsExtensible(O). + let extensible = object.internal_extensible(agent); + + // 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). + validate_and_apply_property_descriptor( + agent, + Some(object), + property_key, + extensible, + descriptor, + current, + ) +} + /// ### [10.1.6.1 OrdinaryDefineOwnProperty ( O, P, Desc )](https://tc39.es/ecma262/#sec-ordinarydefineownproperty) pub(crate) fn ordinary_define_own_property( agent: &mut Agent, - mut gc: GcScope<'_, '_>, - object: Object, + gc: NoGcScope<'_, '_>, + object: OrdinaryObject, property_key: PropertyKey, descriptor: PropertyDescriptor, -) -> JsResult { +) -> bool { // 1. Let current be ? O.[[GetOwnProperty]](P). - let current = object.internal_get_own_property(agent, gc.reborrow(), property_key)?; + let current = object + .try_get_own_property(agent, gc, property_key) + .unwrap(); // 2. Let extensible be ? IsExtensible(O). let extensible = object.internal_extensible(agent); @@ -268,7 +290,7 @@ pub(crate) fn is_compatible_property_descriptor( extensible: bool, descriptor: PropertyDescriptor, current: Option, -) -> JsResult { +) -> bool { let property_key = PropertyKey::from_str(agent, gc, ""); validate_and_apply_property_descriptor( agent, @@ -283,24 +305,24 @@ pub(crate) fn is_compatible_property_descriptor( /// ### [10.1.6.3 ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current )](https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor) fn validate_and_apply_property_descriptor( agent: &mut Agent, - object: Option, + object: Option, property_key: PropertyKey, extensible: bool, descriptor: PropertyDescriptor, current: Option, -) -> JsResult { +) -> bool { // 1. Assert: IsPropertyKey(P) is true. // 2. If current is undefined, then let Some(current) = current else { // a. If extensible is false, return false. if !extensible { - return Ok(false); + return false; } // b. If O is undefined, return true. let Some(object) = object else { - return Ok(true); + return true; }; // c. If IsAccessorDescriptor(Desc) is true, then @@ -309,7 +331,7 @@ fn validate_and_apply_property_descriptor( // [[Enumerable]], and [[Configurable]] attributes are set to the value of the // corresponding field in Desc if Desc has that field, or to the attribute's default // value otherwise. - object.property_storage().set( + object.into_object().property_storage().set( agent, property_key, PropertyDescriptor { @@ -327,7 +349,7 @@ fn validate_and_apply_property_descriptor( // [[Enumerable]], and [[Configurable]] attributes are set to the value of the // corresponding field in Desc if Desc has that field, or to the attribute's default // value otherwise. - object.property_storage().set( + object.into_object().property_storage().set( agent, property_key, PropertyDescriptor { @@ -341,7 +363,7 @@ fn validate_and_apply_property_descriptor( } // e. Return true. - return Ok(true); + return true; }; // 3. Assert: current is a fully populated Property Descriptor. @@ -349,20 +371,20 @@ fn validate_and_apply_property_descriptor( // 4. If Desc does not have any fields, return true. if !descriptor.has_fields() { - return Ok(true); + return true; } // 5. If current.[[Configurable]] is false, then if !current.configurable.unwrap() { // a. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false. if let Some(true) = descriptor.configurable { - return Ok(false); + return false; } // b. If Desc has an [[Enumerable]] field and SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) // is false, return false. if descriptor.enumerable.is_some() && descriptor.enumerable != current.enumerable { - return Ok(false); + return false; } // c. If IsGenericDescriptor(Desc) is false and SameValue(IsAccessorDescriptor(Desc), IsAccessorDescriptor(current)) @@ -370,7 +392,7 @@ fn validate_and_apply_property_descriptor( if !descriptor.is_generic_descriptor() && descriptor.is_accessor_descriptor() != current.is_accessor_descriptor() { - return Ok(false); + return false; } // d. If IsAccessorDescriptor(current) is true, then @@ -380,10 +402,10 @@ fn validate_and_apply_property_descriptor( if let Some(desc_get) = descriptor.get { if let Some(cur_get) = current.get { if desc_get != cur_get { - return Ok(false); + return false; } } else { - return Ok(false); + return false; } } @@ -392,10 +414,10 @@ fn validate_and_apply_property_descriptor( if let Some(desc_set) = descriptor.set { if let Some(cur_set) = current.set { if desc_set != cur_set { - return Ok(false); + return false; } } else { - return Ok(false); + return false; } } } @@ -403,7 +425,7 @@ fn validate_and_apply_property_descriptor( else if current.writable == Some(false) { // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is true, return false. if let Some(true) = descriptor.writable { - return Ok(false); + return false; } // ii. If Desc has a [[Value]] field and SameValue(Desc.[[Value]], current.[[Value]]) @@ -411,10 +433,10 @@ fn validate_and_apply_property_descriptor( if let Some(desc_value) = descriptor.value { if let Some(cur_value) = current.value { if !same_value(agent, desc_value, cur_value) { - return Ok(false); + return false; } } else { - return Ok(false); + return false; } } } @@ -441,7 +463,7 @@ fn validate_and_apply_property_descriptor( // enumerable, respectively, and whose [[Get]] and [[Set]] attributes are set to // the value of the corresponding field in Desc if Desc has that field, or to the // attribute's default value otherwise. - object.property_storage().set( + object.into_object().property_storage().set( agent, property_key, PropertyDescriptor { @@ -478,7 +500,7 @@ fn validate_and_apply_property_descriptor( // .enumerable = enumerable, // .configurable = configurable, // }); - object.property_storage().set( + object.into_object().property_storage().set( agent, property_key, PropertyDescriptor { @@ -494,7 +516,7 @@ fn validate_and_apply_property_descriptor( else { // i. For each field of Desc, set the corresponding attribute of the property named P // of object O to the value of the field. - object.property_storage().set( + object.into_object().property_storage().set( agent, property_key, PropertyDescriptor { @@ -510,14 +532,48 @@ fn validate_and_apply_property_descriptor( } // 7. Return true. - Ok(true) + true +} + +/// ### [10.1.7.1 OrdinaryHasProperty ( O, P )](https://tc39.es/ecma262/#sec-ordinaryhasproperty) +pub(crate) fn ordinary_try_has_property( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + object: OrdinaryObject, + property_key: PropertyKey, +) -> Option { + // 1. Let hasOwn be ? O.[[GetOwnProperty]](P). + // Note: ? means that if we'd call a Proxy's GetOwnProperty trap then we'll + // intead return None. + let has_own = object.try_get_own_property(agent, gc, property_key)?; + + // 2. If hasOwn is not undefined, return true. + if has_own.is_some() { + return Some(true); + } + + // 3. Let parent be ? O.[[GetPrototypeOf]](). + // Note: ? means that if we'd call a Proxy's GetPrototypeOf trap then we'll + // instead return None. + let parent = object.try_get_prototype_of(agent, gc)?; + + // 4. If parent is not null, then + if let Some(parent) = parent { + // a. Return ? parent.[[HasProperty]](P). + // Note: Here too, if we would call a Proxy's HasProperty trap then + // we'll instead return None. + return parent.try_has_property(agent, gc, property_key); + } + + // 5. Return false. + Some(false) } /// ### [10.1.7.1 OrdinaryHasProperty ( O, P )](https://tc39.es/ecma262/#sec-ordinaryhasproperty) pub(crate) fn ordinary_has_property( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, + object: OrdinaryObject, property_key: PropertyKey, ) -> JsResult { // 1. Let hasOwn be ? O.[[GetOwnProperty]](P). @@ -529,7 +585,16 @@ pub(crate) fn ordinary_has_property( } // 3. Let parent be ? O.[[GetPrototypeOf]](). - let parent = object.internal_get_prototype_of(agent, gc.reborrow())?; + let (parent, property_key) = if let Some(parent) = object.try_get_prototype_of(agent, gc.nogc()) + { + (parent, property_key) + } else { + // Note: We should root property_key here. + ( + object.internal_get_prototype_of(agent, gc.reborrow())?, + property_key, + ) + }; // 4. If parent is not null, then if let Some(parent) = parent { @@ -541,11 +606,70 @@ pub(crate) fn ordinary_has_property( Ok(false) } +/// ### [10.1.8.1 OrdinaryGet ( O, P, Receiver )](https://tc39.es/ecma262/#sec-ordinaryget) +pub(crate) fn ordinary_try_get( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + object: OrdinaryObject, + property_key: PropertyKey, + receiver: Value, +) -> Option { + // 1. Let desc be ? O.[[GetOwnProperty]](P). + let Some(descriptor) = object.try_get_own_property(agent, gc, property_key)? else { + // 2. If desc is undefined, then + + // a. Let parent be ? O.[[GetPrototypeOf]](). + let (parent, property_key, receiver) = + if let Some(parent) = object.try_get_prototype_of(agent, gc) { + let Some(parent) = parent else { + return Some(Value::Undefined); + }; + (parent, property_key, receiver) + } else { + // Note: We should root property_key and receiver here. + let receiver = receiver.scope(agent, gc); + let Some(parent) = object.try_get_prototype_of(agent, gc)? else { + return Some(Value::Undefined); + }; + let receiver = receiver.get(agent); + (parent, property_key, receiver) + }; + + // c. Return ? parent.[[Get]](P, Receiver). + return parent.try_get(agent, gc, property_key, receiver); + }; + + // 3. If IsDataDescriptor(desc) is true, return desc.[[Value]]. + if let Some(value) = descriptor.value { + debug_assert!(descriptor.is_data_descriptor()); + return Some(value); + } + + // 4. Assert: IsAccessorDescriptor(desc) is true. + debug_assert!(descriptor.is_accessor_descriptor()); + + // 5. Let getter be desc.[[Get]]. + // 6. If getter is undefined, return undefined. + let Some(_getter) = descriptor.get else { + return Some(Value::Undefined); + }; + + // 7. Return ? Call(getter, Receiver). + // call_function(agent, gc, getter, receiver, None) + // Note: We cannot call a function without risking GC! There are future + // options here: + // 1. Special function types that are guaranteed to trigger no GC. + // 2. Return a special value that tells which getter to call. Note that the + // receiver is statically known, so just returning the getter function + // should be enough. + None +} + /// ### [10.1.8.1 OrdinaryGet ( O, P, Receiver )](https://tc39.es/ecma262/#sec-ordinaryget) pub(crate) fn ordinary_get( agent: &mut Agent, mut gc: GcScope<'_, '_>, - object: Object, + object: OrdinaryObject, property_key: PropertyKey, receiver: Value, ) -> JsResult { @@ -555,9 +679,21 @@ pub(crate) fn ordinary_get( // 2. If desc is undefined, then // a. Let parent be ? O.[[GetPrototypeOf]](). - let Some(parent) = object.internal_get_prototype_of(agent, gc.reborrow())? else { - return Ok(Value::Undefined); - }; + let (parent, property_key, receiver) = + if let Some(parent) = object.try_get_prototype_of(agent, gc.nogc()) { + let Some(parent) = parent else { + return Ok(Value::Undefined); + }; + (parent, property_key, receiver) + } else { + // Note: We should root property_key and receiver here. + let receiver = receiver.scope(agent, gc.nogc()); + let Some(parent) = object.internal_get_prototype_of(agent, gc.reborrow())? else { + return Ok(Value::Undefined); + }; + let receiver = receiver.get(agent); + (parent, property_key, receiver) + }; // c. Return ? parent.[[Get]](P, Receiver). return parent.internal_get(agent, gc, property_key, receiver); @@ -582,6 +718,33 @@ pub(crate) fn ordinary_get( call_function(agent, gc, getter, receiver, None) } +/// ### [10.1.9.1 OrdinarySet ( O, P, V, Receiver )](https://tc39.es/ecma262/#sec-ordinaryset) +pub(crate) fn ordinary_try_set( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + object: Object, + property_key: PropertyKey, + value: Value, + receiver: Value, +) -> Option { + // 1. Let ownDesc be ! O.[[GetOwnProperty]](P). + // We're guaranteed to always get a result here. + let own_descriptor = object + .try_get_own_property(agent, gc, property_key) + .unwrap(); + + // 2. Return ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc). + ordinary_try_set_with_own_descriptor( + agent, + gc, + object, + property_key, + value, + receiver, + own_descriptor, + ) +} + /// ### [10.1.9.1 OrdinarySet ( O, P, V, Receiver )](https://tc39.es/ecma262/#sec-ordinaryset) pub(crate) fn ordinary_set( agent: &mut Agent, @@ -606,6 +769,116 @@ pub(crate) fn ordinary_set( ) } +/// ### [10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc )](https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor) +pub(crate) fn ordinary_try_set_with_own_descriptor( + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + object: Object, + property_key: PropertyKey, + value: Value, + receiver: Value, + own_descriptor: Option, +) -> Option { + let own_descriptor = if let Some(own_descriptor) = own_descriptor { + own_descriptor + } else { + // 1. If ownDesc is undefined, then + // a. Let parent be ! O.[[GetPrototypeOf]](). + let parent = object.try_get_prototype_of(agent, gc).unwrap(); + + // b. If parent is not null, then + if let Some(parent) = parent { + // i. Return ? parent.[[Set]](P, V, Receiver). + // Note: Here we do not have guarantees: Parent could be a Proxy. + return parent.try_set(agent, gc, property_key, value, receiver); + } + // c. Else, + else { + // i. Set ownDesc to the PropertyDescriptor { + // [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true + // }. + PropertyDescriptor { + value: Some(Value::Undefined), + writable: Some(true), + enumerable: Some(true), + configurable: Some(true), + ..Default::default() + } + } + }; + + // 2. If IsDataDescriptor(ownDesc) is true, then + if own_descriptor.is_data_descriptor() { + // a. If ownDesc.[[Writable]] is false, return false. + if own_descriptor.writable == Some(false) { + return Some(false); + } + + // b. If Receiver is not an Object, return false. + let Ok(receiver) = Object::try_from(receiver) else { + return Some(false); + }; + + // c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P). + // Note: Here again we do not have guarantees; the receiver could be a + // Proxy. + let existing_descriptor = receiver.try_get_own_property(agent, gc, property_key)?; + + // d. If existingDescriptor is not undefined, then + if let Some(existing_descriptor) = existing_descriptor { + // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. + if existing_descriptor.is_accessor_descriptor() { + return Some(false); + } + + // ii. If existingDescriptor.[[Writable]] is false, return false. + if existing_descriptor.writable == Some(false) { + return Some(false); + } + + // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }. + let value_descriptor = PropertyDescriptor { + value: Some(value), + ..Default::default() + }; + + // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc). + // Again: Receiver could be a Proxy. + return receiver.try_define_own_property(agent, gc, property_key, value_descriptor); + } + // e. Else, + else { + // i. Assert: Receiver does not currently have a property P. + debug_assert!(receiver + .try_get_own_property(agent, gc, property_key) + .unwrap() + .is_none()); + + // ii. Return ? CreateDataProperty(Receiver, P, V). + // Again: Receiver could be a Proxy. + return try_create_data_property(agent, gc, receiver, property_key, value); + } + } + + // 3. Assert: IsAccessorDescriptor(ownDesc) is true. + debug_assert!(own_descriptor.is_accessor_descriptor()); + + // 4. Let setter be ownDesc.[[Set]]. + // 5. If setter is undefined, return false. + let Some(_setter) = own_descriptor.set else { + return Some(false); + }; + + // 6. Perform ? Call(setter, Receiver, « V »). + // Note: We cannot call a function as it may trigger GC. See above for + // future options. + // call_function(agent, gc, setter, receiver, Some(ArgumentsList(&[value])))?; + + // 7. Return true. + // Some(true) + None +} + /// ### [10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc )](https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor) pub(crate) fn ordinary_set_with_own_descriptor( agent: &mut Agent, @@ -621,12 +894,14 @@ pub(crate) fn ordinary_set_with_own_descriptor( } else { // 1. If ownDesc is undefined, then // a. Let parent be ? O.[[GetPrototypeOf]](). - let parent = object.internal_get_prototype_of(agent, gc.reborrow())?; + // Note: OrdinaryObject never fails to get prototype. + let parent = object.try_get_prototype_of(agent, gc.nogc()).unwrap(); // b. If parent is not null, then if let Some(parent) = parent { // i. Return ? parent.[[Set]](P, V, Receiver). - return parent.internal_set(agent, gc.reborrow(), property_key, value, receiver); + // Note: Prototype might be a Proxy or contain a setter. + return parent.internal_set(agent, gc, property_key, value, receiver); } // c. Else, else { @@ -656,8 +931,19 @@ pub(crate) fn ordinary_set_with_own_descriptor( }; // c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P). - let existing_descriptor = - receiver.internal_get_own_property(agent, gc.reborrow(), property_key)?; + let existing_descriptor = if matches!( + receiver, + Object::Proxy(_) | Object::Module(_) | Object::EmbedderObject(_) + ) { + // TODO: Handle rooting. + receiver.internal_get_own_property(agent, gc.reborrow(), property_key)? + } else { + // Note: Only Proxy, Module, and Embedder objects cannot fail + // getting own property. + receiver + .try_get_own_property(agent, gc.nogc(), property_key) + .unwrap() + }; // d. If existingDescriptor is not undefined, then if let Some(existing_descriptor) = existing_descriptor { @@ -717,29 +1003,35 @@ pub(crate) fn ordinary_set_with_own_descriptor( /// ### [10.1.10.1 OrdinaryDelete ( O, P )](https://tc39.es/ecma262/#sec-ordinarydelete) pub(crate) fn ordinary_delete( agent: &mut Agent, - gc: GcScope<'_, '_>, - object: Object, + gc: NoGcScope<'_, '_>, + object: OrdinaryObject, property_key: PropertyKey, -) -> JsResult { +) -> bool { // 1. Let desc be ? O.[[GetOwnProperty]](P). - let descriptor = object.internal_get_own_property(agent, gc, property_key)?; + // We're guaranteed to always get a result here. + let descriptor = object + .try_get_own_property(agent, gc, property_key) + .unwrap(); // 2. If desc is undefined, return true. let Some(descriptor) = descriptor else { - return Ok(true); + return true; }; // 3. If desc.[[Configurable]] is true, then if let Some(true) = descriptor.configurable { // a. Remove the own property with name P from O. - object.property_storage().remove(agent, property_key); + object + .into_object() + .property_storage() + .remove(agent, property_key); // b. Return true. - return Ok(true); + return true; } // 4. Return false. - Ok(false) + false } /// ### [10.1.11.1 OrdinaryOwnPropertyKeys ( O )](https://tc39.es/ecma262/#sec-ordinaryownpropertykeys) @@ -1205,15 +1497,15 @@ pub(crate) fn get_prototype_from_constructor( #[inline] pub(crate) fn set_immutable_prototype( agent: &mut Agent, - gc: GcScope<'_, '_>, - o: Object, + gc: NoGcScope<'_, '_>, + o: Module, v: Option, -) -> JsResult { +) -> bool { // 1. Let current be ? O.[[GetPrototypeOf]](). - let current = o.internal_get_prototype_of(agent, gc)?; + let current = o.try_get_prototype_of(agent, gc).unwrap(); // 2. If SameValue(V, current) is true, return true. // 3. Return false. - Ok(same_value(agent, v, current)) + v == current } impl HeapMarkAndSweep for OrdinaryObject { diff --git a/nova_vm/src/ecmascript/builtins/primitive_objects.rs b/nova_vm/src/ecmascript/builtins/primitive_objects.rs index 770abbe6..8f069d27 100644 --- a/nova_vm/src/ecmascript/builtins/primitive_objects.rs +++ b/nova_vm/src/ecmascript/builtins/primitive_objects.rs @@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ builtins::ordinary::{ @@ -203,12 +203,12 @@ impl InternalSlots for PrimitiveObject { } impl InternalMethods for PrimitiveObject { - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + _: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { + ) -> Option> { // For non-string primitive objects: // 1. Return OrdinaryGetOwnProperty(O, P). // For string exotic objects: @@ -216,27 +216,27 @@ impl InternalMethods for PrimitiveObject { // 2. If desc is not undefined, return desc. if let Some(backing_object) = self.get_backing_object(agent) { if let Some(property_descriptor) = - ordinary_get_own_property(agent, backing_object.into_object(), property_key) + ordinary_get_own_property(agent, backing_object, property_key) { - return Ok(Some(property_descriptor)); + return Some(Some(property_descriptor)); } } if let Ok(string) = String::try_from(agent[self].data) { // 3. Return StringGetOwnProperty(S, P). - Ok(string.get_property_descriptor(agent, property_key)) + Some(string.get_property_descriptor(agent, property_key)) } else { - Ok(None) + Some(None) } } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { + ) -> Option { if let Ok(string) = String::try_from(agent[self].data) { // For string exotic objects: // 1. Let stringDesc be StringGetOwnProperty(S, P). @@ -244,22 +244,27 @@ impl InternalMethods for PrimitiveObject { if let Some(string_desc) = string.get_property_descriptor(agent, property_key) { // a. Let extensible be S.[[Extensible]]. // b. Return IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc). - return is_compatible_property_descriptor( + return Some(is_compatible_property_descriptor( agent, - gc.nogc(), + gc, self.internal_extensible(agent), property_descriptor, Some(string_desc), - ); + )); } // 3. Return ! OrdinaryDefineOwnProperty(S, P, Desc). } let backing_object = self .get_backing_object(agent) - .unwrap_or_else(|| self.create_backing_object(agent)) - .into_object(); - ordinary_define_own_property(agent, gc, backing_object, property_key, property_descriptor) + .unwrap_or_else(|| self.create_backing_object(agent)); + Some(ordinary_define_own_property( + agent, + gc, + backing_object, + property_key, + property_descriptor, + )) } fn internal_has_property( @@ -279,9 +284,7 @@ impl InternalMethods for PrimitiveObject { // 1. Return ? OrdinaryHasProperty(O, P). match self.get_backing_object(agent) { - Some(backing_object) => { - ordinary_has_property(agent, gc, backing_object.into_object(), property_key) - } + Some(backing_object) => ordinary_has_property(agent, gc, backing_object, property_key), None => { // 3. Let parent be ? O.[[GetPrototypeOf]](). let parent = self.internal_get_prototype_of(agent, gc.reborrow())?; @@ -313,13 +316,7 @@ impl InternalMethods for PrimitiveObject { // 1. Return ? OrdinaryGet(O, P, Receiver). match self.get_backing_object(agent) { - Some(backing_object) => ordinary_get( - agent, - gc, - backing_object.into_object(), - property_key, - receiver, - ), + Some(backing_object) => ordinary_get(agent, gc, backing_object, property_key, receiver), None => { // a. Let parent be ? O.[[GetPrototypeOf]](). let Some(parent) = self.internal_get_prototype_of(agent, gc.reborrow())? else { @@ -351,37 +348,46 @@ impl InternalMethods for PrimitiveObject { } // 1. Return ? OrdinarySet(O, P, V, Receiver). - let backing_object = self - .get_backing_object(agent) - .unwrap_or_else(|| self.create_backing_object(agent)) - .into_object(); - ordinary_set(agent, gc, backing_object, property_key, value, receiver) + ordinary_set(agent, gc, self.into_object(), property_key, value, receiver) } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { + ) -> Option { if let Ok(string) = String::try_from(agent[self].data) { - if string - .get_property_descriptor(agent, property_key) - .is_some() - { - return Ok(false); + // A String will return unconfigurable descriptors for length and + // all valid string indexes, making delete return false. + if property_key == BUILTIN_STRING_MEMORY.length.into() { + return Some(false); + } else if let PropertyKey::Integer(index) = property_key { + let index = index.into_i64(); + if index >= 0 && (index as usize) < string.utf16_len(agent) { + return Some(false); + } } } - // 1. Return ? OrdinaryDelete(O, P). + // 1. Return ! OrdinaryDelete(O, P). match self.get_backing_object(agent) { - Some(backing_object) => { - ordinary_delete(agent, gc, backing_object.into_object(), property_key) - } - None => Ok(true), + Some(backing_object) => Some(ordinary_delete(agent, gc, backing_object, property_key)), + None => Some(true), } } + fn internal_delete( + self, + agent: &mut Agent, + gc: GcScope<'_, '_>, + property_key: PropertyKey, + ) -> JsResult { + Ok(self + .try_delete(agent, gc.into_nogc(), property_key) + .unwrap()) + } + fn internal_own_property_keys( self, agent: &mut Agent, diff --git a/nova_vm/src/ecmascript/builtins/proxy.rs b/nova_vm/src/ecmascript/builtins/proxy.rs index a192dcf6..e119db66 100644 --- a/nova_vm/src/ecmascript/builtins/proxy.rs +++ b/nova_vm/src/ecmascript/builtins/proxy.rs @@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut}; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{Agent, JsResult}, @@ -105,6 +105,14 @@ impl InternalSlots for Proxy { } impl InternalMethods for Proxy { + fn try_get_prototype_of( + self, + agent: &mut Agent, + _gc: NoGcScope<'_, '_>, + ) -> Option> { + Some(self.internal_prototype(agent)) + } + fn internal_get_prototype_of( self, agent: &mut Agent, diff --git a/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs b/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs index d7bf6949..81d561ee 100644 --- a/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs +++ b/nova_vm/src/ecmascript/builtins/reflection/reflect_object.rs @@ -339,7 +339,7 @@ impl ReflectObject { // 1. If target is not an Object, throw a TypeError exception. if !arguments.get(0).is_object() { return Err(agent.throw_exception_with_static_message( - gc.nogc(), + gc.into_nogc(), ExceptionType::TypeError, "Value is not an object", )); diff --git a/nova_vm/src/ecmascript/execution/environments/function_environment.rs b/nova_vm/src/ecmascript/execution/environments/function_environment.rs index cfa23c8b..a97439c2 100644 --- a/nova_vm/src/ecmascript/execution/environments/function_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/function_environment.rs @@ -5,7 +5,8 @@ use super::{ DeclarativeEnvironment, DeclarativeEnvironmentIndex, EnvironmentIndex, FunctionEnvironmentIndex, }; -use crate::engine::context::{GcScope, NoGcScope}; +use crate::ecmascript::types::OrdinaryObject; +use crate::engine::context::NoGcScope; use crate::{ ecmascript::{ builtins::{ECMAScriptFunction, ThisMode}, @@ -442,30 +443,30 @@ impl FunctionEnvironmentIndex { /// /// The GetSuperBase concrete method of a Function Environment Record /// envRec takes no arguments and returns either a normal completion - /// containing either an Object, null, or undefined, or a throw completion. - pub(crate) fn get_super_base(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { + /// containing either an Object, null, or undefined. + pub(crate) fn get_super_base( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + ) -> Option> { let env_rec: &FunctionEnvironment = &agent[self]; // 1. Let home be envRec.[[FunctionObject]].[[HomeObject]]. let home = match env_rec.function_object { Function::BoundFunction(_) => todo!(), Function::BuiltinFunction(_) => unreachable!(), - Function::ECMAScriptFunction(func) => agent[func].ecmascript_function.home_object, + // 2. If home is undefined, return undefined. + Function::ECMAScriptFunction(func) => agent[func].ecmascript_function.home_object?, Function::BuiltinGeneratorFunction => todo!(), Function::BuiltinConstructorFunction(_) => unreachable!(), Function::BuiltinPromiseResolvingFunction(_) => unreachable!(), Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), }; - // 2. If home is undefined, return undefined. - let Some(home) = home else { - return Ok(Value::Undefined); - }; - // 3. Assert: home is an Object. - // Type guarantees Objectness. - // 4. Return ? home.[[GetPrototypeOf]](). - home.internal_get_prototype_of(agent, gc) - .map(|proto| proto.map_or_else(|| Value::Null, |proto| proto.into_value())) + // 3. Assert: home is an ordinary object. + let home = OrdinaryObject::try_from(home).unwrap(); + // 4. Return ! home.[[GetPrototypeOf]](). + Some(home.try_get_prototype_of(agent, gc).unwrap()) } } diff --git a/nova_vm/src/ecmascript/scripts_and_modules/script.rs b/nova_vm/src/ecmascript/scripts_and_modules/script.rs index cd6ab19c..2855fa7a 100644 --- a/nova_vm/src/ecmascript/scripts_and_modules/script.rs +++ b/nova_vm/src/ecmascript/scripts_and_modules/script.rs @@ -1571,7 +1571,9 @@ mod test { }; let instance = Object::try_from(result).unwrap(); assert_eq!( - instance.internal_get_prototype_of(&mut agent, gc).unwrap(), + instance + .try_get_prototype_of(&mut agent, gc.nogc()) + .unwrap(), Some(foo_prototype) ); } diff --git a/nova_vm/src/ecmascript/types/language.rs b/nova_vm/src/ecmascript/types/language.rs index aa9ef3f9..20f723bb 100644 --- a/nova_vm/src/ecmascript/types/language.rs +++ b/nova_vm/src/ecmascript/types/language.rs @@ -20,8 +20,9 @@ pub(crate) use function::{ function_create_backing_object, function_internal_define_own_property, function_internal_delete, function_internal_get, function_internal_get_own_property, function_internal_has_property, function_internal_own_property_keys, function_internal_set, - BoundFunctionHeapData, BuiltinConstructorHeapData, BuiltinFunctionHeapData, - ECMAScriptFunctionHeapData, FunctionInternalProperties, + function_try_get, function_try_has_property, function_try_set, BoundFunctionHeapData, + BuiltinConstructorHeapData, BuiltinFunctionHeapData, ECMAScriptFunctionHeapData, + FunctionInternalProperties, }; pub use function::{Function, IntoFunction}; pub use into_numeric::IntoNumeric; diff --git a/nova_vm/src/ecmascript/types/language/function.rs b/nova_vm/src/ecmascript/types/language/function.rs index cc737454..eeac30c7 100644 --- a/nova_vm/src/ecmascript/types/language/function.rs +++ b/nova_vm/src/ecmascript/types/language/function.rs @@ -31,7 +31,7 @@ pub(crate) use into_function::{ function_create_backing_object, function_internal_define_own_property, function_internal_delete, function_internal_get, function_internal_get_own_property, function_internal_has_property, function_internal_own_property_keys, function_internal_set, - FunctionInternalProperties, + function_try_get, function_try_has_property, function_try_set, FunctionInternalProperties, }; /// https://tc39.es/ecma262/#function-object @@ -264,118 +264,134 @@ impl InternalSlots for Function { } impl InternalMethods for Function { - fn internal_get_prototype_of( + fn try_get_prototype_of( self, agent: &mut Agent, - gc: GcScope<'_, '_>, - ) -> JsResult> { + gc: NoGcScope<'_, '_>, + ) -> Option> { match self { - Function::BoundFunction(x) => x.internal_get_prototype_of(agent, gc), - Function::BuiltinFunction(x) => x.internal_get_prototype_of(agent, gc), - Function::ECMAScriptFunction(x) => x.internal_get_prototype_of(agent, gc), + Function::BoundFunction(x) => x.try_get_prototype_of(agent, gc), + Function::BuiltinFunction(x) => x.try_get_prototype_of(agent, gc), + Function::ECMAScriptFunction(x) => x.try_get_prototype_of(agent, gc), Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(x) => x.internal_get_prototype_of(agent, gc), - Function::BuiltinPromiseResolvingFunction(x) => x.internal_get_prototype_of(agent, gc), + Function::BuiltinConstructorFunction(x) => x.try_get_prototype_of(agent, gc), + Function::BuiltinPromiseResolvingFunction(x) => x.try_get_prototype_of(agent, gc), Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } } - fn internal_set_prototype_of( + fn try_set_prototype_of( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, prototype: Option, - ) -> JsResult { + ) -> Option { match self { - Function::BoundFunction(x) => x.internal_set_prototype_of(agent, gc, prototype), - Function::BuiltinFunction(x) => x.internal_set_prototype_of(agent, gc, prototype), - Function::ECMAScriptFunction(x) => x.internal_set_prototype_of(agent, gc, prototype), + Function::BoundFunction(x) => x.try_set_prototype_of(agent, gc, prototype), + Function::BuiltinFunction(x) => x.try_set_prototype_of(agent, gc, prototype), + Function::ECMAScriptFunction(x) => x.try_set_prototype_of(agent, gc, prototype), Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(x) => { - x.internal_set_prototype_of(agent, gc, prototype) - } + Function::BuiltinConstructorFunction(x) => x.try_set_prototype_of(agent, gc, prototype), Function::BuiltinPromiseResolvingFunction(x) => { - x.internal_set_prototype_of(agent, gc, prototype) + x.try_set_prototype_of(agent, gc, prototype) } Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } } - fn internal_is_extensible(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { + fn try_is_extensible(self, agent: &mut Agent, gc: NoGcScope<'_, '_>) -> Option { match self { - Function::BoundFunction(x) => x.internal_is_extensible(agent, gc), - Function::BuiltinFunction(x) => x.internal_is_extensible(agent, gc), - Function::ECMAScriptFunction(x) => x.internal_is_extensible(agent, gc), + Function::BoundFunction(x) => x.try_is_extensible(agent, gc), + Function::BuiltinFunction(x) => x.try_is_extensible(agent, gc), + Function::ECMAScriptFunction(x) => x.try_is_extensible(agent, gc), Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(x) => x.internal_is_extensible(agent, gc), - Function::BuiltinPromiseResolvingFunction(x) => x.internal_is_extensible(agent, gc), + Function::BuiltinConstructorFunction(x) => x.try_is_extensible(agent, gc), + Function::BuiltinPromiseResolvingFunction(x) => x.try_is_extensible(agent, gc), Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } } - fn internal_prevent_extensions(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { + fn try_prevent_extensions(self, agent: &mut Agent, gc: NoGcScope<'_, '_>) -> Option { match self { - Function::BoundFunction(x) => x.internal_prevent_extensions(agent, gc), - Function::BuiltinFunction(x) => x.internal_prevent_extensions(agent, gc), - Function::ECMAScriptFunction(x) => x.internal_prevent_extensions(agent, gc), + Function::BoundFunction(x) => x.try_prevent_extensions(agent, gc), + Function::BuiltinFunction(x) => x.try_prevent_extensions(agent, gc), + Function::ECMAScriptFunction(x) => x.try_prevent_extensions(agent, gc), Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(x) => x.internal_prevent_extensions(agent, gc), - Function::BuiltinPromiseResolvingFunction(x) => { - x.internal_prevent_extensions(agent, gc) - } + Function::BuiltinConstructorFunction(x) => x.try_prevent_extensions(agent, gc), + Function::BuiltinPromiseResolvingFunction(x) => x.try_prevent_extensions(agent, gc), Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } } - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { + ) -> Option> { match self { - Function::BoundFunction(x) => x.internal_get_own_property(agent, gc, property_key), - Function::BuiltinFunction(x) => x.internal_get_own_property(agent, gc, property_key), - Function::ECMAScriptFunction(x) => x.internal_get_own_property(agent, gc, property_key), + Function::BoundFunction(x) => x.try_get_own_property(agent, gc, property_key), + Function::BuiltinFunction(x) => x.try_get_own_property(agent, gc, property_key), + Function::ECMAScriptFunction(x) => x.try_get_own_property(agent, gc, property_key), Function::BuiltinGeneratorFunction => todo!(), Function::BuiltinConstructorFunction(x) => { - x.internal_get_own_property(agent, gc, property_key) + x.try_get_own_property(agent, gc, property_key) } Function::BuiltinPromiseResolvingFunction(x) => { - x.internal_get_own_property(agent, gc, property_key) + x.try_get_own_property(agent, gc, property_key) } Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } } - fn internal_define_own_property( + fn try_define_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { + ) -> Option { match self { Function::BoundFunction(x) => { - x.internal_define_own_property(agent, gc, property_key, property_descriptor) + x.try_define_own_property(agent, gc, property_key, property_descriptor) } Function::BuiltinFunction(x) => { - x.internal_define_own_property(agent, gc, property_key, property_descriptor) + x.try_define_own_property(agent, gc, property_key, property_descriptor) } Function::ECMAScriptFunction(x) => { - x.internal_define_own_property(agent, gc, property_key, property_descriptor) + x.try_define_own_property(agent, gc, property_key, property_descriptor) } Function::BuiltinGeneratorFunction => todo!(), Function::BuiltinConstructorFunction(x) => { - x.internal_define_own_property(agent, gc, property_key, property_descriptor) + x.try_define_own_property(agent, gc, property_key, property_descriptor) } Function::BuiltinPromiseResolvingFunction(x) => { - x.internal_define_own_property(agent, gc, property_key, property_descriptor) + x.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Function::BuiltinPromiseCollectorFunction => todo!(), + Function::BuiltinProxyRevokerFunction => todo!(), + } + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + match self { + Function::BoundFunction(x) => x.try_has_property(agent, gc, property_key), + Function::BuiltinFunction(x) => x.try_has_property(agent, gc, property_key), + Function::ECMAScriptFunction(x) => x.try_has_property(agent, gc, property_key), + Function::BuiltinGeneratorFunction => todo!(), + Function::BuiltinConstructorFunction(x) => x.try_has_property(agent, gc, property_key), + Function::BuiltinPromiseResolvingFunction(x) => { + x.try_has_property(agent, gc, property_key) } Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), @@ -404,6 +420,27 @@ impl InternalMethods for Function { } } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + match self { + Function::BoundFunction(x) => x.try_get(agent, gc, property_key, receiver), + Function::BuiltinFunction(x) => x.try_get(agent, gc, property_key, receiver), + Function::ECMAScriptFunction(x) => x.try_get(agent, gc, property_key, receiver), + Function::BuiltinGeneratorFunction => todo!(), + Function::BuiltinConstructorFunction(x) => x.try_get(agent, gc, property_key, receiver), + Function::BuiltinPromiseResolvingFunction(x) => { + x.try_get(agent, gc, property_key, receiver) + } + Function::BuiltinPromiseCollectorFunction => todo!(), + Function::BuiltinProxyRevokerFunction => todo!(), + } + } + fn internal_get( self, agent: &mut Agent, @@ -427,6 +464,30 @@ impl InternalMethods for Function { } } + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + match self { + Function::BoundFunction(x) => x.try_set(agent, gc, property_key, value, receiver), + Function::BuiltinFunction(x) => x.try_set(agent, gc, property_key, value, receiver), + Function::ECMAScriptFunction(x) => x.try_set(agent, gc, property_key, value, receiver), + Function::BuiltinGeneratorFunction => todo!(), + Function::BuiltinConstructorFunction(x) => { + x.try_set(agent, gc, property_key, value, receiver) + } + Function::BuiltinPromiseResolvingFunction(x) => { + x.try_set(agent, gc, property_key, value, receiver) + } + Function::BuiltinPromiseCollectorFunction => todo!(), + Function::BuiltinProxyRevokerFunction => todo!(), + } + } + fn internal_set( self, agent: &mut Agent, @@ -455,38 +516,36 @@ impl InternalMethods for Function { } } - fn internal_delete( + fn try_delete( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult { + ) -> Option { match self { - Function::BoundFunction(x) => x.internal_delete(agent, gc, property_key), - Function::BuiltinFunction(x) => x.internal_delete(agent, gc, property_key), - Function::ECMAScriptFunction(x) => x.internal_delete(agent, gc, property_key), + Function::BoundFunction(x) => x.try_delete(agent, gc, property_key), + Function::BuiltinFunction(x) => x.try_delete(agent, gc, property_key), + Function::ECMAScriptFunction(x) => x.try_delete(agent, gc, property_key), Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(x) => x.internal_delete(agent, gc, property_key), - Function::BuiltinPromiseResolvingFunction(x) => { - x.internal_delete(agent, gc, property_key) - } + Function::BuiltinConstructorFunction(x) => x.try_delete(agent, gc, property_key), + Function::BuiltinPromiseResolvingFunction(x) => x.try_delete(agent, gc, property_key), Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } } - fn internal_own_property_keys( + fn try_own_property_keys( self, agent: &mut Agent, - gc: GcScope<'_, '_>, - ) -> JsResult> { + gc: NoGcScope<'_, '_>, + ) -> Option> { match self { - Function::BoundFunction(x) => x.internal_own_property_keys(agent, gc), - Function::BuiltinFunction(x) => x.internal_own_property_keys(agent, gc), - Function::ECMAScriptFunction(x) => x.internal_own_property_keys(agent, gc), + Function::BoundFunction(x) => x.try_own_property_keys(agent, gc), + Function::BuiltinFunction(x) => x.try_own_property_keys(agent, gc), + Function::ECMAScriptFunction(x) => x.try_own_property_keys(agent, gc), Function::BuiltinGeneratorFunction => todo!(), - Function::BuiltinConstructorFunction(x) => x.internal_own_property_keys(agent, gc), - Function::BuiltinPromiseResolvingFunction(x) => x.internal_own_property_keys(agent, gc), + Function::BuiltinConstructorFunction(x) => x.try_own_property_keys(agent, gc), + Function::BuiltinPromiseResolvingFunction(x) => x.try_own_property_keys(agent, gc), Function::BuiltinPromiseCollectorFunction => todo!(), Function::BuiltinProxyRevokerFunction => todo!(), } diff --git a/nova_vm/src/ecmascript/types/language/function/into_function.rs b/nova_vm/src/ecmascript/types/language/function/into_function.rs index 07728f20..ff6dad74 100644 --- a/nova_vm/src/ecmascript/types/language/function/into_function.rs +++ b/nova_vm/src/ecmascript/types/language/function/into_function.rs @@ -4,7 +4,7 @@ use super::Function; use crate::ecmascript::builtins::ordinary::ordinary_get_own_property; -use crate::engine::context::GcScope; +use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ execution::{Agent, JsResult}, @@ -78,45 +78,63 @@ pub(crate) fn function_internal_get_own_property( func: impl FunctionInternalProperties, agent: &mut Agent, property_key: PropertyKey, -) -> JsResult> { +) -> Option { if let Some(backing_object) = func.get_backing_object(agent) { - Ok(ordinary_get_own_property( - agent, - backing_object.into_object(), - property_key, - )) + ordinary_get_own_property(agent, backing_object, property_key) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { - Ok(Some(PropertyDescriptor { + Some(PropertyDescriptor { value: Some(func.get_length(agent).into()), writable: Some(false), enumerable: Some(false), configurable: Some(true), ..Default::default() - })) + }) } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.name) { - Ok(Some(PropertyDescriptor { + Some(PropertyDescriptor { value: Some(func.get_name(agent).into_value()), writable: Some(false), enumerable: Some(false), configurable: Some(true), ..Default::default() - })) + }) } else { - Ok(None) + None } } pub(crate) fn function_internal_define_own_property( func: impl FunctionInternalProperties, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, property_descriptor: PropertyDescriptor, -) -> JsResult { +) -> bool { let backing_object = func .get_backing_object(agent) .unwrap_or_else(|| func.create_backing_object(agent)); - backing_object.internal_define_own_property(agent, gc, property_key, property_descriptor) + backing_object + .try_define_own_property(agent, gc, property_key, property_descriptor) + .unwrap() +} + +pub(crate) fn function_try_has_property( + func: impl FunctionInternalProperties, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, +) -> Option { + if let Some(backing_object) = func.get_backing_object(agent) { + backing_object.try_has_property(agent, gc, property_key) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) + || property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.name) + { + Some(true) + } else { + let parent = func.try_get_prototype_of(agent, gc).unwrap(); + parent.map_or(Some(false), |parent| { + parent.try_has_property(agent, gc, property_key) + }) + } } pub(crate) fn function_internal_has_property( @@ -132,13 +150,34 @@ pub(crate) fn function_internal_has_property( { Ok(true) } else { - let parent = func.internal_get_prototype_of(agent, gc.reborrow())?; + let parent = func.try_get_prototype_of(agent, gc.nogc()).unwrap(); parent.map_or(Ok(false), |parent| { parent.internal_has_property(agent, gc, property_key) }) } } +pub(crate) fn function_try_get( + func: impl FunctionInternalProperties, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, +) -> Option { + if let Some(backing_object) = func.get_backing_object(agent) { + backing_object.try_get(agent, gc, property_key, receiver) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) { + Some(func.get_length(agent).into()) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.name) { + Some(func.get_name(agent).into_value()) + } else { + let parent = func.try_get_prototype_of(agent, gc).unwrap(); + parent.map_or(Some(Value::Undefined), |parent| { + parent.try_get(agent, gc, property_key, receiver) + }) + } +} + pub(crate) fn function_internal_get( func: impl FunctionInternalProperties, agent: &mut Agent, @@ -160,6 +199,27 @@ pub(crate) fn function_internal_get( } } +pub(crate) fn function_try_set( + func: impl FunctionInternalProperties, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, +) -> Option { + if let Some(backing_object) = func.get_backing_object(agent) { + backing_object.try_set(agent, gc, property_key, value, receiver) + } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) + || property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.name) + { + // length and name are not writable + Some(false) + } else { + func.create_backing_object(agent) + .try_set(agent, gc, property_key, value, receiver) + } +} + pub(crate) fn function_internal_set( func: impl FunctionInternalProperties, agent: &mut Agent, @@ -184,33 +244,33 @@ pub(crate) fn function_internal_set( pub(crate) fn function_internal_delete( func: impl FunctionInternalProperties, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, -) -> JsResult { +) -> bool { if let Some(backing_object) = func.get_backing_object(agent) { - backing_object.internal_delete(agent, gc, property_key) + backing_object.try_delete(agent, gc, property_key).unwrap() } else if property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.length) || property_key == PropertyKey::from(BUILTIN_STRING_MEMORY.name) { let backing_object = func.create_backing_object(agent); - backing_object.internal_delete(agent, gc, property_key) + backing_object.try_delete(agent, gc, property_key).unwrap() } else { // Non-existing property - Ok(true) + true } } pub(crate) fn function_internal_own_property_keys( func: impl FunctionInternalProperties, agent: &mut Agent, - gc: GcScope<'_, '_>, -) -> JsResult> { + gc: NoGcScope<'_, '_>, +) -> Vec { if let Some(backing_object) = func.get_backing_object(agent) { - backing_object.internal_own_property_keys(agent, gc) + backing_object.try_own_property_keys(agent, gc).unwrap() } else { - Ok(vec![ + vec![ PropertyKey::from(BUILTIN_STRING_MEMORY.length), PropertyKey::from(BUILTIN_STRING_MEMORY.name), - ]) + ] } } diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index 6f375a70..28d98a4c 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -51,10 +51,10 @@ use crate::ecmascript::builtins::regexp::RegExp; use crate::ecmascript::builtins::shared_array_buffer::SharedArrayBuffer; #[cfg(feature = "weak-refs")] use crate::ecmascript::builtins::{weak_map::WeakMap, weak_ref::WeakRef, weak_set::WeakSet}; -use crate::engine::context::NoGcScope; #[cfg(feature = "array-buffer")] use crate::{ ecmascript::builtins::{data_view::DataView, typed_array::TypedArray, ArrayBuffer}, + engine::{context::NoGcScope, Scoped}, heap::indexes::TypedArrayIndex, }; use crate::{ @@ -302,6 +302,7 @@ impl TryFrom for OrdinaryObject { impl TryFrom for OrdinaryObject { type Error = (); + #[inline] fn try_from(value: Object) -> Result { match value { Object::Object(data) => Ok(data), @@ -553,6 +554,14 @@ impl Object { self } + pub fn scope<'scope>( + self, + agent: &mut Agent, + gc: NoGcScope<'_, 'scope>, + ) -> Scoped<'scope, Object> { + Scoped::new(agent, gc, self) + } + pub fn into_value(self) -> Value { self.into() } @@ -992,6 +1001,100 @@ impl InternalSlots for Object { } impl InternalMethods for Object { + fn try_get_prototype_of( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + ) -> Option> { + match self { + Object::Object(data) => data.try_get_prototype_of(agent, gc), + Object::Array(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "date")] + Object::Date(data) => data.try_get_prototype_of(agent, gc), + Object::Error(data) => data.try_get_prototype_of(agent, gc), + Object::BoundFunction(data) => data.try_get_prototype_of(agent, gc), + Object::BuiltinFunction(data) => data.try_get_prototype_of(agent, gc), + Object::ECMAScriptFunction(data) => data.try_get_prototype_of(agent, gc), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => data.try_get_prototype_of(agent, gc), + Object::BuiltinPromiseResolvingFunction(data) => data.try_get_prototype_of(agent, gc), + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_get_prototype_of(agent, gc), + Object::Arguments(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_get_prototype_of(agent, gc), + Object::FinalizationRegistry(data) => data.try_get_prototype_of(agent, gc), + Object::Map(data) => data.try_get_prototype_of(agent, gc), + Object::Promise(data) => data.try_get_prototype_of(agent, gc), + Object::Proxy(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_get_prototype_of(agent, gc), + Object::Set(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_get_prototype_of(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => TypedArray::Int8Array(data).try_get_prototype_of(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_get_prototype_of(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_get_prototype_of(agent, gc) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_get_prototype_of(agent, gc), + Object::SetIterator(data) => data.try_get_prototype_of(agent, gc), + Object::MapIterator(data) => data.try_get_prototype_of(agent, gc), + Object::Generator(data) => data.try_get_prototype_of(agent, gc), + Object::Module(data) => data.try_get_prototype_of(agent, gc), + Object::EmbedderObject(data) => data.try_get_prototype_of(agent, gc), + } + } + fn internal_get_prototype_of( self, agent: &mut Agent, @@ -1090,6 +1193,107 @@ impl InternalMethods for Object { } } + fn try_set_prototype_of( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + prototype: Option, + ) -> Option { + match self { + Object::Object(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Array(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "date")] + Object::Date(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Error(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::BoundFunction(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::BuiltinFunction(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::ECMAScriptFunction(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => { + data.try_set_prototype_of(agent, gc, prototype) + } + Object::BuiltinPromiseResolvingFunction(data) => { + data.try_set_prototype_of(agent, gc, prototype) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Arguments(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::FinalizationRegistry(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Map(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Promise(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Proxy(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Set(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_set_prototype_of(agent, gc, prototype), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_set_prototype_of(agent, gc, prototype) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_set_prototype_of(agent, gc, prototype) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::SetIterator(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::MapIterator(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Generator(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::Module(data) => data.try_set_prototype_of(agent, gc, prototype), + Object::EmbedderObject(data) => data.try_set_prototype_of(agent, gc, prototype), + } + } + fn internal_set_prototype_of( self, agent: &mut Agent, @@ -1195,6 +1399,86 @@ impl InternalMethods for Object { } } + fn try_is_extensible(self, agent: &mut Agent, gc: NoGcScope<'_, '_>) -> Option { + match self { + Object::Object(data) => data.try_is_extensible(agent, gc), + Object::Array(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "date")] + Object::Date(data) => data.try_is_extensible(agent, gc), + Object::Error(data) => data.try_is_extensible(agent, gc), + Object::BoundFunction(data) => data.try_is_extensible(agent, gc), + Object::BuiltinFunction(data) => data.try_is_extensible(agent, gc), + Object::ECMAScriptFunction(data) => data.try_is_extensible(agent, gc), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => data.try_is_extensible(agent, gc), + Object::BuiltinPromiseResolvingFunction(data) => data.try_is_extensible(agent, gc), + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_is_extensible(agent, gc), + Object::Arguments(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_is_extensible(agent, gc), + Object::FinalizationRegistry(data) => data.try_is_extensible(agent, gc), + Object::Map(data) => data.try_is_extensible(agent, gc), + Object::Promise(data) => data.try_is_extensible(agent, gc), + Object::Proxy(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_is_extensible(agent, gc), + Object::Set(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => TypedArray::Int8Array(data).try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => TypedArray::Uint8Array(data).try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_is_extensible(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => TypedArray::Int16Array(data).try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => TypedArray::Uint16Array(data).try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => TypedArray::Int32Array(data).try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => TypedArray::Uint32Array(data).try_is_extensible(agent, gc), + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_is_extensible(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_is_extensible(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_is_extensible(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_is_extensible(agent, gc) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_is_extensible(agent, gc), + Object::SetIterator(data) => data.try_is_extensible(agent, gc), + Object::MapIterator(data) => data.try_is_extensible(agent, gc), + Object::Generator(data) => data.try_is_extensible(agent, gc), + Object::Module(data) => data.try_is_extensible(agent, gc), + Object::EmbedderObject(data) => data.try_is_extensible(agent, gc), + } + } + fn internal_is_extensible(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { match self { Object::Object(data) => data.internal_is_extensible(agent, gc), @@ -1287,6 +1571,98 @@ impl InternalMethods for Object { } } + fn try_prevent_extensions(self, agent: &mut Agent, gc: NoGcScope<'_, '_>) -> Option { + match self { + Object::Object(data) => data.try_prevent_extensions(agent, gc), + Object::Array(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "date")] + Object::Date(data) => data.try_prevent_extensions(agent, gc), + Object::Error(data) => data.try_prevent_extensions(agent, gc), + Object::BoundFunction(data) => data.try_prevent_extensions(agent, gc), + Object::BuiltinFunction(data) => data.try_prevent_extensions(agent, gc), + Object::ECMAScriptFunction(data) => data.try_prevent_extensions(agent, gc), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => data.try_prevent_extensions(agent, gc), + Object::BuiltinPromiseResolvingFunction(data) => data.try_prevent_extensions(agent, gc), + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_prevent_extensions(agent, gc), + Object::Arguments(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_prevent_extensions(agent, gc), + Object::FinalizationRegistry(data) => data.try_prevent_extensions(agent, gc), + Object::Map(data) => data.try_prevent_extensions(agent, gc), + Object::Promise(data) => data.try_prevent_extensions(agent, gc), + Object::Proxy(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_prevent_extensions(agent, gc), + Object::Set(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_prevent_extensions(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_prevent_extensions(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_prevent_extensions(agent, gc) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_prevent_extensions(agent, gc), + Object::SetIterator(data) => data.try_prevent_extensions(agent, gc), + Object::MapIterator(data) => data.try_prevent_extensions(agent, gc), + Object::Generator(data) => data.try_prevent_extensions(agent, gc), + Object::Module(data) => data.try_prevent_extensions(agent, gc), + Object::EmbedderObject(data) => data.try_prevent_extensions(agent, gc), + } + } + fn internal_prevent_extensions(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { match self { Object::Object(data) => data.internal_prevent_extensions(agent, gc), @@ -1381,117 +1757,401 @@ impl InternalMethods for Object { } } - fn internal_get_own_property( + fn try_get_own_property( self, agent: &mut Agent, - gc: GcScope<'_, '_>, + gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { + ) -> Option> { match self { - Object::Object(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Array(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Object(data) => data.try_get_own_property(agent, gc, property_key), + Object::Array(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "array-buffer")] - Object::ArrayBuffer(data) => data.internal_get_own_property(agent, gc, property_key), + Object::ArrayBuffer(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "date")] - Object::Date(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Error(data) => data.internal_get_own_property(agent, gc, property_key), - Object::BoundFunction(data) => data.internal_get_own_property(agent, gc, property_key), - Object::BuiltinFunction(data) => { - data.internal_get_own_property(agent, gc, property_key) - } - Object::ECMAScriptFunction(data) => { - data.internal_get_own_property(agent, gc, property_key) - } + Object::Date(data) => data.try_get_own_property(agent, gc, property_key), + Object::Error(data) => data.try_get_own_property(agent, gc, property_key), + Object::BoundFunction(data) => data.try_get_own_property(agent, gc, property_key), + Object::BuiltinFunction(data) => data.try_get_own_property(agent, gc, property_key), + Object::ECMAScriptFunction(data) => data.try_get_own_property(agent, gc, property_key), Object::BuiltinGeneratorFunction => todo!(), Object::BuiltinConstructorFunction(data) => { - data.internal_get_own_property(agent, gc, property_key) + data.try_get_own_property(agent, gc, property_key) } Object::BuiltinPromiseResolvingFunction(data) => { - data.internal_get_own_property(agent, gc, property_key) + data.try_get_own_property(agent, gc, property_key) } Object::BuiltinPromiseCollectorFunction => todo!(), Object::BuiltinProxyRevokerFunction => todo!(), - Object::PrimitiveObject(data) => { - data.internal_get_own_property(agent, gc, property_key) - } - Object::Arguments(data) => data.internal_get_own_property(agent, gc, property_key), + Object::PrimitiveObject(data) => data.try_get_own_property(agent, gc, property_key), + Object::Arguments(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "array-buffer")] - Object::DataView(data) => data.internal_get_own_property(agent, gc, property_key), + Object::DataView(data) => data.try_get_own_property(agent, gc, property_key), Object::FinalizationRegistry(data) => { - data.internal_get_own_property(agent, gc, property_key) + data.try_get_own_property(agent, gc, property_key) } - Object::Map(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Promise(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Proxy(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Map(data) => data.try_get_own_property(agent, gc, property_key), + Object::Promise(data) => data.try_get_own_property(agent, gc, property_key), + Object::Proxy(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "regexp")] - Object::RegExp(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Set(data) => data.internal_get_own_property(agent, gc, property_key), + Object::RegExp(data) => data.try_get_own_property(agent, gc, property_key), + Object::Set(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "shared-array-buffer")] - Object::SharedArrayBuffer(data) => { - data.internal_get_own_property(agent, gc, property_key) - } + Object::SharedArrayBuffer(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "weak-refs")] - Object::WeakMap(data) => data.internal_get_own_property(agent, gc, property_key), + Object::WeakMap(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "weak-refs")] - Object::WeakRef(data) => data.internal_get_own_property(agent, gc, property_key), + Object::WeakRef(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "weak-refs")] - Object::WeakSet(data) => data.internal_get_own_property(agent, gc, property_key), + Object::WeakSet(data) => data.try_get_own_property(agent, gc, property_key), #[cfg(feature = "array-buffer")] Object::Int8Array(data) => { - TypedArray::Int8Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Int8Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::Uint8Array(data) => { - TypedArray::Uint8Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Uint8Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] - Object::Uint8ClampedArray(data) => TypedArray::Uint8ClampedArray(data) - .internal_get_own_property(agent, gc, property_key), + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_get_own_property(agent, gc, property_key) + } #[cfg(feature = "array-buffer")] Object::Int16Array(data) => { - TypedArray::Int16Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Int16Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::Uint16Array(data) => { - TypedArray::Uint16Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Uint16Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::Int32Array(data) => { - TypedArray::Int32Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Int32Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::Uint32Array(data) => { - TypedArray::Uint32Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Uint32Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::BigInt64Array(data) => { - TypedArray::BigInt64Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::BigInt64Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::BigUint64Array(data) => { - TypedArray::BigUint64Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::BigUint64Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::Float32Array(data) => { - TypedArray::Float32Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Float32Array(data).try_get_own_property(agent, gc, property_key) } #[cfg(feature = "array-buffer")] Object::Float64Array(data) => { - TypedArray::Float64Array(data).internal_get_own_property(agent, gc, property_key) + TypedArray::Float64Array(data).try_get_own_property(agent, gc, property_key) } Object::AsyncFromSyncIterator => todo!(), Object::AsyncIterator => todo!(), Object::Iterator => todo!(), - Object::ArrayIterator(data) => data.internal_get_own_property(agent, gc, property_key), - Object::SetIterator(data) => data.internal_get_own_property(agent, gc, property_key), - Object::MapIterator(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Generator(data) => data.internal_get_own_property(agent, gc, property_key), - Object::Module(data) => data.internal_get_own_property(agent, gc, property_key), - Object::EmbedderObject(data) => data.internal_get_own_property(agent, gc, property_key), + Object::ArrayIterator(data) => data.try_get_own_property(agent, gc, property_key), + Object::SetIterator(data) => data.try_get_own_property(agent, gc, property_key), + Object::MapIterator(data) => data.try_get_own_property(agent, gc, property_key), + Object::Generator(data) => data.try_get_own_property(agent, gc, property_key), + Object::Module(data) => data.try_get_own_property(agent, gc, property_key), + Object::EmbedderObject(data) => data.try_get_own_property(agent, gc, property_key), } } - fn internal_define_own_property( + fn internal_get_own_property( + self, + agent: &mut Agent, + gc: GcScope<'_, '_>, + property_key: PropertyKey, + ) -> JsResult> { + match self { + Object::Object(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Array(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "date")] + Object::Date(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Error(data) => data.internal_get_own_property(agent, gc, property_key), + Object::BoundFunction(data) => data.internal_get_own_property(agent, gc, property_key), + Object::BuiltinFunction(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + Object::ECMAScriptFunction(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + Object::BuiltinPromiseResolvingFunction(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + Object::Arguments(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.internal_get_own_property(agent, gc, property_key), + Object::FinalizationRegistry(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + Object::Map(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Promise(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Proxy(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Set(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => { + data.internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => TypedArray::Uint8ClampedArray(data) + .internal_get_own_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).internal_get_own_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).internal_get_own_property(agent, gc, property_key) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.internal_get_own_property(agent, gc, property_key), + Object::SetIterator(data) => data.internal_get_own_property(agent, gc, property_key), + Object::MapIterator(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Generator(data) => data.internal_get_own_property(agent, gc, property_key), + Object::Module(data) => data.internal_get_own_property(agent, gc, property_key), + Object::EmbedderObject(data) => data.internal_get_own_property(agent, gc, property_key), + } + } + + fn try_define_own_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + property_descriptor: PropertyDescriptor, + ) -> Option { + match self { + Object::Object(idx) => { + idx.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Array(idx) => { + idx.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(idx) => { + idx.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "date")] + Object::Date(idx) => { + idx.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Error(idx) => { + idx.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::BoundFunction(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::BuiltinFunction(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::ECMAScriptFunction(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::BuiltinPromiseResolvingFunction(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Arguments(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "array-buffer")] + Object::DataView(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::FinalizationRegistry(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Map(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Promise(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Proxy(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "regexp")] + Object::RegExp(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Set(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => TypedArray::Int8Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => TypedArray::Uint8Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => TypedArray::Uint8ClampedArray(data) + .try_define_own_property(agent, gc, property_key, property_descriptor), + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => TypedArray::Int16Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => TypedArray::Uint16Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => TypedArray::Int32Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => TypedArray::Uint32Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => TypedArray::BigInt64Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => TypedArray::BigUint64Array(data) + .try_define_own_property(agent, gc, property_key, property_descriptor), + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => TypedArray::Float32Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => TypedArray::Float64Array(data).try_define_own_property( + agent, + gc, + property_key, + property_descriptor, + ), + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::SetIterator(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::MapIterator(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Generator(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Module(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::EmbedderObject(data) => { + data.try_define_own_property(agent, gc, property_key, property_descriptor) + } + } + } + + fn internal_define_own_property( self, agent: &mut Agent, gc: GcScope<'_, '_>, @@ -1634,21 +2294,122 @@ impl InternalMethods for Object { Object::ArrayIterator(data) => { data.internal_define_own_property(agent, gc, property_key, property_descriptor) } - Object::SetIterator(data) => { - data.internal_define_own_property(agent, gc, property_key, property_descriptor) + Object::SetIterator(data) => { + data.internal_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::MapIterator(data) => { + data.internal_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Generator(data) => { + data.internal_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::Module(data) => { + data.internal_define_own_property(agent, gc, property_key, property_descriptor) + } + Object::EmbedderObject(data) => { + data.internal_define_own_property(agent, gc, property_key, property_descriptor) + } + } + } + + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + match self { + Object::Object(data) => data.try_has_property(agent, gc, property_key), + Object::Array(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "date")] + Object::Date(data) => data.try_has_property(agent, gc, property_key), + Object::Error(data) => data.try_has_property(agent, gc, property_key), + Object::BoundFunction(data) => data.try_has_property(agent, gc, property_key), + Object::BuiltinFunction(data) => data.try_has_property(agent, gc, property_key), + Object::ECMAScriptFunction(data) => data.try_has_property(agent, gc, property_key), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => { + data.try_has_property(agent, gc, property_key) + } + Object::BuiltinPromiseResolvingFunction(data) => { + data.try_has_property(agent, gc, property_key) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_has_property(agent, gc, property_key), + Object::Arguments(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_has_property(agent, gc, property_key), + Object::FinalizationRegistry(data) => data.try_has_property(agent, gc, property_key), + Object::Map(data) => data.try_has_property(agent, gc, property_key), + Object::Promise(data) => data.try_has_property(agent, gc, property_key), + Object::Proxy(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_has_property(agent, gc, property_key), + Object::Set(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_has_property(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).try_has_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_has_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_has_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_has_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_has_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_has_property(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_has_property(agent, gc, property_key) } - Object::MapIterator(data) => { - data.internal_define_own_property(agent, gc, property_key, property_descriptor) + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_has_property(agent, gc, property_key) } - Object::Generator(data) => { - data.internal_define_own_property(agent, gc, property_key, property_descriptor) + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_has_property(agent, gc, property_key) } - Object::Module(data) => { - data.internal_define_own_property(agent, gc, property_key, property_descriptor) + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_has_property(agent, gc, property_key) } - Object::EmbedderObject(data) => { - data.internal_define_own_property(agent, gc, property_key, property_descriptor) + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_has_property(agent, gc, property_key) } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_has_property(agent, gc, property_key), + Object::SetIterator(data) => data.try_has_property(agent, gc, property_key), + Object::MapIterator(data) => data.try_has_property(agent, gc, property_key), + Object::Generator(data) => data.try_has_property(agent, gc, property_key), + Object::Module(data) => data.try_has_property(agent, gc, property_key), + Object::EmbedderObject(data) => data.try_has_property(agent, gc, property_key), } } @@ -1755,6 +2516,108 @@ impl InternalMethods for Object { } } + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + match self { + Object::Object(data) => data.try_get(agent, gc, property_key, receiver), + Object::Array(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "date")] + Object::Date(data) => data.try_get(agent, gc, property_key, receiver), + Object::Error(data) => data.try_get(agent, gc, property_key, receiver), + Object::BoundFunction(data) => data.try_get(agent, gc, property_key, receiver), + Object::BuiltinFunction(data) => data.try_get(agent, gc, property_key, receiver), + Object::ECMAScriptFunction(data) => data.try_get(agent, gc, property_key, receiver), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => { + data.try_get(agent, gc, property_key, receiver) + } + Object::BuiltinPromiseResolvingFunction(data) => { + data.try_get(agent, gc, property_key, receiver) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_get(agent, gc, property_key, receiver), + Object::Arguments(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_get(agent, gc, property_key, receiver), + Object::FinalizationRegistry(data) => data.try_get(agent, gc, property_key, receiver), + Object::Map(data) => data.try_get(agent, gc, property_key, receiver), + Object::Promise(data) => data.try_get(agent, gc, property_key, receiver), + Object::Proxy(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_get(agent, gc, property_key, receiver), + Object::Set(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_get(agent, gc, property_key, receiver), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_get(agent, gc, property_key, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_get(agent, gc, property_key, receiver) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_get(agent, gc, property_key, receiver), + Object::SetIterator(data) => data.try_get(agent, gc, property_key, receiver), + Object::MapIterator(data) => data.try_get(agent, gc, property_key, receiver), + Object::Generator(data) => data.try_get(agent, gc, property_key, receiver), + Object::Module(data) => data.try_get(agent, gc, property_key, receiver), + Object::EmbedderObject(data) => data.try_get(agent, gc, property_key, receiver), + } + } + fn internal_get( self, agent: &mut Agent, @@ -1861,6 +2724,119 @@ impl InternalMethods for Object { } } + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + match self { + Object::Object(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Array(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "date")] + Object::Date(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Error(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::BoundFunction(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::BuiltinFunction(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::ECMAScriptFunction(data) => { + data.try_set(agent, gc, property_key, value, receiver) + } + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => { + data.try_set(agent, gc, property_key, value, receiver) + } + Object::BuiltinPromiseResolvingFunction(data) => { + data.try_set(agent, gc, property_key, value, receiver) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Arguments(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::FinalizationRegistry(data) => { + data.try_set(agent, gc, property_key, value, receiver) + } + Object::Map(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Promise(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Proxy(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Set(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => { + data.try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_set(agent, gc, property_key, value, receiver), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => TypedArray::Uint8ClampedArray(data).try_set( + agent, + gc, + property_key, + value, + receiver, + ), + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_set(agent, gc, property_key, value, receiver) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_set(agent, gc, property_key, value, receiver) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::SetIterator(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::MapIterator(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Generator(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::Module(data) => data.try_set(agent, gc, property_key, value, receiver), + Object::EmbedderObject(data) => data.try_set(agent, gc, property_key, value, receiver), + } + } + fn internal_set( self, agent: &mut Agent, @@ -2006,6 +2982,105 @@ impl InternalMethods for Object { } } + fn try_delete( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + match self { + Object::Object(data) => data.try_delete(agent, gc, property_key), + Object::Array(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "date")] + Object::Date(data) => data.try_delete(agent, gc, property_key), + Object::Error(data) => data.try_delete(agent, gc, property_key), + Object::BoundFunction(data) => data.try_delete(agent, gc, property_key), + Object::BuiltinFunction(data) => data.try_delete(agent, gc, property_key), + Object::ECMAScriptFunction(data) => data.try_delete(agent, gc, property_key), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => data.try_delete(agent, gc, property_key), + Object::BuiltinPromiseResolvingFunction(data) => { + data.try_delete(agent, gc, property_key) + } + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_delete(agent, gc, property_key), + Object::Arguments(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_delete(agent, gc, property_key), + Object::FinalizationRegistry(data) => data.try_delete(agent, gc, property_key), + Object::Map(data) => data.try_delete(agent, gc, property_key), + Object::Promise(data) => data.try_delete(agent, gc, property_key), + Object::Proxy(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_delete(agent, gc, property_key), + Object::Set(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_delete(agent, gc, property_key), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => { + TypedArray::Int8Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_delete(agent, gc, property_key) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_delete(agent, gc, property_key) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_delete(agent, gc, property_key), + Object::SetIterator(data) => data.try_delete(agent, gc, property_key), + Object::MapIterator(data) => data.try_delete(agent, gc, property_key), + Object::Generator(data) => data.try_delete(agent, gc, property_key), + Object::Module(data) => data.try_delete(agent, gc, property_key), + Object::EmbedderObject(data) => data.try_delete(agent, gc, property_key), + } + } + fn internal_delete( self, agent: &mut Agent, @@ -2107,6 +3182,100 @@ impl InternalMethods for Object { } } + fn try_own_property_keys( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + ) -> Option> { + match self { + Object::Object(data) => data.try_own_property_keys(agent, gc), + Object::Array(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "array-buffer")] + Object::ArrayBuffer(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "date")] + Object::Date(data) => data.try_own_property_keys(agent, gc), + Object::Error(data) => data.try_own_property_keys(agent, gc), + Object::BoundFunction(data) => data.try_own_property_keys(agent, gc), + Object::BuiltinFunction(data) => data.try_own_property_keys(agent, gc), + Object::ECMAScriptFunction(data) => data.try_own_property_keys(agent, gc), + Object::BuiltinGeneratorFunction => todo!(), + Object::BuiltinConstructorFunction(data) => data.try_own_property_keys(agent, gc), + Object::BuiltinPromiseResolvingFunction(data) => data.try_own_property_keys(agent, gc), + Object::BuiltinPromiseCollectorFunction => todo!(), + Object::BuiltinProxyRevokerFunction => todo!(), + Object::PrimitiveObject(data) => data.try_own_property_keys(agent, gc), + Object::Arguments(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "array-buffer")] + Object::DataView(data) => data.try_own_property_keys(agent, gc), + Object::FinalizationRegistry(data) => data.try_own_property_keys(agent, gc), + Object::Map(data) => data.try_own_property_keys(agent, gc), + Object::Promise(data) => data.try_own_property_keys(agent, gc), + Object::Proxy(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "regexp")] + Object::RegExp(data) => data.try_own_property_keys(agent, gc), + Object::Set(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "shared-array-buffer")] + Object::SharedArrayBuffer(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakMap(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakRef(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "weak-refs")] + Object::WeakSet(data) => data.try_own_property_keys(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Int8Array(data) => TypedArray::Int8Array(data).try_own_property_keys(agent, gc), + #[cfg(feature = "array-buffer")] + Object::Uint8Array(data) => { + TypedArray::Uint8Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint8ClampedArray(data) => { + TypedArray::Uint8ClampedArray(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int16Array(data) => { + TypedArray::Int16Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint16Array(data) => { + TypedArray::Uint16Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Int32Array(data) => { + TypedArray::Int32Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Uint32Array(data) => { + TypedArray::Uint32Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigInt64Array(data) => { + TypedArray::BigInt64Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::BigUint64Array(data) => { + TypedArray::BigUint64Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float32Array(data) => { + TypedArray::Float32Array(data).try_own_property_keys(agent, gc) + } + #[cfg(feature = "array-buffer")] + Object::Float64Array(data) => { + TypedArray::Float64Array(data).try_own_property_keys(agent, gc) + } + Object::AsyncFromSyncIterator => todo!(), + Object::AsyncIterator => todo!(), + Object::Iterator => todo!(), + Object::ArrayIterator(data) => data.try_own_property_keys(agent, gc), + Object::SetIterator(data) => data.try_own_property_keys(agent, gc), + Object::MapIterator(data) => data.try_own_property_keys(agent, gc), + Object::Generator(data) => data.try_own_property_keys(agent, gc), + Object::Module(data) => data.try_own_property_keys(agent, gc), + Object::EmbedderObject(data) => data.try_own_property_keys(agent, gc), + } + } + fn internal_own_property_keys( self, agent: &mut Agent, diff --git a/nova_vm/src/ecmascript/types/language/object/internal_methods.rs b/nova_vm/src/ecmascript/types/language/object/internal_methods.rs index e4d59293..3da8b538 100644 --- a/nova_vm/src/ecmascript/types/language/object/internal_methods.rs +++ b/nova_vm/src/ecmascript/types/language/object/internal_methods.rs @@ -2,173 +2,268 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use super::{InternalSlots, IntoObject, Object, PropertyKey}; +use super::{InternalSlots, Object, PropertyKey}; use crate::{ ecmascript::{ - abstract_operations::testing_and_comparison::same_value, builtins::{ ordinary::{ - ordinary_define_own_property, ordinary_delete, ordinary_get, - ordinary_get_own_property, ordinary_get_prototype_of, ordinary_has_property, - ordinary_is_extensible, ordinary_own_property_keys, ordinary_prevent_extensions, - ordinary_set, ordinary_set_prototype_of, ordinary_set_prototype_of_check_loop, + ordinary_delete, ordinary_get, ordinary_get_own_property, + ordinary_get_prototype_of, ordinary_has_property, ordinary_is_extensible, + ordinary_own_property_keys, ordinary_prevent_extensions, ordinary_set, + ordinary_set_prototype_of, ordinary_try_define_own_property, ordinary_try_get, + ordinary_try_has_property, ordinary_try_set, }, ArgumentsList, }, execution::{Agent, JsResult}, types::{Function, PropertyDescriptor, Value}, }, - engine::context::GcScope, + engine::context::{GcScope, NoGcScope}, }; /// ### [6.1.7.2 Object Internal Methods and Internal Slots](https://tc39.es/ecma262/#sec-object-internal-methods-and-internal-slots) pub trait InternalMethods where - Self: Sized + Clone + Copy + Into + InternalSlots, + Self: std::fmt::Debug + Sized + Clone + Copy + Into + InternalSlots, { - /// \[\[GetPrototypeOf\]\] + /// ## Infallible \[\[GetPrototypeOf\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_get_prototype_of( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + ) -> Option> { + match self.get_backing_object(agent) { + Some(backing_object) => Some(ordinary_get_prototype_of(agent, gc, backing_object)), + None => Some(self.internal_prototype(agent)), + } + } + + /// ## \[\[GetPrototypeOf\]\] fn internal_get_prototype_of( self, agent: &mut Agent, // Note: Because of Proxies, this can trigger GC. - _gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, ) -> JsResult> { - match self.get_backing_object(agent) { - Some(backing_object) => Ok(ordinary_get_prototype_of( - agent, - backing_object.into_object(), - )), - None => Ok(self.internal_prototype(agent)), - } + Ok(self + .try_get_prototype_of(agent, gc.nogc()) + // Note: We unwrap because we'd just call ordinary_get_prototype_of + // which cannot trigger GC: No object should ever have a try_proto + // method that can return None while also using this default impl. + .unwrap()) } - /// \[\[SetPrototypeOf\]\] + /// ## Infallible \[\[SetPrototypeOf\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_set_prototype_of( + self, + agent: &mut Agent, + _gc: NoGcScope<'_, '_>, + prototype: Option, + ) -> Option { + Some(ordinary_set_prototype_of( + agent, + self.into_object(), + prototype, + )) + } + + /// ## \[\[SetPrototypeOf\]\] fn internal_set_prototype_of( self, agent: &mut Agent, // Note: Because of Proxies, this can trigger GC. - _gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, prototype: Option, ) -> JsResult { - match self.get_backing_object(agent) { - Some(backing_object) => Ok(ordinary_set_prototype_of( - agent, - backing_object.into_object(), - prototype, - )), - None => { - // 1. Let current be O.[[Prototype]]. - let current = self.internal_prototype(agent); - - // 2. If SameValue(V, current) is true, return true. - match (prototype, current) { - (Some(prototype), Some(current)) if same_value(agent, prototype, current) => { - return Ok(true) - } - (None, None) => return Ok(true), - _ => {} - } - - // 3. Let extensible be O.[[Extensible]]. - let extensible = self.internal_extensible(agent); - - // 4. If extensible is false, return false. - if !extensible { - // 7.b.i. Return false. - return Ok(false); - } - - if !ordinary_set_prototype_of_check_loop(agent, self.into(), prototype) { - return Ok(false); - } - - // 8. Set O.[[Prototype]] to V. - self.internal_set_prototype(agent, prototype); - - // 9. Return true. - Ok(true) - } - } + Ok(self + .try_set_prototype_of(agent, gc.into_nogc(), prototype) + .unwrap()) } - /// \[\[IsExtensible\]\] - fn internal_is_extensible( + /// ## Infallible \[\[IsExtensible\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_is_extensible( self, agent: &mut Agent, // Note: Because of Proxies, this can call JS. - _gc: GcScope<'_, '_>, - ) -> JsResult { + _gc: NoGcScope<'_, '_>, + ) -> Option { // 1. Return OrdinaryIsExtensible(O). match self.get_backing_object(agent) { - Some(backing_object) => Ok(ordinary_is_extensible(agent, backing_object.into_object())), - None => Ok(self.internal_extensible(agent)), + Some(backing_object) => Some(ordinary_is_extensible(agent, backing_object)), + None => Some(self.internal_extensible(agent)), } } - /// \[\[PreventExtensions\]\] - fn internal_prevent_extensions( + /// ## \[\[IsExtensible\]\] + fn internal_is_extensible( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + // Note: Because of Proxies, this can call JS. + gc: GcScope<'_, '_>, ) -> JsResult { + Ok(self.try_is_extensible(agent, gc.into_nogc()).unwrap()) + } + + /// ## Infallible \[\[PreventExtensions\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_prevent_extensions(self, agent: &mut Agent, _gc: NoGcScope<'_, '_>) -> Option { // 1. Return OrdinaryPreventExtensions(O). match self.get_backing_object(agent) { - Some(backing_object) => Ok(ordinary_prevent_extensions( - agent, - backing_object.into_object(), - )), + Some(backing_object) => Some(ordinary_prevent_extensions(agent, backing_object)), None => { self.internal_set_extensible(agent, false); - Ok(true) + Some(true) } } } - /// \[\[GetOwnProperty\]\] - fn internal_get_own_property( + /// ## \[\[PreventExtensions\]\] + fn internal_prevent_extensions(self, agent: &mut Agent, gc: GcScope<'_, '_>) -> JsResult { + Ok(self.try_prevent_extensions(agent, gc.into_nogc()).unwrap()) + } + + /// ## Infallible \[\[GetOwnProperty\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_get_own_property( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + _gc: NoGcScope<'_, '_>, property_key: PropertyKey, - ) -> JsResult> { + ) -> Option> { // 1. Return OrdinaryGetOwnProperty(O, P). match self.get_backing_object(agent) { - Some(backing_object) => Ok(ordinary_get_own_property( + Some(backing_object) => Some(ordinary_get_own_property( agent, - backing_object.into_object(), + backing_object, property_key, )), - None => Ok(None), + None => Some(None), } } - /// \[\[DefineOwnProperty\]\] - fn internal_define_own_property( + /// ## \[\[GetOwnProperty\]\] + fn internal_get_own_property( self, agent: &mut Agent, gc: GcScope<'_, '_>, property_key: PropertyKey, + ) -> JsResult> { + Ok(self + .try_get_own_property(agent, gc.into_nogc(), property_key) + .unwrap()) + } + + /// ## Infallible \[\[DefineOwnProperty\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_define_own_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, property_descriptor: PropertyDescriptor, - ) -> JsResult { + ) -> Option { let backing_object = self .get_backing_object(agent) - .unwrap_or_else(|| self.create_backing_object(agent)) - .into_object(); - ordinary_define_own_property(agent, gc, backing_object, property_key, property_descriptor) + .unwrap_or_else(|| self.create_backing_object(agent)); + Some(ordinary_try_define_own_property( + agent, + gc, + backing_object, + property_key, + property_descriptor, + )) } - /// \[\[HasProperty\]\] - fn internal_has_property( + /// ## \[\[DefineOwnProperty\]\] + fn internal_define_own_property( self, agent: &mut Agent, - mut gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, property_key: PropertyKey, + property_descriptor: PropertyDescriptor, ) -> JsResult { + Ok(self + .try_define_own_property(agent, gc.into_nogc(), property_key, property_descriptor) + .unwrap()) + } + + /// ## Infallible \[\[HasProperty\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_has_property( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { // 1. Return ? OrdinaryHasProperty(O, P). match self.get_backing_object(agent) { Some(backing_object) => { - ordinary_has_property(agent, gc, backing_object.into_object(), property_key) + ordinary_try_has_property(agent, gc, backing_object, property_key) } + None => { + // 3. Let parent be ? O.[[GetPrototypeOf]](). + let parent = self.try_get_prototype_of(agent, gc)?; + + // 4. If parent is not null, then + if let Some(parent) = parent { + // a. Return ? parent.[[HasProperty]](P). + parent.try_has_property(agent, gc, property_key) + } else { + // 5. Return false. + Some(false) + } + } + } + } + + /// ## \[\[HasProperty\]\] + fn internal_has_property( + self, + agent: &mut Agent, + mut gc: GcScope<'_, '_>, + property_key: PropertyKey, + ) -> JsResult { + // 1. Return ? OrdinaryHasProperty(O, P). + match self.get_backing_object(agent) { + Some(backing_object) => ordinary_has_property(agent, gc, backing_object, property_key), None => { // 3. Let parent be ? O.[[GetPrototypeOf]](). let parent = self.internal_get_prototype_of(agent, gc.reborrow())?; @@ -185,7 +280,39 @@ where } } - /// \[\[Get\]\] + /// ## Infallible \[\[Get\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_get( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + receiver: Value, + ) -> Option { + // 1. Return ? OrdinaryGet(O, P, Receiver). + match self.get_backing_object(agent) { + Some(backing_object) => { + ordinary_try_get(agent, gc, backing_object, property_key, receiver) + } + None => { + // a. Let parent be ? O.[[GetPrototypeOf]](). + let Some(parent) = self.try_get_prototype_of(agent, gc)? else { + // b. If parent is null, return undefined. + return Some(Value::Undefined); + }; + + // c. Return ? parent.[[Get]](P, Receiver). + parent.try_get(agent, gc, property_key, receiver) + } + } + } + + /// ## \[\[Get\]\] fn internal_get( self, agent: &mut Agent, @@ -195,13 +322,7 @@ where ) -> JsResult { // 1. Return ? OrdinaryGet(O, P, Receiver). match self.get_backing_object(agent) { - Some(backing_object) => ordinary_get( - agent, - gc, - backing_object.into_object(), - property_key, - receiver, - ), + Some(backing_object) => ordinary_get(agent, gc, backing_object, property_key, receiver), None => { // a. Let parent be ? O.[[GetPrototypeOf]](). let Some(parent) = self.internal_get_prototype_of(agent, gc.reborrow())? else { @@ -215,7 +336,26 @@ where } } - /// \[\[Set\]\] + /// ## Infallible \[\[Set\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_set( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + value: Value, + receiver: Value, + ) -> Option { + // 1. Return ? OrdinarySet(O, P, V, Receiver). + ordinary_try_set(agent, gc, self.into_object(), property_key, value, receiver) + } + + /// ## \[\[Set\]\] fn internal_set( self, agent: &mut Agent, @@ -225,43 +365,70 @@ where receiver: Value, ) -> JsResult { // 1. Return ? OrdinarySet(O, P, V, Receiver). - let backing_object = self - .get_backing_object(agent) - .unwrap_or_else(|| self.create_backing_object(agent)) - .into_object(); - ordinary_set(agent, gc, backing_object, property_key, value, receiver) + ordinary_set(agent, gc, self.into_object(), property_key, value, receiver) } - /// \[\[Delete\]\] + /// ## Infallible \[\[Delete\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_delete( + self, + agent: &mut Agent, + gc: NoGcScope<'_, '_>, + property_key: PropertyKey, + ) -> Option { + // 1. Return ? OrdinaryDelete(O, P). + match self.get_backing_object(agent) { + Some(backing_object) => Some(ordinary_delete(agent, gc, backing_object, property_key)), + None => Some(true), + } + } + + /// ## \[\[Delete\]\] fn internal_delete( self, agent: &mut Agent, gc: GcScope<'_, '_>, property_key: PropertyKey, ) -> JsResult { - // 1. Return ? OrdinaryDelete(O, P). + Ok(self + .try_delete(agent, gc.into_nogc(), property_key) + .unwrap()) + } + + /// ## Infallible \[\[OwnPropertyKeys\]\] + /// + /// This is an infallible variant of the method that does not allow calling + /// into JavaScript or triggering garbage collection. If the internal + /// method cannot be completed without calling into JavaScript, then `None` + /// is returned. It is preferable to call this method first and only call + /// the main method if this returns None. + fn try_own_property_keys( + self, + agent: &mut Agent, + _gc: NoGcScope<'_, '_>, + ) -> Option> { + // 1. Return OrdinaryOwnPropertyKeys(O). match self.get_backing_object(agent) { - Some(backing_object) => { - ordinary_delete(agent, gc, backing_object.into_object(), property_key) - } - None => Ok(true), + Some(backing_object) => Some(ordinary_own_property_keys(agent, backing_object)), + None => Some(vec![]), } } - /// \[\[OwnPropertyKeys\]\] + /// ## \[\[OwnPropertyKeys\]\] fn internal_own_property_keys( self, agent: &mut Agent, - _gc: GcScope<'_, '_>, + gc: GcScope<'_, '_>, ) -> JsResult> { - // 1. Return OrdinaryOwnPropertyKeys(O). - match self.get_backing_object(agent) { - Some(backing_object) => Ok(ordinary_own_property_keys(agent, backing_object)), - None => Ok(vec![]), - } + Ok(self.try_own_property_keys(agent, gc.into_nogc()).unwrap()) } - /// \[\[Call\]\] + /// ## \[\[Call\]\] fn internal_call( self, _agent: &mut Agent, @@ -272,7 +439,7 @@ where unreachable!() } - /// \[\[Construct\]\] + /// ## \[\[Construct\]\] fn internal_construct( self, _agent: &mut Agent, diff --git a/nova_vm/src/engine/bytecode/iterator.rs b/nova_vm/src/engine/bytecode/iterator.rs index 576b9ef9..e6f1241a 100644 --- a/nova_vm/src/engine/bytecode/iterator.rs +++ b/nova_vm/src/engine/bytecode/iterator.rs @@ -213,7 +213,13 @@ impl ObjectPropertiesIterator { } } } - let prototype = object.internal_get_prototype_of(agent, gc.reborrow())?; + let got_prototype = object.try_get_prototype_of(agent, gc.nogc()); + let prototype = if let Some(prototype) = got_prototype { + prototype + } else { + // Note: We should be probably be rooting some values here. + object.internal_get_prototype_of(agent, gc.reborrow())? + }; if let Some(prototype) = prototype { self.object_was_visited = false; self.object = prototype; diff --git a/nova_vm/src/engine/bytecode/vm.rs b/nova_vm/src/engine/bytecode/vm.rs index 62075c21..cd7622ef 100644 --- a/nova_vm/src/engine/bytecode/vm.rs +++ b/nova_vm/src/engine/bytecode/vm.rs @@ -45,8 +45,8 @@ use crate::{ types::{ get_this_value, get_value, initialize_referenced_binding, is_private_reference, is_super_reference, put_value, Base, BigInt, Function, InternalMethods, IntoFunction, - IntoObject, IntoValue, Number, Numeric, Object, Primitive, PropertyDescriptor, - PropertyKey, Reference, String, Value, BUILTIN_STRING_MEMORY, + IntoObject, IntoValue, Number, Numeric, Object, OrdinaryObject, Primitive, + PropertyDescriptor, PropertyKey, Reference, String, Value, BUILTIN_STRING_MEMORY, }, }, engine::context::GcScope, @@ -669,7 +669,7 @@ impl Vm { let closure = ordinary_function_create(agent, gc.nogc(), params); // 7. Perform MakeMethod(closure, object). let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); - make_method(agent, closure, object); + make_method(agent, closure, object.into_object()); // 8. Perform SetFunctionName(closure, propKey, "get"). set_function_name( agent, @@ -738,7 +738,7 @@ impl Vm { let closure = ordinary_function_create(agent, gc.nogc(), params); // 6. Perform MakeMethod(closure, object). let object = Object::try_from(*vm.stack.last().unwrap()).unwrap(); - make_method(agent, closure, object); + make_method(agent, closure, object.into_object()); // 7. Perform SetFunctionName(closure, propKey, "set"). set_function_name( agent, @@ -1040,11 +1040,11 @@ impl Vm { } else { None }; - let proto = Object::try_from(*vm.stack.last().unwrap()).unwrap(); + let proto = OrdinaryObject::try_from(*vm.stack.last().unwrap()).unwrap(); let is_null_derived_class = !has_constructor_parent && proto - .internal_get_prototype_of(agent, gc.reborrow()) + .try_get_prototype_of(agent, gc.nogc()) .unwrap() .is_none(); @@ -1077,7 +1077,7 @@ impl Vm { } set_function_name(agent, gc.nogc(), function, class_name.into(), None); make_constructor(agent, function, Some(false), Some(proto)); - agent[function].ecmascript_function.home_object = Some(proto); + agent[function].ecmascript_function.home_object = Some(proto.into_object()); agent[function].ecmascript_function.constructor_status = if has_constructor_parent || is_null_derived_class { ConstructorStatus::DerivedClass diff --git a/tests/expectations.json b/tests/expectations.json index 6491376c..c016c8d5 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -323,8 +323,6 @@ "built-ins/Array/prototype/map/resizable-buffer-shrink-mid-iteration.js": "CRASH", "built-ins/Array/prototype/map/resizable-buffer.js": "CRASH", "built-ins/Array/prototype/methods-called-as-functions.js": "CRASH", - "built-ins/Array/prototype/push/set-length-array-is-frozen.js": "FAIL", - "built-ins/Array/prototype/push/set-length-array-length-is-non-writable.js": "FAIL", "built-ins/Array/prototype/reduce/15.4.4.21-1-11.js": "CRASH", "built-ins/Array/prototype/reduce/15.4.4.21-1-12.js": "CRASH", "built-ins/Array/prototype/reduce/15.4.4.21-8-b-iii-1-20.js": "FAIL", @@ -369,7 +367,6 @@ "built-ins/Array/prototype/some/15.4.4.17-5-16.js": "CRASH", "built-ins/Array/prototype/some/15.4.4.17-7-b-16.js": "FAIL", "built-ins/Array/prototype/some/15.4.4.17-7-c-i-20.js": "FAIL", - "built-ins/Array/prototype/some/15.4.4.17-7-c-i-28.js": "FAIL", "built-ins/Array/prototype/some/15.4.4.17-7-c-iii-21.js": "CRASH", "built-ins/Array/prototype/some/15.4.4.17-7-c-iii-22.js": "CRASH", "built-ins/Array/prototype/some/callbackfn-resize-arraybuffer.js": "CRASH", @@ -379,22 +376,6 @@ "built-ins/Array/prototype/sort/comparefn-grow.js": "CRASH", "built-ins/Array/prototype/sort/comparefn-resizable-buffer.js": "CRASH", "built-ins/Array/prototype/sort/comparefn-shrink.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-appends-elements.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-decreases-length.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-deletes-predecessor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-deletes-successor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-increases-length.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-pops-elements.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-sets-predecessor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-getter-sets-successor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-appends-elements.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-decreases-length.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-deletes-predecessor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-deletes-successor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-increases-length.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-pops-elements.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-sets-predecessor.js": "CRASH", - "built-ins/Array/prototype/sort/precise-setter-sets-successor.js": "CRASH", "built-ins/Array/prototype/sort/resizable-buffer-default-comparator.js": "CRASH", "built-ins/Array/prototype/splice/create-proto-from-ctor-realm-array.js": "FAIL", "built-ins/Array/prototype/splice/create-proto-from-ctor-realm-non-array.js": "FAIL", @@ -1565,8 +1546,6 @@ "built-ins/DisposableStack/prototype/use/throws-if-value-not-object.js": "FAIL", "built-ins/Error/cause_abrupt.js": "CRASH", "built-ins/Error/proto-from-ctor-realm.js": "FAIL", - "built-ins/Error/prototype/toString/15.11.4.4-10-1.js": "FAIL", - "built-ins/Error/prototype/toString/15.11.4.4-8-1.js": "FAIL", "built-ins/FinalizationRegistry/gc-has-one-chance-to-call-cleanupCallback-for-object.js": "CRASH", "built-ins/FinalizationRegistry/instance-extensible.js": "CRASH", "built-ins/FinalizationRegistry/is-a-constructor.js": "CRASH", @@ -2458,22 +2437,12 @@ "built-ins/Object/defineProperties/15.2.3.7-6-a-19.js": "CRASH", "built-ins/Object/defineProperties/15.2.3.7-6-a-195.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-202.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-204.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-205.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-207.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-209.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-222.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-223.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-229.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-231.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-241.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-242.js": "FAIL", "built-ins/Object/defineProperties/15.2.3.7-6-a-245.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-257.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-259.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-260.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-261.js": "FAIL", - "built-ins/Object/defineProperties/15.2.3.7-6-a-262.js": "FAIL", "built-ins/Object/defineProperties/proxy-no-ownkeys-returned-keys-order.js": "CRASH", "built-ins/Object/defineProperties/typedarray-backed-by-resizable-buffer.js": "CRASH", "built-ins/Object/defineProperty/15.2.3.6-0-2.js": "FAIL", @@ -2529,28 +2498,14 @@ "built-ins/Object/defineProperty/15.2.3.6-4-200.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-205.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-206.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-207.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-208.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-209.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-211.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-213.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-230.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-231.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-232.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-233.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-235.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-240.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-242.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-252.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-254.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-255.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-256.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-257.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-268.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-270.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-271.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-272.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-273.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-292-1.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-293-2.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-293-3.js": "FAIL", @@ -2581,11 +2536,8 @@ "built-ins/Object/defineProperty/15.2.3.6-4-507.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-516.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-525.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-531-2.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-534.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-535.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-540-2.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-540-4.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-543.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-544.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-547-1.js": "FAIL", @@ -15763,14 +15715,14 @@ "language/expressions/class/elements/wrapped-in-sc-static-private-methods-with-fields.js": "CRASH", "language/expressions/class/elements/wrapped-in-sc-static-private-methods.js": "CRASH", "language/expressions/class/elements/wrapped-in-sc-string-literal-names.js": "CRASH", - "language/expressions/class/gen-method-length-dflt.js": "CRASH", + "language/expressions/class/gen-method-length-dflt.js": "FAIL", "language/expressions/class/gen-method-static/dflt-params-abrupt.js": "CRASH", "language/expressions/class/gen-method-static/dflt-params-ref-later.js": "CRASH", "language/expressions/class/gen-method-static/dflt-params-ref-self.js": "CRASH", "language/expressions/class/gen-method/dflt-params-abrupt.js": "CRASH", "language/expressions/class/gen-method/dflt-params-ref-later.js": "CRASH", "language/expressions/class/gen-method/dflt-params-ref-self.js": "CRASH", - "language/expressions/class/method-length-dflt.js": "CRASH", + "language/expressions/class/method-length-dflt.js": "FAIL", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-eval-indirect.js": "CRASH", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-eval.js": "CRASH", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-factory.js": "CRASH", @@ -17241,7 +17193,7 @@ "language/expressions/object/dstr/object-rest-proxy-ownkeys-returned-keys-order.js": "CRASH", "language/expressions/object/fn-name-accessor-get.js": "FAIL", "language/expressions/object/fn-name-accessor-set.js": "FAIL", - "language/expressions/object/fn-name-class.js": "CRASH", + "language/expressions/object/fn-name-class.js": "FAIL", "language/expressions/object/fn-name-cover.js": "FAIL", "language/expressions/object/getter-super-prop.js": "CRASH", "language/expressions/object/identifier-shorthand-static-init-await-valid.js": "FAIL", @@ -20968,14 +20920,14 @@ "language/statements/class/elements/wrapped-in-sc-static-private-methods-with-fields.js": "CRASH", "language/statements/class/elements/wrapped-in-sc-static-private-methods.js": "CRASH", "language/statements/class/elements/wrapped-in-sc-string-literal-names.js": "CRASH", - "language/statements/class/gen-method-length-dflt.js": "CRASH", + "language/statements/class/gen-method-length-dflt.js": "FAIL", "language/statements/class/gen-method-static/dflt-params-abrupt.js": "CRASH", "language/statements/class/gen-method-static/dflt-params-ref-later.js": "CRASH", "language/statements/class/gen-method-static/dflt-params-ref-self.js": "CRASH", "language/statements/class/gen-method/dflt-params-abrupt.js": "CRASH", "language/statements/class/gen-method/dflt-params-ref-later.js": "CRASH", "language/statements/class/gen-method/dflt-params-ref-self.js": "CRASH", - "language/statements/class/method-length-dflt.js": "CRASH", + "language/statements/class/method-length-dflt.js": "FAIL", "language/statements/class/static-init-arguments-functions.js": "CRASH", "language/statements/class/static-init-arguments-methods.js": "CRASH", "language/statements/class/static-init-await-binding-valid.js": "FAIL", @@ -23100,7 +23052,7 @@ "language/statements/generators/dstr/obj-ptrn-prop-obj.js": "CRASH", "language/statements/generators/eval-var-scope-syntax-err.js": "CRASH", "language/statements/generators/generator-created-after-decl-inst.js": "CRASH", - "language/statements/generators/length-dflt.js": "CRASH", + "language/statements/generators/length-dflt.js": "FAIL", "language/statements/generators/scope-param-rest-elem-var-close.js": "CRASH", "language/statements/generators/scope-param-rest-elem-var-open.js": "CRASH", "language/statements/generators/unscopables-with-in-nested-fn.js": "FAIL", diff --git a/tests/metrics.json b/tests/metrics.json index 2671f525..288e3acf 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { "crash": 14781, - "fail": 8751, - "pass": 21711, + "fail": 8703, + "pass": 21759, "skip": 45, "timeout": 3, "unresolved": 0 From 93c13cf27ee86315b45696202ce9e4c0c5f11013 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 8 Dec 2024 17:44:44 +0200 Subject: [PATCH 2/2] typo --- nova_vm/src/ecmascript/builtins/ordinary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova_vm/src/ecmascript/builtins/ordinary.rs b/nova_vm/src/ecmascript/builtins/ordinary.rs index ea1663d4..066db113 100644 --- a/nova_vm/src/ecmascript/builtins/ordinary.rs +++ b/nova_vm/src/ecmascript/builtins/ordinary.rs @@ -544,7 +544,7 @@ pub(crate) fn ordinary_try_has_property( ) -> Option { // 1. Let hasOwn be ? O.[[GetOwnProperty]](P). // Note: ? means that if we'd call a Proxy's GetOwnProperty trap then we'll - // intead return None. + // instead return None. let has_own = object.try_get_own_property(agent, gc, property_key)?; // 2. If hasOwn is not undefined, return true.