2020-02-04 02:22:44 +00:00
|
|
|
//! ActionScript Virtual Machine 2 (AS3) support
|
|
|
|
|
2022-07-21 06:11:46 +00:00
|
|
|
use crate::avm2::class::AllocatorFn;
|
2022-08-28 16:30:20 +00:00
|
|
|
use crate::avm2::function::Executable;
|
2022-06-29 14:28:42 +00:00
|
|
|
use crate::avm2::globals::SystemClasses;
|
2022-06-16 02:11:14 +00:00
|
|
|
use crate::avm2::method::{Method, NativeMethodImpl};
|
2020-09-24 00:07:09 +00:00
|
|
|
use crate::avm2::script::{Script, TranslationUnit};
|
2020-02-04 18:51:18 +00:00
|
|
|
use crate::context::UpdateContext;
|
2023-03-09 04:00:20 +00:00
|
|
|
use crate::display_object::{DisplayObject, DisplayObjectWeak, TDisplayObject};
|
2021-09-12 10:20:51 +00:00
|
|
|
use crate::string::AvmString;
|
2021-11-27 20:49:54 +00:00
|
|
|
use fnv::FnvHashMap;
|
2022-11-12 00:38:00 +00:00
|
|
|
use gc_arena::{Collect, GcCell, MutationContext};
|
2020-02-04 18:51:18 +00:00
|
|
|
use swf::avm2::read::Reader;
|
2023-02-12 20:32:04 +00:00
|
|
|
use swf::DoAbc2Flag;
|
2020-02-04 02:22:44 +00:00
|
|
|
|
2020-02-20 19:41:15 +00:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! avm_debug {
|
2020-07-23 20:19:52 +00:00
|
|
|
($avm: expr, $($arg:tt)*) => (
|
|
|
|
if $avm.show_debug_output() {
|
2023-01-04 10:09:47 +00:00
|
|
|
tracing::debug!($($arg)*)
|
2020-07-23 20:19:52 +00:00
|
|
|
}
|
2020-02-20 19:41:15 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-03-07 20:03:04 +00:00
|
|
|
pub mod activation;
|
2022-08-28 20:45:10 +00:00
|
|
|
mod amf;
|
2020-08-25 03:00:08 +00:00
|
|
|
mod array;
|
avm2: Partially implement `URLLoader` and related classes
This PR implements the `URLLoader` class, allowing AVM2 scripts
to load data from a URL. This requires several other related
classes (`URLLoaderDataFormat`, `URLRequest`, `IOError`) to be
implemented as well.
Currently implemented:
* Fetching from URLs using the 'navigator' backend
* The `text` and `binary` data formats (which store data
in a `String` or `ByteArray` respectively)
* The `open`, `complete`, and `ioError` events
* The `bytesLoaded`, `bytesTotal`, and `data` properties
Not yet implemented:
* The HTTP and security events
* All of the properties of `IOError`
* The properties on `URLRequest` (besides `url`)
* The "variables" data format
This should be enough to get some basic uses of `URLLoader` working
(e.g. simple GET requests to a particular website).
Note that in Flash's `playerglobal`, the `URLLoader` class is just
a think wrapper around the more general `URLStream`. However,
implementing `URLStream` will require changes to `Navigator``
to support notifications when data arrives in the stream. When
that happens, we should be able to re-use a large amount of the
code in this PR.
2022-04-06 01:27:39 +00:00
|
|
|
pub mod bytearray;
|
2022-08-28 16:30:20 +00:00
|
|
|
mod call_stack;
|
2020-06-30 03:58:48 +00:00
|
|
|
mod class;
|
2020-09-24 00:07:09 +00:00
|
|
|
mod domain;
|
2023-02-25 20:06:36 +00:00
|
|
|
mod e4x;
|
2022-09-13 17:17:24 +00:00
|
|
|
pub mod error;
|
2020-12-08 02:37:43 +00:00
|
|
|
mod events;
|
2023-02-22 22:22:12 +00:00
|
|
|
mod filters;
|
2020-02-06 04:15:03 +00:00
|
|
|
mod function;
|
2022-08-28 20:45:10 +00:00
|
|
|
pub mod globals;
|
2020-07-03 01:49:53 +00:00
|
|
|
mod method;
|
2022-08-12 22:29:46 +00:00
|
|
|
mod multiname;
|
|
|
|
mod namespace;
|
2022-03-07 20:03:04 +00:00
|
|
|
pub mod object;
|
2023-03-08 22:19:32 +00:00
|
|
|
mod parameters;
|
2020-02-15 01:30:19 +00:00
|
|
|
mod property;
|
2020-07-18 20:41:35 +00:00
|
|
|
mod property_map;
|
2022-08-12 22:29:46 +00:00
|
|
|
mod qname;
|
2021-02-11 07:56:21 +00:00
|
|
|
mod regexp;
|
2020-02-10 19:54:55 +00:00
|
|
|
mod scope;
|
2020-07-02 03:21:30 +00:00
|
|
|
mod script;
|
2020-07-18 20:20:58 +00:00
|
|
|
mod string;
|
2023-01-31 17:09:42 +00:00
|
|
|
mod stubs;
|
2020-08-15 01:20:41 +00:00
|
|
|
mod traits;
|
2020-02-04 02:22:44 +00:00
|
|
|
mod value;
|
2021-03-11 02:41:20 +00:00
|
|
|
mod vector;
|
2021-12-01 09:08:25 +00:00
|
|
|
mod vtable;
|
2020-02-04 02:22:44 +00:00
|
|
|
|
2020-08-06 02:11:52 +00:00
|
|
|
pub use crate::avm2::activation::Activation;
|
2021-02-26 00:04:25 +00:00
|
|
|
pub use crate::avm2::array::ArrayStorage;
|
2022-08-28 16:30:20 +00:00
|
|
|
pub use crate::avm2::call_stack::{CallNode, CallStack};
|
2020-10-07 03:48:43 +00:00
|
|
|
pub use crate::avm2::domain::Domain;
|
2022-09-13 17:17:24 +00:00
|
|
|
pub use crate::avm2::error::Error;
|
2022-09-14 20:29:25 +00:00
|
|
|
pub use crate::avm2::globals::flash::ui::context_menu::make_context_menu_state;
|
2022-08-12 22:29:46 +00:00
|
|
|
pub use crate::avm2::multiname::Multiname;
|
2023-02-09 16:54:38 +00:00
|
|
|
pub use crate::avm2::namespace::{Namespace, NamespaceData};
|
2021-09-24 20:54:36 +00:00
|
|
|
pub use crate::avm2::object::{
|
2022-07-27 14:44:42 +00:00
|
|
|
ArrayObject, ClassObject, EventObject, Object, ScriptObject, SoundChannelObject, StageObject,
|
|
|
|
TObject,
|
2021-09-24 20:54:36 +00:00
|
|
|
};
|
2022-08-12 22:29:46 +00:00
|
|
|
pub use crate::avm2::qname::QName;
|
2020-07-21 04:24:02 +00:00
|
|
|
pub use crate::avm2::value::Value;
|
|
|
|
|
2022-09-08 23:11:49 +00:00
|
|
|
use self::scope::Scope;
|
|
|
|
|
2023-01-29 04:38:59 +00:00
|
|
|
const BROADCAST_WHITELIST: [&str; 4] = ["enterFrame", "exitFrame", "frameConstructed", "render"];
|
2021-02-24 03:03:23 +00:00
|
|
|
|
2020-02-04 02:22:44 +00:00
|
|
|
/// The state of an AVM2 interpreter.
|
|
|
|
#[derive(Collect)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct Avm2<'gc> {
|
|
|
|
/// Values currently present on the operand stack.
|
|
|
|
stack: Vec<Value<'gc>>,
|
2020-02-12 19:58:33 +00:00
|
|
|
|
2022-09-08 23:11:49 +00:00
|
|
|
/// Scopes currently present of the scope stack.
|
|
|
|
scope_stack: Vec<Scope<'gc>>,
|
|
|
|
|
2022-08-28 16:30:20 +00:00
|
|
|
/// The current call stack of the player.
|
|
|
|
call_stack: GcCell<'gc, CallStack<'gc>>,
|
|
|
|
|
2020-02-12 19:58:33 +00:00
|
|
|
/// Global scope object.
|
2020-10-09 01:54:51 +00:00
|
|
|
globals: Domain<'gc>,
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2021-06-25 22:01:27 +00:00
|
|
|
/// System classes.
|
2021-06-18 22:11:37 +00:00
|
|
|
system_classes: Option<SystemClasses<'gc>>,
|
2021-05-28 02:22:38 +00:00
|
|
|
|
2023-02-09 16:54:38 +00:00
|
|
|
pub public_namespace: Namespace<'gc>,
|
|
|
|
pub as3_namespace: Namespace<'gc>,
|
|
|
|
pub vector_public_namespace: Namespace<'gc>,
|
|
|
|
pub vector_internal_namespace: Namespace<'gc>,
|
|
|
|
pub proxy_namespace: Namespace<'gc>,
|
2023-02-09 20:13:14 +00:00
|
|
|
// these are required to facilitate shared access between Rust and AS
|
|
|
|
pub flash_display_internal: Namespace<'gc>,
|
|
|
|
pub flash_utils_internal: Namespace<'gc>,
|
|
|
|
pub flash_geom_internal: Namespace<'gc>,
|
2023-02-23 19:01:20 +00:00
|
|
|
pub flash_events_internal: Namespace<'gc>,
|
2023-02-09 16:54:38 +00:00
|
|
|
|
2022-06-16 02:11:14 +00:00
|
|
|
#[collect(require_static)]
|
2022-09-25 00:14:40 +00:00
|
|
|
native_method_table: &'static [Option<(&'static str, NativeMethodImpl)>],
|
2022-07-21 06:11:46 +00:00
|
|
|
|
|
|
|
#[collect(require_static)]
|
2022-09-25 00:14:40 +00:00
|
|
|
native_instance_allocator_table: &'static [Option<(&'static str, AllocatorFn)>],
|
2022-06-16 02:11:14 +00:00
|
|
|
|
2022-11-23 23:30:47 +00:00
|
|
|
#[collect(require_static)]
|
|
|
|
native_instance_init_table: &'static [Option<(&'static str, NativeMethodImpl)>],
|
|
|
|
|
2023-03-15 20:05:30 +00:00
|
|
|
#[collect(require_static)]
|
|
|
|
native_call_handler_table: &'static [Option<(&'static str, NativeMethodImpl)>],
|
|
|
|
|
2021-01-22 01:09:08 +00:00
|
|
|
/// A list of objects which are capable of recieving broadcasts.
|
|
|
|
///
|
|
|
|
/// Certain types of events are "broadcast events" that are emitted on all
|
|
|
|
/// constructed objects in order of their creation, whether or not they are
|
|
|
|
/// currently present on the display list. This list keeps track of that.
|
|
|
|
///
|
|
|
|
/// TODO: These should be weak object pointers, but our current garbage
|
|
|
|
/// collector does not support weak references.
|
2021-11-27 20:49:54 +00:00
|
|
|
broadcast_list: FnvHashMap<AvmString<'gc>, Vec<Object<'gc>>>,
|
2021-01-22 01:09:08 +00:00
|
|
|
|
2023-03-09 04:00:20 +00:00
|
|
|
/// The list of 'orphan' objects - these objects have no parent,
|
2022-10-24 00:58:44 +00:00
|
|
|
/// so we need to manually run their frames in `run_all_phases_avm2` to match
|
|
|
|
/// Flash's behavior. Clips are added to this list with `add_orphan_movie`.
|
|
|
|
/// and are removed automatically by `cleanup_dead_orphans`.
|
|
|
|
///
|
2023-03-09 04:00:20 +00:00
|
|
|
/// We store `DisplayObjectWeak`, since we don't want to keep these objects
|
2022-10-24 00:58:44 +00:00
|
|
|
/// alive if they would otherwise be garbage-collected. The movie will
|
|
|
|
/// stop ticking whenever garbage collection runs if there are no more
|
|
|
|
/// strong references around (this matches Flash's behavior).
|
2023-03-09 04:00:20 +00:00
|
|
|
orphan_objects: Vec<DisplayObjectWeak<'gc>>,
|
2022-10-24 00:58:44 +00:00
|
|
|
|
2020-07-23 20:19:52 +00:00
|
|
|
#[cfg(feature = "avm_debug")]
|
|
|
|
pub debug_output: bool,
|
2020-02-04 02:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> Avm2<'gc> {
|
|
|
|
/// Construct a new AVM interpreter.
|
2020-02-12 19:58:33 +00:00
|
|
|
pub fn new(mc: MutationContext<'gc, '_>) -> Self {
|
2020-09-24 03:35:11 +00:00
|
|
|
let globals = Domain::global_domain(mc);
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
Self {
|
|
|
|
stack: Vec::new(),
|
2022-09-08 23:11:49 +00:00
|
|
|
scope_stack: Vec::new(),
|
2022-08-28 16:30:20 +00:00
|
|
|
call_stack: GcCell::allocate(mc, CallStack::new()),
|
2020-02-19 03:26:08 +00:00
|
|
|
globals,
|
2021-06-18 22:11:37 +00:00
|
|
|
system_classes: None,
|
2023-02-09 16:54:38 +00:00
|
|
|
|
|
|
|
public_namespace: Namespace::package("", mc),
|
|
|
|
as3_namespace: Namespace::package("http://adobe.com/AS3/2006/builtin", mc),
|
|
|
|
vector_public_namespace: Namespace::package("__AS3__.vec", mc),
|
|
|
|
vector_internal_namespace: Namespace::internal("__AS3__.vec", mc),
|
|
|
|
proxy_namespace: Namespace::package(
|
|
|
|
"http://www.adobe.com/2006/actionscript/flash/proxy",
|
|
|
|
mc,
|
|
|
|
),
|
2023-02-09 20:13:14 +00:00
|
|
|
// these are required to facilitate shared access between Rust and AS
|
|
|
|
flash_display_internal: Namespace::internal("flash.display", mc),
|
|
|
|
flash_utils_internal: Namespace::internal("flash.utils", mc),
|
|
|
|
flash_geom_internal: Namespace::internal("flash.geom", mc),
|
2023-02-23 19:01:20 +00:00
|
|
|
flash_events_internal: Namespace::internal("flash.events", mc),
|
2023-02-09 16:54:38 +00:00
|
|
|
|
2022-07-21 06:11:46 +00:00
|
|
|
native_method_table: Default::default(),
|
|
|
|
native_instance_allocator_table: Default::default(),
|
2022-11-23 23:30:47 +00:00
|
|
|
native_instance_init_table: Default::default(),
|
2023-03-15 20:05:30 +00:00
|
|
|
native_call_handler_table: Default::default(),
|
2021-11-27 20:49:54 +00:00
|
|
|
broadcast_list: Default::default(),
|
2020-07-23 20:19:52 +00:00
|
|
|
|
2023-03-09 04:00:20 +00:00
|
|
|
orphan_objects: Vec::new(),
|
2022-10-24 00:58:44 +00:00
|
|
|
|
2020-07-23 20:19:52 +00:00
|
|
|
#[cfg(feature = "avm_debug")]
|
|
|
|
debug_output: false,
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
2020-02-04 02:22:44 +00:00
|
|
|
}
|
2020-02-04 18:51:18 +00:00
|
|
|
|
2023-01-06 23:33:31 +00:00
|
|
|
pub fn load_player_globals(context: &mut UpdateContext<'_, 'gc>) -> Result<(), Error<'gc>> {
|
2020-09-24 03:35:11 +00:00
|
|
|
let globals = context.avm2.globals;
|
2020-07-07 04:24:16 +00:00
|
|
|
let mut activation = Activation::from_nothing(context.reborrow());
|
2020-09-24 03:35:11 +00:00
|
|
|
globals::load_player_globals(&mut activation, globals)
|
2020-07-07 04:24:16 +00:00
|
|
|
}
|
|
|
|
|
2021-06-18 22:11:37 +00:00
|
|
|
/// Return the current set of system classes.
|
2021-05-28 02:22:38 +00:00
|
|
|
///
|
|
|
|
/// This function panics if the interpreter has not yet been initialized.
|
2021-06-18 22:11:37 +00:00
|
|
|
pub fn classes(&self) -> &SystemClasses<'gc> {
|
|
|
|
self.system_classes.as_ref().unwrap()
|
2021-05-28 02:22:38 +00:00
|
|
|
}
|
|
|
|
|
2020-07-04 21:18:41 +00:00
|
|
|
/// Run a script's initializer method.
|
|
|
|
pub fn run_script_initializer(
|
2020-10-09 01:54:51 +00:00
|
|
|
script: Script<'gc>,
|
2023-01-06 23:33:31 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2022-09-13 21:04:04 +00:00
|
|
|
) -> Result<(), Error<'gc>> {
|
2020-09-24 03:35:11 +00:00
|
|
|
let mut init_activation = Activation::from_script(context.reborrow(), script)?;
|
2020-07-04 21:18:41 +00:00
|
|
|
|
2021-09-07 06:29:07 +00:00
|
|
|
let (method, scope, _domain) = script.init();
|
2020-10-09 01:54:51 +00:00
|
|
|
match method {
|
2021-06-19 01:49:40 +00:00
|
|
|
Method::Native(method) => {
|
2021-06-13 04:03:41 +00:00
|
|
|
//This exists purely to check if the builtin is OK with being called with
|
|
|
|
//no parameters.
|
2023-02-11 18:28:53 +00:00
|
|
|
init_activation.resolve_parameters(method.name, &[], &method.signature)?;
|
2022-08-28 16:30:20 +00:00
|
|
|
init_activation
|
|
|
|
.context
|
|
|
|
.avm2
|
2023-03-06 17:34:06 +00:00
|
|
|
.push_global_init(init_activation.context.gc_context, script);
|
2022-08-28 16:30:20 +00:00
|
|
|
let r = (method.method)(&mut init_activation, Some(scope), &[]);
|
|
|
|
init_activation
|
|
|
|
.context
|
|
|
|
.avm2
|
|
|
|
.pop_call(init_activation.context.gc_context);
|
|
|
|
r?;
|
2020-10-09 01:54:51 +00:00
|
|
|
}
|
2022-07-15 21:18:49 +00:00
|
|
|
Method::Bytecode(method) => {
|
2022-08-28 16:30:20 +00:00
|
|
|
init_activation
|
|
|
|
.context
|
|
|
|
.avm2
|
2023-03-06 17:34:06 +00:00
|
|
|
.push_global_init(init_activation.context.gc_context, script);
|
2022-08-28 16:30:20 +00:00
|
|
|
let r = init_activation.run_actions(method);
|
|
|
|
init_activation
|
|
|
|
.context
|
|
|
|
.avm2
|
|
|
|
.pop_call(init_activation.context.gc_context);
|
|
|
|
r?;
|
2020-10-09 01:54:51 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
2020-07-04 21:18:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 00:58:44 +00:00
|
|
|
/// Adds a `MovieClip` to the orphan list. In AVM2, movies advance their
|
|
|
|
/// frames even when they are not on a display list. Unfortunately,
|
|
|
|
/// mutliple SWFS rely on this behavior, so we need to match Flash's
|
|
|
|
/// behavior. This should not be called manually - `movie_clip` will
|
|
|
|
/// call it when necessary.
|
2023-03-09 04:00:20 +00:00
|
|
|
pub fn add_orphan_obj(&mut self, dobj: DisplayObject<'gc>) {
|
2022-10-24 00:58:44 +00:00
|
|
|
if self
|
2023-03-09 04:00:20 +00:00
|
|
|
.orphan_objects
|
2022-10-24 00:58:44 +00:00
|
|
|
.iter()
|
2023-03-09 04:00:20 +00:00
|
|
|
.all(|d| d.as_ptr() != dobj.as_ptr())
|
2022-10-24 00:58:44 +00:00
|
|
|
{
|
2023-03-09 04:00:20 +00:00
|
|
|
self.orphan_objects.push(dobj.downgrade());
|
2022-10-24 00:58:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-09 04:00:20 +00:00
|
|
|
pub fn each_orphan_obj(
|
2022-10-24 00:58:44 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2023-03-09 04:00:20 +00:00
|
|
|
mut f: impl FnMut(DisplayObject<'gc>, &mut UpdateContext<'_, 'gc>),
|
2022-10-24 00:58:44 +00:00
|
|
|
) {
|
|
|
|
let mut i = 0;
|
|
|
|
// FIXME - should we handle movies added while we're looping?
|
2023-03-09 04:00:20 +00:00
|
|
|
let total = context.avm2.orphan_objects.len();
|
2022-10-24 00:58:44 +00:00
|
|
|
|
|
|
|
// We cannot use an iterator, as it would conflict with the mutable borrow of `context`.
|
|
|
|
while i < total {
|
2023-03-09 04:00:20 +00:00
|
|
|
if let Some(dobj) = valid_orphan(context.avm2.orphan_objects[i], context.gc_context) {
|
|
|
|
f(dobj, context);
|
2022-10-24 00:58:44 +00:00
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Called at the end of `run_all_phases_avm2` - removes any movies
|
|
|
|
/// that have been garbage collected, or are no longer orphans
|
|
|
|
/// (they've since acquired a parent).
|
|
|
|
pub fn cleanup_dead_orphans(context: &mut UpdateContext<'_, 'gc>) {
|
2023-03-09 04:00:20 +00:00
|
|
|
context.avm2.orphan_objects.retain(|d| {
|
|
|
|
if let Some(dobj) = valid_orphan(*d, context.gc_context) {
|
2023-03-13 23:37:11 +00:00
|
|
|
// All clips that become orphaned (have their parent removed, or start out with no parent)
|
|
|
|
// get added to the orphan list. However, there's a distinction between clips
|
|
|
|
// that are removed from a RemoveObject tag, and clips that are removed from ActionScript.
|
|
|
|
//
|
|
|
|
// Clips removed from a RemoveObject tag only stay on the orphan list until the end
|
|
|
|
// of the frame - this lets them run a framescript (with 'this.parent == null')
|
|
|
|
// before they're removed. After that, they're removed from the orphan list,
|
|
|
|
// and will not be run in any way.
|
|
|
|
//
|
|
|
|
// Clips removed from ActionScript stay on the orphan list, and will be run
|
|
|
|
// indefinitely (if there are no remaining strong references, they will eventually
|
|
|
|
// be garbage collected).
|
|
|
|
//
|
|
|
|
// To detect this, we check 'placed_by_script'. This flag get set to 'true'
|
|
|
|
// for objects constructed from ActionScript, and for objects moved around
|
|
|
|
// in the timeline (add/remove child, swap depths) by ActionScript. A
|
|
|
|
// RemoveObject tag will only affect objects instantiated by the timeline,
|
|
|
|
// which have not been moved in the displaylist by ActionScript. Therefore,
|
|
|
|
// any orphan we see that has 'placed_by_script()' should stay on the orphan
|
|
|
|
// list, because it was not removed by a RemoveObject tag.
|
2023-03-09 04:00:20 +00:00
|
|
|
dobj.placed_by_script()
|
2023-03-13 23:37:11 +00:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
});
|
2022-10-24 00:58:44 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 01:02:07 +00:00
|
|
|
/// Dispatch an event on an object.
|
|
|
|
///
|
|
|
|
/// The `bool` parameter reads true if the event was cancelled.
|
|
|
|
pub fn dispatch_event(
|
2023-01-06 23:33:31 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2022-07-27 14:44:42 +00:00
|
|
|
event: Object<'gc>,
|
2021-01-13 01:02:07 +00:00
|
|
|
target: Object<'gc>,
|
2022-09-13 21:04:04 +00:00
|
|
|
) -> Result<bool, Error<'gc>> {
|
2021-01-13 01:02:07 +00:00
|
|
|
use crate::avm2::events::dispatch_event;
|
|
|
|
let mut activation = Activation::from_nothing(context.reborrow());
|
2022-07-27 14:44:42 +00:00
|
|
|
dispatch_event(&mut activation, target, event)
|
2021-01-13 01:02:07 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 01:09:08 +00:00
|
|
|
/// Add an object to the broadcast list.
|
|
|
|
///
|
2021-02-24 03:03:23 +00:00
|
|
|
/// Each broadcastable event contains it's own broadcast list. You must
|
|
|
|
/// register all objects that have event handlers with that event's
|
|
|
|
/// broadcast list by calling this function. Attempting to register a
|
|
|
|
/// broadcast listener for a non-broadcast event will do nothing.
|
|
|
|
///
|
|
|
|
/// Attempts to register the same listener for the same event will also do
|
|
|
|
/// nothing.
|
2021-01-22 01:09:08 +00:00
|
|
|
pub fn register_broadcast_listener(
|
2023-01-06 23:33:31 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2021-01-22 01:09:08 +00:00
|
|
|
object: Object<'gc>,
|
2021-02-24 02:49:42 +00:00
|
|
|
event_name: AvmString<'gc>,
|
2021-01-22 01:09:08 +00:00
|
|
|
) {
|
2021-10-05 17:25:46 +00:00
|
|
|
if !BROADCAST_WHITELIST
|
|
|
|
.iter()
|
|
|
|
.any(|x| AvmString::from(*x) == event_name)
|
|
|
|
{
|
2021-02-24 03:03:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-24 02:49:42 +00:00
|
|
|
let bucket = context.avm2.broadcast_list.entry(event_name).or_default();
|
|
|
|
|
|
|
|
if bucket.iter().any(|x| Object::ptr_eq(*x, object)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bucket.push(object);
|
2021-01-22 01:09:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Dispatch an event on all objects in the current execution list.
|
|
|
|
///
|
2021-06-03 02:15:26 +00:00
|
|
|
/// `on_type` specifies a class or interface constructor whose instances,
|
|
|
|
/// implementers, and/or subclasses define the set of objects that will
|
|
|
|
/// receive the event. You can broadcast to just display objects, or
|
|
|
|
/// specific interfaces, and so on.
|
2021-02-24 03:03:23 +00:00
|
|
|
///
|
2021-06-03 02:15:26 +00:00
|
|
|
/// Attempts to broadcast a non-broadcast event will do nothing. To add a
|
|
|
|
/// new broadcast type, you must add it to the `BROADCAST_WHITELIST` first.
|
2021-01-22 01:09:08 +00:00
|
|
|
pub fn broadcast_event(
|
2023-01-06 23:33:31 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2022-07-27 14:44:42 +00:00
|
|
|
event: Object<'gc>,
|
2021-09-24 20:54:36 +00:00
|
|
|
on_type: ClassObject<'gc>,
|
2022-09-13 21:04:04 +00:00
|
|
|
) -> Result<(), Error<'gc>> {
|
2022-07-27 14:44:42 +00:00
|
|
|
let base_event = event.as_event().unwrap(); // TODO: unwrap?
|
|
|
|
let event_name = base_event.event_type();
|
|
|
|
drop(base_event);
|
2021-10-05 17:25:46 +00:00
|
|
|
if !BROADCAST_WHITELIST
|
|
|
|
.iter()
|
|
|
|
.any(|x| AvmString::from(*x) == event_name)
|
|
|
|
{
|
2021-02-24 03:03:23 +00:00
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2021-02-24 02:49:42 +00:00
|
|
|
let el_length = context
|
|
|
|
.avm2
|
|
|
|
.broadcast_list
|
|
|
|
.entry(event_name)
|
|
|
|
.or_default()
|
|
|
|
.len();
|
2021-01-22 01:09:08 +00:00
|
|
|
|
|
|
|
for i in 0..el_length {
|
2021-02-24 02:49:42 +00:00
|
|
|
let object = context
|
|
|
|
.avm2
|
|
|
|
.broadcast_list
|
|
|
|
.get(&event_name)
|
|
|
|
.unwrap()
|
|
|
|
.get(i)
|
|
|
|
.copied();
|
2021-01-22 01:09:08 +00:00
|
|
|
|
|
|
|
if let Some(object) = object {
|
2021-07-01 02:16:57 +00:00
|
|
|
let mut activation = Activation::from_nothing(context.reborrow());
|
|
|
|
|
2022-09-02 08:48:10 +00:00
|
|
|
if object.is_of_type(on_type, &mut activation) {
|
2022-07-27 14:44:42 +00:00
|
|
|
Avm2::dispatch_event(&mut activation.context, event, object)?;
|
2021-01-22 01:09:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-08-22 20:16:47 +00:00
|
|
|
pub fn run_stack_frame_for_callable(
|
|
|
|
callable: Object<'gc>,
|
|
|
|
reciever: Option<Object<'gc>>,
|
|
|
|
args: &[Value<'gc>],
|
2023-01-06 23:33:31 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2022-09-13 21:04:04 +00:00
|
|
|
) -> Result<(), Error<'gc>> {
|
2020-08-22 20:16:47 +00:00
|
|
|
let mut evt_activation = Activation::from_nothing(context.reborrow());
|
2021-10-24 02:48:48 +00:00
|
|
|
callable.call(reciever, args, &mut evt_activation)?;
|
2020-08-22 20:16:47 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-02-12 20:32:04 +00:00
|
|
|
/// Load an ABC file embedded in a `DoAbc` or `DoAbc2` tag.
|
|
|
|
pub fn do_abc(
|
2023-01-06 23:33:31 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc>,
|
2023-02-12 20:32:04 +00:00
|
|
|
data: &[u8],
|
2023-03-06 17:34:06 +00:00
|
|
|
name: Option<AvmString<'gc>>,
|
2023-02-12 20:32:04 +00:00
|
|
|
flags: DoAbc2Flag,
|
2020-10-09 01:54:51 +00:00
|
|
|
domain: Domain<'gc>,
|
2022-09-13 21:04:04 +00:00
|
|
|
) -> Result<(), Error<'gc>> {
|
2023-02-12 20:32:04 +00:00
|
|
|
let mut reader = Reader::new(data);
|
2022-09-26 10:12:21 +00:00
|
|
|
let abc = match reader.read() {
|
|
|
|
Ok(abc) => abc,
|
|
|
|
Err(_) => {
|
|
|
|
let mut activation = Activation::from_nothing(context.reborrow());
|
|
|
|
return Err(Error::AvmError(crate::avm2::error::verify_error(
|
|
|
|
&mut activation,
|
|
|
|
"Error #1107: The ABC data is corrupt, attempt to read out of bounds.",
|
|
|
|
1107,
|
|
|
|
)?));
|
|
|
|
}
|
|
|
|
};
|
2020-02-18 22:54:39 +00:00
|
|
|
|
2022-09-26 10:12:21 +00:00
|
|
|
let num_scripts = abc.scripts.len();
|
2023-03-06 17:34:06 +00:00
|
|
|
let tunit = TranslationUnit::from_abc(abc, domain, name, context.gc_context);
|
2023-02-23 20:24:08 +00:00
|
|
|
for i in 0..num_scripts {
|
2023-02-11 00:38:38 +00:00
|
|
|
tunit.load_script(i as u32, context)?;
|
|
|
|
}
|
2020-07-04 21:18:41 +00:00
|
|
|
|
2023-02-12 20:32:04 +00:00
|
|
|
if !flags.contains(DoAbc2Flag::LAZY_INITIALIZE) {
|
2023-02-11 00:38:38 +00:00
|
|
|
for i in 0..num_scripts {
|
|
|
|
if let Some(mut script) = tunit.get_script(i) {
|
|
|
|
script.globals(context)?;
|
|
|
|
}
|
2020-10-09 01:54:51 +00:00
|
|
|
}
|
2020-02-18 22:54:39 +00:00
|
|
|
}
|
2020-02-04 18:51:18 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-06 04:15:03 +00:00
|
|
|
|
2020-10-09 01:54:51 +00:00
|
|
|
pub fn global_domain(&self) -> Domain<'gc> {
|
2020-02-12 19:58:33 +00:00
|
|
|
self.globals
|
|
|
|
}
|
|
|
|
|
2022-08-28 16:30:20 +00:00
|
|
|
/// Pushes an executable on the call stack
|
|
|
|
pub fn push_call(&self, mc: MutationContext<'gc, '_>, calling: Executable<'gc>) {
|
|
|
|
self.call_stack.write(mc).push(calling)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Pushes script initializer (global init) on the call stack
|
2023-03-06 17:34:06 +00:00
|
|
|
pub fn push_global_init(&self, mc: MutationContext<'gc, '_>, script: Script<'gc>) {
|
|
|
|
self.call_stack.write(mc).push_global_init(script)
|
2022-08-28 16:30:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Pops an executable off the call stack
|
|
|
|
pub fn pop_call(&self, mc: MutationContext<'gc, '_>) -> Option<CallNode<'gc>> {
|
|
|
|
self.call_stack.write(mc).pop()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn call_stack(&self) -> GcCell<'gc, CallStack<'gc>> {
|
|
|
|
self.call_stack
|
|
|
|
}
|
|
|
|
|
2020-02-07 00:28:54 +00:00
|
|
|
/// Push a value onto the operand stack.
|
2022-09-08 23:51:43 +00:00
|
|
|
fn push(&mut self, value: impl Into<Value<'gc>>, depth: usize, max: usize) {
|
|
|
|
if self.stack.len() - depth > max {
|
2023-01-04 10:09:47 +00:00
|
|
|
tracing::warn!("Avm2::push: Stack overflow");
|
2022-09-08 23:51:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-09-28 01:41:29 +00:00
|
|
|
let mut value = value.into();
|
|
|
|
if let Value::Object(o) = value {
|
|
|
|
if let Some(prim) = o.as_primitive() {
|
2021-12-07 01:19:12 +00:00
|
|
|
value = *prim;
|
2021-09-28 01:41:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 22:15:55 +00:00
|
|
|
avm_debug!(self, "Stack push {}: {value:?}", self.stack.len());
|
2020-02-07 00:28:54 +00:00
|
|
|
self.stack.push(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve the top-most value on the operand stack.
|
|
|
|
#[allow(clippy::let_and_return)]
|
2022-09-08 23:51:43 +00:00
|
|
|
fn pop(&mut self, depth: usize) -> Value<'gc> {
|
2022-09-09 23:04:21 +00:00
|
|
|
let value = if self.stack.len() <= depth {
|
2023-01-04 10:09:47 +00:00
|
|
|
tracing::warn!("Avm2::pop: Stack underflow");
|
2022-09-09 23:04:21 +00:00
|
|
|
Value::Undefined
|
|
|
|
} else {
|
|
|
|
self.stack.pop().unwrap_or(Value::Undefined)
|
2022-09-08 23:51:43 +00:00
|
|
|
};
|
2020-02-07 00:28:54 +00:00
|
|
|
|
2022-12-14 22:15:55 +00:00
|
|
|
avm_debug!(self, "Stack pop {}: {value:?}", self.stack.len());
|
2020-02-07 00:28:54 +00:00
|
|
|
|
|
|
|
value
|
|
|
|
}
|
|
|
|
|
2022-08-24 20:25:18 +00:00
|
|
|
/// Peek the n-th value from the end of the operand stack.
|
|
|
|
#[allow(clippy::let_and_return)]
|
|
|
|
fn peek(&mut self, index: usize) -> Value<'gc> {
|
|
|
|
let value = self
|
|
|
|
.stack
|
|
|
|
.get(self.stack.len() - index - 1)
|
|
|
|
.copied()
|
|
|
|
.unwrap_or_else(|| {
|
2023-01-04 10:09:47 +00:00
|
|
|
tracing::warn!("Avm1::pop: Stack underflow");
|
2022-08-24 20:25:18 +00:00
|
|
|
Value::Undefined
|
|
|
|
});
|
|
|
|
|
2022-12-14 22:15:55 +00:00
|
|
|
avm_debug!(self, "Stack peek {}: {value:?}", self.stack.len());
|
2022-08-24 20:25:18 +00:00
|
|
|
|
|
|
|
value
|
|
|
|
}
|
|
|
|
|
2022-09-08 23:51:43 +00:00
|
|
|
fn pop_args(&mut self, arg_count: u32, depth: usize) -> Vec<Value<'gc>> {
|
2022-03-14 20:10:18 +00:00
|
|
|
let mut args = vec![Value::Undefined; arg_count as usize];
|
2020-02-24 19:12:36 +00:00
|
|
|
for arg in args.iter_mut().rev() {
|
2022-09-08 23:51:43 +00:00
|
|
|
*arg = self.pop(depth);
|
2020-02-24 19:12:36 +00:00
|
|
|
}
|
|
|
|
args
|
|
|
|
}
|
2020-07-23 20:19:52 +00:00
|
|
|
|
2022-09-09 00:02:34 +00:00
|
|
|
fn push_scope(&mut self, scope: Scope<'gc>, depth: usize, max: usize) {
|
|
|
|
if self.scope_stack.len() - depth > max {
|
2023-01-04 10:09:47 +00:00
|
|
|
tracing::warn!("Avm2::push_scope: Scope Stack overflow");
|
2022-09-09 00:02:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.scope_stack.push(scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pop_scope(&mut self, depth: usize) -> Option<Scope<'gc>> {
|
2022-09-09 23:04:21 +00:00
|
|
|
if self.scope_stack.len() <= depth {
|
2023-01-04 10:09:47 +00:00
|
|
|
tracing::warn!("Avm2::pop_scope: Scope Stack underflow");
|
2022-09-09 23:04:21 +00:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
self.scope_stack.pop()
|
2022-09-09 00:02:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 20:19:52 +00:00
|
|
|
#[cfg(feature = "avm_debug")]
|
|
|
|
#[inline]
|
|
|
|
pub fn show_debug_output(&self) -> bool {
|
|
|
|
self.debug_output
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "avm_debug"))]
|
|
|
|
pub const fn show_debug_output(&self) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "avm_debug")]
|
|
|
|
pub fn set_show_debug_output(&mut self, visible: bool) {
|
|
|
|
self.debug_output = visible;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "avm_debug"))]
|
|
|
|
pub const fn set_show_debug_output(&self, _visible: bool) {}
|
2020-02-04 02:22:44 +00:00
|
|
|
}
|
2022-10-24 00:58:44 +00:00
|
|
|
|
2023-03-09 04:00:20 +00:00
|
|
|
/// If the provided `DisplayObjectWeak` should have frames run, returns
|
2022-10-24 00:58:44 +00:00
|
|
|
/// Some(clip) with an upgraded `MovieClip`.
|
|
|
|
/// If this returns `None`, the entry should be removed from the orphan list.
|
|
|
|
fn valid_orphan<'gc>(
|
2023-03-09 04:00:20 +00:00
|
|
|
dobj: DisplayObjectWeak<'gc>,
|
2022-10-24 00:58:44 +00:00
|
|
|
mc: MutationContext<'gc, '_>,
|
2023-03-09 04:00:20 +00:00
|
|
|
) -> Option<DisplayObject<'gc>> {
|
|
|
|
if let Some(dobj) = dobj.upgrade(mc) {
|
|
|
|
if dobj.parent().is_none() {
|
|
|
|
return Some(dobj);
|
2022-10-24 00:58:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|