avm2: Impl `Proxy`'s enumeration-related methods.

This commit is contained in:
David Wendt 2021-11-05 22:02:46 -04:00 committed by kmeisthax
parent 4289f89350
commit 4cfa3253d5
6 changed files with 115 additions and 33 deletions

View File

@ -2442,7 +2442,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let cur_index = self.context.avm2.pop().coerce_to_u32(self)?; let cur_index = self.context.avm2.pop().coerce_to_u32(self)?;
let object = self.context.avm2.pop().coerce_to_object(self)?; let object = self.context.avm2.pop().coerce_to_object(self)?;
if let Some(next_index) = object.get_next_enumerant(cur_index) { if let Some(next_index) = object.get_next_enumerant(cur_index, self)? {
self.context.avm2.push(next_index); self.context.avm2.push(next_index);
} else { } else {
self.context.avm2.push(0.0); self.context.avm2.push(0.0);
@ -2463,7 +2463,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
); );
while let Some(cur_object) = object { while let Some(cur_object) = object {
if let Some(index) = cur_object.get_next_enumerant(cur_index) { if let Some(index) = cur_object.get_next_enumerant(cur_index, self)? {
cur_index = index; cur_index = index;
break; break;
} else { } else {
@ -2491,9 +2491,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let cur_index = self.context.avm2.pop().coerce_to_number(self)?; let cur_index = self.context.avm2.pop().coerce_to_number(self)?;
let object = self.context.avm2.pop().coerce_to_object(self)?; let object = self.context.avm2.pop().coerce_to_object(self)?;
let name = object.get_enumerant_name(cur_index as u32); let name = object.get_enumerant_name(cur_index as u32, self)?;
self.context.avm2.push(name.unwrap_or(Value::Undefined)); self.context.avm2.push(name);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }

View File

@ -794,10 +794,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// Repeated calls to this function with prior return values must /// Repeated calls to this function with prior return values must
/// eventually return `None`. Furthermore, returning `0`, while valid, is /// eventually return `None`. Furthermore, returning `0`, while valid, is
/// treated by AVM2 code as signalling `None`. /// treated by AVM2 code as signalling `None`.
fn get_next_enumerant(self, last_index: u32) -> Option<u32> { fn get_next_enumerant(
self,
last_index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Option<u32>, Error> {
let base = self.base(); let base = self.base();
base.get_next_enumerant(last_index) Ok(base.get_next_enumerant(last_index))
} }
/// Retrieve a given enumerable name by index. /// Retrieve a given enumerable name by index.
@ -806,10 +810,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// `get_next_enumerant`. Only enumerants returned by that function are /// `get_next_enumerant`. Only enumerants returned by that function are
/// valid here. A value of `None` indicates that no enumerant with that /// valid here. A value of `None` indicates that no enumerant with that
/// index exists. /// index exists.
fn get_enumerant_name(self, index: u32) -> Option<Value<'gc>> { fn get_enumerant_name(
self,
index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
let base = self.base(); let base = self.base();
base.get_enumerant_name(index) Ok(base.get_enumerant_name(index).unwrap_or(Value::Undefined))
} }
/// Retrieve a given enumerable value by index. /// Retrieve a given enumerable value by index.
@ -821,13 +829,10 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
index: u32, index: u32,
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let name = self.get_enumerant_name(index); let name = self
if let Some(name) = name { .get_enumerant_name(index, activation)?
let name = name.coerce_to_string(activation)?; .coerce_to_string(activation)?;
Ok(self.get_property(self.into(), &QName::dynamic_name(name).into(), activation)?) self.get_property(self.into(), &QName::dynamic_name(name).into(), activation)
} else {
Ok(Value::Undefined)
}
} }
/// Determine if a property is currently enumerable. /// Determine if a property is currently enumerable.

View File

@ -199,24 +199,38 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> {
self.0.read().base.resolve_any(local_name) self.0.read().base.resolve_any(local_name)
} }
fn get_next_enumerant(self, last_index: u32) -> Option<u32> { fn get_next_enumerant(
self,
last_index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Option<u32>, Error> {
let read = self.0.read(); let read = self.0.read();
let last_enumerant = read.base.get_last_enumerant(); let last_enumerant = read.base.get_last_enumerant();
let array_length = read.array.length() as u32; let array_length = read.array.length() as u32;
if last_index < last_enumerant + array_length { if last_index < last_enumerant + array_length {
Some(last_index.saturating_add(1)) Ok(Some(last_index.saturating_add(1)))
} else { } else {
None Ok(None)
} }
} }
fn get_enumerant_name(self, index: u32) -> Option<Value<'gc>> { fn get_enumerant_name(
self,
index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
let arr_len = self.0.read().array.length() as u32; let arr_len = self.0.read().array.length() as u32;
if arr_len >= index { if arr_len >= index {
index.checked_sub(1).map(|index| index.into()) Ok(index
.checked_sub(1)
.map(|index| index.into())
.unwrap_or(Value::Undefined))
} else { } else {
self.base().get_enumerant_name(index - arr_len) Ok(self
.base()
.get_enumerant_name(index - arr_len)
.unwrap_or(Value::Undefined))
} }
} }

View File

@ -111,32 +111,45 @@ impl<'gc> TObject<'gc> for DictionaryObject<'gc> {
Some(self) Some(self)
} }
fn get_next_enumerant(self, last_index: u32) -> Option<u32> { fn get_next_enumerant(
self,
last_index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Option<u32>, Error> {
let read = self.0.read(); let read = self.0.read();
let last_enumerant = read.base.get_last_enumerant(); let last_enumerant = read.base.get_last_enumerant();
let object_space_length = read.object_space.keys().len() as u32; let object_space_length = read.object_space.keys().len() as u32;
if last_index < last_enumerant + object_space_length { if last_index < last_enumerant + object_space_length {
Some(last_index.saturating_add(1)) Ok(Some(last_index.saturating_add(1)))
} else { } else {
None Ok(None)
} }
} }
fn get_enumerant_name(self, index: u32) -> Option<Value<'gc>> { fn get_enumerant_name(
self,
index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
let read = self.0.read(); let read = self.0.read();
let last_enumerant = read.base.get_last_enumerant(); let last_enumerant = read.base.get_last_enumerant();
if index < last_enumerant { if index < last_enumerant {
read.base.get_enumerant_name(index) Ok(read
.base
.get_enumerant_name(index)
.unwrap_or(Value::Undefined))
} else { } else {
let object_space_index = index.saturating_sub(last_enumerant); let object_space_index = index.saturating_sub(last_enumerant);
read.object_space Ok(read
.object_space
.keys() .keys()
.nth(object_space_index as usize) .nth(object_space_index as usize)
.cloned() .cloned()
.map(|v| v.into()) .map(|v| v.into())
.unwrap_or(Value::Undefined))
} }
} }
} }

View File

@ -221,4 +221,43 @@ impl<'gc> TObject<'gc> for ProxyObject<'gc> {
)? )?
.coerce_to_boolean()) .coerce_to_boolean())
} }
fn get_next_enumerant(
self,
last_index: u32,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Option<u32>, Error> {
Ok(Some(
self.call_property(
&QName::new(Namespace::Namespace(NS_FLASH_PROXY.into()), "nextNameIndex").into(),
&[last_index.into()],
activation,
)?
.coerce_to_u32(activation)?,
))
}
fn get_enumerant_name(
self,
index: u32,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
self.call_property(
&QName::new(Namespace::Namespace(NS_FLASH_PROXY.into()), "nextName").into(),
&[index.into()],
activation,
)
}
fn get_enumerant_value(
self,
index: u32,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
self.call_property(
&QName::new(Namespace::Namespace(NS_FLASH_PROXY.into()), "nextValue").into(),
&[index.into()],
activation,
)
}
} }

View File

@ -227,19 +227,30 @@ impl<'gc> TObject<'gc> for VectorObject<'gc> {
self.0.read().base.resolve_any(local_name) self.0.read().base.resolve_any(local_name)
} }
fn get_next_enumerant(self, last_index: u32) -> Option<u32> { fn get_next_enumerant(
self,
last_index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Option<u32>, Error> {
if last_index < self.0.read().vector.length() as u32 { if last_index < self.0.read().vector.length() as u32 {
Some(last_index.saturating_add(1)) Ok(Some(last_index.saturating_add(1)))
} else { } else {
None Ok(None)
} }
} }
fn get_enumerant_name(self, index: u32) -> Option<Value<'gc>> { fn get_enumerant_name(
self,
index: u32,
_activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
if self.0.read().vector.length() as u32 >= index { if self.0.read().vector.length() as u32 >= index {
index.checked_sub(1).map(|index| index.into()) Ok(index
.checked_sub(1)
.map(|index| index.into())
.unwrap_or(Value::Undefined))
} else { } else {
None Ok("".into())
} }
} }