avm1: Match Flash's property enumeration order (fix #153)

This commit is contained in:
Mike Welsh 2020-03-27 17:12:50 -07:00
parent 2cdf780e6f
commit 8da9487c0a
4 changed files with 23 additions and 21 deletions

View File

@ -1522,7 +1522,7 @@ impl<'gc> Avm1<'gc> {
match object { match object {
Value::Object(ob) => { Value::Object(ob) => {
for k in ob.get_keys(self) { for k in ob.get_keys(self).into_iter().rev() {
self.push(k); self.push(k);
} }
} }
@ -1539,7 +1539,7 @@ impl<'gc> Avm1<'gc> {
let object = self.pop().as_object()?; let object = self.pop().as_object()?;
self.push(Value::Null); // Sentinel that indicates end of enumeration self.push(Value::Null); // Sentinel that indicates end of enumeration
for k in object.get_keys(self) { for k in object.get_keys(self).into_iter().rev() {
self.push(k); self.push(k);
} }

View File

@ -463,20 +463,23 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
let proto_keys = self.proto().map_or_else(Vec::new, |p| p.get_keys(avm)); let proto_keys = self.proto().map_or_else(Vec::new, |p| p.get_keys(avm));
let mut out_keys = vec![]; let mut out_keys = vec![];
let object = self.0.read(); let object = self.0.read();
for key in proto_keys {
if !object.values.contains_key(&key, avm.is_case_sensitive()) { // Prototype keys come first.
out_keys.push(key); out_keys.extend(
} proto_keys
} .into_iter()
for key in self.0.read().values.iter().filter_map(move |(k, p)| { .filter(|k| !object.values.contains_key(k, avm.is_case_sensitive())),
);
// Then our own keys.
out_keys.extend(self.0.read().values.iter().filter_map(move |(k, p)| {
if p.is_enumerable() { if p.is_enumerable() {
Some(k.to_string()) Some(k.to_string())
} else { } else {
None None
} }
}) { }));
out_keys.push(key)
}
out_keys out_keys
} }

View File

@ -255,14 +255,11 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
// Keys from the underlying object are listed first, followed by // Keys from the underlying object are listed first, followed by
// child display objects in order from highest depth to lowest depth. // child display objects in order from highest depth to lowest depth.
let mut keys = self.base.get_keys(avm); let mut keys = self.base.get_keys(avm);
for child in self keys.extend(
.display_object self.display_object
.children() .children()
.map(|child| child.name().to_string()) .map(|child| child.name().to_string()),
{ );
keys.push(child)
}
keys keys
} }

View File

@ -81,12 +81,14 @@ impl<V> PropertyMap<V> {
} }
} }
/// Returns the value tuples in Flash's iteration order (most recently added first).
pub fn iter(&self) -> impl Iterator<Item = (&String, &V)> { pub fn iter(&self) -> impl Iterator<Item = (&String, &V)> {
self.0.iter().map(|(k, v)| (&k.0, v)) self.0.iter().rev().map(|(k, v)| (&k.0, v))
} }
/// Returns the key-value tuples in Flash's iteration order (most recently added first).
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&String, &mut V)> { pub fn iter_mut(&mut self) -> impl Iterator<Item = (&String, &mut V)> {
self.0.iter_mut().map(|(k, v)| (&k.0, v)) self.0.iter_mut().rev().map(|(k, v)| (&k.0, v))
} }
pub fn remove(&mut self, key: &str, case_sensitive: bool) -> Option<V> { pub fn remove(&mut self, key: &str, case_sensitive: bool) -> Option<V> {