From e86efd5c63be5cdbce6a8842621b3e5a2cf8f635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=96R=C3=96K=20Attila?= Date: Fri, 26 Aug 2022 02:08:08 +0200 Subject: [PATCH] avm2: Add flash.utils.[set|clear][Timeout|Interval](...) --- core/src/avm2/globals/Function.as | 5 ++ core/src/avm2/globals/flash/utils.as | 4 + core/src/avm2/globals/flash/utils.rs | 86 ++++++++++++++++++++++ core/src/avm2/globals/flash/utils/timer.rs | 5 +- core/src/avm2/globals/stubs.as | 1 + core/src/timer.rs | 12 ++- 6 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 core/src/avm2/globals/Function.as diff --git a/core/src/avm2/globals/Function.as b/core/src/avm2/globals/Function.as new file mode 100644 index 000000000..f7beaeaac --- /dev/null +++ b/core/src/avm2/globals/Function.as @@ -0,0 +1,5 @@ +// This is a stub - the actual class is defined in `function.rs` +package { + public final class Function { + } +} diff --git a/core/src/avm2/globals/flash/utils.as b/core/src/avm2/globals/flash/utils.as index 8fb17e5e4..f7d87738a 100644 --- a/core/src/avm2/globals/flash/utils.as +++ b/core/src/avm2/globals/flash/utils.as @@ -3,4 +3,8 @@ package flash.utils { public native function getQualifiedClassName(value:*):String; public native function getQualifiedSuperclassName(value:*):String; public native function getTimer():int; + public native function setInterval(closure:Function, delay:Number, ... arguments):uint; + public native function clearInterval(id:uint):void; + public native function setTimeout(closure:Function, delay:Number, ... arguments):uint; + public native function clearTimeout(id:uint):void; } diff --git a/core/src/avm2/globals/flash/utils.rs b/core/src/avm2/globals/flash/utils.rs index 7d20c94ad..90fd19ef7 100644 --- a/core/src/avm2/globals/flash/utils.rs +++ b/core/src/avm2/globals/flash/utils.rs @@ -25,6 +25,92 @@ pub fn get_timer<'gc>( .into()) } +/// Implements `flash.utils.setInterval` +pub fn set_interval<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if args.len() < 2 { + return Err(Error::from("setInterval: not enough arguments")); + } + let (args, params) = args.split_at(2); + let callback = crate::timer::TimerCallback::Avm2Callback { + closure: args + .get(0) + .expect("setInterval: not enough arguments") + .as_object() + .ok_or("setInterval: argument 0 is not an object")?, + params: params.to_vec(), + }; + let interval = args + .get(1) + .expect("setInterval: not enough arguments") + .coerce_to_number(activation)?; + Ok(Value::Integer(activation.context.timers.add_timer( + callback, + interval as i32, + false, + ))) +} + +/// Implements `flash.utils.clearInterval` +pub fn clear_interval<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + let id = args + .get(0) + .ok_or("clearInterval: not enough arguments")? + .coerce_to_number(activation)?; + activation.context.timers.remove(id as i32); + Ok(Value::Undefined) +} + +/// Implements `flash.utils.setTimeout` +pub fn set_timeout<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if args.len() < 2 { + return Err(Error::from("setTimeout: not enough arguments")); + } + let (args, params) = args.split_at(2); + let callback = crate::timer::TimerCallback::Avm2Callback { + closure: args + .get(0) + .expect("setTimeout: not enough arguments") + .as_object() + .ok_or("setTimeout: argument 0 is not an object")?, + params: params.to_vec(), + }; + let interval = args + .get(1) + .expect("setTimeout: not enough arguments") + .coerce_to_number(activation)?; + Ok(Value::Integer(activation.context.timers.add_timer( + callback, + interval as i32, + true, + ))) +} + +/// Implements `flash.utils.clearTimeout` +pub fn clear_timeout<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + let id = args + .get(0) + .ok_or("clearTimeout: not enough arguments")? + .coerce_to_number(activation)?; + activation.context.timers.remove(id as i32); + Ok(Value::Undefined) +} + /// Implements `flash.utils.getQualifiedClassName` pub fn get_qualified_class_name<'gc>( activation: &mut Activation<'_, 'gc, '_>, diff --git a/core/src/avm2/globals/flash/utils/timer.rs b/core/src/avm2/globals/flash/utils/timer.rs index be5f6a73e..0364b5b72 100644 --- a/core/src/avm2/globals/flash/utils/timer.rs +++ b/core/src/avm2/globals/flash/utils/timer.rs @@ -66,7 +66,10 @@ pub fn start<'gc>( )? .coerce_to_object(activation)?; let id = activation.context.timers.add_timer( - TimerCallback::Avm2Callback(on_update), + TimerCallback::Avm2Callback { + closure: on_update, + params: vec![], + }, delay as _, false, ); diff --git a/core/src/avm2/globals/stubs.as b/core/src/avm2/globals/stubs.as index f042b4f19..81228f439 100644 --- a/core/src/avm2/globals/stubs.as +++ b/core/src/avm2/globals/stubs.as @@ -15,6 +15,7 @@ include "flash/display/LoaderInfo.as" include "flash/events/EventDispatcher.as" include "flash/system/ApplicationDomain.as" include "flash/utils/ByteArray.as" +include "Function.as" include "Number.as" include "String.as" include "int.as" diff --git a/core/src/timer.rs b/core/src/timer.rs index c6bee31eb..2cc25381f 100644 --- a/core/src/timer.rs +++ b/core/src/timer.rs @@ -9,7 +9,7 @@ use crate::avm1::{ Activation, ActivationIdentifier, Object as Avm1Object, TObject as _, Value as Avm1Value, }; use crate::avm2::object::TObject; -use crate::avm2::{Activation as Avm2Activation, Object as Avm2Object}; +use crate::avm2::{Activation as Avm2Activation, Object as Avm2Object, Value as Avm2Value}; use crate::context::UpdateContext; use crate::string::AvmString; use gc_arena::Collect; @@ -108,10 +108,11 @@ impl<'gc> Timers<'gc> { ); false } - TimerCallback::Avm2Callback(obj) => { + TimerCallback::Avm2Callback { closure, params } => { let mut avm2_activation = Avm2Activation::from_nothing(activation.context.reborrow()); - obj.call(None, &[], &mut avm2_activation) + closure + .call(None, ¶ms, &mut avm2_activation) .unwrap() .coerce_to_boolean() } @@ -296,5 +297,8 @@ pub enum TimerCallback<'gc> { params: Vec>, }, - Avm2Callback(Avm2Object<'gc>), + Avm2Callback { + closure: Avm2Object<'gc>, + params: Vec>, + }, }