avm2: Impl `Vector.insertAt`

This commit is contained in:
David Wendt 2021-07-09 19:25:39 -04:00 committed by kmeisthax
parent e3ad30a0f7
commit a053015558
7 changed files with 600 additions and 0 deletions

View File

@ -630,6 +630,33 @@ pub fn unshift<'gc>(
Ok(Value::Undefined)
}
/// Implements `Vector.insertAt`
pub fn insert_at<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
if let Some(mut vs) = this.as_vector_storage_mut(activation.context.gc_context) {
let index = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_i32(activation)?;
let value_type = vs.value_type();
let value = args
.get(1)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_type(activation, value_type)?;
vs.insert(index, Some(value))?;
}
}
Ok(Value::Undefined)
}
/// Construct `Vector`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
let class = Class::new(
@ -670,6 +697,7 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
("push", push),
("shift", shift),
("unshift", unshift),
("insertAt", insert_at),
];
write.define_public_builtin_instance_methods(mc, PUBLIC_INSTANCE_METHODS);

View File

@ -5,6 +5,7 @@ use crate::avm2::object::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::Collect;
use std::cmp::max;
/// The vector storage portion of a vector object.
///
@ -224,6 +225,34 @@ impl<'gc> VectorStorage<'gc> {
}
}
/// Insert a value at a specific position in the vector.
///
/// This function returns an error if the vector is fixed.
///
/// This function does no coercion as calling it requires mutably borrowing
/// the vector (and thus it is unwise to reenter the AVM2 runtime to coerce
/// things). You must use the associated `coerce` fn before storing things
/// in the vector.
pub fn insert(&mut self, position: i32, value: Option<Value<'gc>>) -> Result<(), Error> {
if self.is_fixed {
return Err("RangeError: Vector is fixed".into());
}
let position = if position < 0 {
max(position + self.storage.len() as i32, 0) as usize
} else {
position as usize
};
if position >= self.storage.len() {
self.storage.push(value);
} else {
self.storage.insert(position, value);
}
Ok(())
}
/// Iterate over vector values.
pub fn iter<'a>(
&'a self,

View File

@ -643,6 +643,7 @@ swf_tests! {
(as3_vector_map, "avm2/vector_map", 1),
(as3_vector_pushpop, "avm2/vector_pushpop", 1),
(as3_vector_shiftunshift, "avm2/vector_shiftunshift", 1),
(as3_vector_insertat, "avm2/vector_insertat", 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,272 @@
package {
public class Test {
}
}
function trace_vector(v: Vector.<*>) {
trace(v.length, "elements");
for (var i = 0; i < v.length; i += 1) {
trace(v[i]);
}
}
trace("/// var a_bool: Vector.<Boolean> = new <Boolean>[true, false];");
var a_bool:Vector.<Boolean> = new <Boolean>[true, false];
trace("/// var b_bool: Vector.<Boolean> = new <Boolean>[false, true, false];");
var b_bool:Vector.<Boolean> = new <Boolean>[false, true, false];
trace("/// a_bool.insertAt(3, false);");
trace(a_bool.insertAt(3, false));
trace("/// (contents of a_bool...)");
trace_vector(a_bool);
trace("/// a_bool.insertAt(0, false);");
trace(a_bool.insertAt(0, false));
trace("/// (contents of a_bool...)");
trace_vector(a_bool);
trace("/// b_bool.insertAt(-2, false);");
trace(b_bool.insertAt(-2, false));
trace("/// (contents of b_bool...)");
trace_vector(b_bool);
trace("/// b_bool.insertAt(-5, false);");
trace(b_bool.insertAt(-5, false));
trace("/// (contents of b_bool...)");
trace_vector(b_bool);
class Superclass {
}
class Subclass extends Superclass {
}
trace("/// var a0_class = new Superclass();");
var a0_class = new Superclass();
trace("/// var a1_class = new Subclass();");
var a1_class = new Subclass();
trace("/// var a_class: Vector.<Superclass> = new <Superclass>[a0_class, a1_class];");
var a_class:Vector.<Superclass> = new <Superclass>[a0_class, a1_class];
trace("/// var b_class: Vector.<Subclass> = new <Subclass>[];");
var b_class:Vector.<Subclass> = new <Subclass>[];
trace("/// b_class.length = 1;");
b_class.length = 1;
trace("/// b_class[0] = new Subclass();");
b_class[0] = new Subclass();
trace("/// a_class.insertAt(0, new Subclass());");
trace(a_class.insertAt(0, new Subclass()));
trace("/// a0_class === a_class[1];");
trace(a0_class === a_class[1]);
trace("/// a1_class === a_class[2];");
trace(a1_class === a_class[2]);
trace("/// b_class.insertAt(-3, new Subclass());");
trace(b_class.insertAt(-3, new Subclass()));
trace("/// (contents of b_class...)");
trace_vector(b_class);
function trace_vector_int(v: Vector.<int>) {
trace(v.length, "elements");
for (var i = 0; i < v.length; i += 1) {
trace(v[i]);
}
}
trace("/// var a_int: Vector.<int> = new <int>[1,2];");
var a_int:Vector.<int> = new <int>[1,2];
trace("/// var b_int: Vector.<int> = new <int>[5,16];");
var b_int:Vector.<int> = new <int>[5,16];
trace("/// a_int.insertAt(1, 3);");
trace(a_int.insertAt(1, 3));
trace("/// (contents of a_int)...");
trace_vector_int(a_int);
trace("/// a_int.insertAt(6, 4);");
trace(a_int.insertAt(6, 4));
trace("/// (contents of a_int)...");
trace_vector_int(a_int);
trace("/// b_int.insertAt(-5, 3);");
trace(b_int.insertAt(-5, 3));
trace("/// (contents of b_int)...");
trace_vector_int(b_int);
trace("/// b_int.insertAt(-1, 3);");
trace(b_int.insertAt(-1, 3));
trace("/// (contents of b_int)...");
trace_vector_int(b_int);
function trace_vector_number(v: Vector.<Number>) {
trace(v.length, "elements");
for (var i = 0; i < v.length; i += 1) {
trace(v[i]);
}
}
trace("/// var a_number: Vector.<Number> = new <Number>[1,2,3,4];");
var a_number:Vector.<Number> = new <Number>[1,2,3,4];
trace("/// var b_number: Vector.<Number> = new <Number>[5, NaN, -5, 0];");
var b_number:Vector.<Number> = new <Number>[5, NaN, -5, 0];
trace("/// a_number.insertAt(1, 5);");
trace(a_number.insertAt(1, 5));
trace("/// (contents of a_number...)");
trace_vector_number(a_number);
trace("/// a_number.insertAt(9, 6);");
trace(a_number.insertAt(9, 6));
trace("/// (contents of a_number...)");
trace_vector_number(a_number);
trace("/// b_number.insertAt(-4, 23);");
trace(b_number.insertAt(-4, 23));
trace("/// (contents of b_number...)");
trace_vector_number(b_number);
trace("/// b_number.insertAt(-8, 99);");
trace(b_number.insertAt(-8, 99));
trace("/// (contents of b_number...)");
trace_vector_number(b_number);
function trace_vector_string(v: Vector.<String>) {
trace(v.length, "elements");
for (var i = 0; i < v.length; i += 1) {
trace(v[i]);
}
}
trace("/// var a_string: Vector.<String> = new <String>[\"a\",\"c\",\"d\",\"f\"];");
var a_string:Vector.<String> = new <String>["a", "c", "d", "f"];
trace("/// var b_string: Vector.<String> = new <String>[\"986\",\"B4\",\"Q\",\"rrr\"];");
var b_string:Vector.<String> = new <String>["986", "B4", "Q", "rrr"];
trace("/// a_string.insertAt(1, \"g\");");
trace(a_string.insertAt(1, "g"));
trace("/// (contents of a_string...)");
trace_vector_string(a_string);
trace("/// a_string.insertAt(8, \"h\");");
trace(a_string.insertAt(8, "h"));
trace("/// (contents of a_string...)");
trace_vector_string(a_string);
trace("/// b_string.insertAt(-9, \"i\");");
trace(b_string.insertAt(-9, "i"));
trace("/// (contents of b_string...)");
trace_vector_string(b_string);
trace("/// b_string.insertAt(-2, \"j\");");
trace(b_string.insertAt(-2, "j"));
trace("/// (contents of b_string...)");
trace_vector_string(b_string);
function trace_vector_uint(v: Vector.<uint>) {
trace(v.length, "elements");
for (var i = 0; i < v.length; i += 1) {
trace(v[i]);
}
}
trace("/// var a_uint: Vector.<uint> = new <uint>[1,2];");
var a_uint:Vector.<uint> = new <uint>[1,2];
trace("/// var b_uint: Vector.<uint> = new <uint>[5,16];");
var b_uint:Vector.<uint> = new <uint>[5,16];
trace("/// a_uint.insertAt(1, 4);");
trace(a_uint.insertAt(1, 4));
trace("/// (contents of a_uint...)");
trace_vector_uint(a_uint);
trace("/// a_uint.insertAt(6, -4);");
trace(a_uint.insertAt(6, -4));
trace("/// (contents of a_uint...)");
trace_vector_uint(a_uint);
trace("/// b_uint.insertAt(-8, 9);");
trace(b_uint.insertAt(-8, 9));
trace("/// (contents of b_uint...)");
trace_vector_uint(b_uint);
trace("/// b_uint.insertAt(-2, 93);");
trace(b_uint.insertAt(-2, 93));
trace("/// (contents of b_uint...)");
trace_vector_uint(b_uint);
function trace_vector_vector(v) {
trace(v.length, "elements");
for (var i = 0; i < v.length; i += 1) {
if (v[i] is Vector.<int>) {
trace("/// (contents of index", i, ")");
trace_vector_vector(v[i]);
} else {
trace(v[i]);
}
}
}
trace("/// var a_vector:Vector.<Vector.<int>> = new <Vector.<int>>[new <int>[1,2], new <int>[4,3]];");
var a_vector:Vector.<Vector.<int>> = new <Vector.<int>>[new <int>[1,2], new <int>[4,3]];
trace("/// var b_vector:Vector.<Vector.<int>> = new <Vector.<int>>[new <int>[5,16], new <int>[19,8]];");
var b_vector:Vector.<Vector.<int>> = new <Vector.<int>>[new <int>[5,16], new <int>[19,8]];
trace("/// a_vector.insertAt(1, new <int>[5,6]);");
trace(a_vector.insertAt(1, new <int>[5,6]));
trace("/// (contents of a_vector...)");
trace_vector_vector(a_vector);
trace("/// a_vector.insertAt(5, new <int>[8,9]);");
trace(a_vector.insertAt(5, new <int>[8,9]));
trace("/// (contents of a_vector...)");
trace_vector_vector(a_vector);
trace("/// b_vector.insertAt(-6, new <int>[10,11]);");
trace(b_vector.insertAt(-6, new <int>[10,11]));
trace("/// (contents of b_vector...)");
trace_vector_vector(b_vector);
trace("/// b_vector.insertAt(-2, new <int>[12,13]);");
trace(b_vector.insertAt(-2, new <int>[12,13]));
trace("/// (contents of b_vector...)");
trace_vector_vector(b_vector);

View File

@ -0,0 +1,270 @@
/// var a_bool: Vector.<Boolean> = new <Boolean>[true, false];
/// var b_bool: Vector.<Boolean> = new <Boolean>[false, true, false];
/// a_bool.insertAt(3, false);
undefined
/// (contents of a_bool...)
3 elements
true
false
false
/// a_bool.insertAt(0, false);
undefined
/// (contents of a_bool...)
4 elements
false
true
false
false
/// b_bool.insertAt(-2, false);
undefined
/// (contents of b_bool...)
4 elements
false
false
true
false
/// b_bool.insertAt(-5, false);
undefined
/// (contents of b_bool...)
5 elements
false
false
false
true
false
/// var a0_class = new Superclass();
/// var a1_class = new Subclass();
/// var a_class: Vector.<Superclass> = new <Superclass>[a0_class, a1_class];
/// var b_class: Vector.<Subclass> = new <Subclass>[];
/// b_class.length = 1;
/// b_class[0] = new Subclass();
/// a_class.insertAt(0, new Subclass());
undefined
/// a0_class === a_class[1];
true
/// a1_class === a_class[2];
true
/// b_class.insertAt(-3, new Subclass());
undefined
/// (contents of b_class...)
2 elements
[object Subclass]
[object Subclass]
/// var a_int: Vector.<int> = new <int>[1,2];
/// var b_int: Vector.<int> = new <int>[5,16];
/// a_int.insertAt(1, 3);
undefined
/// (contents of a_int)...
3 elements
1
3
2
/// a_int.insertAt(6, 4);
undefined
/// (contents of a_int)...
4 elements
1
3
2
4
/// b_int.insertAt(-5, 3);
undefined
/// (contents of b_int)...
3 elements
3
5
16
/// b_int.insertAt(-1, 3);
undefined
/// (contents of b_int)...
4 elements
3
5
3
16
/// var a_number: Vector.<Number> = new <Number>[1,2,3,4];
/// var b_number: Vector.<Number> = new <Number>[5, NaN, -5, 0];
/// a_number.insertAt(1, 5);
undefined
/// (contents of a_number...)
5 elements
1
5
2
3
4
/// a_number.insertAt(9, 6);
undefined
/// (contents of a_number...)
6 elements
1
5
2
3
4
6
/// b_number.insertAt(-4, 23);
undefined
/// (contents of b_number...)
5 elements
23
5
NaN
-5
0
/// b_number.insertAt(-8, 99);
undefined
/// (contents of b_number...)
6 elements
99
23
5
NaN
-5
0
/// var a_string: Vector.<String> = new <String>["a","c","d","f"];
/// var b_string: Vector.<String> = new <String>["986","B4","Q","rrr"];
/// a_string.insertAt(1, "g");
undefined
/// (contents of a_string...)
5 elements
a
g
c
d
f
/// a_string.insertAt(8, "h");
undefined
/// (contents of a_string...)
6 elements
a
g
c
d
f
h
/// b_string.insertAt(-9, "i");
undefined
/// (contents of b_string...)
5 elements
i
986
B4
Q
rrr
/// b_string.insertAt(-2, "j");
undefined
/// (contents of b_string...)
6 elements
i
986
B4
j
Q
rrr
/// var a_uint: Vector.<uint> = new <uint>[1,2];
/// var b_uint: Vector.<uint> = new <uint>[5,16];
/// a_uint.insertAt(1, 4);
undefined
/// (contents of a_uint...)
3 elements
1
4
2
/// a_uint.insertAt(6, -4);
undefined
/// (contents of a_uint...)
4 elements
1
4
2
4294967292
/// b_uint.insertAt(-8, 9);
undefined
/// (contents of b_uint...)
3 elements
9
5
16
/// b_uint.insertAt(-2, 93);
undefined
/// (contents of b_uint...)
4 elements
9
93
5
16
/// var a_vector:Vector.<Vector.<int>> = new <Vector.<int>>[new <int>[1,2], new <int>[4,3]];
/// var b_vector:Vector.<Vector.<int>> = new <Vector.<int>>[new <int>[5,16], new <int>[19,8]];
/// a_vector.insertAt(1, new <int>[5,6]);
undefined
/// (contents of a_vector...)
3 elements
/// (contents of index 0 )
2 elements
1
2
/// (contents of index 1 )
2 elements
5
6
/// (contents of index 2 )
2 elements
4
3
/// a_vector.insertAt(5, new <int>[8,9]);
undefined
/// (contents of a_vector...)
4 elements
/// (contents of index 0 )
2 elements
1
2
/// (contents of index 1 )
2 elements
5
6
/// (contents of index 2 )
2 elements
4
3
/// (contents of index 3 )
2 elements
8
9
/// b_vector.insertAt(-6, new <int>[10,11]);
undefined
/// (contents of b_vector...)
3 elements
/// (contents of index 0 )
2 elements
10
11
/// (contents of index 1 )
2 elements
5
16
/// (contents of index 2 )
2 elements
19
8
/// b_vector.insertAt(-2, new <int>[12,13]);
undefined
/// (contents of b_vector...)
4 elements
/// (contents of index 0 )
2 elements
10
11
/// (contents of index 1 )
2 elements
12
13
/// (contents of index 2 )
2 elements
5
16
/// (contents of index 3 )
2 elements
19
8

Binary file not shown.

Binary file not shown.