avm2: Translate ExternalInterface to AS
This commit is contained in:
parent
52d94a4a13
commit
92998e2c91
|
@ -643,15 +643,6 @@ pub fn load_player_globals<'gc>(
|
|||
);
|
||||
class(activation, flash::text::font::create_class(mc), script)?;
|
||||
|
||||
// package `flash.crypto`
|
||||
|
||||
// package `flash.external`
|
||||
class(
|
||||
activation,
|
||||
flash::external::externalinterface::create_class(mc),
|
||||
script,
|
||||
)?;
|
||||
|
||||
// Inside this call, the macro `avm2_system_classes_playerglobal`
|
||||
// triggers classloading. Therefore, we run `load_playerglobal`
|
||||
// relative late, so that it can access classes defined before
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
//! `flash.external` namespace
|
||||
|
||||
pub mod externalinterface;
|
||||
pub mod external_interface;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package flash.external
|
||||
{
|
||||
public final class ExternalInterface
|
||||
{
|
||||
public static native function get available(): Boolean;
|
||||
|
||||
public static native function addCallback(functionName: String, closure: Function) : void;
|
||||
|
||||
public static native function call(functionName: String, ... arguments) : *;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
use crate::avm2::{Activation, Error, Object, Value};
|
||||
use crate::external::{Callback, Value as ExternalValue};
|
||||
|
||||
pub fn call<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if args.is_empty() {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
|
||||
let name = args.get(0).unwrap().coerce_to_string(activation)?;
|
||||
if let Some(method) = activation
|
||||
.context
|
||||
.external_interface
|
||||
.get_method_for(&name.to_utf8_lossy())
|
||||
{
|
||||
let mut external_args = Vec::with_capacity(args.len() - 1);
|
||||
for arg in &args[1..] {
|
||||
external_args.push(ExternalValue::from_avm2(arg.to_owned()));
|
||||
}
|
||||
Ok(method
|
||||
.call(&mut activation.context, &external_args)
|
||||
.into_avm2(activation))
|
||||
} else {
|
||||
Ok(Value::Null)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
Ok(activation.context.external_interface.available().into())
|
||||
}
|
||||
|
||||
pub fn add_callback<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if args.len() < 2 {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
let name = args.get(0).unwrap().coerce_to_string(activation)?;
|
||||
let method = args.get(1).unwrap();
|
||||
|
||||
if let Value::Object(method) = method {
|
||||
activation
|
||||
.context
|
||||
.external_interface
|
||||
.add_callback(name.to_string(), Callback::Avm2 { method: *method });
|
||||
}
|
||||
Ok(Value::Undefined)
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
//! `flash.external.ExternalInterface` builtin/prototype
|
||||
|
||||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
use crate::avm2::Multiname;
|
||||
use crate::avm2::{Activation, Error, Namespace, Object, QName, Value};
|
||||
use crate::external::{Callback, Value as ExternalValue};
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
|
||||
fn instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(this) = this {
|
||||
activation.super_init(this, &[])?;
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn call<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if args.is_empty() {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
|
||||
let name = args.get(0).unwrap().coerce_to_string(activation)?;
|
||||
if let Some(method) = activation
|
||||
.context
|
||||
.external_interface
|
||||
.get_method_for(&name.to_utf8_lossy())
|
||||
{
|
||||
let mut external_args = Vec::with_capacity(args.len() - 1);
|
||||
for arg in &args[1..] {
|
||||
external_args.push(ExternalValue::from_avm2(arg.to_owned()));
|
||||
}
|
||||
Ok(method
|
||||
.call(&mut activation.context, &external_args)
|
||||
.into_avm2(activation))
|
||||
} else {
|
||||
Ok(Value::Null)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn available<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
Ok(activation.context.external_interface.available().into())
|
||||
}
|
||||
|
||||
pub fn add_callback<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if args.len() < 2 {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
let name = args.get(0).unwrap().coerce_to_string(activation)?;
|
||||
let method = args.get(1).unwrap();
|
||||
|
||||
if let Value::Object(method) = method {
|
||||
activation
|
||||
.context
|
||||
.external_interface
|
||||
.add_callback(name.to_string(), Callback::Avm2 { method: *method });
|
||||
}
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Construct `ExternalInterface`'s class.
|
||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||
let class = Class::new(
|
||||
QName::new(Namespace::package("flash.external"), "ExternalInterface"),
|
||||
Some(Multiname::public("Object")),
|
||||
Method::from_builtin(
|
||||
instance_init,
|
||||
"<ExternalInterface instance initializer>",
|
||||
mc,
|
||||
),
|
||||
Method::from_builtin(class_init, "<ExternalInterface class initializer>", mc),
|
||||
mc,
|
||||
);
|
||||
|
||||
let mut write = class.write(mc);
|
||||
write.set_attributes(ClassAttributes::FINAL | ClassAttributes::SEALED);
|
||||
|
||||
const PUBLIC_CLASS_METHODS: &[(&str, NativeMethodImpl)] =
|
||||
&[("call", call), ("addCallback", add_callback)];
|
||||
|
||||
write.define_public_builtin_class_methods(mc, PUBLIC_CLASS_METHODS);
|
||||
|
||||
const PUBLIC_INSTANCE_PROPERTIES: &[(
|
||||
&str,
|
||||
Option<NativeMethodImpl>,
|
||||
Option<NativeMethodImpl>,
|
||||
)] = &[("available", Some(available), None)];
|
||||
|
||||
write.define_public_builtin_class_properties(mc, PUBLIC_INSTANCE_PROPERTIES);
|
||||
|
||||
class
|
||||
}
|
|
@ -163,6 +163,8 @@ include "flash/media/StageVideoAvailabilityReason.as"
|
|||
include "flash/media/VideoCodec.as"
|
||||
include "flash/media/VideoStatus.as"
|
||||
|
||||
include "flash/external/ExternalInterface.as"
|
||||
|
||||
include "flash/net.as"
|
||||
include "flash/net/FileFilter.as"
|
||||
include "flash/net/FileReference.as"
|
||||
|
|
Loading…
Reference in New Issue