From 51321713b5220c466dc4c57ce6acc5afac0fcfe6 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Thu, 2 Jul 2020 23:37:18 +0200 Subject: [PATCH] avm1: Show stack frame with the avm_debug feature --- core/src/avm1.rs | 16 +++- core/src/avm1/activation.rs | 105 ++++++++++++++++++++++--- core/src/avm1/function.rs | 26 +++++- core/src/avm1/globals/array.rs | 2 +- core/src/avm1/globals/function.rs | 11 ++- core/src/avm1/object.rs | 3 +- core/src/avm1/script_object.rs | 16 +++- core/src/avm1/shared_object.rs | 4 +- core/src/avm1/sound_object.rs | 4 +- core/src/avm1/stage_object.rs | 4 +- core/src/avm1/super_object.rs | 5 +- core/src/avm1/test_utils.rs | 5 +- core/src/avm1/value.rs | 24 +++--- core/src/avm1/value_object.rs | 4 +- core/src/avm1/xml_attributes_object.rs | 4 +- core/src/avm1/xml_idmap_object.rs | 4 +- core/src/avm1/xml_object.rs | 4 +- core/src/display_object/edit_text.rs | 2 + core/src/display_object/movie_clip.rs | 7 +- core/src/loader.rs | 3 +- core/src/player.rs | 18 ++++- 21 files changed, 212 insertions(+), 59 deletions(-) diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 63abae5d9..f172cc9b3 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -39,7 +39,7 @@ pub mod xml_object; #[cfg(test)] mod tests; -use crate::avm1::activation::Activation; +use crate::avm1::activation::{Activation, ActivationIdentifier}; use crate::avm1::error::Error; use crate::avm1::listeners::SystemListener; pub use globals::SystemPrototypes; @@ -131,9 +131,10 @@ impl<'gc> Avm1<'gc> { /// Add a stack frame that executes code in timeline scope /// /// This creates a new frame stack. - pub fn run_stack_frame_for_action( + pub fn run_stack_frame_for_action>>( &mut self, active_clip: DisplayObject<'gc>, + name: S, swf_version: u8, code: SwfSlice, context: &mut UpdateContext<'_, 'gc, '_>, @@ -145,6 +146,7 @@ impl<'gc> Avm1<'gc> { let mut parent_activation = Activation::from_nothing( self, + ActivationIdentifier::root("[Actions Parent]"), swf_version, self.global_object_cell(), context.gc_context, @@ -165,6 +167,7 @@ impl<'gc> Avm1<'gc> { let constant_pool = parent_activation.avm.constant_pool; let mut child_activation = Activation::from_action( parent_activation.avm, + parent_activation.id.child(name), swf_version, child_scope, constant_pool, @@ -204,6 +207,7 @@ impl<'gc> Avm1<'gc> { ); let mut activation = Activation::from_action( self, + ActivationIdentifier::root("[Display Object]"), swf_version, child_scope, self.constant_pool, @@ -231,6 +235,7 @@ impl<'gc> Avm1<'gc> { let mut parent_activation = Activation::from_nothing( self, + ActivationIdentifier::root("[Init Parent]"), swf_version, self.global_object_cell(), context.gc_context, @@ -252,6 +257,7 @@ impl<'gc> Avm1<'gc> { let constant_pool = parent_activation.avm.constant_pool; let mut child_activation = Activation::from_action( parent_activation.avm, + parent_activation.id.child("[Init]"), swf_version, child_scope, constant_pool, @@ -284,6 +290,7 @@ impl<'gc> Avm1<'gc> { let mut activation = Activation::from_nothing( self, + ActivationIdentifier::root(name.to_owned()), swf_version, self.global_object_cell(), context.gc_context, @@ -294,7 +301,7 @@ impl<'gc> Avm1<'gc> { search_prototype(Some(obj), name, &mut activation, context, obj).map(|r| (r.0, r.1)); if let Ok((callback, base_proto)) = search_result { - let _ = callback.call(&mut activation, context, obj, base_proto, args); + let _ = callback.call(name, &mut activation, context, obj, base_proto, args); } } @@ -309,6 +316,7 @@ impl<'gc> Avm1<'gc> { ) { let mut activation = Activation::from_nothing( self, + ActivationIdentifier::root("[System Listeners]"), swf_version, self.global_object_cell(), context.gc_context, @@ -319,7 +327,7 @@ impl<'gc> Avm1<'gc> { let mut handlers = listeners.prepare_handlers(&mut activation, context, method); for (listener, handler) in handlers.drain(..) { - let _ = handler.call(&mut activation, context, listener, None, &args); + let _ = handler.call(method, &mut activation, context, listener, None, &args); } } diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index bbe8dad55..4365bc9a1 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -18,6 +18,7 @@ use smallvec::SmallVec; use std::borrow::Cow; use std::cell::{Ref, RefMut}; use std::collections::HashMap; +use std::fmt; use swf::avm1::read::Reader; use swf::avm1::types::{Action, CatchVar, Function, TryBlock}; use url::form_urlencoded; @@ -88,8 +89,58 @@ enum FrameControl<'gc> { Return(ReturnType<'gc>), } +#[derive(Debug, Clone)] +pub struct ActivationIdentifier<'a> { + parent: Option<&'a ActivationIdentifier<'a>>, + name: Cow<'static, str>, + depth: usize, +} + +impl fmt::Display for ActivationIdentifier<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(parent) = self.parent { + write!(f, "{} / ", parent)?; + } + + f.write_str(&self.name)?; + + Ok(()) + } +} + +impl<'a> ActivationIdentifier<'a> { + pub fn root>>(name: S) -> Self { + Self { + parent: None, + name: name.into(), + depth: 0, + } + } + + pub fn child>>(&'a self, name: S) -> Self { + Self { + parent: Some(self), + name: name.into(), + depth: self.depth + 1, + } + } + + pub fn depth(&self) -> usize { + self.depth + } +} + +unsafe impl<'gc> gc_arena::Collect for ActivationIdentifier<'gc> { + fn needs_trace() -> bool { + false + } + + #[inline] + fn trace(&self, _cc: gc_arena::CollectionContext) {} +} + #[derive(Collect)] -#[collect(no_drop)] +#[collect(unsafe_drop)] pub struct Activation<'a, 'gc: 'a> { pub avm: &'a mut Avm1<'gc>, @@ -137,11 +188,24 @@ pub struct Activation<'a, 'gc: 'a> { /// The current target display object of this stack frame. /// This can be changed with `tellTarget` (via `ActionSetTarget` and `ActionSetTarget2`). target_clip: Option>, + + /// An identifier to refer to this activation by, when debugging. + /// This is often the name of a function (if known), or some static name to indicate where + /// in the code it is (for example, a with{} block). + pub id: ActivationIdentifier<'a>, +} + +impl Drop for Activation<'_, '_> { + fn drop(&mut self) { + avm_debug!("END {}", self.id); + } } impl<'a, 'gc: 'a> Activation<'a, 'gc> { + #[allow(clippy::too_many_arguments)] pub fn from_action( avm: &'a mut Avm1<'gc>, + id: ActivationIdentifier<'a>, swf_version: u8, scope: GcCell<'gc, Scope<'gc>>, constant_pool: GcCell<'gc, Vec>, @@ -149,8 +213,10 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { this: Object<'gc>, arguments: Option>, ) -> Self { + avm_debug!("START {}", id); Self { avm, + id, swf_version, scope, constant_pool, @@ -164,9 +230,16 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { } /// Create a new activation to run a block of code with a given scope. - pub fn with_new_scope<'b>(&'b mut self, scope: GcCell<'gc, Scope<'gc>>) -> Activation<'b, 'gc> { + pub fn with_new_scope<'b, S: Into>>( + &'b mut self, + name: S, + scope: GcCell<'gc, Scope<'gc>>, + ) -> Activation<'b, 'gc> { + let id = self.id.child(name); + avm_debug!("START {}", id); Activation { avm: self.avm, + id, swf_version: self.swf_version, scope, constant_pool: self.constant_pool, @@ -185,6 +258,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { /// activation frame with access to the global context. pub fn from_nothing( avm: &'a mut Avm1<'gc>, + id: ActivationIdentifier<'a>, swf_version: u8, globals: Object<'gc>, mc: MutationContext<'gc, '_>, @@ -193,9 +267,11 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { let global_scope = GcCell::allocate(mc, Scope::from_global_object(globals)); let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc)); let empty_constant_pool = GcCell::allocate(mc, Vec::new()); + avm_debug!("START {}", id); Self { avm, + id, swf_version, scope: child_scope, constant_pool: empty_constant_pool, @@ -209,8 +285,9 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { } /// Add a stack frame that executes code in timeline scope - pub fn run_child_frame_for_action( + pub fn run_child_frame_for_action>>( &mut self, + name: S, active_clip: DisplayObject<'gc>, swf_version: u8, code: SwfSlice, @@ -218,6 +295,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { ) -> Result, Error<'gc>> { let mut parent_activation = Activation::from_nothing( self.avm, + self.id.child("[Actions Parent]"), swf_version, self.avm.globals, context.gc_context, @@ -237,6 +315,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { let constant_pool = parent_activation.avm.constant_pool; let mut child_activation = Activation::from_action( parent_activation.avm, + parent_activation.id.child(name), swf_version, child_scope, constant_pool, @@ -248,8 +327,9 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { } /// Add a stack frame that executes code in initializer scope. - pub fn run_with_child_frame_for_display_object<'c, F, R>( + pub fn run_with_child_frame_for_display_object<'c, F, R, S: Into>>( &mut self, + name: S, active_clip: DisplayObject<'gc>, swf_version: u8, action_context: &mut UpdateContext<'c, 'gc, '_>, @@ -273,6 +353,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { let constant_pool = self.avm.constant_pool; let mut activation = Activation::from_action( self.avm, + self.id.child(name), swf_version, child_scope, constant_pool, @@ -311,7 +392,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { //Executing beyond the end of a function constitutes an implicit return. Ok(FrameControl::Return(ReturnType::Implicit)) } else if let Some(action) = reader.read_action()? { - avm_debug!("Action: {:?}", action); + avm_debug!("({}) Action: {:?}", self.id.depth(), action); let result = match action { Action::Add => self.action_add(context), @@ -654,6 +735,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { if let Some(frame) = frame { for action in clip.actions_on_frame(context, frame) { let _ = self.run_child_frame_for_action( + "[Frame Call]", self.target_clip_or_root(), self.current_swf_version(), action, @@ -687,7 +769,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { .target_clip_or_root() .object() .coerce_to_object(self, context); - let result = target_fn.call(self, context, this, None, &args)?; + let result = target_fn.call(&fn_name, self, context, this, None, &args)?; self.avm.push(result); Ok(FrameControl::Continue) @@ -712,12 +794,12 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { .target_clip_or_root() .object() .coerce_to_object(self, context); - let result = object.call(self, context, this, None, &args)?; + let result = object.call("[Anonymous]", self, context, this, None, &args)?; self.avm.push(result); } Value::String(name) => { if name.is_empty() { - let result = object.call(self, context, object, None, &args)?; + let result = object.call("[Anonymous]", self, context, object, None, &args)?; self.avm.push(result); } else { let result = object.call_method(&name, &args, self, context)?; @@ -1601,7 +1683,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { } //TODO: What happens if you `ActionNewMethod` without a method name? - constructor.call(self, context, this, None, &args)?; + constructor.call("[ctor]", self, context, this, None, &args)?; self.avm.push(this); } else { @@ -1652,7 +1734,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { ); } - constructor.call(self, context, this, None, &args)?; + constructor.call("[ctor]", self, context, this, None, &args)?; self.avm.push(this); @@ -2247,7 +2329,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { ) -> Result, Error<'gc>> { let object = self.avm.pop().coerce_to_object(self, context); let with_scope = Scope::new_with_scope(self.scope_cell(), object, context.gc_context); - let mut new_activation = self.with_new_scope(with_scope); + let mut new_activation = self.with_new_scope("[With]", with_scope); if let ReturnType::Explicit(value) = new_activation.run_actions(context, code)? { Ok(FrameControl::Return(ReturnType::Explicit(value))) } else { @@ -2270,6 +2352,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> { if let Err(Error::ThrownValue(value)) = &result { let mut activation = Activation::from_action( self.avm, + self.id.child("[Catch]"), self.swf_version, self.scope, self.constant_pool, diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 393579334..436dd44de 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -222,6 +222,7 @@ impl<'gc> Executable<'gc> { /// create a new stack frame and execute the action data yourself. pub fn exec( &self, + name: &str, activation: &mut Activation<'_, 'gc>, ac: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -278,8 +279,29 @@ impl<'gc> Executable<'gc> { .unwrap_or(ac.player_version) }; + let name = if cfg!(feature = "avm_debug") { + let mut result = match &af.name { + None => name.to_string(), + Some(name) => name.to_string(), + }; + + result.push('('); + for i in 0..args.len() { + result.push_str(args.get(i).unwrap().type_of()); + if i < args.len() - 1 { + result.push_str(", "); + } + } + result.push(')'); + + result + } else { + af.name.clone().unwrap_or_else(|| name.to_string()) + }; + let mut frame = Activation::from_action( activation.avm, + activation.id.child(name), effective_ver, child_scope, af.constant_pool, @@ -468,9 +490,9 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { ) -> Result<(), Error<'gc>> { self.base.set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -478,7 +500,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { if let Some(exec) = self.as_executable() { - exec.exec(activation, context, this, base_proto, args) + exec.exec(name, activation, context, this, base_proto, args) } else { Ok(Value::Undefined) } diff --git a/core/src/avm1/globals/array.rs b/core/src/avm1/globals/array.rs index b43e187a2..765f7cb9a 100644 --- a/core/src/avm1/globals/array.rs +++ b/core/src/avm1/globals/array.rs @@ -811,7 +811,7 @@ fn sort_compare_custom<'gc>( // TODO: Handle errors. let args = [a.clone(), b.clone()]; let ret = compare_fn - .call(activation, context, this, None, &args) + .call("[Compare]", activation, context, this, None, &args) .unwrap_or(Value::Undefined); match ret { Value::Number(n) if n > 0.0 => Ordering::Greater, diff --git a/core/src/avm1/globals/function.rs b/core/src/avm1/globals/function.rs index bbefcb4ea..890e6f6f5 100644 --- a/core/src/avm1/globals/function.rs +++ b/core/src/avm1/globals/function.rs @@ -35,7 +35,7 @@ pub fn call<'gc>( }; match func.as_executable() { - Some(exec) => exec.exec(activation, action_context, this, None, args), + Some(exec) => exec.exec("[Anonymous]", activation, action_context, this, None, args), _ => Ok(Value::Undefined), } } @@ -68,7 +68,14 @@ pub fn apply<'gc>( } match func.as_executable() { - Some(exec) => exec.exec(activation, action_context, this, None, &child_args), + Some(exec) => exec.exec( + "[Anonymous]", + activation, + action_context, + this, + None, + &child_args, + ), _ => Ok(Value::Undefined), } } diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 0cde26b9e..2f8534f64 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -85,6 +85,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// it can be changed by `Function.apply`/`Function.call`. fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -120,7 +121,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy log::warn!("Object method {} is not callable", name); } - method.call(activation, context, (*self).into(), base_proto, args) + method.call(name, activation, context, (*self).into(), base_proto, args) } /// Call a setter defined in this object. diff --git a/core/src/avm1/script_object.rs b/core/src/avm1/script_object.rs index 710b34653..5a1d1ca56 100644 --- a/core/src/avm1/script_object.rs +++ b/core/src/avm1/script_object.rs @@ -247,6 +247,7 @@ impl<'gc> ScriptObject<'gc> { this_proto.call_setter(name, value.clone(), activation, context) { let _ = rval.exec( + "[Setter]", activation, context, this, @@ -280,7 +281,8 @@ impl<'gc> ScriptObject<'gc> { }; if let Some(rval) = rval { - let _ = rval.exec(activation, context, this, base_proto, &[value])?; + let _ = + rval.exec("[Setter]", activation, context, this, base_proto, &[value])?; } } } @@ -324,7 +326,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { } if let Some(get) = exec { - get.exec(activation, context, this, Some((*self).into()), &[]) + get.exec( + "[Getter]", + activation, + context, + this, + Some((*self).into()), + &[], + ) } else { Ok(Value::Undefined) } @@ -359,6 +368,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { /// overrides that may need to interact with the underlying object. fn call( &self, + _name: &str, _activation: &mut Activation<'_, 'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, _this: Object<'gc>, @@ -735,6 +745,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { mod tests { use super::*; + use crate::avm1::activation::ActivationIdentifier; use crate::avm1::globals::system::SystemProperties; use crate::avm1::property::Attribute::*; use crate::avm1::Avm1; @@ -811,6 +822,7 @@ mod tests { let globals = avm.global_object_cell(); let mut activation = Activation::from_nothing( &mut avm, + ActivationIdentifier::root("[Test]"), context.swf.version(), globals, context.gc_context, diff --git a/core/src/avm1/shared_object.rs b/core/src/avm1/shared_object.rs index 8961fb0a6..fea930ea6 100644 --- a/core/src/avm1/shared_object.rs +++ b/core/src/avm1/shared_object.rs @@ -89,9 +89,9 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> { ) -> Result<(), Error<'gc>> { self.base().set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -99,7 +99,7 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { self.base() - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/sound_object.rs b/core/src/avm1/sound_object.rs index 3f29bc2e6..057a61f27 100644 --- a/core/src/avm1/sound_object.rs +++ b/core/src/avm1/sound_object.rs @@ -150,9 +150,9 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> { ) -> Result<(), Error<'gc>> { self.base().set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -160,7 +160,7 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { self.base() - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/stage_object.rs b/core/src/avm1/stage_object.rs index 40826fe63..e395a528a 100644 --- a/core/src/avm1/stage_object.rs +++ b/core/src/avm1/stage_object.rs @@ -218,9 +218,9 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { ) } } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -230,7 +230,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { self.0 .read() .base - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/super_object.rs b/core/src/avm1/super_object.rs index e839a547a..771fa0701 100644 --- a/core/src/avm1/super_object.rs +++ b/core/src/avm1/super_object.rs @@ -101,9 +101,9 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> { //TODO: What happens if you set `super.__proto__`? Ok(()) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, _this: Object<'gc>, @@ -112,6 +112,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> { ) -> Result, Error<'gc>> { if let Some(constr) = self.super_constr(activation, context)? { constr.call( + name, activation, context, self.0.read().child, @@ -140,7 +141,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> { log::warn!("Super method {} is not callable", name); } - method.call(activation, context, child, base_proto, args) + method.call(name, activation, context, child, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/test_utils.rs b/core/src/avm1/test_utils.rs index 2769db304..b2666dfd7 100644 --- a/core/src/avm1/test_utils.rs +++ b/core/src/avm1/test_utils.rs @@ -1,4 +1,4 @@ -use crate::avm1::activation::Activation; +use crate::avm1::activation::{Activation, ActivationIdentifier}; use crate::avm1::error::Error; use crate::avm1::globals::system::SystemProperties; use crate::avm1::{Avm1, Object, UpdateContext}; @@ -99,6 +99,7 @@ where let globals = avm.global_object_cell(); let mut activation = Activation::from_nothing( &mut avm, + ActivationIdentifier::root("[Test]"), context.swf.version(), globals, context.gc_context, @@ -128,7 +129,7 @@ macro_rules! test_method { $( args.push($arg.into()); )* - assert_eq!(function.call(activation, context, object, None, &args)?, $out.into(), "{:?} => {:?} in swf {}", args, $out, version); + assert_eq!(function.call($name, activation, context, object, None, &args)?, $out.into(), "{:?} => {:?} in swf {}", args, $out, version); )* Ok(()) diff --git a/core/src/avm1/value.rs b/core/src/avm1/value.rs index 155a32eaf..e4e7ea584 100644 --- a/core/src/avm1/value.rs +++ b/core/src/avm1/value.rs @@ -501,18 +501,15 @@ impl<'gc> Value<'gc> { } } - pub fn type_of(&self) -> Value<'gc> { - Value::String( - match self { - Value::Undefined => "undefined", - Value::Null => "null", - Value::Number(_) => "number", - Value::Bool(_) => "boolean", - Value::String(_) => "string", - Value::Object(object) => object.type_of(), - } - .to_string(), - ) + pub fn type_of(&self) -> &'static str { + match self { + Value::Undefined => "undefined", + Value::Null => "null", + Value::Number(_) => "number", + Value::Bool(_) => "boolean", + Value::String(_) => "string", + Value::Object(object) => object.type_of(), + } } pub fn coerce_to_object( @@ -525,6 +522,7 @@ impl<'gc> Value<'gc> { pub fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -532,7 +530,7 @@ impl<'gc> Value<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { if let Value::Object(object) = self { - object.call(activation, context, this, base_proto, args) + object.call(name, activation, context, this, base_proto, args) } else { Ok(Value::Undefined) } diff --git a/core/src/avm1/value_object.rs b/core/src/avm1/value_object.rs index a2a113b70..e92be441c 100644 --- a/core/src/avm1/value_object.rs +++ b/core/src/avm1/value_object.rs @@ -154,9 +154,9 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> { ) -> Result<(), Error<'gc>> { self.0.read().base.set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -166,7 +166,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> { self.0 .read() .base - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/xml_attributes_object.rs b/core/src/avm1/xml_attributes_object.rs index aa5b2bd4a..6b6a5b42e 100644 --- a/core/src/avm1/xml_attributes_object.rs +++ b/core/src/avm1/xml_attributes_object.rs @@ -85,9 +85,9 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> { ); self.base().set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -95,7 +95,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { self.base() - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/xml_idmap_object.rs b/core/src/avm1/xml_idmap_object.rs index 02cb8442a..1a463027c 100644 --- a/core/src/avm1/xml_idmap_object.rs +++ b/core/src/avm1/xml_idmap_object.rs @@ -85,9 +85,9 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> { ) -> Result<(), Error<'gc>> { self.base().set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -95,7 +95,7 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { self.base() - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/avm1/xml_object.rs b/core/src/avm1/xml_object.rs index dcbf8a8cf..2b58c65de 100644 --- a/core/src/avm1/xml_object.rs +++ b/core/src/avm1/xml_object.rs @@ -77,9 +77,9 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> { ) -> Result<(), Error<'gc>> { self.base().set(name, value, activation, context) } - fn call( &self, + name: &str, activation: &mut Activation<'_, 'gc>, context: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, @@ -87,7 +87,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> { args: &[Value<'gc>], ) -> Result, Error<'gc>> { self.base() - .call(activation, context, this, base_proto, args) + .call(name, activation, context, this, base_proto, args) } fn call_setter( diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 2b1e4ffd2..021562bb7 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -716,6 +716,7 @@ impl<'gc> EditText<'gc> { let parent = self.parent().unwrap(); activation.run_with_child_frame_for_display_object( + "[Text Field Binding]", parent, context.swf.header().version, context, @@ -806,6 +807,7 @@ impl<'gc> EditText<'gc> { // Note that this can call virtual setters, even though the opposite direction won't work // (virtual property changes do not affect the text field) activation.run_with_child_frame_for_display_object( + "[Propagate Text Binding]", self.parent().unwrap(), context.swf.header().version, context, diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index e270cd320..351b8d7e4 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -2,7 +2,7 @@ use crate::avm1::{Avm1, Object, StageObject, TObject, Value}; use crate::backend::audio::AudioStreamHandle; -use crate::avm1::activation::Activation; +use crate::avm1::activation::{Activation, ActivationIdentifier}; use crate::character::Character; use crate::context::{ActionType, RenderContext, UpdateContext}; use crate::display_object::{ @@ -977,6 +977,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Mouse Pick]"), context.swf.version(), avm.global_object_cell(), context.gc_context, @@ -1042,6 +1043,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { if instantiated_from_avm && self.0.read().avm1_constructor.is_some() { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Construct]"), context.swf.version(), avm.global_object_cell(), context.gc_context, @@ -1067,7 +1069,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { } } self.0.write(context.gc_context).object = Some(object); - let _ = constructor.call(&mut activation, context, object, None, &[]); + let _ = constructor.call("[ctor]", &mut activation, context, object, None, &[]); } return; @@ -1081,6 +1083,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { if let Some(init_object) = init_object { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Init]"), context.swf.version(), avm.global_object_cell(), context.gc_context, diff --git a/core/src/loader.rs b/core/src/loader.rs index 6ac40af9c..593491d6b 100644 --- a/core/src/loader.rs +++ b/core/src/loader.rs @@ -1,6 +1,6 @@ //! Management of async loaders -use crate::avm1::activation::Activation; +use crate::avm1::activation::{Activation, ActivationIdentifier}; use crate::avm1::{Object, TObject, Value}; use crate::backend::navigator::OwnedFuture; use crate::context::{ActionQueue, ActionType}; @@ -476,6 +476,7 @@ impl<'gc> Loader<'gc> { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Form Loader]"), uc.swf.version(), avm.global_object_cell(), uc.gc_context, diff --git a/core/src/player.rs b/core/src/player.rs index ba6e6a058..e80e2e203 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -1,4 +1,4 @@ -use crate::avm1::activation::Activation; +use crate::avm1::activation::{Activation, ActivationIdentifier}; use crate::avm1::debug::VariableDumper; use crate::avm1::globals::system::SystemProperties; use crate::avm1::listeners::SystemListener; @@ -274,6 +274,7 @@ impl Player { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Version Setter]"), context.swf.version(), avm.global_object_cell(), context.gc_context, @@ -390,6 +391,7 @@ impl Player { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Variable Dumper]"), context.swf.version(), avm.global_object_cell(), context.gc_context, @@ -740,6 +742,7 @@ impl Player { ActionType::Normal { bytecode } => { avm.run_stack_frame_for_action( actions.clip, + "[Frame]", context.swf.header().version, bytecode, context, @@ -752,6 +755,7 @@ impl Player { } => { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Construct]"), context.swf.version(), avm.global_object_cell(), context.gc_context, @@ -765,6 +769,7 @@ impl Player { object.set_proto(context.gc_context, Some(prototype)); for event in events { let _ = activation.run_child_frame_for_action( + "[Actions]", actions.clip, context.swf.header().version, event, @@ -772,7 +777,14 @@ impl Player { ); } - let _ = constructor.call(&mut activation, context, object, None, &[]); + let _ = constructor.call( + "[ctor]", + &mut activation, + context, + object, + None, + &[], + ); } } } @@ -784,6 +796,7 @@ impl Player { for event in events { avm.run_stack_frame_for_action( actions.clip, + "[Construct]", context.swf.header().version, event, context, @@ -1005,6 +1018,7 @@ impl Player { self.update(|avm, context| { let mut activation = Activation::from_nothing( avm, + ActivationIdentifier::root("[Flush]"), context.swf.version(), avm.global_object_cell(), context.gc_context,