avm2: Implement `rest` parameters in function calls.

This commit is contained in:
David Wendt 2020-12-10 22:51:10 -05:00 committed by Mike Welsh
parent 453e013c2c
commit 09f9e99fbb
6 changed files with 80 additions and 13 deletions

View File

@ -184,11 +184,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
.body()
.ok_or_else(|| "Cannot execute non-native method without body".into());
let num_locals = body?.num_locals;
let arg_register = if method.method().needs_arguments_object {
1
} else {
0
};
let has_rest_or_args = method.method().needs_arguments_object || method.method().needs_rest;
let arg_register = if has_rest_or_args { 1 } else { 0 };
let num_declared_arguments = method.method().params.len() as u32;
let local_registers = GcCell::allocate(
context.gc_context,
@ -219,8 +216,19 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
context,
};
if method.method().needs_arguments_object {
let args_array = ArrayStorage::from_args(arguments);
if has_rest_or_args {
let args_array = if method.method().needs_arguments_object {
ArrayStorage::from_args(arguments)
} else if method.method().needs_rest {
if let Some(rest_args) = arguments.get(num_declared_arguments as usize..) {
ArrayStorage::from_args(rest_args)
} else {
ArrayStorage::new(0)
}
} else {
unreachable!();
};
let mut args_object = ArrayObject::from_array(
args_array,
activation
@ -233,12 +241,14 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
activation.context.gc_context,
);
if method.method().needs_arguments_object {
args_object.set_property(
args_object,
&QName::new(Namespace::public_namespace(), "callee"),
callee.into(),
&mut activation,
)?;
}
*local_registers
.write(activation.context.gc_context)

View File

@ -455,6 +455,7 @@ swf_tests! {
(as3_event_isdefaultprevented, "avm2/event_isdefaultprevented", 1),
(as3_function_call_via_apply, "avm2/function_call_via_apply", 1),
(as3_function_call_arguments, "avm2/function_call_arguments", 1),
(as3_function_call_rest, "avm2/function_call_rest", 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,34 @@
package {
public class Test {}
}
function testrest(arg0, arg1, ...rest) {
trace(arg0);
trace(arg1);
trace("///(contents of rest...)");
trace(rest.length);
for (var i = 0; i < rest.length; i += 1) {
trace(rest[i]);
}
}
function restprops(...rest) {
trace("///Array.prototype.test = \"test\";");
Array.prototype.test = "test";
trace("///rest.test");
trace(rest.test);
}
trace("///testrest(\"arg1\");");
testrest("arg1");
trace("///testrest(\"arg1\", \"arg2\", \"arg3\");");
testrest("arg1", "arg2", "arg3");
trace("///testrest(\"arg1\", \"arg2\", \"arg3\", \"arg4\", \"arg5\");");
testrest("arg1", "arg2", "arg3", "arg4", "arg5");
restprops();

View File

@ -0,0 +1,22 @@
///testrest("arg1");
arg1
undefined
///(contents of rest...)
0
///testrest("arg1", "arg2", "arg3");
arg1
arg2
///(contents of rest...)
1
arg3
///testrest("arg1", "arg2", "arg3", "arg4", "arg5");
arg1
arg2
///(contents of rest...)
3
arg3
arg4
arg5
///Array.prototype.test = "test";
///rest.test
test

Binary file not shown.

Binary file not shown.