ruffle/core/src/avm2/globals.rs

852 lines
26 KiB
Rust
Raw Normal View History

use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::domain::Domain;
use crate::avm2::method::{Method, NativeMethodImpl};
use crate::avm2::names::{Namespace, QName};
2021-10-02 03:13:42 +00:00
use crate::avm2::object::{
ClassObject, FunctionObject, NamespaceObject, Object, ScriptObject, TObject,
};
2021-09-07 22:17:38 +00:00
use crate::avm2::scope::{Scope, ScopeChain};
use crate::avm2::script::Script;
use crate::avm2::value::Value;
use crate::avm2::Avm2;
use crate::avm2::Error;
use crate::string::AvmString;
use crate::tag_utils::{self, SwfMovie, SwfSlice, SwfStream};
use gc_arena::{Collect, GcCell, MutationContext};
use std::sync::Arc;
use swf::TagCode;
mod array;
2020-07-17 02:33:26 +00:00
mod boolean;
mod class;
2021-07-27 23:58:13 +00:00
mod date;
mod flash;
mod function;
mod global_scope;
2020-07-17 02:33:26 +00:00
mod int;
mod json;
2020-11-10 23:43:44 +00:00
mod math;
2020-07-17 02:33:26 +00:00
mod namespace;
mod number;
mod object;
mod qname;
2021-02-11 07:56:21 +00:00
mod regexp;
2020-07-17 02:33:26 +00:00
mod string;
mod toplevel;
2020-07-17 02:33:26 +00:00
mod r#uint;
2021-03-13 03:24:20 +00:00
mod vector;
mod xml;
mod xml_list;
pub(crate) const NS_RUFFLE_INTERNAL: &str = "https://ruffle.rs/AS3/impl/";
2022-07-01 04:34:26 +00:00
pub(crate) const NS_VECTOR: &str = "__AS3__.vec";
pub use flash::utils::NS_FLASH_PROXY;
/// This structure represents all system builtin classes.
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub struct SystemClasses<'gc> {
pub object: ClassObject<'gc>,
pub function: ClassObject<'gc>,
pub class: ClassObject<'gc>,
pub global: ClassObject<'gc>,
pub string: ClassObject<'gc>,
pub boolean: ClassObject<'gc>,
pub number: ClassObject<'gc>,
pub int: ClassObject<'gc>,
pub uint: ClassObject<'gc>,
pub namespace: ClassObject<'gc>,
pub array: ClassObject<'gc>,
pub movieclip: ClassObject<'gc>,
pub framelabel: ClassObject<'gc>,
pub scene: ClassObject<'gc>,
pub application_domain: ClassObject<'gc>,
pub event: ClassObject<'gc>,
pub fullscreenevent: ClassObject<'gc>,
pub video: ClassObject<'gc>,
pub xml: ClassObject<'gc>,
pub xml_list: ClassObject<'gc>,
pub display_object: ClassObject<'gc>,
pub shape: ClassObject<'gc>,
pub textfield: ClassObject<'gc>,
pub textformat: ClassObject<'gc>,
pub graphics: ClassObject<'gc>,
pub loaderinfo: ClassObject<'gc>,
pub bytearray: ClassObject<'gc>,
pub stage: ClassObject<'gc>,
pub sprite: ClassObject<'gc>,
pub simplebutton: ClassObject<'gc>,
pub regexp: ClassObject<'gc>,
pub vector: ClassObject<'gc>,
pub soundtransform: ClassObject<'gc>,
pub soundchannel: ClassObject<'gc>,
pub bitmap: ClassObject<'gc>,
pub bitmapdata: ClassObject<'gc>,
pub date: ClassObject<'gc>,
pub qname: ClassObject<'gc>,
pub sharedobject: ClassObject<'gc>,
2021-11-10 14:40:02 +00:00
pub nativemenu: ClassObject<'gc>,
pub contextmenu: ClassObject<'gc>,
pub mouseevent: ClassObject<'gc>,
pub textevent: ClassObject<'gc>,
pub errorevent: ClassObject<'gc>,
pub ioerrorevent: ClassObject<'gc>,
pub securityerrorevent: ClassObject<'gc>,
}
impl<'gc> SystemClasses<'gc> {
/// Construct a minimal set of system classes necessary for bootstrapping
/// player globals.
///
/// All other system classes aside from the three given here will be set to
/// the empty object also handed to this function. It is the caller's
/// responsibility to instantiate each class and replace the empty object
/// with that.
2021-12-04 21:32:39 +00:00
fn new(
object: ClassObject<'gc>,
function: ClassObject<'gc>,
class: ClassObject<'gc>,
global: ClassObject<'gc>,
) -> Self {
SystemClasses {
object,
function,
class,
global,
// temporary initialization
string: object,
boolean: object,
number: object,
int: object,
uint: object,
namespace: object,
array: object,
movieclip: object,
framelabel: object,
scene: object,
application_domain: object,
event: object,
fullscreenevent: object,
video: object,
xml: object,
xml_list: object,
display_object: object,
shape: object,
textfield: object,
textformat: object,
graphics: object,
loaderinfo: object,
bytearray: object,
stage: object,
sprite: object,
simplebutton: object,
regexp: object,
vector: object,
soundtransform: object,
soundchannel: object,
bitmap: object,
bitmapdata: object,
date: object,
qname: object,
sharedobject: object,
2021-11-10 14:40:02 +00:00
nativemenu: object,
contextmenu: object,
mouseevent: object,
textevent: object,
errorevent: object,
ioerrorevent: object,
securityerrorevent: object,
}
}
}
/// Add a free-function builtin to the global scope.
fn function<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
package: impl Into<AvmString<'gc>>,
name: &'static str,
nf: NativeMethodImpl,
script: Script<'gc>,
) -> Result<(), Error> {
let (_, mut global, mut domain) = script.init();
let mc = activation.context.gc_context;
let scope = activation.create_scopechain();
let qname = QName::new(Namespace::package(package), name);
let method = Method::from_builtin(nf, name, mc);
let as3fn = FunctionObject::from_method(activation, method, scope, None, None).into();
2021-11-27 17:50:13 +00:00
domain.export_definition(qname, script, mc)?;
2022-07-01 04:34:26 +00:00
global.install_const_late(mc, qname, as3fn, activation.avm2().classes().function);
Ok(())
}
/// Add a fully-formed class object builtin to the global scope.
///
/// This allows the caller to pre-populate the class's prototype with dynamic
/// properties, if necessary.
fn dynamic_class<'gc>(
mc: MutationContext<'gc, '_>,
class_object: ClassObject<'gc>,
script: Script<'gc>,
2022-07-01 04:34:26 +00:00
// The `ClassObject` of the `Class` class
class_class: ClassObject<'gc>,
) -> Result<(), Error> {
let (_, mut global, mut domain) = script.init();
let class = class_object.inner_class_definition();
2021-11-27 17:50:13 +00:00
let name = class.read().name();
2022-07-01 04:34:26 +00:00
global.install_const_late(mc, name, class_object.into(), class_class);
domain.export_definition(name, script, mc)
}
/// Add a class builtin to the global scope.
///
2022-06-29 14:28:42 +00:00
/// This function returns the class object and class prototype as a class, which
/// may be stored in `SystemClasses`
fn class<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
class_def: GcCell<'gc, Class<'gc>>,
script: Script<'gc>,
2022-06-29 14:28:42 +00:00
) -> Result<ClassObject<'gc>, Error> {
let (_, mut global, mut domain) = script.init();
let class_read = class_def.read();
let super_class = if let Some(sc_name) = class_read.super_class_name() {
let super_class: Result<Object<'gc>, Error> = global
.get_property(sc_name, activation)
.ok()
.and_then(|v| v.as_object())
.ok_or_else(|| {
format!(
"Could not resolve superclass {} when defining global class {}",
sc_name.to_qualified_name(activation.context.gc_context),
class_read
.name()
.to_qualified_name(activation.context.gc_context)
)
.into()
});
let super_class = super_class?
.as_class_object()
.ok_or_else(|| Error::from("Base class of a global class is not a class"))?;
Some(super_class)
} else {
None
};
2021-11-27 17:50:13 +00:00
let class_name = class_read.name();
drop(class_read);
let class_object = ClassObject::from_class(activation, class_def, super_class)?;
global.install_const_late(
activation.context.gc_context,
2021-11-27 17:50:13 +00:00
class_name,
class_object.into(),
2022-07-01 04:34:26 +00:00
activation.avm2().classes().class,
);
domain.export_definition(class_name, script, activation.context.gc_context)?;
2022-06-29 14:28:42 +00:00
Ok(class_object)
}
/// Add a builtin constant to the global scope.
fn constant<'gc>(
mc: MutationContext<'gc, '_>,
package: impl Into<AvmString<'gc>>,
name: impl Into<AvmString<'gc>>,
value: Value<'gc>,
script: Script<'gc>,
2022-07-01 04:34:26 +00:00
class: ClassObject<'gc>,
) -> Result<(), Error> {
let (_, mut global, mut domain) = script.init();
let name = QName::new(Namespace::package(package), name);
2021-11-27 17:50:13 +00:00
domain.export_definition(name, script, mc)?;
2022-07-01 04:34:26 +00:00
global.install_const_late(mc, name, value, class);
Ok(())
}
2021-10-02 03:13:42 +00:00
fn namespace<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
package: impl Into<AvmString<'gc>>,
name: impl Into<AvmString<'gc>>,
uri: impl Into<AvmString<'gc>>,
script: Script<'gc>,
) -> Result<(), Error> {
let namespace = NamespaceObject::from_namespace(activation, Namespace::Namespace(uri.into()))?;
constant(
activation.context.gc_context,
package,
name,
namespace.into(),
script,
2022-07-01 04:34:26 +00:00
activation.avm2().classes().namespace,
2021-10-02 03:13:42 +00:00
)
}
macro_rules! avm2_system_class {
($field:ident, $activation:ident, $class:expr, $script:expr) => {
2022-06-29 14:28:42 +00:00
let class_object = class($activation, $class, $script)?;
let sc = $activation.avm2().system_classes.as_mut().unwrap();
sc.$field = class_object;
};
}
/// Initialize the player global domain.
///
/// This should be called only once, to construct the global scope of the
/// player. It will return a list of prototypes it has created, which should be
/// stored on the AVM. All relevant declarations will also be attached to the
/// given domain.
pub fn load_player_globals<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
domain: Domain<'gc>,
) -> Result<(), Error> {
let mc = activation.context.gc_context;
let globals = ScriptObject::custom_object(activation.context.gc_context, None, None);
2021-09-07 22:17:38 +00:00
let gs = ScopeChain::new(domain).chain(mc, &[Scope::new(globals)]);
let script = Script::empty_script(mc, globals, domain);
// Set the outer scope of this activation to the global scope.
activation.set_outer(gs);
// public / root package
//
// This part of global initialization is very complicated, because
// everything has to circularly reference everything else:
//
// - Object is an instance of itself, as well as it's prototype
// - All other types are instances of Class, which is an instance of
// itself
// - Function's prototype is an instance of itself
// - All methods created by the above-mentioned classes are also instances
// of Function
// - All classes are put on Global's trait list, but Global needs
// to be initialized first, but you can't do that until Object/Class are ready.
//
// Hence, this ridiculously complicated dance of classdef, type allocation,
// and partial initialization.
let object_classdef = object::create_class(mc);
let object_class = ClassObject::from_class_partial(activation, object_classdef, None)?;
let object_proto = ScriptObject::custom_object(mc, Some(object_class), None);
let fn_classdef = function::create_class(mc);
2021-10-09 08:47:28 +00:00
let fn_class = ClassObject::from_class_partial(activation, fn_classdef, Some(object_class))?;
let fn_proto = ScriptObject::custom_object(mc, Some(fn_class), Some(object_proto));
let class_classdef = class::create_class(mc);
let class_class =
2021-10-09 08:47:28 +00:00
ClassObject::from_class_partial(activation, class_classdef, Some(object_class))?;
let class_proto = ScriptObject::custom_object(mc, Some(object_class), Some(object_proto));
let global_classdef = global_scope::create_class(mc);
let global_class =
ClassObject::from_class_partial(activation, global_classdef, Some(object_class))?;
let global_proto = ScriptObject::custom_object(mc, Some(object_class), Some(object_proto));
// Now to weave the Gordian knot...
object_class.link_prototype(activation, object_proto)?;
object_class.link_type(activation, class_proto, class_class);
fn_class.link_prototype(activation, fn_proto)?;
fn_class.link_type(activation, class_proto, class_class);
class_class.link_prototype(activation, class_proto)?;
class_class.link_type(activation, class_proto, class_class);
global_class.link_prototype(activation, global_proto)?;
global_class.link_type(activation, class_proto, class_class);
2022-06-29 14:28:42 +00:00
// At this point, we need at least a partial set of system classes in
// order to continue initializing the player. The rest of the classes
// are set to a temporary class until we have a chance to initialize them.
2021-12-04 21:32:39 +00:00
activation.context.avm2.system_classes = Some(SystemClasses::new(
object_class,
fn_class,
class_class,
global_class,
));
// Our activation environment is now functional enough to finish
// initializing the core class weave. The order of initialization shouldn't
// matter here, as long as all the initialization machinery can see and
// link the various system types together correctly.
let class_class = class_class.into_finished_class(activation)?;
let fn_class = fn_class.into_finished_class(activation)?;
let object_class = object_class.into_finished_class(activation)?;
let _global_class = global_class.into_finished_class(activation)?;
2022-06-29 14:28:42 +00:00
globals.set_proto(mc, global_proto);
globals.set_instance_of(mc, global_class);
globals.fork_vtable(activation.context.gc_context);
// From this point, `globals` is safe to be modified
2021-06-08 00:53:24 +00:00
2022-07-01 04:34:26 +00:00
dynamic_class(mc, object_class, script, class_class)?;
dynamic_class(mc, fn_class, script, class_class)?;
dynamic_class(mc, class_class, script, class_class)?;
// After this point, it is safe to initialize any other classes.
// Make sure to initialize superclasses *before* their subclasses!
avm2_system_class!(string, activation, string::create_class(mc), script);
avm2_system_class!(boolean, activation, boolean::create_class(mc), script);
avm2_system_class!(number, activation, number::create_class(mc), script);
avm2_system_class!(int, activation, int::create_class(mc), script);
avm2_system_class!(uint, activation, uint::create_class(mc), script);
avm2_system_class!(namespace, activation, namespace::create_class(mc), script);
avm2_system_class!(qname, activation, qname::create_class(mc), script);
avm2_system_class!(array, activation, array::create_class(mc), script);
function(activation, "", "trace", toplevel::trace, script)?;
function(activation, "", "isFinite", toplevel::is_finite, script)?;
function(activation, "", "isNaN", toplevel::is_nan, script)?;
2021-12-13 14:46:57 +00:00
function(activation, "", "parseInt", toplevel::parse_int, script)?;
function(activation, "", "parseFloat", toplevel::parse_float, script)?;
function(activation, "", "escape", toplevel::escape, script)?;
2022-07-01 04:34:26 +00:00
constant(mc, "", "undefined", Value::Undefined, script, object_class)?;
constant(mc, "", "null", Value::Null, script, object_class)?;
constant(mc, "", "NaN", f64::NAN.into(), script, object_class)?;
constant(
mc,
"",
"Infinity",
f64::INFINITY.into(),
script,
object_class,
)?;
class(activation, math::create_class(mc), script)?;
class(activation, json::create_class(mc), script)?;
avm2_system_class!(regexp, activation, regexp::create_class(mc), script);
avm2_system_class!(vector, activation, vector::create_class(mc), script);
avm2_system_class!(xml, activation, xml::create_class(mc), script);
avm2_system_class!(xml_list, activation, xml_list::create_class(mc), script);
avm2_system_class!(date, activation, date::create_class(mc), script);
2021-07-27 23:58:13 +00:00
// package `flash.system`
avm2_system_class!(
application_domain,
activation,
flash::system::application_domain::create_class(mc),
script
);
class(
activation,
flash::system::capabilities::create_class(mc),
script,
)?;
2021-08-25 18:05:37 +00:00
class(
activation,
flash::system::security::create_class(mc),
script,
)?;
class(activation, flash::system::system::create_class(mc), script)?;
// package `flash.events`
avm2_system_class!(
event,
activation,
flash::events::event::create_class(mc),
script
);
2020-07-11 22:09:51 +00:00
class(
activation,
flash::events::ieventdispatcher::create_interface(mc),
script,
2020-07-11 22:09:51 +00:00
)?;
class(
activation,
flash::events::eventdispatcher::create_class(mc),
script,
)?;
avm2_system_class!(
mouseevent,
activation,
flash::events::mouseevent::create_class(mc),
script
);
avm2_system_class!(
textevent,
activation,
flash::events::textevent::create_class(mc),
script
);
avm2_system_class!(
errorevent,
activation,
flash::events::errorevent::create_class(mc),
script
);
avm2_system_class!(
securityerrorevent,
activation,
flash::events::securityerrorevent::create_class(mc),
script
);
avm2_system_class!(
ioerrorevent,
2022-02-20 12:03:51 +00:00
activation,
flash::events::ioerrorevent::create_class(mc),
script
);
2022-03-30 18:56:21 +00:00
class(
activation,
flash::events::contextmenuevent::create_class(mc),
script,
)?;
class(
activation,
flash::events::keyboardevent::create_class(mc),
script,
)?;
class(
activation,
flash::events::progressevent::create_class(mc),
script,
)?;
class(
activation,
flash::events::activityevent::create_class(mc),
script,
)?;
avm2_system_class!(
fullscreenevent,
activation,
flash::events::fullscreenevent::create_class(mc),
script
);
class(
activation,
flash::events::eventphase::create_class(mc),
script,
)?;
2021-03-05 23:01:02 +00:00
// package `flash.utils`
avm2_system_class!(
bytearray,
2021-03-05 23:01:02 +00:00
activation,
flash::utils::bytearray::create_class(mc),
script
);
domain.init_default_domain_memory(activation)?;
2021-03-05 23:01:02 +00:00
class(
activation,
flash::utils::dictionary::create_class(mc),
script,
)?;
class(activation, flash::utils::timer::create_class(mc), script)?;
2021-10-02 03:13:42 +00:00
namespace(
activation,
"flash.utils",
"flash_proxy",
flash::utils::NS_FLASH_PROXY,
script,
)?;
class(activation, flash::utils::proxy::create_class(mc), script)?;
2021-03-27 16:59:56 +00:00
function(
activation,
2021-03-27 16:59:56 +00:00
"flash.utils",
"getTimer",
flash::utils::get_timer,
script,
)?;
function(
activation,
"flash.utils",
"getQualifiedClassName",
flash::utils::get_qualified_class_name,
script,
)?;
function(
activation,
"flash.utils",
"getQualifiedSuperclassName",
flash::utils::get_qualified_super_class_name,
script,
)?;
function(
activation,
"flash.utils",
"getDefinitionByName",
flash::utils::get_definition_by_name,
script,
)?;
// package `flash.display`
class(
activation,
flash::display::ibitmapdrawable::create_interface(mc),
script,
)?;
avm2_system_class!(
display_object,
activation,
flash::display::displayobject::create_class(mc),
script
);
avm2_system_class!(
shape,
2021-02-09 03:40:22 +00:00
activation,
flash::display::shape::create_class(mc),
script
);
class(
activation,
flash::display::interactiveobject::create_class(mc),
script,
)?;
avm2_system_class!(
simplebutton,
activation,
flash::display::simplebutton::create_class(mc),
script
);
class(
activation,
flash::display::displayobjectcontainer::create_class(mc),
script,
)?;
avm2_system_class!(
sprite,
activation,
flash::display::sprite::create_class(mc),
script
);
avm2_system_class!(
movieclip,
activation,
flash::display::movieclip::create_class(mc),
script
);
avm2_system_class!(
framelabel,
2020-09-17 01:32:00 +00:00
activation,
flash::display::framelabel::create_class(mc),
script
);
avm2_system_class!(
graphics,
2021-02-11 00:25:27 +00:00
activation,
flash::display::graphics::create_class(mc),
script
);
class(activation, flash::display::loader::create_class(mc), script)?;
avm2_system_class!(
loaderinfo,
activation,
flash::display::loaderinfo::create_class(mc),
script
);
avm2_system_class!(
stage,
2021-04-17 20:03:19 +00:00
activation,
flash::display::stage::create_class(mc),
script
);
avm2_system_class!(
bitmap,
activation,
flash::display::bitmap::create_class(mc),
script
);
avm2_system_class!(
bitmapdata,
activation,
flash::display::bitmapdata::create_class(mc),
script
);
2021-11-10 14:40:02 +00:00
avm2_system_class!(
nativemenu,
activation,
flash::display::nativemenu::create_class(mc),
script
);
2022-02-20 12:16:24 +00:00
class(
activation,
flash::display::nativemenuitem::create_class(mc),
2022-02-24 23:39:14 +00:00
script,
2022-02-20 12:16:24 +00:00
)?;
// package `flash.filters`
class(
activation,
flash::filters::bitmapfilter::create_class(mc),
script,
)?;
class(
activation,
flash::filters::blurfilter::create_class(mc),
script,
)?;
class(
activation,
flash::filters::glowfilter::create_class(mc),
script,
)?;
// package `flash.geom`
2021-01-05 03:14:14 +00:00
// package `flash.media`
avm2_system_class!(
video,
2021-01-05 03:14:14 +00:00
activation,
flash::media::video::create_class(mc),
script
);
class(activation, flash::media::sound::create_class(mc), script)?;
2021-08-14 22:33:01 +00:00
avm2_system_class!(
soundtransform,
activation,
flash::media::soundtransform::create_class(mc),
script
2021-08-14 22:33:01 +00:00
);
class(
activation,
flash::media::soundmixer::create_class(mc),
script,
)?;
avm2_system_class!(
soundchannel,
activation,
flash::media::soundchannel::create_class(mc),
script
);
2021-01-05 03:14:14 +00:00
2021-11-10 14:40:02 +00:00
// package `flash.ui`
avm2_system_class!(
contextmenu,
activation,
flash::ui::contextmenu::create_class(mc),
script
);
2022-02-24 23:39:14 +00:00
class(
activation,
flash::ui::contextmenuitem::create_class(mc),
script,
)?;
2021-12-19 14:14:42 +00:00
class(activation, flash::ui::mouse::create_class(mc), script)?;
class(activation, flash::ui::keyboard::create_class(mc), script)?;
2021-11-10 14:40:02 +00:00
// package `flash.net`
avm2_system_class!(
sharedobject,
activation,
flash::net::sharedobject::create_class(mc),
script
);
2021-07-23 22:59:06 +00:00
class(
activation,
flash::net::object_encoding::create_class(mc),
script,
)?;
class(activation, flash::net::url_loader::create_class(mc), script)?;
class(
activation,
flash::net::url_request::create_class(mc),
script,
)?;
2021-07-23 22:59:06 +00:00
2021-02-13 01:32:13 +00:00
// package `flash.text`
avm2_system_class!(
textfield,
2021-02-13 01:32:13 +00:00
activation,
flash::text::textfield::create_class(mc),
script
);
avm2_system_class!(
textformat,
2021-02-18 02:40:01 +00:00
activation,
flash::text::textformat::create_class(mc),
script
);
class(activation, flash::text::font::create_class(mc), script)?;
2021-02-13 01:32:13 +00:00
// package `flash.crypto`
function(
activation,
"flash.crypto",
"generateRandomBytes",
flash::crypto::generate_random_bytes,
script,
)?;
// package `flash.external`
class(
activation,
flash::external::externalinterface::create_class(mc),
script,
)?;
2022-07-01 04:34:26 +00:00
// Inside this call, the macro `avm2_system_classes_playerglobal`
// triggers classloading. Therefore, we run `load_playerglobal` last,
// so that all of our classes have been defined.
load_playerglobal(activation, domain)?;
Ok(())
}
/// This file is built by 'core/build_playerglobal/'
/// See that tool, and 'core/src/avm2/globals/README.md', for more details
const PLAYERGLOBAL: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/playerglobal.swf"));
/// Loads classes from our custom 'playerglobal' (which are written in ActionScript)
/// into the environment. See 'core/src/avm2/globals/README.md' for more information
fn load_playerglobal<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
domain: Domain<'gc>,
) -> Result<(), Error> {
let movie = Arc::new(SwfMovie::from_data(PLAYERGLOBAL, None, None)?);
let slice = SwfSlice::from(movie);
let mut reader = slice.read_from(0);
let tag_callback = |reader: &mut SwfStream<'_>, tag_code, tag_len| {
if tag_code == TagCode::DoAbc {
Avm2::load_abc_from_do_abc(&mut activation.context, &slice, domain, reader, tag_len)?;
} else if tag_code != TagCode::End {
panic!(
"playerglobal should only contain `DoAbc` tag - found tag {:?}",
tag_code
)
}
Ok(())
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::End);
macro_rules! avm2_system_classes_playerglobal {
($activation:expr, $script:expr, [$(($package:expr, $class_name:expr, $field:ident)),* $(,)?]) => {
$(
let qname = QName::new(Namespace::package($package), $class_name);
let class_object = activation.resolve_class(&qname.into())?;
let sc = $activation.avm2().system_classes.as_mut().unwrap();
sc.$field = class_object;
)*
}
}
// This acts the same way as 'avm2_system_class', but for classes
// declared in 'playerglobal'. Classes are declared as ("package", "class", field_name),
2022-06-29 14:28:42 +00:00
// and are stored in 'avm2().system_classes'
avm2_system_classes_playerglobal!(activation, script, [("flash.display", "Scene", scene)]);
Ok(())
}