avm2: Implement resolveValue
This commit is contained in:
parent
4285d998dc
commit
709306be81
|
@ -53,7 +53,7 @@ impl<'gc> XmlListObject<'gc> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
children: Vec<E4XOrXml<'gc>>,
|
children: Vec<E4XOrXml<'gc>>,
|
||||||
target_object: Option<Object<'gc>>,
|
target_object: Option<XmlOrXmlListObject<'gc>>,
|
||||||
target_property: Option<Multiname<'gc>>,
|
target_property: Option<Multiname<'gc>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = ScriptObjectData::new(activation.context.avm2.classes().xml_list);
|
let base = ScriptObjectData::new(activation.context.avm2.classes().xml_list);
|
||||||
|
@ -97,7 +97,7 @@ impl<'gc> XmlListObject<'gc> {
|
||||||
self.0.write(mc).children = children;
|
self.0.write(mc).children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_object(&self) -> Option<Object<'gc>> {
|
pub fn target_object(&self) -> Option<XmlOrXmlListObject<'gc>> {
|
||||||
self.0.read().target_object
|
self.0.read().target_object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,67 @@ impl<'gc> XmlListObject<'gc> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_value(
|
||||||
|
&self,
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
) -> Result<Option<XmlOrXmlListObject<'gc>>, Error<'gc>> {
|
||||||
|
// 1. If x.[[Length]] > 0, return x
|
||||||
|
if self.length() > 0 {
|
||||||
|
Ok(Some(XmlOrXmlListObject::XmlList(*self)))
|
||||||
|
// 2. Else
|
||||||
|
} else {
|
||||||
|
// 2.a. If (x.[[TargetObject]] == null)
|
||||||
|
let Some(target_object) = self.target_object() else {
|
||||||
|
// 2.a.i. Return null
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
// or (x.[[TargetProperty]] == null)
|
||||||
|
let Some(target_property) = self.target_property() else {
|
||||||
|
// 2.a.i. Return null
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// or (type(x.[[TargetProperty]]) is AttributeName) or (x.[[TargetProperty]].localName == "*")
|
||||||
|
if target_property.is_attribute() || target_property.is_any_name() {
|
||||||
|
// 2.a.i. Return null
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.b. Let base be the result of calling the [[ResolveValue]] method of x.[[TargetObject]] recursively
|
||||||
|
let Some(base) = target_object.resolve_value(activation)? else {
|
||||||
|
// 2.c. If base == null, return null
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2.d. Let target be the result of calling [[Get]] on base with argument x.[[TargetProperty]]
|
||||||
|
let Some(target) = base.get_property_local(&target_property, activation)? else {
|
||||||
|
// NOTE: Not specified in spec, but avmplus checks if null/undefined was returned, so we do the same, since there is
|
||||||
|
// an invariant in get_property_local of XmlListObject/XmlObject.
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2.e. If (target.[[Length]] == 0)
|
||||||
|
if target.length().unwrap_or(0) == 0 {
|
||||||
|
// 2.e.i. If (Type(base) is XMLList) and (base.[[Length]] > 1), return null
|
||||||
|
if let XmlOrXmlListObject::XmlList(x) = &base {
|
||||||
|
if x.length() > 1 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.e.ii. Call [[Put]] on base with arguments x.[[TargetProperty]] and the empty string
|
||||||
|
base.as_object()
|
||||||
|
.set_property_local(&target_property, "".into(), activation)?;
|
||||||
|
|
||||||
|
// 2.e.iii. Let target be the result of calling [[Get]] on base with argument x.[[TargetProperty]]
|
||||||
|
return base.get_property_local(&target_property, activation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.f. Return target
|
||||||
|
Ok(Some(target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn equals(
|
pub fn equals(
|
||||||
&self,
|
&self,
|
||||||
other: &Value<'gc>,
|
other: &Value<'gc>,
|
||||||
|
@ -189,7 +250,7 @@ pub struct XmlListObjectData<'gc> {
|
||||||
/// The XML or XMLList object that this list was created from.
|
/// The XML or XMLList object that this list was created from.
|
||||||
/// If `Some`, then modifications to this list are reflected
|
/// If `Some`, then modifications to this list are reflected
|
||||||
/// in the original object.
|
/// in the original object.
|
||||||
target_object: Option<Object<'gc>>,
|
target_object: Option<XmlOrXmlListObject<'gc>>,
|
||||||
|
|
||||||
target_property: Option<Multiname<'gc>>,
|
target_property: Option<Multiname<'gc>>,
|
||||||
}
|
}
|
||||||
|
@ -244,6 +305,79 @@ impl<'a, 'gc> Deref for E4XWrapper<'a, 'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents either a XmlObject or a XmlListObject. Used
|
||||||
|
/// for resolving the value of empty XMLLists.
|
||||||
|
#[derive(Clone, Collect, Copy, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub enum XmlOrXmlListObject<'gc> {
|
||||||
|
XmlList(XmlListObject<'gc>),
|
||||||
|
Xml(XmlObject<'gc>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> XmlOrXmlListObject<'gc> {
|
||||||
|
pub fn length(&self) -> Option<usize> {
|
||||||
|
match self {
|
||||||
|
XmlOrXmlListObject::Xml(x) => x.length(),
|
||||||
|
XmlOrXmlListObject::XmlList(x) => Some(x.length()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_value(
|
||||||
|
&self,
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
) -> Result<Option<XmlOrXmlListObject<'gc>>, Error<'gc>> {
|
||||||
|
match self {
|
||||||
|
// NOTE: XmlObjects just resolve to themselves.
|
||||||
|
XmlOrXmlListObject::Xml(x) => Ok(Some(XmlOrXmlListObject::Xml(*x))),
|
||||||
|
XmlOrXmlListObject::XmlList(x) => x.resolve_value(activation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_object(&self) -> Object<'gc> {
|
||||||
|
match self {
|
||||||
|
XmlOrXmlListObject::Xml(x) => Object::XmlObject(*x),
|
||||||
|
XmlOrXmlListObject::XmlList(x) => Object::XmlListObject(*x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_property_local(
|
||||||
|
&self,
|
||||||
|
name: &Multiname<'gc>,
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
) -> Result<Option<XmlOrXmlListObject<'gc>>, Error<'gc>> {
|
||||||
|
let value = self.as_object().get_property_local(name, activation)?;
|
||||||
|
|
||||||
|
if let Some(xml) = value.as_object().and_then(|x| x.as_xml_object()) {
|
||||||
|
return Ok(Some(XmlOrXmlListObject::Xml(xml)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(list) = value.as_object().and_then(|x| x.as_xml_list_object()) {
|
||||||
|
return Ok(Some(XmlOrXmlListObject::XmlList(list)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(value, Value::Null | Value::Undefined) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!(
|
||||||
|
"Invalid value {:?}, expected XmlListObject/XmlObject or a null value",
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> From<XmlListObject<'gc>> for XmlOrXmlListObject<'gc> {
|
||||||
|
fn from(value: XmlListObject<'gc>) -> XmlOrXmlListObject<'gc> {
|
||||||
|
XmlOrXmlListObject::XmlList(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> From<XmlObject<'gc>> for XmlOrXmlListObject<'gc> {
|
||||||
|
fn from(value: XmlObject<'gc>) -> XmlOrXmlListObject<'gc> {
|
||||||
|
XmlOrXmlListObject::Xml(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'gc> TObject<'gc> for XmlListObject<'gc> {
|
impl<'gc> TObject<'gc> for XmlListObject<'gc> {
|
||||||
fn base(&self) -> Ref<ScriptObjectData<'gc>> {
|
fn base(&self) -> Ref<ScriptObjectData<'gc>> {
|
||||||
Ref::map(self.0.read(), |read| &read.base)
|
Ref::map(self.0.read(), |read| &read.base)
|
||||||
|
|
|
@ -69,6 +69,11 @@ impl<'gc> XmlObject<'gc> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> Option<usize> {
|
||||||
|
self.node().length()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_node(&self, mc: &Mutation<'gc>, node: E4XNode<'gc>) {
|
pub fn set_node(&self, mc: &Mutation<'gc>, node: E4XNode<'gc>) {
|
||||||
self.0.write(mc).node = node;
|
self.0.write(mc).node = node;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue