avm2: Implement `indexOf` and `lastIndexOf`
This commit is contained in:
parent
7aa1ab82e4
commit
0ece924877
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use gc_arena::Collect;
|
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.
|
/// The array storage portion of an array object.
|
||||||
///
|
///
|
||||||
|
@ -93,7 +102,7 @@ impl<'gc> ArrayStorage<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over array values.
|
/// 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()
|
self.storage.iter().cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,6 +452,64 @@ pub fn some<'gc>(
|
||||||
Ok(Value::Undefined)
|
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.
|
/// Construct `Array`'s class.
|
||||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
let class = Class::new(
|
let class = Class::new(
|
||||||
|
@ -512,5 +570,15 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
||||||
Method::from_builtin(some),
|
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
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,6 +369,8 @@ swf_tests! {
|
||||||
(as3_array_filter, "avm2/array_filter", 1),
|
(as3_array_filter, "avm2/array_filter", 1),
|
||||||
(as3_array_every, "avm2/array_every", 1),
|
(as3_array_every, "avm2/array_every", 1),
|
||||||
(as3_array_some, "avm2/array_some", 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.
|
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||||
|
|
|
@ -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"));
|
|
@ -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.
|
@ -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"));
|
|
@ -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.
Loading…
Reference in New Issue