From c0aedcc18f26f68092bd111b2830c169a9585d44 Mon Sep 17 00:00:00 2001 From: Lord-McSweeney Date: Sun, 12 Jan 2025 12:57:35 -0800 Subject: [PATCH] avm2: Change ArrayIter to use `get_index_property` instead of `get_public_property` --- core/src/avm2/globals/array.rs | 70 ++++++++++++++++----------------- core/src/avm2/globals/json.rs | 6 +-- core/src/avm2/globals/vector.rs | 31 ++++----------- 3 files changed, 44 insertions(+), 63 deletions(-) diff --git a/core/src/avm2/globals/array.rs b/core/src/avm2/globals/array.rs index c491c55ce5c9..723349c1a884 100644 --- a/core/src/avm2/globals/array.rs +++ b/core/src/avm2/globals/array.rs @@ -3,7 +3,7 @@ use crate::avm2::activation::Activation; use crate::avm2::array::ArrayStorage; use crate::avm2::class::Class; -use crate::avm2::error::range_error; +use crate::avm2::error::{make_error_1125, range_error}; use crate::avm2::method::{Method, NativeMethodImpl}; use crate::avm2::object::{array_allocator, ArrayObject, FunctionObject, Object, TObject}; use crate::avm2::value::Value; @@ -333,7 +333,7 @@ pub fn to_locale_string<'gc>( /// array while this happens would cause a panic; this code exists to prevent /// that. pub struct ArrayIter<'gc> { - array_object: Value<'gc>, + array_object: Object<'gc>, pub index: u32, pub rev_index: u32, } @@ -359,7 +359,7 @@ impl<'gc> ArrayIter<'gc> { .coerce_to_u32(activation)?; Ok(Self { - array_object: Value::from(array_object), + array_object, index: start_index.min(length), rev_index: end_index.saturating_add(1).min(length), }) @@ -372,22 +372,25 @@ impl<'gc> ArrayIter<'gc> { pub fn next( &mut self, activation: &mut Activation<'_, 'gc>, - ) -> Option), Error<'gc>>> { + ) -> Result)>, Error<'gc>> { if self.index < self.rev_index { let i = self.index; self.index += 1; - Some( - self.array_object - .get_public_property( - AvmString::new_utf8(activation.gc(), i.to_string()), - activation, - ) - .map(|val| (i, val)), - ) + let val = self.array_object.get_index_property(i as usize); + + let val = if let Some(storage) = self.array_object.as_vector_storage() { + // Special case for Vector- it throws an error if trying to access + // an element that was removed + val.ok_or_else(|| make_error_1125(activation, i as usize, storage.length()))? + } else { + val.unwrap_or(Value::Undefined) + }; + + Ok(Some((i, val))) } else { - None + Ok(None) } } @@ -398,22 +401,25 @@ impl<'gc> ArrayIter<'gc> { pub fn next_back( &mut self, activation: &mut Activation<'_, 'gc>, - ) -> Option), Error<'gc>>> { + ) -> Result)>, Error<'gc>> { if self.index < self.rev_index { self.rev_index -= 1; let i = self.rev_index; - Some( - self.array_object - .get_public_property( - AvmString::new_utf8(activation.gc(), i.to_string()), - activation, - ) - .map(|val| (i, val)), - ) + let val = self.array_object.get_index_property(i as usize); + + let val = if let Some(storage) = self.array_object.as_vector_storage() { + // Special case for Vector- it throws an error if trying to access + // an element that was removed + val.ok_or_else(|| make_error_1125(activation, i as usize, storage.length()))? + } else { + val.unwrap_or(Value::Undefined) + }; + + Ok(Some((i, val))) } else { - None + Ok(None) } } } @@ -430,9 +436,7 @@ pub fn for_each<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { callback.call(activation, receiver, &[item, i.into(), this.into()])?; } @@ -452,8 +456,7 @@ pub fn map<'gc>( let mut new_array = ArrayStorage::new(0); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; + while let Some((i, item)) = iter.next(activation)? { let new_item = callback.call(activation, receiver, &[item, i.into(), this.into()])?; new_array.push(new_item); @@ -475,8 +478,7 @@ pub fn filter<'gc>( let mut new_array = ArrayStorage::new(0); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; + while let Some((i, item)) = iter.next(activation)? { let is_allowed = callback .call(activation, receiver, &[item, i.into(), this.into()])? .coerce_to_boolean(); @@ -501,9 +503,7 @@ pub fn every<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { let result = callback .call(activation, receiver, &[item, i.into(), this.into()])? .coerce_to_boolean(); @@ -528,9 +528,7 @@ pub fn some<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { let result = callback .call(activation, receiver, &[item, i.into(), this.into()])? .coerce_to_boolean(); diff --git a/core/src/avm2/globals/json.rs b/core/src/avm2/globals/json.rs index 0e4a527cc6ab..12e9111578b0 100644 --- a/core/src/avm2/globals/json.rs +++ b/core/src/avm2/globals/json.rs @@ -151,8 +151,7 @@ impl<'gc> AvmSerializer<'gc> { // If the user supplied a PropList, we use that to find properties on the object. if let Some(Replacer::PropList(props)) = self.replacer { let mut iter = ArrayIter::new(activation, props.into())?; - while let Some(r) = iter.next(activation) { - let item = r?.1; + while let Some((_, item)) = iter.next(activation)? { let key = item.coerce_to_string(activation)?; let value = Value::from(obj).get_public_property(key, activation)?; let mapped = self.map_value(activation, || key, value)?; @@ -202,8 +201,7 @@ impl<'gc> AvmSerializer<'gc> { ) -> Result> { let mut js_arr = Vec::new(); let mut iter = ArrayIter::new(activation, iterable)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; + while let Some((i, item)) = iter.next(activation)? { let mc = activation.gc(); let mapped = self.map_value(activation, || AvmString::new_utf8(mc, i.to_string()), item)?; diff --git a/core/src/avm2/globals/vector.rs b/core/src/avm2/globals/vector.rs index 5aaec97c6d99..c7c76a7b0ceb 100644 --- a/core/src/avm2/globals/vector.rs +++ b/core/src/avm2/globals/vector.rs @@ -99,8 +99,7 @@ fn class_call<'gc>( let mut iter = ArrayIter::new(activation, arg)?; - while let Some(r) = iter.next(activation) { - let (_, item) = r?; + while let Some((_, item)) = iter.next(activation)? { let coerced_item = item.coerce_to_type(activation, value_type_for_coercion)?; new_storage.push(coerced_item, activation)?; } @@ -398,9 +397,7 @@ pub fn every<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { let result = callback .call(activation, receiver, &[item, i.into(), this.into()])? .coerce_to_boolean(); @@ -425,9 +422,7 @@ pub fn some<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { let result = callback .call(activation, receiver, &[item, i.into(), this.into()])? .coerce_to_boolean(); @@ -458,9 +453,7 @@ pub fn filter<'gc>( let mut new_storage = VectorStorage::new(0, false, value_type, activation); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { let result = callback .call(activation, receiver, &[item, i.into(), this.into()])? .coerce_to_boolean(); @@ -485,9 +478,7 @@ pub fn for_each<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { callback.call(activation, receiver, &[item, i.into(), this.into()])?; } @@ -519,9 +510,7 @@ pub fn index_of<'gc>( let mut iter = ArrayIter::with_bounds(activation, this, from_index, u32::MAX)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { if item == search_for { return Ok(i.into()); } @@ -555,9 +544,7 @@ pub fn last_index_of<'gc>( let mut iter = ArrayIter::with_bounds(activation, this, 0, from_index)?; - while let Some(r) = iter.next_back(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next_back(activation)? { if item == search_for { return Ok(i.into()); } @@ -585,9 +572,7 @@ pub fn map<'gc>( let value_type_for_coercion = new_storage.value_type_for_coercion(activation); let mut iter = ArrayIter::new(activation, this)?; - while let Some(r) = iter.next(activation) { - let (i, item) = r?; - + while let Some((i, item)) = iter.next(activation)? { let new_item = callback.call(activation, receiver, &[item, i.into(), this.into()])?; let coerced_item = new_item.coerce_to_type(activation, value_type_for_coercion)?;