avm1: Show stack frame with the avm_debug feature

This commit is contained in:
Nathan Adams 2020-07-02 23:37:18 +02:00 committed by Mike Welsh
parent c976cf8efb
commit 51321713b5
21 changed files with 212 additions and 59 deletions

View File

@ -39,7 +39,7 @@ pub mod xml_object;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::avm1::activation::Activation; use crate::avm1::activation::{Activation, ActivationIdentifier};
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::listeners::SystemListener; use crate::avm1::listeners::SystemListener;
pub use globals::SystemPrototypes; pub use globals::SystemPrototypes;
@ -131,9 +131,10 @@ impl<'gc> Avm1<'gc> {
/// Add a stack frame that executes code in timeline scope /// Add a stack frame that executes code in timeline scope
/// ///
/// This creates a new frame stack. /// This creates a new frame stack.
pub fn run_stack_frame_for_action( pub fn run_stack_frame_for_action<S: Into<Cow<'static, str>>>(
&mut self, &mut self,
active_clip: DisplayObject<'gc>, active_clip: DisplayObject<'gc>,
name: S,
swf_version: u8, swf_version: u8,
code: SwfSlice, code: SwfSlice,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
@ -145,6 +146,7 @@ impl<'gc> Avm1<'gc> {
let mut parent_activation = Activation::from_nothing( let mut parent_activation = Activation::from_nothing(
self, self,
ActivationIdentifier::root("[Actions Parent]"),
swf_version, swf_version,
self.global_object_cell(), self.global_object_cell(),
context.gc_context, context.gc_context,
@ -165,6 +167,7 @@ impl<'gc> Avm1<'gc> {
let constant_pool = parent_activation.avm.constant_pool; let constant_pool = parent_activation.avm.constant_pool;
let mut child_activation = Activation::from_action( let mut child_activation = Activation::from_action(
parent_activation.avm, parent_activation.avm,
parent_activation.id.child(name),
swf_version, swf_version,
child_scope, child_scope,
constant_pool, constant_pool,
@ -204,6 +207,7 @@ impl<'gc> Avm1<'gc> {
); );
let mut activation = Activation::from_action( let mut activation = Activation::from_action(
self, self,
ActivationIdentifier::root("[Display Object]"),
swf_version, swf_version,
child_scope, child_scope,
self.constant_pool, self.constant_pool,
@ -231,6 +235,7 @@ impl<'gc> Avm1<'gc> {
let mut parent_activation = Activation::from_nothing( let mut parent_activation = Activation::from_nothing(
self, self,
ActivationIdentifier::root("[Init Parent]"),
swf_version, swf_version,
self.global_object_cell(), self.global_object_cell(),
context.gc_context, context.gc_context,
@ -252,6 +257,7 @@ impl<'gc> Avm1<'gc> {
let constant_pool = parent_activation.avm.constant_pool; let constant_pool = parent_activation.avm.constant_pool;
let mut child_activation = Activation::from_action( let mut child_activation = Activation::from_action(
parent_activation.avm, parent_activation.avm,
parent_activation.id.child("[Init]"),
swf_version, swf_version,
child_scope, child_scope,
constant_pool, constant_pool,
@ -284,6 +290,7 @@ impl<'gc> Avm1<'gc> {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
self, self,
ActivationIdentifier::root(name.to_owned()),
swf_version, swf_version,
self.global_object_cell(), self.global_object_cell(),
context.gc_context, 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)); search_prototype(Some(obj), name, &mut activation, context, obj).map(|r| (r.0, r.1));
if let Ok((callback, base_proto)) = search_result { 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( let mut activation = Activation::from_nothing(
self, self,
ActivationIdentifier::root("[System Listeners]"),
swf_version, swf_version,
self.global_object_cell(), self.global_object_cell(),
context.gc_context, context.gc_context,
@ -319,7 +327,7 @@ impl<'gc> Avm1<'gc> {
let mut handlers = listeners.prepare_handlers(&mut activation, context, method); let mut handlers = listeners.prepare_handlers(&mut activation, context, method);
for (listener, handler) in handlers.drain(..) { 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);
} }
} }

View File

@ -18,6 +18,7 @@ use smallvec::SmallVec;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use swf::avm1::read::Reader; use swf::avm1::read::Reader;
use swf::avm1::types::{Action, CatchVar, Function, TryBlock}; use swf::avm1::types::{Action, CatchVar, Function, TryBlock};
use url::form_urlencoded; use url::form_urlencoded;
@ -88,8 +89,58 @@ enum FrameControl<'gc> {
Return(ReturnType<'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<S: Into<Cow<'static, str>>>(name: S) -> Self {
Self {
parent: None,
name: name.into(),
depth: 0,
}
}
pub fn child<S: Into<Cow<'static, str>>>(&'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)] #[derive(Collect)]
#[collect(no_drop)] #[collect(unsafe_drop)]
pub struct Activation<'a, 'gc: 'a> { pub struct Activation<'a, 'gc: 'a> {
pub avm: &'a mut Avm1<'gc>, 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. /// The current target display object of this stack frame.
/// This can be changed with `tellTarget` (via `ActionSetTarget` and `ActionSetTarget2`). /// This can be changed with `tellTarget` (via `ActionSetTarget` and `ActionSetTarget2`).
target_clip: Option<DisplayObject<'gc>>, target_clip: Option<DisplayObject<'gc>>,
/// 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> { impl<'a, 'gc: 'a> Activation<'a, 'gc> {
#[allow(clippy::too_many_arguments)]
pub fn from_action( pub fn from_action(
avm: &'a mut Avm1<'gc>, avm: &'a mut Avm1<'gc>,
id: ActivationIdentifier<'a>,
swf_version: u8, swf_version: u8,
scope: GcCell<'gc, Scope<'gc>>, scope: GcCell<'gc, Scope<'gc>>,
constant_pool: GcCell<'gc, Vec<String>>, constant_pool: GcCell<'gc, Vec<String>>,
@ -149,8 +213,10 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
this: Object<'gc>, this: Object<'gc>,
arguments: Option<Object<'gc>>, arguments: Option<Object<'gc>>,
) -> Self { ) -> Self {
avm_debug!("START {}", id);
Self { Self {
avm, avm,
id,
swf_version, swf_version,
scope, scope,
constant_pool, 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. /// 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<Cow<'static, str>>>(
&'b mut self,
name: S,
scope: GcCell<'gc, Scope<'gc>>,
) -> Activation<'b, 'gc> {
let id = self.id.child(name);
avm_debug!("START {}", id);
Activation { Activation {
avm: self.avm, avm: self.avm,
id,
swf_version: self.swf_version, swf_version: self.swf_version,
scope, scope,
constant_pool: self.constant_pool, constant_pool: self.constant_pool,
@ -185,6 +258,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
/// activation frame with access to the global context. /// activation frame with access to the global context.
pub fn from_nothing( pub fn from_nothing(
avm: &'a mut Avm1<'gc>, avm: &'a mut Avm1<'gc>,
id: ActivationIdentifier<'a>,
swf_version: u8, swf_version: u8,
globals: Object<'gc>, globals: Object<'gc>,
mc: MutationContext<'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 global_scope = GcCell::allocate(mc, Scope::from_global_object(globals));
let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc)); let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc));
let empty_constant_pool = GcCell::allocate(mc, Vec::new()); let empty_constant_pool = GcCell::allocate(mc, Vec::new());
avm_debug!("START {}", id);
Self { Self {
avm, avm,
id,
swf_version, swf_version,
scope: child_scope, scope: child_scope,
constant_pool: empty_constant_pool, 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 /// Add a stack frame that executes code in timeline scope
pub fn run_child_frame_for_action( pub fn run_child_frame_for_action<S: Into<Cow<'static, str>>>(
&mut self, &mut self,
name: S,
active_clip: DisplayObject<'gc>, active_clip: DisplayObject<'gc>,
swf_version: u8, swf_version: u8,
code: SwfSlice, code: SwfSlice,
@ -218,6 +295,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
) -> Result<ReturnType<'gc>, Error<'gc>> { ) -> Result<ReturnType<'gc>, Error<'gc>> {
let mut parent_activation = Activation::from_nothing( let mut parent_activation = Activation::from_nothing(
self.avm, self.avm,
self.id.child("[Actions Parent]"),
swf_version, swf_version,
self.avm.globals, self.avm.globals,
context.gc_context, context.gc_context,
@ -237,6 +315,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
let constant_pool = parent_activation.avm.constant_pool; let constant_pool = parent_activation.avm.constant_pool;
let mut child_activation = Activation::from_action( let mut child_activation = Activation::from_action(
parent_activation.avm, parent_activation.avm,
parent_activation.id.child(name),
swf_version, swf_version,
child_scope, child_scope,
constant_pool, constant_pool,
@ -248,8 +327,9 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
} }
/// Add a stack frame that executes code in initializer scope. /// 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<Cow<'static, str>>>(
&mut self, &mut self,
name: S,
active_clip: DisplayObject<'gc>, active_clip: DisplayObject<'gc>,
swf_version: u8, swf_version: u8,
action_context: &mut UpdateContext<'c, 'gc, '_>, 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 constant_pool = self.avm.constant_pool;
let mut activation = Activation::from_action( let mut activation = Activation::from_action(
self.avm, self.avm,
self.id.child(name),
swf_version, swf_version,
child_scope, child_scope,
constant_pool, constant_pool,
@ -311,7 +392,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
//Executing beyond the end of a function constitutes an implicit return. //Executing beyond the end of a function constitutes an implicit return.
Ok(FrameControl::Return(ReturnType::Implicit)) Ok(FrameControl::Return(ReturnType::Implicit))
} else if let Some(action) = reader.read_action()? { } else if let Some(action) = reader.read_action()? {
avm_debug!("Action: {:?}", action); avm_debug!("({}) Action: {:?}", self.id.depth(), action);
let result = match action { let result = match action {
Action::Add => self.action_add(context), Action::Add => self.action_add(context),
@ -654,6 +735,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
if let Some(frame) = frame { if let Some(frame) = frame {
for action in clip.actions_on_frame(context, frame) { for action in clip.actions_on_frame(context, frame) {
let _ = self.run_child_frame_for_action( let _ = self.run_child_frame_for_action(
"[Frame Call]",
self.target_clip_or_root(), self.target_clip_or_root(),
self.current_swf_version(), self.current_swf_version(),
action, action,
@ -687,7 +769,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
.target_clip_or_root() .target_clip_or_root()
.object() .object()
.coerce_to_object(self, context); .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); self.avm.push(result);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
@ -712,12 +794,12 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
.target_clip_or_root() .target_clip_or_root()
.object() .object()
.coerce_to_object(self, context); .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); self.avm.push(result);
} }
Value::String(name) => { Value::String(name) => {
if name.is_empty() { 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); self.avm.push(result);
} else { } else {
let result = object.call_method(&name, &args, self, context)?; 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? //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); self.avm.push(this);
} else { } 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); self.avm.push(this);
@ -2247,7 +2329,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
) -> Result<FrameControl<'gc>, Error<'gc>> { ) -> Result<FrameControl<'gc>, Error<'gc>> {
let object = self.avm.pop().coerce_to_object(self, context); 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 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)? { if let ReturnType::Explicit(value) = new_activation.run_actions(context, code)? {
Ok(FrameControl::Return(ReturnType::Explicit(value))) Ok(FrameControl::Return(ReturnType::Explicit(value)))
} else { } else {
@ -2270,6 +2352,7 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
if let Err(Error::ThrownValue(value)) = &result { if let Err(Error::ThrownValue(value)) = &result {
let mut activation = Activation::from_action( let mut activation = Activation::from_action(
self.avm, self.avm,
self.id.child("[Catch]"),
self.swf_version, self.swf_version,
self.scope, self.scope,
self.constant_pool, self.constant_pool,

View File

@ -222,6 +222,7 @@ impl<'gc> Executable<'gc> {
/// create a new stack frame and execute the action data yourself. /// create a new stack frame and execute the action data yourself.
pub fn exec( pub fn exec(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -278,8 +279,29 @@ impl<'gc> Executable<'gc> {
.unwrap_or(ac.player_version) .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( let mut frame = Activation::from_action(
activation.avm, activation.avm,
activation.id.child(name),
effective_ver, effective_ver,
child_scope, child_scope,
af.constant_pool, af.constant_pool,
@ -468,9 +490,9 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base.set(name, value, activation, context) self.base.set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -478,7 +500,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(exec) = self.as_executable() { 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 { } else {
Ok(Value::Undefined) Ok(Value::Undefined)
} }

View File

@ -811,7 +811,7 @@ fn sort_compare_custom<'gc>(
// TODO: Handle errors. // TODO: Handle errors.
let args = [a.clone(), b.clone()]; let args = [a.clone(), b.clone()];
let ret = compare_fn let ret = compare_fn
.call(activation, context, this, None, &args) .call("[Compare]", activation, context, this, None, &args)
.unwrap_or(Value::Undefined); .unwrap_or(Value::Undefined);
match ret { match ret {
Value::Number(n) if n > 0.0 => Ordering::Greater, Value::Number(n) if n > 0.0 => Ordering::Greater,

View File

@ -35,7 +35,7 @@ pub fn call<'gc>(
}; };
match func.as_executable() { 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), _ => Ok(Value::Undefined),
} }
} }
@ -68,7 +68,14 @@ pub fn apply<'gc>(
} }
match func.as_executable() { 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), _ => Ok(Value::Undefined),
} }
} }

View File

@ -85,6 +85,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// it can be changed by `Function.apply`/`Function.call`. /// it can be changed by `Function.apply`/`Function.call`.
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -120,7 +121,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
log::warn!("Object method {} is not callable", name); 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. /// Call a setter defined in this object.

View File

@ -247,6 +247,7 @@ impl<'gc> ScriptObject<'gc> {
this_proto.call_setter(name, value.clone(), activation, context) this_proto.call_setter(name, value.clone(), activation, context)
{ {
let _ = rval.exec( let _ = rval.exec(
"[Setter]",
activation, activation,
context, context,
this, this,
@ -280,7 +281,8 @@ impl<'gc> ScriptObject<'gc> {
}; };
if let Some(rval) = rval { 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 { if let Some(get) = exec {
get.exec(activation, context, this, Some((*self).into()), &[]) get.exec(
"[Getter]",
activation,
context,
this,
Some((*self).into()),
&[],
)
} else { } else {
Ok(Value::Undefined) Ok(Value::Undefined)
} }
@ -359,6 +368,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
/// overrides that may need to interact with the underlying object. /// overrides that may need to interact with the underlying object.
fn call( fn call(
&self, &self,
_name: &str,
_activation: &mut Activation<'_, 'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
@ -735,6 +745,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
mod tests { mod tests {
use super::*; use super::*;
use crate::avm1::activation::ActivationIdentifier;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::Avm1; use crate::avm1::Avm1;
@ -811,6 +822,7 @@ mod tests {
let globals = avm.global_object_cell(); let globals = avm.global_object_cell();
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
&mut avm, &mut avm,
ActivationIdentifier::root("[Test]"),
context.swf.version(), context.swf.version(),
globals, globals,
context.gc_context, context.gc_context,

View File

@ -89,9 +89,9 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -99,7 +99,7 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base() self.base()
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -150,9 +150,9 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -160,7 +160,7 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base() self.base()
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -218,9 +218,9 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
) )
} }
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -230,7 +230,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
self.0 self.0
.read() .read()
.base .base
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -101,9 +101,9 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
//TODO: What happens if you set `super.__proto__`? //TODO: What happens if you set `super.__proto__`?
Ok(()) Ok(())
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
@ -112,6 +112,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(constr) = self.super_constr(activation, context)? { if let Some(constr) = self.super_constr(activation, context)? {
constr.call( constr.call(
name,
activation, activation,
context, context,
self.0.read().child, self.0.read().child,
@ -140,7 +141,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
log::warn!("Super method {} is not callable", name); 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( fn call_setter(

View File

@ -1,4 +1,4 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::{Activation, ActivationIdentifier};
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::{Avm1, Object, UpdateContext}; use crate::avm1::{Avm1, Object, UpdateContext};
@ -99,6 +99,7 @@ where
let globals = avm.global_object_cell(); let globals = avm.global_object_cell();
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
&mut avm, &mut avm,
ActivationIdentifier::root("[Test]"),
context.swf.version(), context.swf.version(),
globals, globals,
context.gc_context, context.gc_context,
@ -128,7 +129,7 @@ macro_rules! test_method {
$( $(
args.push($arg.into()); 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(()) Ok(())

View File

@ -501,18 +501,15 @@ impl<'gc> Value<'gc> {
} }
} }
pub fn type_of(&self) -> Value<'gc> { pub fn type_of(&self) -> &'static str {
Value::String( match self {
match self { Value::Undefined => "undefined",
Value::Undefined => "undefined", Value::Null => "null",
Value::Null => "null", Value::Number(_) => "number",
Value::Number(_) => "number", Value::Bool(_) => "boolean",
Value::Bool(_) => "boolean", Value::String(_) => "string",
Value::String(_) => "string", Value::Object(object) => object.type_of(),
Value::Object(object) => object.type_of(), }
}
.to_string(),
)
} }
pub fn coerce_to_object( pub fn coerce_to_object(
@ -525,6 +522,7 @@ impl<'gc> Value<'gc> {
pub fn call( pub fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -532,7 +530,7 @@ impl<'gc> Value<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Value::Object(object) = self { if let Value::Object(object) = self {
object.call(activation, context, this, base_proto, args) object.call(name, activation, context, this, base_proto, args)
} else { } else {
Ok(Value::Undefined) Ok(Value::Undefined)
} }

View File

@ -154,9 +154,9 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.0.read().base.set(name, value, activation, context) self.0.read().base.set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -166,7 +166,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
self.0 self.0
.read() .read()
.base .base
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -85,9 +85,9 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
); );
self.base().set(name, value, activation, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -95,7 +95,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base() self.base()
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -85,9 +85,9 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -95,7 +95,7 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base() self.base()
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -77,9 +77,9 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
name: &str,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -87,7 +87,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base() self.base()
.call(activation, context, this, base_proto, args) .call(name, activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(

View File

@ -716,6 +716,7 @@ impl<'gc> EditText<'gc> {
let parent = self.parent().unwrap(); let parent = self.parent().unwrap();
activation.run_with_child_frame_for_display_object( activation.run_with_child_frame_for_display_object(
"[Text Field Binding]",
parent, parent,
context.swf.header().version, context.swf.header().version,
context, context,
@ -806,6 +807,7 @@ impl<'gc> EditText<'gc> {
// Note that this can call virtual setters, even though the opposite direction won't work // Note that this can call virtual setters, even though the opposite direction won't work
// (virtual property changes do not affect the text field) // (virtual property changes do not affect the text field)
activation.run_with_child_frame_for_display_object( activation.run_with_child_frame_for_display_object(
"[Propagate Text Binding]",
self.parent().unwrap(), self.parent().unwrap(),
context.swf.header().version, context.swf.header().version,
context, context,

View File

@ -2,7 +2,7 @@
use crate::avm1::{Avm1, Object, StageObject, TObject, Value}; use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
use crate::backend::audio::AudioStreamHandle; use crate::backend::audio::AudioStreamHandle;
use crate::avm1::activation::Activation; use crate::avm1::activation::{Activation, ActivationIdentifier};
use crate::character::Character; use crate::character::Character;
use crate::context::{ActionType, RenderContext, UpdateContext}; use crate::context::{ActionType, RenderContext, UpdateContext};
use crate::display_object::{ use crate::display_object::{
@ -977,6 +977,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Mouse Pick]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, 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() { if instantiated_from_avm && self.0.read().avm1_constructor.is_some() {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Construct]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, context.gc_context,
@ -1067,7 +1069,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
} }
} }
self.0.write(context.gc_context).object = Some(object); 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; return;
@ -1081,6 +1083,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
if let Some(init_object) = init_object { if let Some(init_object) = init_object {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Init]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, context.gc_context,

View File

@ -1,6 +1,6 @@
//! Management of async loaders //! Management of async loaders
use crate::avm1::activation::Activation; use crate::avm1::activation::{Activation, ActivationIdentifier};
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Object, TObject, Value};
use crate::backend::navigator::OwnedFuture; use crate::backend::navigator::OwnedFuture;
use crate::context::{ActionQueue, ActionType}; use crate::context::{ActionQueue, ActionType};
@ -476,6 +476,7 @@ impl<'gc> Loader<'gc> {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Form Loader]"),
uc.swf.version(), uc.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
uc.gc_context, uc.gc_context,

View File

@ -1,4 +1,4 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::{Activation, ActivationIdentifier};
use crate::avm1::debug::VariableDumper; use crate::avm1::debug::VariableDumper;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::listeners::SystemListener; use crate::avm1::listeners::SystemListener;
@ -274,6 +274,7 @@ impl Player {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Version Setter]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, context.gc_context,
@ -390,6 +391,7 @@ impl Player {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Variable Dumper]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, context.gc_context,
@ -740,6 +742,7 @@ impl Player {
ActionType::Normal { bytecode } => { ActionType::Normal { bytecode } => {
avm.run_stack_frame_for_action( avm.run_stack_frame_for_action(
actions.clip, actions.clip,
"[Frame]",
context.swf.header().version, context.swf.header().version,
bytecode, bytecode,
context, context,
@ -752,6 +755,7 @@ impl Player {
} => { } => {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Construct]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, context.gc_context,
@ -765,6 +769,7 @@ impl Player {
object.set_proto(context.gc_context, Some(prototype)); object.set_proto(context.gc_context, Some(prototype));
for event in events { for event in events {
let _ = activation.run_child_frame_for_action( let _ = activation.run_child_frame_for_action(
"[Actions]",
actions.clip, actions.clip,
context.swf.header().version, context.swf.header().version,
event, 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 { for event in events {
avm.run_stack_frame_for_action( avm.run_stack_frame_for_action(
actions.clip, actions.clip,
"[Construct]",
context.swf.header().version, context.swf.header().version,
event, event,
context, context,
@ -1005,6 +1018,7 @@ impl Player {
self.update(|avm, context| { self.update(|avm, context| {
let mut activation = Activation::from_nothing( let mut activation = Activation::from_nothing(
avm, avm,
ActivationIdentifier::root("[Flush]"),
context.swf.version(), context.swf.version(),
avm.global_object_cell(), avm.global_object_cell(),
context.gc_context, context.gc_context,