Skip to content

Commit

Permalink
avm2: Match Flash behavior for DoAbc/SymbolClass script initializers
Browse files Browse the repository at this point in the history
Flash Player handles SymbolClass tags and eager (non-lazy) DoAbc2 tags in an unusual way:
During the first time that a given frame is executed:
1. All SymbolClass tags are processed in order, triggering ClassObject loading (and the associated
   script initializer execution, if it hasn't already been run)
2. All eager (non-lazy) DoAbc/DoAbc2 tags have their *final* script initializer executed.

To implement this, we now cache the needed `Script` and symbol class
data during preloading. The first time we execute a frame, we run
the symbolclass class lookup and assignment, followed by the final
script initializer for each DoAbc2 tag in the frame.

This fixes 'minidash' (and probably other crossbridge games as well).
  • Loading branch information
Aaron1011 authored and Dinnerbone committed Jun 21, 2024
1 parent adea7ae commit dc03f54
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 160 deletions.
12 changes: 4 additions & 8 deletions core/src/avm2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ mod property_map;
mod qname;
mod regexp;
mod scope;
mod script;
pub mod script;
#[cfg(feature = "known_stubs")]
pub mod specification;
mod string;
Expand Down Expand Up @@ -580,7 +580,7 @@ impl<'gc> Avm2<'gc> {
flags: DoAbc2Flag,
domain: Domain<'gc>,
movie: Arc<SwfMovie>,
) -> Result<(), Error<'gc>> {
) -> Result<Option<Script<'gc>>, Error<'gc>> {
let mut reader = Reader::new(data);
let abc = match reader.read() {
Ok(abc) => abc,
Expand All @@ -604,13 +604,9 @@ impl<'gc> Avm2<'gc> {
}

if !flags.contains(DoAbc2Flag::LAZY_INITIALIZE) {
for i in 0..num_scripts {
if let Some(script) = tunit.get_script(i) {
script.globals(&mut activation.context)?;
}
}
return Ok(Some(tunit.get_script(num_scripts - 1).unwrap()));
}
Ok(())
Ok(None)
}

pub fn stage_domain(&self) -> Domain<'gc> {
Expand Down
5 changes: 3 additions & 2 deletions core/src/avm2/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,13 @@ impl<'gc> Domain<'gc> {
}
// FIXME - is this the correct api version?
let api_version = activation.avm2().root_api_version;
let name = QName::from_qualified_name(name, api_version, activation);
let name = QName::from_qualified_name(name, api_version, &mut activation.context);

let res = self.get_defined_value(activation, name);

if let Some(type_name) = type_name {
let type_qname = QName::from_qualified_name(type_name, api_version, activation);
let type_qname =
QName::from_qualified_name(type_name, api_version, &mut activation.context);
let type_class = self.get_defined_value(activation, type_qname)?;
if let Ok(res) = res {
let class = res.as_object().ok_or_else(|| {
Expand Down
6 changes: 3 additions & 3 deletions core/src/avm2/qname.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::avm2::script::TranslationUnit;
use crate::avm2::{Activation, Error, Namespace};
use crate::avm2::{Error, Namespace};
use crate::context::UpdateContext;
use crate::string::{AvmString, WStr, WString};
use either::Either;
Expand Down Expand Up @@ -100,13 +100,13 @@ impl<'gc> QName<'gc> {
pub fn from_qualified_name(
name: AvmString<'gc>,
api_version: ApiVersion,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc>,
) -> Self {
let parts = name
.rsplit_once(WStr::from_units(b"::"))
.or_else(|| name.rsplit_once(WStr::from_units(b".")));

let mut context = activation.borrow_gc();
let mut context = context.borrow_gc();
if let Some((package_name, local_name)) = parts {
let package_name = context
.interner
Expand Down
6 changes: 5 additions & 1 deletion core/src/avm2/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ impl<'gc> TranslationUnit<'gc> {
/// A loaded Script from an ABC file.
#[derive(Copy, Clone, Collect)]
#[collect(no_drop)]
pub struct Script<'gc>(GcCell<'gc, ScriptData<'gc>>);
pub struct Script<'gc>(pub GcCell<'gc, ScriptData<'gc>>);

#[derive(Clone, Collect)]
#[collect(no_drop)]
Expand Down Expand Up @@ -443,6 +443,8 @@ pub struct ScriptData<'gc> {

/// The `TranslationUnit` this script was loaded from.
translation_unit: Option<TranslationUnit<'gc>>,

pub abc_index: Option<u32>,
}

impl<'gc> Script<'gc> {
Expand Down Expand Up @@ -472,6 +474,7 @@ impl<'gc> Script<'gc> {
traits_loaded: true,
initialized: false,
translation_unit: None,
abc_index: None,
},
))
}
Expand Down Expand Up @@ -513,6 +516,7 @@ impl<'gc> Script<'gc> {
traits_loaded: false,
initialized: false,
translation_unit: Some(unit),
abc_index: Some(script_index),
},
)))
}
Expand Down
Loading

0 comments on commit dc03f54

Please sign in to comment.