avm2: Implement `rest` parameters in function calls.
This commit is contained in:
parent
453e013c2c
commit
09f9e99fbb
|
@ -184,11 +184,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
.body()
|
.body()
|
||||||
.ok_or_else(|| "Cannot execute non-native method without body".into());
|
.ok_or_else(|| "Cannot execute non-native method without body".into());
|
||||||
let num_locals = body?.num_locals;
|
let num_locals = body?.num_locals;
|
||||||
let arg_register = if method.method().needs_arguments_object {
|
let has_rest_or_args = method.method().needs_arguments_object || method.method().needs_rest;
|
||||||
1
|
let arg_register = if has_rest_or_args { 1 } else { 0 };
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
let num_declared_arguments = method.method().params.len() as u32;
|
let num_declared_arguments = method.method().params.len() as u32;
|
||||||
let local_registers = GcCell::allocate(
|
let local_registers = GcCell::allocate(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -219,8 +216,19 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
context,
|
context,
|
||||||
};
|
};
|
||||||
|
|
||||||
if method.method().needs_arguments_object {
|
if has_rest_or_args {
|
||||||
let args_array = ArrayStorage::from_args(arguments);
|
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(
|
let mut args_object = ArrayObject::from_array(
|
||||||
args_array,
|
args_array,
|
||||||
activation
|
activation
|
||||||
|
@ -233,12 +241,14 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
);
|
);
|
||||||
|
|
||||||
args_object.set_property(
|
if method.method().needs_arguments_object {
|
||||||
args_object,
|
args_object.set_property(
|
||||||
&QName::new(Namespace::public_namespace(), "callee"),
|
args_object,
|
||||||
callee.into(),
|
&QName::new(Namespace::public_namespace(), "callee"),
|
||||||
&mut activation,
|
callee.into(),
|
||||||
)?;
|
&mut activation,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
*local_registers
|
*local_registers
|
||||||
.write(activation.context.gc_context)
|
.write(activation.context.gc_context)
|
||||||
|
|
|
@ -455,6 +455,7 @@ swf_tests! {
|
||||||
(as3_event_isdefaultprevented, "avm2/event_isdefaultprevented", 1),
|
(as3_event_isdefaultprevented, "avm2/event_isdefaultprevented", 1),
|
||||||
(as3_function_call_via_apply, "avm2/function_call_via_apply", 1),
|
(as3_function_call_via_apply, "avm2/function_call_via_apply", 1),
|
||||||
(as3_function_call_arguments, "avm2/function_call_arguments", 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.
|
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||||
|
|
|
@ -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();
|
|
@ -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.
Loading…
Reference in New Issue