avm2: Implement ApplicationDomain constructor and fix parent handling
Previously, the `ApplicationDomain` constructor ignored its argument, instead of constructing a new domain with the specified domain as the parent. Additionally, we were incorrectly executing code with `Activation::from_nothing` in several places, causing `ApplicationDomain.currentDomain` to return the system domain instead of the correct parent domain. I've introduced a new method `Activation::from_domain`, which allows explicitly passing in the domain. Internally, we now store an `Option<Domain>`, and panic when calling `caller_domain` with a `None` domain. Several places in the codebase have been adjusted to pass in the correct domain.
This commit is contained in:
parent
da6384b30e
commit
1a352aa453
|
@ -180,7 +180,7 @@ impl<'gc> Avm2<'gc> {
|
|||
|
||||
pub fn load_player_globals(context: &mut UpdateContext<'_, 'gc>) -> Result<(), Error<'gc>> {
|
||||
let globals = context.avm2.globals;
|
||||
let mut activation = Activation::from_nothing(context.reborrow());
|
||||
let mut activation = Activation::from_domain(context.reborrow(), globals);
|
||||
globals::load_player_globals(&mut activation, globals)
|
||||
}
|
||||
|
||||
|
@ -400,9 +400,10 @@ impl<'gc> Avm2<'gc> {
|
|||
callable: Object<'gc>,
|
||||
reciever: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
domain: Domain<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
) -> Result<(), String> {
|
||||
let mut evt_activation = Activation::from_nothing(context.reborrow());
|
||||
let mut evt_activation = Activation::from_domain(context.reborrow(), domain);
|
||||
callable
|
||||
.call(reciever, args, &mut evt_activation)
|
||||
.map_err(|e| e.detailed_message(&mut evt_activation))?;
|
||||
|
|
|
@ -117,7 +117,7 @@ pub struct Activation<'a, 'gc: 'a> {
|
|||
///
|
||||
/// If this activation was not made for a builtin method, this will be the
|
||||
/// current domain instead.
|
||||
caller_domain: Domain<'gc>,
|
||||
caller_domain: Option<Domain<'gc>>,
|
||||
|
||||
/// The class that yielded the currently executing method.
|
||||
///
|
||||
|
@ -176,7 +176,38 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
local_registers,
|
||||
return_value: None,
|
||||
outer: ScopeChain::new(context.avm2.globals),
|
||||
caller_domain: context.avm2.globals,
|
||||
caller_domain: None,
|
||||
subclass_object: None,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
scope_depth: context.avm2.scope_stack.len(),
|
||||
max_stack_size: 0,
|
||||
max_scope_size: 0,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `from_nothing`, but with a specified domain.
|
||||
///
|
||||
/// This should be used when you actually need to run AVM2 code, but
|
||||
/// don't have a particular scope to run it in. For example, this is
|
||||
/// used to run frame scripts for AVM2 movies.
|
||||
///
|
||||
/// The 'Domain' should come from the SwfMovie associated with whatever
|
||||
/// action you're performing. When running frame scripts, this is the
|
||||
/// `SwfMovie` associated with the `MovieClip` being processed.
|
||||
pub fn from_domain(context: UpdateContext<'a, 'gc>, domain: Domain<'gc>) -> Self {
|
||||
let local_registers = RegisterSet::new(0);
|
||||
|
||||
Self {
|
||||
this: None,
|
||||
arguments: None,
|
||||
is_executing: false,
|
||||
actions_since_timeout_check: 0,
|
||||
local_registers,
|
||||
return_value: None,
|
||||
outer: ScopeChain::new(context.avm2.globals),
|
||||
caller_domain: Some(domain),
|
||||
subclass_object: None,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
|
@ -220,7 +251,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
local_registers,
|
||||
return_value: None,
|
||||
outer: ScopeChain::new(domain),
|
||||
caller_domain: domain,
|
||||
caller_domain: Some(domain),
|
||||
subclass_object: None,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
|
@ -443,7 +474,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
drop(cached_cls);
|
||||
let translation_unit = method.translation_unit();
|
||||
let abc_method = method.method();
|
||||
let mut dummy_activation = Activation::from_nothing(context.reborrow());
|
||||
let mut dummy_activation =
|
||||
Activation::from_domain(context.reborrow(), outer.domain());
|
||||
dummy_activation.set_outer(outer);
|
||||
let activation_class = Class::for_activation(
|
||||
&mut dummy_activation,
|
||||
|
@ -472,7 +504,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
local_registers,
|
||||
return_value: None,
|
||||
outer,
|
||||
caller_domain: outer.domain(),
|
||||
caller_domain: Some(outer.domain()),
|
||||
subclass_object,
|
||||
activation_class,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
|
@ -555,7 +587,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
local_registers,
|
||||
return_value: None,
|
||||
outer,
|
||||
caller_domain,
|
||||
caller_domain: Some(caller_domain),
|
||||
subclass_object,
|
||||
activation_class: None,
|
||||
stack_depth: context.avm2.stack.len(),
|
||||
|
@ -641,7 +673,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
|
||||
/// Returns the domain of the original AS3 caller.
|
||||
pub fn caller_domain(&self) -> Domain<'gc> {
|
||||
self.caller_domain
|
||||
self.caller_domain.expect("No caller domain available - use Activation::from_domain when constructing your domain")
|
||||
}
|
||||
|
||||
/// Returns the global scope of this activation.
|
||||
|
|
|
@ -526,14 +526,6 @@ pub fn load_player_globals<'gc>(
|
|||
|
||||
avm2_system_class!(date, activation, date::create_class(activation), script);
|
||||
|
||||
// package `flash.system`
|
||||
avm2_system_class!(
|
||||
application_domain,
|
||||
activation,
|
||||
flash::system::application_domain::create_class(activation),
|
||||
script
|
||||
);
|
||||
|
||||
// package `flash.text`
|
||||
class(
|
||||
flash::text::font::create_class(activation),
|
||||
|
@ -687,6 +679,7 @@ fn load_playerglobal<'gc>(
|
|||
("flash.media", "SoundTransform", soundtransform),
|
||||
("flash.net", "URLVariables", urlvariables),
|
||||
("flash.utils", "ByteArray", bytearray),
|
||||
("flash.system", "ApplicationDomain", application_domain),
|
||||
("flash.text", "StaticText", statictext),
|
||||
("flash.text", "TextFormat", textformat),
|
||||
("flash.text", "TextField", textfield),
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
// This is a stub - the actual class is defined in `application_domain.rs`
|
||||
package flash.system {
|
||||
public class ApplicationDomain {
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
[Ruffle(InstanceAllocator)]
|
||||
public final class ApplicationDomain {
|
||||
public static native function get currentDomain():ApplicationDomain;
|
||||
|
||||
public function ApplicationDomain(parentDomain:ApplicationDomain = null) {
|
||||
this.init(parentDomain)
|
||||
}
|
||||
|
||||
private native function init(parentDomain:ApplicationDomain):void;
|
||||
|
||||
public native function get domainMemory():ByteArray;
|
||||
public native function set domainMemory(value:ByteArray):void;
|
||||
public native function get parentDomain():ApplicationDomain;
|
||||
|
||||
public native function getDefinition(name:String):Object;
|
||||
public native function hasDefinition(name:String):Boolean;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
//! `flash.system.ApplicationDomain` class
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
use crate::avm2::object::{appdomain_allocator, DomainObject, Object, TObject};
|
||||
use crate::avm2::object::{DomainObject, Object, TObject};
|
||||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use crate::avm2::Multiname;
|
||||
use crate::avm2::Namespace;
|
||||
use crate::avm2::QName;
|
||||
use gc_arena::GcCell;
|
||||
use crate::avm2::{Domain, Error};
|
||||
|
||||
/// Implements `flash.system.ApplicationDomain`'s instance constructor.
|
||||
pub fn instance_init<'gc>(
|
||||
pub use crate::avm2::object::application_domain_allocator;
|
||||
|
||||
/// Implements `flash.system.ApplicationDomain`'s init method, which
|
||||
/// is called from the constructor
|
||||
pub fn init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(this) = this {
|
||||
activation.super_init(this, &[])?;
|
||||
|
||||
let parent_domain = if matches!(args[0], Value::Null) {
|
||||
activation.avm2().global_domain()
|
||||
} else {
|
||||
args.get_object(activation, 0, "parentDomain")?
|
||||
.as_application_domain()
|
||||
.expect("Invalid parent domain")
|
||||
};
|
||||
let fresh_domain = Domain::movie_domain(activation, parent_domain);
|
||||
this.init_application_domain(activation.context.gc_context, fresh_domain);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `flash.system.ApplicationDomain`'s class constructor.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// `currentDomain` static property.
|
||||
pub fn current_domain<'gc>(
|
||||
pub fn get_current_domain<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
|
@ -45,7 +45,7 @@ pub fn current_domain<'gc>(
|
|||
}
|
||||
|
||||
/// `parentDomain` property
|
||||
pub fn parent_domain<'gc>(
|
||||
pub fn get_parent_domain<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
|
@ -120,7 +120,7 @@ pub fn set_domain_memory<'gc>(
|
|||
}
|
||||
|
||||
/// `domainMemory` property getter
|
||||
pub fn domain_memory<'gc>(
|
||||
pub fn get_domain_memory<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
|
@ -132,56 +132,3 @@ pub fn domain_memory<'gc>(
|
|||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Construct `ApplicationDomain`'s class.
|
||||
pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> GcCell<'gc, Class<'gc>> {
|
||||
let mc = activation.context.gc_context;
|
||||
let class = Class::new(
|
||||
QName::new(Namespace::package("flash.system", mc), "ApplicationDomain"),
|
||||
Some(Multiname::new(activation.avm2().public_namespace, "Object")),
|
||||
Method::from_builtin(
|
||||
instance_init,
|
||||
"<ApplicationDomain instance initializer>",
|
||||
mc,
|
||||
),
|
||||
Method::from_builtin(class_init, "<ApplicationDomain class initializer>", mc),
|
||||
mc,
|
||||
);
|
||||
|
||||
let mut write = class.write(mc);
|
||||
write.set_instance_allocator(appdomain_allocator);
|
||||
|
||||
const PUBLIC_CLASS_PROPERTIES: &[(&str, Option<NativeMethodImpl>, Option<NativeMethodImpl>)] =
|
||||
&[("currentDomain", Some(current_domain), None)];
|
||||
write.define_builtin_class_properties(
|
||||
mc,
|
||||
activation.avm2().public_namespace,
|
||||
PUBLIC_CLASS_PROPERTIES,
|
||||
);
|
||||
|
||||
const PUBLIC_INSTANCE_PROPERTIES: &[(
|
||||
&str,
|
||||
Option<NativeMethodImpl>,
|
||||
Option<NativeMethodImpl>,
|
||||
)] = &[
|
||||
("domainMemory", Some(domain_memory), Some(set_domain_memory)),
|
||||
("parentDomain", Some(parent_domain), None),
|
||||
];
|
||||
write.define_builtin_instance_properties(
|
||||
mc,
|
||||
activation.avm2().public_namespace,
|
||||
PUBLIC_INSTANCE_PROPERTIES,
|
||||
);
|
||||
|
||||
const PUBLIC_INSTANCE_METHODS: &[(&str, NativeMethodImpl)] = &[
|
||||
("getDefinition", get_definition),
|
||||
("hasDefinition", has_definition),
|
||||
];
|
||||
write.define_builtin_instance_methods(
|
||||
mc,
|
||||
activation.avm2().public_namespace,
|
||||
PUBLIC_INSTANCE_METHODS,
|
||||
);
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -264,6 +264,7 @@ include "flash/printing/PrintJobOrientation.as"
|
|||
include "flash/profiler.as"
|
||||
|
||||
include "flash/security/CertificateStatus.as"
|
||||
include "flash/system/ApplicationDomain.as"
|
||||
include "flash/system/Capabilities.as"
|
||||
include "flash/system/IME.as"
|
||||
include "flash/system/IMEConversionMode.as"
|
||||
|
|
|
@ -9,7 +9,6 @@ include "Array.as"
|
|||
include "Boolean.as"
|
||||
include "Date.as"
|
||||
|
||||
include "flash/system/ApplicationDomain.as"
|
||||
include "Function.as"
|
||||
include "Number.as"
|
||||
include "String.as"
|
||||
|
|
|
@ -70,7 +70,7 @@ pub use crate::avm2::object::context3d_object::Context3DObject;
|
|||
pub use crate::avm2::object::date_object::{date_allocator, DateObject};
|
||||
pub use crate::avm2::object::dictionary_object::{dictionary_allocator, DictionaryObject};
|
||||
pub use crate::avm2::object::dispatch_object::DispatchObject;
|
||||
pub use crate::avm2::object::domain_object::{appdomain_allocator, DomainObject};
|
||||
pub use crate::avm2::object::domain_object::{application_domain_allocator, DomainObject};
|
||||
pub use crate::avm2::object::error_object::{error_allocator, ErrorObject};
|
||||
pub use crate::avm2::object::event_object::{event_allocator, EventObject};
|
||||
pub use crate::avm2::object::function_object::{function_allocator, FunctionObject};
|
||||
|
@ -1155,6 +1155,10 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
fn init_display_object(&self, _context: &mut UpdateContext<'_, 'gc>, _obj: DisplayObject<'gc>) {
|
||||
}
|
||||
|
||||
fn init_application_domain(&self, _mc: MutationContext<'gc, '_>, _domain: Domain<'gc>) {
|
||||
panic!("Tried to init an application domain on a non-ApplicationDomain object!")
|
||||
}
|
||||
|
||||
/// Unwrap this object as an ApplicationDomain.
|
||||
fn as_application_domain(&self) -> Option<Domain<'gc>> {
|
||||
None
|
||||
|
|
|
@ -11,7 +11,7 @@ use gc_arena::{Collect, GcCell, MutationContext};
|
|||
use std::cell::{Ref, RefMut};
|
||||
|
||||
/// A class instance allocator that allocates AppDomain objects.
|
||||
pub fn appdomain_allocator<'gc>(
|
||||
pub fn application_domain_allocator<'gc>(
|
||||
class: ClassObject<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
|
@ -65,8 +65,12 @@ impl<'gc> DomainObject<'gc> {
|
|||
.into();
|
||||
this.install_instance_slots(activation);
|
||||
|
||||
class.call_init(Some(this), &[], activation)?;
|
||||
|
||||
// Note - we do *not* call the normal constructor, since that
|
||||
// creates a new domain using the system domain as a parent.
|
||||
class
|
||||
.superclass_object()
|
||||
.unwrap()
|
||||
.call_native_init(Some(this), &[], activation)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +92,10 @@ impl<'gc> TObject<'gc> for DomainObject<'gc> {
|
|||
Some(self.0.read().domain)
|
||||
}
|
||||
|
||||
fn init_application_domain(&self, mc: MutationContext<'gc, '_>, domain: Domain<'gc>) {
|
||||
self.0.write(mc).domain = domain;
|
||||
}
|
||||
|
||||
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let this: Object<'gc> = Object::DomainObject(*self);
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
|
||||
drop(read);
|
||||
|
||||
let mut activation = Activation::from_nothing(uc.reborrow());
|
||||
let mut activation = Activation::from_domain(uc.reborrow(), domain);
|
||||
let global_class = activation.avm2().classes().global;
|
||||
let global_obj = global_class.construct(&mut activation, &[])?;
|
||||
global_obj.fork_vtable(activation.context.gc_context);
|
||||
|
|
|
@ -479,13 +479,6 @@ pub enum ActionType<'gc> {
|
|||
method: &'static str,
|
||||
args: Vec<Avm1Value<'gc>>,
|
||||
},
|
||||
|
||||
/// An AVM2 callable, e.g. a frame script or event handler.
|
||||
Callable2 {
|
||||
callable: Avm2Object<'gc>,
|
||||
reciever: Option<Avm2Object<'gc>>,
|
||||
args: Vec<Avm2Value<'gc>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ActionType<'_> {
|
||||
|
@ -533,16 +526,6 @@ impl fmt::Debug for ActionType<'_> {
|
|||
.field("method", method)
|
||||
.field("args", args)
|
||||
.finish(),
|
||||
ActionType::Callable2 {
|
||||
callable,
|
||||
reciever,
|
||||
args,
|
||||
} => f
|
||||
.debug_struct("ActionType::Callable2")
|
||||
.field("callable", callable)
|
||||
.field("reciever", reciever)
|
||||
.field("args", args)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2490,10 +2490,19 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
.insert(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT);
|
||||
|
||||
drop(write);
|
||||
|
||||
let movie = self.movie();
|
||||
let domain = context
|
||||
.library
|
||||
.library_for_movie(movie)
|
||||
.unwrap()
|
||||
.avm2_domain();
|
||||
|
||||
if let Err(e) = Avm2::run_stack_frame_for_callable(
|
||||
callable,
|
||||
Some(avm2_object),
|
||||
&[],
|
||||
domain,
|
||||
context,
|
||||
) {
|
||||
tracing::error!(
|
||||
|
|
|
@ -745,7 +745,8 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
// TODO: Replace this when we have a convenience method for constructing AVM2 native objects.
|
||||
// TODO: We should only do this if the movie is actually an AVM2 movie.
|
||||
// This is necessary for EventDispatcher super-constructor to run.
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let global_domain = context.avm2.global_domain();
|
||||
let mut activation = Avm2Activation::from_domain(context.reborrow(), global_domain);
|
||||
let avm2_stage = Avm2StageObject::for_display_object_childless(
|
||||
&mut activation,
|
||||
(*self).into(),
|
||||
|
|
|
@ -243,7 +243,12 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
|||
_run_frame: bool,
|
||||
) {
|
||||
if context.is_action_script_3() {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let domain = context
|
||||
.library
|
||||
.library_for_movie(self.movie())
|
||||
.unwrap()
|
||||
.avm2_domain();
|
||||
let mut activation = Avm2Activation::from_domain(context.reborrow(), domain);
|
||||
let statictext = activation.avm2().classes().statictext;
|
||||
match Avm2StageObject::for_display_object_childless(
|
||||
&mut activation,
|
||||
|
|
|
@ -279,7 +279,12 @@ impl<'gc> Callback<'gc> {
|
|||
Value::Null
|
||||
}
|
||||
Callback::Avm2 { method } => {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let domain = context
|
||||
.library
|
||||
.library_for_movie(context.swf.clone())
|
||||
.unwrap()
|
||||
.avm2_domain();
|
||||
let mut activation = Avm2Activation::from_domain(context.reborrow(), domain);
|
||||
let args: Vec<Avm2Value> = args
|
||||
.into_iter()
|
||||
.map(|v| v.into_avm2(&mut activation))
|
||||
|
|
|
@ -678,7 +678,12 @@ impl<'gc> Loader<'gc> {
|
|||
.set_skip_next_enter_frame(true);
|
||||
|
||||
if let Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) = event_handler {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let domain = context
|
||||
.library
|
||||
.library_for_movie(mc.movie())
|
||||
.unwrap()
|
||||
.avm2_domain();
|
||||
let mut activation = Avm2Activation::from_domain(context.reborrow(), domain);
|
||||
let mut loader = loader_info
|
||||
.get_public_property("loader", &mut activation)
|
||||
.map_err(|e| Error::Avm2Error(e.to_string()))?
|
||||
|
|
|
@ -358,9 +358,13 @@ impl Player {
|
|||
.stage
|
||||
.set_movie(context.gc_context, context.swf.clone());
|
||||
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
let global_domain = activation.avm2().global_domain();
|
||||
let domain = Avm2Domain::movie_domain(&mut activation, global_domain);
|
||||
let global_domain = context.avm2.global_domain();
|
||||
let mut global_activation =
|
||||
Avm2Activation::from_domain(context.reborrow(), global_domain);
|
||||
let domain = Avm2Domain::movie_domain(&mut global_activation, global_domain);
|
||||
|
||||
let mut activation =
|
||||
Avm2Activation::from_domain(global_activation.context.reborrow(), domain);
|
||||
|
||||
activation
|
||||
.context
|
||||
|
@ -1686,18 +1690,6 @@ impl Player {
|
|||
&args,
|
||||
);
|
||||
}
|
||||
|
||||
ActionType::Callable2 {
|
||||
callable,
|
||||
reciever,
|
||||
args,
|
||||
} => {
|
||||
if let Err(e) =
|
||||
Avm2::run_stack_frame_for_callable(callable, reciever, &args[..], context)
|
||||
{
|
||||
tracing::error!("Unhandled AVM2 exception in event handler: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AVM1 bytecode may leave the stack unbalanced, so do not let garbage values accumulate
|
||||
|
|
Loading…
Reference in New Issue