diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index d2153323a..8cadbc083 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -1405,9 +1405,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { (multiname, object) }; - self.context - .avm2 - .push(object.delete_property(self.context.gc_context, &multiname)?); + let did_delete = object.delete_property(self, &multiname)?; + + self.context.avm2.push(did_delete); Ok(FrameControl::Continue) } diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index d325b764c..2ed1bc206 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -687,22 +687,36 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy Ok(base.delete_property(name)) } + /// Delete a property that does not exist. + /// + /// By default, undefined property deletion succeeds for dynamic classes, + /// and fails for sealed ones. Objects that have particular alternative + /// behavior for undefined values may substitute their own implementation + /// here without disturbing the rest of `deleteproperty`'s implementation. + fn delete_property_undef( + &self, + _activation: &mut Activation<'_, 'gc, '_>, + _multiname: &Multiname<'gc>, + ) -> Result { + // Unknown properties on a dynamic class delete successfully. + return Ok(!self + .instance_of_class_definition() + .map(|c| c.read().is_sealed()) + .unwrap_or(false)); + } + /// Delete a named property from the object. /// /// Returns false if the property cannot be deleted. fn delete_property( &self, - gc_context: MutationContext<'gc, '_>, + activation: &mut Activation<'_, 'gc, '_>, multiname: &Multiname<'gc>, ) -> Result { let name = self.resolve_multiname(multiname)?; if name.is_none() { - // Unknown properties on a dynamic class delete successfully. - return Ok(!self - .instance_of_class_definition() - .map(|c| c.read().is_sealed()) - .unwrap_or(false)); + return self.delete_property_undef(activation, multiname); } //At this point, the name should be known. @@ -732,7 +746,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy } } - self.delete_property_local(gc_context, &name) + self.delete_property_local(activation.context.gc_context, &name) } /// Retrieve the `__proto__` of a given object. diff --git a/core/src/avm2/object/proxy_object.rs b/core/src/avm2/object/proxy_object.rs index 4490cb19e..011513efa 100644 --- a/core/src/avm2/object/proxy_object.rs +++ b/core/src/avm2/object/proxy_object.rs @@ -172,4 +172,39 @@ impl<'gc> TObject<'gc> for ProxyObject<'gc> { ) .into()) } + + fn delete_property_undef( + &self, + activation: &mut Activation<'_, 'gc, '_>, + multiname: &Multiname<'gc>, + ) -> Result { + for namespace in multiname.namespace_set() { + if let Some(local_name) = multiname.local_name() { + if namespace.is_any() || namespace.is_public() || namespace.is_namespace() { + let qname = QNameObject::from_qname( + activation, + QName::new(namespace.clone(), local_name), + )?; + + return Ok(self + .call_property( + &QName::new( + Namespace::Namespace(NS_FLASH_PROXY.into()), + "deleteProperty", + ) + .into(), + &[qname.into()], + activation, + )? + .coerce_to_boolean()); + } + } + } + + // Unknown properties on a dynamic class delete successfully. + return Ok(!self + .instance_of_class_definition() + .map(|c| c.read().is_sealed()) + .unwrap_or(false)); + } } diff --git a/tests/tests/swfs/avm2/proxy_deleteproperty/Test.as b/tests/tests/swfs/avm2/proxy_deleteproperty/Test.as index 4f5a33690..8d5dbcb3c 100644 --- a/tests/tests/swfs/avm2/proxy_deleteproperty/Test.as +++ b/tests/tests/swfs/avm2/proxy_deleteproperty/Test.as @@ -17,6 +17,11 @@ dynamic class TestProxy extends Proxy { trace("///name.uri"); trace(name.uri); + + if (name.localName === "crazy_return") { + trace("///returning non-bool value"); + return "This isn't a bool at all"; + } } else if (name is String) { trace("///type of name is String"); } else { @@ -71,4 +76,7 @@ trace("///delete p.proxy_var"); trace(delete p.proxy_var); trace("///delete p.proxy_fn"); -trace(delete p.proxy_fn); \ No newline at end of file +trace(delete p.proxy_fn); + +trace("///delete p.crazy_return"); +trace(delete p.crazy_return); \ No newline at end of file diff --git a/tests/tests/swfs/avm2/proxy_deleteproperty/output.txt b/tests/tests/swfs/avm2/proxy_deleteproperty/output.txt index 4586c7927..e3037eb42 100644 --- a/tests/tests/swfs/avm2/proxy_deleteproperty/output.txt +++ b/tests/tests/swfs/avm2/proxy_deleteproperty/output.txt @@ -57,3 +57,12 @@ proxy_fn ///name.uri true +///delete p.crazy_return +///attempted to delete property: crazy_return +///type of name is QName +///name.localName +crazy_return +///name.uri + +///returning non-bool value +true diff --git a/tests/tests/swfs/avm2/proxy_deleteproperty/test.swf b/tests/tests/swfs/avm2/proxy_deleteproperty/test.swf index da98fc771..ecbcafb78 100644 Binary files a/tests/tests/swfs/avm2/proxy_deleteproperty/test.swf and b/tests/tests/swfs/avm2/proxy_deleteproperty/test.swf differ