avm2: Add flash.utils.[set|clear][Timeout|Interval](...)

This commit is contained in:
TÖRÖK Attila 2022-08-26 02:08:08 +02:00 committed by Mike Welsh
parent 5c29da6707
commit e86efd5c63
6 changed files with 108 additions and 5 deletions

View File

@ -0,0 +1,5 @@
// This is a stub - the actual class is defined in `function.rs`
package {
public final class Function {
}
}

View File

@ -3,4 +3,8 @@ package flash.utils {
public native function getQualifiedClassName(value:*):String; public native function getQualifiedClassName(value:*):String;
public native function getQualifiedSuperclassName(value:*):String; public native function getQualifiedSuperclassName(value:*):String;
public native function getTimer():int; 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;
} }

View File

@ -25,6 +25,92 @@ pub fn get_timer<'gc>(
.into()) .into())
} }
/// Implements `flash.utils.setInterval`
pub fn set_interval<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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` /// Implements `flash.utils.getQualifiedClassName`
pub fn get_qualified_class_name<'gc>( pub fn get_qualified_class_name<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,

View File

@ -66,7 +66,10 @@ pub fn start<'gc>(
)? )?
.coerce_to_object(activation)?; .coerce_to_object(activation)?;
let id = activation.context.timers.add_timer( let id = activation.context.timers.add_timer(
TimerCallback::Avm2Callback(on_update), TimerCallback::Avm2Callback {
closure: on_update,
params: vec![],
},
delay as _, delay as _,
false, false,
); );

View File

@ -15,6 +15,7 @@ include "flash/display/LoaderInfo.as"
include "flash/events/EventDispatcher.as" include "flash/events/EventDispatcher.as"
include "flash/system/ApplicationDomain.as" include "flash/system/ApplicationDomain.as"
include "flash/utils/ByteArray.as" include "flash/utils/ByteArray.as"
include "Function.as"
include "Number.as" include "Number.as"
include "String.as" include "String.as"
include "int.as" include "int.as"

View File

@ -9,7 +9,7 @@ use crate::avm1::{
Activation, ActivationIdentifier, Object as Avm1Object, TObject as _, Value as Avm1Value, Activation, ActivationIdentifier, Object as Avm1Object, TObject as _, Value as Avm1Value,
}; };
use crate::avm2::object::TObject; 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::context::UpdateContext;
use crate::string::AvmString; use crate::string::AvmString;
use gc_arena::Collect; use gc_arena::Collect;
@ -108,10 +108,11 @@ impl<'gc> Timers<'gc> {
); );
false false
} }
TimerCallback::Avm2Callback(obj) => { TimerCallback::Avm2Callback { closure, params } => {
let mut avm2_activation = let mut avm2_activation =
Avm2Activation::from_nothing(activation.context.reborrow()); Avm2Activation::from_nothing(activation.context.reborrow());
obj.call(None, &[], &mut avm2_activation) closure
.call(None, &params, &mut avm2_activation)
.unwrap() .unwrap()
.coerce_to_boolean() .coerce_to_boolean()
} }
@ -296,5 +297,8 @@ pub enum TimerCallback<'gc> {
params: Vec<Avm1Value<'gc>>, params: Vec<Avm1Value<'gc>>,
}, },
Avm2Callback(Avm2Object<'gc>), Avm2Callback {
closure: Avm2Object<'gc>,
params: Vec<Avm2Value<'gc>>,
},
} }