avm2: Implement `Proxy.deleteproperty`

This commit is contained in:
David Wendt 2021-11-04 20:53:18 -04:00 committed by kmeisthax
parent 503dc08594
commit 427b2bf17a
6 changed files with 77 additions and 11 deletions

View File

@ -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)
}

View File

@ -687,17 +687,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
Ok(base.delete_property(name))
}
/// Delete a named property from the object.
/// Delete a property that does not exist.
///
/// Returns false if the property cannot be deleted.
fn delete_property(
/// 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,
gc_context: MutationContext<'gc, '_>,
multiname: &Multiname<'gc>,
_activation: &mut Activation<'_, 'gc, '_>,
_multiname: &Multiname<'gc>,
) -> Result<bool, Error> {
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()
@ -705,6 +705,20 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
.unwrap_or(false));
}
/// Delete a named property from the object.
///
/// Returns false if the property cannot be deleted.
fn delete_property(
&self,
activation: &mut Activation<'_, 'gc, '_>,
multiname: &Multiname<'gc>,
) -> Result<bool, Error> {
let name = self.resolve_multiname(multiname)?;
if name.is_none() {
return self.delete_property_undef(activation, multiname);
}
//At this point, the name should be known.
let name = name.unwrap();
@ -732,7 +746,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + 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.

View File

@ -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<bool, Error> {
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));
}
}

View File

@ -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 {
@ -72,3 +77,6 @@ trace(delete p.proxy_var);
trace("///delete p.proxy_fn");
trace(delete p.proxy_fn);
trace("///delete p.crazy_return");
trace(delete p.crazy_return);

View File

@ -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