avm2: Implement `indexOf` and `lastIndexOf`

This commit is contained in:
David Wendt 2020-08-29 19:44:22 -04:00 committed by Mike Welsh
parent 7aa1ab82e4
commit 0ece924877
11 changed files with 176 additions and 1 deletions

View File

@ -2,6 +2,15 @@
use crate::avm2::value::Value;
use gc_arena::Collect;
use std::iter::ExactSizeIterator;
/// Trait which exists purely so that we can reverse the iterators that come
/// out of `ArrayStorage.iter`.
///
/// Not to be confused with the `ArrayIterator` struct in `globals::array`.
pub trait ArrayIterator: DoubleEndedIterator + ExactSizeIterator {}
impl<T> ArrayIterator for T where T: DoubleEndedIterator + ExactSizeIterator {}
/// The array storage portion of an array object.
///
@ -93,7 +102,7 @@ impl<'gc> ArrayStorage<'gc> {
}
/// Iterate over array values.
pub fn iter<'a>(&'a self) -> impl Iterator<Item = Option<Value<'gc>>> + 'a {
pub fn iter<'a>(&'a self) -> impl ArrayIterator<Item = Option<Value<'gc>>> + 'a {
self.storage.iter().cloned()
}
}

View File

@ -452,6 +452,64 @@ pub fn some<'gc>(
Ok(Value::Undefined)
}
/// Implements `Array.indexOf`
pub fn index_of<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
if let Some(array) = this.as_array_storage() {
let search_val = args.get(0).cloned().unwrap_or(Value::Undefined);
let from = args
.get(1)
.cloned()
.unwrap_or_else(|| 0.into())
.coerce_to_u32(activation)?;
for (i, val) in array.iter().enumerate() {
let val = resolve_array_hole(activation, this, i, val)?;
if i >= from as usize && val == search_val {
return Ok(i.into());
}
}
return Ok((-1).into());
}
}
Ok(Value::Undefined)
}
/// Implements `Array.lastIndexOf`
pub fn last_index_of<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
if let Some(array) = this.as_array_storage() {
let search_val = args.get(0).cloned().unwrap_or(Value::Undefined);
let from = args
.get(1)
.cloned()
.unwrap_or_else(|| i32::MAX.into())
.coerce_to_u32(activation)?;
for (i, val) in array.iter().enumerate().rev() {
let val = resolve_array_hole(activation, this, i, val)?;
if i <= from as usize && val == search_val {
return Ok(i.into());
}
}
return Ok((-1).into());
}
}
Ok(Value::Undefined)
}
/// Construct `Array`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
let class = Class::new(
@ -512,5 +570,15 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
Method::from_builtin(some),
));
class.write(mc).define_instance_trait(Trait::from_method(
QName::new(Namespace::public_namespace(), "indexOf"),
Method::from_builtin(index_of),
));
class.write(mc).define_instance_trait(Trait::from_method(
QName::new(Namespace::public_namespace(), "lastIndexOf"),
Method::from_builtin(last_index_of),
));
class
}

View File

@ -369,6 +369,8 @@ swf_tests! {
(as3_array_filter, "avm2/array_filter", 1),
(as3_array_every, "avm2/array_every", 1),
(as3_array_some, "avm2/array_some", 1),
(as3_array_indexof, "avm2/array_indexof", 1),
(as3_array_lastindexof, "avm2/array_lastindexof", 1),
}
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -0,0 +1,31 @@
package {
public class Test {
}
}
trace("//var a = new Array(5,\"5\",3,false,4,5,undefined,9)");
var a = new Array(5,"5",3,false,4,5,undefined,9);
trace("//a.indexOf(5);")
trace(a.indexOf(5));
trace("//a.indexOf(5, 1);")
trace(a.indexOf(5, 1));
trace("//a.indexOf(5, 2);")
trace(a.indexOf(5, 2));
trace("//a.indexOf(5, 6);")
trace(a.indexOf(5, 6));
trace("//a.indexOf(5, 10);")
trace(a.indexOf(5, 10));
trace("//a.indexOf(true);");
trace(a.indexOf(true));
trace("//a.indexOf(undefined);");
trace(a.indexOf(undefined));
trace("//a.indexOf(\"5\");");
trace(a.indexOf("5"));

View File

@ -0,0 +1,17 @@
//var a = new Array(5,"5",3,false,4,5,undefined,9)
//a.indexOf(5);
0
//a.indexOf(5, 1);
5
//a.indexOf(5, 2);
5
//a.indexOf(5, 6);
-1
//a.indexOf(5, 10);
-1
//a.indexOf(true);
-1
//a.indexOf(undefined);
6
//a.indexOf("5");
1

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,31 @@
package {
public class Test {
}
}
trace("//var a = new Array(5,\"5\",3,false,4,5,undefined,9)");
var a = new Array(5,"5",3,false,4,5,undefined,9);
trace("//a.lastIndexOf(5);")
trace(a.lastIndexOf(5));
trace("//a.lastIndexOf(5, 1);")
trace(a.lastIndexOf(5, 1));
trace("//a.lastIndexOf(5, 2);")
trace(a.lastIndexOf(5, 2));
trace("//a.lastIndexOf(5, 6);")
trace(a.lastIndexOf(5, 6));
trace("//a.lastIndexOf(5, 10);")
trace(a.lastIndexOf(5, 10));
trace("//a.lastIndexOf(true);");
trace(a.lastIndexOf(true));
trace("//a.lastIndexOf(undefined);");
trace(a.lastIndexOf(undefined));
trace("//a.lastIndexOf(\"5\");");
trace(a.lastIndexOf("5"));

View File

@ -0,0 +1,17 @@
//var a = new Array(5,"5",3,false,4,5,undefined,9)
//a.lastIndexOf(5);
5
//a.lastIndexOf(5, 1);
0
//a.lastIndexOf(5, 2);
0
//a.lastIndexOf(5, 6);
5
//a.lastIndexOf(5, 10);
5
//a.lastIndexOf(true);
-1
//a.lastIndexOf(undefined);
6
//a.lastIndexOf("5");
1

Binary file not shown.

Binary file not shown.