avm2: Store caller movie in Activation

This commit is contained in:
Lord-McSweeney 2023-08-21 18:25:30 -07:00 committed by Aaron Hill
parent cf9171cb78
commit 69cdb93d53
7 changed files with 58 additions and 5 deletions

View File

@ -10,9 +10,11 @@ use crate::avm2::script::{Script, TranslationUnit};
use crate::context::{GcContext, UpdateContext};
use crate::display_object::{DisplayObject, DisplayObjectWeak, TDisplayObject};
use crate::string::AvmString;
use crate::tag_utils::SwfMovie;
use fnv::FnvHashMap;
use gc_arena::{Collect, GcCell, Mutation};
use std::sync::Arc;
use swf::avm2::read::Reader;
use swf::DoAbc2Flag;
@ -479,6 +481,7 @@ impl<'gc> Avm2<'gc> {
name: Option<AvmString<'gc>>,
flags: DoAbc2Flag,
domain: Domain<'gc>,
movie: Arc<SwfMovie>,
) -> Result<(), Error<'gc>> {
let mut reader = Reader::new(data);
let abc = match reader.read() {
@ -494,7 +497,7 @@ impl<'gc> Avm2<'gc> {
};
let num_scripts = abc.scripts.len();
let tunit = TranslationUnit::from_abc(abc, domain, name, context.gc_context);
let tunit = TranslationUnit::from_abc(abc, domain, name, movie, context.gc_context);
for i in 0..num_scripts {
tunit.load_script(i as u32, context)?;
}

View File

@ -24,10 +24,12 @@ use crate::avm2::{value, Avm2, Error};
use crate::context::{GcContext, UpdateContext};
use crate::string::{AvmAtom, AvmString};
use crate::swf::extensions::ReadSwfExt;
use crate::tag_utils::SwfMovie;
use gc_arena::{Gc, GcCell};
use smallvec::SmallVec;
use std::borrow::Cow;
use std::cmp::{min, Ordering};
use std::sync::Arc;
use swf::avm2::read::Reader;
use swf::avm2::types::{
Class as AbcClass, Exception, Index, Method as AbcMethod, MethodFlags as AbcMethodFlags,
@ -100,6 +102,11 @@ pub struct Activation<'a, 'gc: 'a> {
/// current domain instead.
caller_domain: Option<Domain<'gc>>,
/// The movie that called this builtin method.
/// This is intended to be used only for builtin methods- if this activation's method
/// is a bytecode method, the movie will instead be the movie that the bytecode method came from.
caller_movie: Option<Arc<SwfMovie>>,
/// The class that yielded the currently executing method.
///
/// This is used to maintain continuity when multiple methods supercall
@ -161,6 +168,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
local_registers,
outer: ScopeChain::new(context.avm2.stage_domain),
caller_domain: None,
caller_movie: None,
subclass_object: None,
activation_class: None,
stack_depth: context.avm2.stack.len(),
@ -188,6 +196,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
local_registers,
outer: ScopeChain::new(context.avm2.stage_domain),
caller_domain: Some(domain),
caller_movie: None,
subclass_object: None,
activation_class: None,
stack_depth: context.avm2.stack.len(),
@ -228,6 +237,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
local_registers,
outer: ScopeChain::new(domain),
caller_domain: Some(domain),
caller_movie: None,
subclass_object: None,
activation_class: None,
stack_depth: context.avm2.stack.len(),
@ -421,6 +431,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
local_registers,
outer,
caller_domain: Some(outer.domain()),
caller_movie: Some(method.owner_movie()),
subclass_object,
activation_class,
stack_depth: context.avm2.stack.len(),
@ -496,6 +507,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
subclass_object: Option<ClassObject<'gc>>,
outer: ScopeChain<'gc>,
caller_domain: Option<Domain<'gc>>,
caller_movie: Option<Arc<SwfMovie>>,
) -> Result<Self, Error<'gc>> {
let local_registers = RegisterSet::new(0);
@ -504,6 +516,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
local_registers,
outer,
caller_domain,
caller_movie,
subclass_object,
activation_class: None,
stack_depth: context.avm2.stack.len(),
@ -574,6 +587,12 @@ impl<'a, 'gc> Activation<'a, 'gc> {
self.caller_domain
}
/// Returns the movie of the original AS3 caller. This will be `None`
/// if this activation was constructed with `from_nothing`
pub fn caller_movie(&self) -> Option<Arc<SwfMovie>> {
self.caller_movie.clone()
}
/// Returns the global scope of this activation.
///
/// The global scope refers to scope at the bottom of the

View File

@ -125,12 +125,14 @@ impl<'gc> Executable<'gc> {
};
let caller_domain = activation.caller_domain();
let caller_movie = activation.caller_movie();
let subclass_object = bm.bound_superclass;
let mut activation = Activation::from_builtin(
activation.context.reborrow(),
subclass_object,
bm.scope,
caller_domain,
caller_movie,
)?;
if arguments.len() > bm.method.signature.len() && !bm.method.is_variadic {

View File

@ -684,10 +684,12 @@ fn load_playerglobal<'gc>(
activation.avm2().native_instance_init_table = native::NATIVE_INSTANCE_INIT_TABLE;
activation.avm2().native_call_handler_table = native::NATIVE_CALL_HANDLER_TABLE;
let movie = SwfMovie::from_data(PLAYERGLOBAL, "file:///".into(), None)
.expect("playerglobal.swf should be valid");
let movie = Arc::new(
SwfMovie::from_data(PLAYERGLOBAL, "file:///".into(), None)
.expect("playerglobal.swf should be valid"),
);
let slice = SwfSlice::from(Arc::new(movie));
let slice = SwfSlice::from(movie.clone());
let mut reader = slice.read_from(0);
@ -702,6 +704,7 @@ fn load_playerglobal<'gc>(
None,
do_abc.flags,
domain,
movie.clone(),
)
.expect("playerglobal.swf should be valid");
} else if tag_code != TagCode::End {

View File

@ -7,12 +7,14 @@ use crate::avm2::value::{abc_default_value, Value};
use crate::avm2::Error;
use crate::avm2::Multiname;
use crate::string::AvmString;
use crate::tag_utils::SwfMovie;
use gc_arena::barrier::unlock;
use gc_arena::lock::Lock;
use gc_arena::{Collect, Gc, Mutation};
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;
use swf::avm2::types::{
AbcFile, Index, Method as AbcMethod, MethodBody as AbcMethodBody,
MethodFlags as AbcMethodFlags, MethodParam as AbcMethodParam,
@ -198,6 +200,11 @@ impl<'gc> BytecodeMethod<'gc> {
self.abc.methods.get(self.abc_method as usize).unwrap()
}
/// Get a reference to the SwfMovie this method came from.
pub fn owner_movie(&self) -> Arc<SwfMovie> {
self.txunit.movie()
}
/// Get a reference to the ABC method body entry this refers to.
///
/// Some methods do not have bodies; this returns `None` in that case.

View File

@ -14,10 +14,12 @@ use crate::avm2::Namespace;
use crate::avm2::{Avm2, Error};
use crate::context::{GcContext, UpdateContext};
use crate::string::{AvmAtom, AvmString};
use crate::tag_utils::SwfMovie;
use gc_arena::{Collect, Gc, GcCell, Mutation};
use std::cell::Ref;
use std::mem::drop;
use std::rc::Rc;
use std::sync::Arc;
use swf::avm2::types::{
AbcFile, Index, Method as AbcMethod, Multiname as AbcMultiname, Namespace as AbcNamespace,
Script as AbcScript,
@ -72,6 +74,9 @@ struct TranslationUnitData<'gc> {
/// Note that some of these may have a runtime (lazy) component.
/// Make sure to check for that before using them.
multinames: Vec<Option<Gc<'gc, Multiname<'gc>>>>,
/// The movie that this TranslationUnit was loaded from.
movie: Arc<SwfMovie>,
}
impl<'gc> TranslationUnit<'gc> {
@ -81,6 +86,7 @@ impl<'gc> TranslationUnit<'gc> {
abc: AbcFile,
domain: Domain<'gc>,
name: Option<AvmString<'gc>>,
movie: Arc<SwfMovie>,
mc: &Mutation<'gc>,
) -> Self {
let classes = vec![None; abc.classes.len()];
@ -102,6 +108,7 @@ impl<'gc> TranslationUnit<'gc> {
strings,
namespaces,
multinames,
movie,
},
))
}
@ -120,6 +127,10 @@ impl<'gc> TranslationUnit<'gc> {
self.0.read().abc.clone()
}
pub fn movie(self) -> Arc<SwfMovie> {
self.0.read().movie.clone()
}
/// Load a method from the ABC file and return its method definition.
pub fn load_method(
self,

View File

@ -802,6 +802,7 @@ impl<'gc> MovieClip<'gc> {
None,
swf::DoAbc2Flag::LAZY_INITIALIZE,
domain,
self.movie(),
) {
tracing::warn!("Error loading ABC file: {e:?}");
}
@ -827,7 +828,14 @@ impl<'gc> MovieClip<'gc> {
let domain = context.library.library_for_movie_mut(movie).avm2_domain();
let name = AvmString::new(context.gc_context, do_abc.name.decode(reader.encoding()));
if let Err(e) = Avm2::do_abc(context, do_abc.data, Some(name), do_abc.flags, domain) {
if let Err(e) = Avm2::do_abc(
context,
do_abc.data,
Some(name),
do_abc.flags,
domain,
self.movie(),
) {
tracing::warn!("Error loading ABC file: {e:?}");
}
}