diff --git a/core/engine/src/object/jsobject.rs b/core/engine/src/object/jsobject.rs index e666d39590e..0c41bc1d330 100644 --- a/core/engine/src/object/jsobject.rs +++ b/core/engine/src/object/jsobject.rs @@ -4,9 +4,7 @@ use super::{ JsPrototype, NativeObject, Object, ObjectData, PrivateName, PropertyMap, - internal_methods::{ - InternalMethodPropertyContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS, - }, + internal_methods::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS}, shape::RootShape, }; use crate::{ @@ -768,75 +766,6 @@ Cannot both specify accessors and a value or writable attribute", Ok(desc.build()) } - /// `7.3.25 CopyDataProperties ( target, source, excludedItems )` - /// - /// More information: - /// - [ECMAScript][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-copydataproperties - pub fn copy_data_properties( - &self, - source: &JsValue, - excluded_keys: Vec, - context: &mut Context, - ) -> JsResult<()> - where - K: Into, - { - let context = &mut InternalMethodPropertyContext::new(context); - - // 1. Assert: Type(target) is Object. - // 2. Assert: excludedItems is a List of property keys. - // 3. If source is undefined or null, return target. - if source.is_null_or_undefined() { - return Ok(()); - } - - // 4. Let from be ! ToObject(source). - let from = source - .to_object(context) - .expect("function ToObject should never complete abruptly here"); - - // 5. Let keys be ? from.[[OwnPropertyKeys]](). - // 6. For each element nextKey of keys, do - let excluded_keys: Vec = excluded_keys.into_iter().map(Into::into).collect(); - for key in from.__own_property_keys__(context)? { - // a. Let excluded be false. - let mut excluded = false; - - // b. For each element e of excludedItems, do - for e in &excluded_keys { - // i. If SameValue(e, nextKey) is true, then - if *e == key { - // 1. Set excluded to true. - excluded = true; - break; - } - } - // c. If excluded is false, then - if !excluded { - // i. Let desc be ? from.[[GetOwnProperty]](nextKey). - let desc = from.__get_own_property__(&key, context)?; - - // ii. If desc is not undefined and desc.[[Enumerable]] is true, then - if let Some(desc) = desc - && let Some(enumerable) = desc.enumerable() - && enumerable - { - // 1. Let propValue be ? Get(from, nextKey). - let prop_value = from.__get__(&key, from.clone().into(), context)?; - - // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue). - self.create_data_property_or_throw(key, prop_value, context) - .expect("CreateDataPropertyOrThrow should never complete abruptly here"); - } - } - } - - // 7. Return target. - Ok(()) - } - // Allow lint, false positive. #[allow(clippy::assigning_clones)] pub(crate) fn get_property(&self, key: &PropertyKey) -> Option { diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index 4dc0c027610..4b913333878 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -177,7 +177,41 @@ impl JsObject { self.__define_own_property__(&key.into(), new_desc.into(), context) } - // todo: CreateMethodProperty + /// `10.2.8 DefineMethodProperty ( homeObject, key, closure, enumerable )` + /// + /// Defines a method property on an object with the specified attributes. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-definemethodproperty + pub(crate) fn define_method_property( + &self, + key: K, + value: V, + context: &mut InternalMethodPropertyContext<'_>, + ) -> JsResult<()> + where + K: Into, + V: Into, + { + // 1. Assert: homeObject is an ordinary, extensible object with no non-configurable properties. + // 2. If key is a Private Name, then + // a. Return PrivateElement { [[Key]]: key, [[Kind]]: method, [[Value]]: closure }. + // 3. Else, + // a. Let desc be the PropertyDescriptor { [[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true }. + let new_desc = PropertyDescriptor::builder() + .value(value) + .writable(true) + .enumerable(false) + .configurable(true); + + // b. Perform ! DefinePropertyOrThrow(homeObject, key, desc). + self.__define_own_property__(&key.into(), new_desc.into(), context)?; + + // c. Return unused. + Ok(()) + } /// Create data property or throw /// @@ -842,7 +876,74 @@ impl JsObject { Ok(context.realm().clone()) } - // todo: CopyDataProperties + /// `7.3.26 CopyDataProperties ( target, source, excludedItems )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-copydataproperties + pub fn copy_data_properties( + &self, + source: &JsValue, + excluded_keys: Vec, + context: &mut Context, + ) -> JsResult<()> + where + K: Into, + { + let context = &mut InternalMethodPropertyContext::new(context); + + // 1. Assert: Type(target) is Object. + // 2. Assert: excludedItems is a List of property keys. + // 3. If source is undefined or null, return target. + if source.is_null_or_undefined() { + return Ok(()); + } + + // 4. Let from be ! ToObject(source). + let from = source + .to_object(context) + .expect("function ToObject should never complete abruptly here"); + + // 5. Let keys be ? from.[[OwnPropertyKeys]](). + // 6. For each element nextKey of keys, do + let excluded_keys: Vec = excluded_keys.into_iter().map(Into::into).collect(); + for key in from.__own_property_keys__(context)? { + // a. Let excluded be false. + let mut excluded = false; + + // b. For each element e of excludedItems, do + for e in &excluded_keys { + // i. If SameValue(e, nextKey) is true, then + if *e == key { + // 1. Set excluded to true. + excluded = true; + break; + } + } + // c. If excluded is false, then + if !excluded { + // i. Let desc be ? from.[[GetOwnProperty]](nextKey). + let desc = from.__get_own_property__(&key, context)?; + + // ii. If desc is not undefined and desc.[[Enumerable]] is true, then + if let Some(desc) = desc + && let Some(enumerable) = desc.enumerable() + && enumerable + { + // 1. Let propValue be ? Get(from, nextKey). + let prop_value = from.__get__(&key, from.clone().into(), context)?; + + // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue). + self.create_data_property_or_throw(key, prop_value, context) + .expect("CreateDataPropertyOrThrow should never complete abruptly here"); + } + } + } + + // 7. Return target. + Ok(()) + } /// Abstract operation `PrivateElementFind ( O, P )` /// diff --git a/core/engine/src/vm/opcode/define/class/method.rs b/core/engine/src/vm/opcode/define/class/method.rs index 7282deeec8d..52a5cfd3ccc 100644 --- a/core/engine/src/vm/opcode/define/class/method.rs +++ b/core/engine/src/vm/opcode/define/class/method.rs @@ -39,14 +39,9 @@ impl DefineClassStaticMethodByName { .set_home_object(class.clone()); } - class.__define_own_property__( - &key, - PropertyDescriptor::builder() - .value(function.clone()) - .writable(true) - .enumerable(false) - .configurable(true) - .build(), + class.define_method_property( + key, + function.clone(), &mut InternalMethodPropertyContext::new(context), )?; Ok(()) @@ -92,14 +87,9 @@ impl DefineClassMethodByName { .set_home_object(class_proto.clone()); } - class_proto.__define_own_property__( - &key, - PropertyDescriptor::builder() - .value(function.clone()) - .writable(true) - .enumerable(false) - .configurable(true) - .build(), + class_proto.define_method_property( + key, + function.clone(), &mut InternalMethodPropertyContext::new(context), )?; Ok(()) @@ -194,14 +184,9 @@ impl DefineClassMethodByValue { .set_home_object(class_proto.clone()); } - class_proto.__define_own_property__( - &key, - PropertyDescriptor::builder() - .value(function.clone()) - .writable(true) - .enumerable(false) - .configurable(true) - .build(), + class_proto.define_method_property( + key, + function.clone(), &mut InternalMethodPropertyContext::new(context), )?; Ok(())