avm2: Ensure that objects are only *ever* constructed at frame construction time.

This ensures that root movie classes see their children in their constructor.
This commit is contained in:
David Wendt 2021-04-08 21:22:43 -04:00 committed by Mike Welsh
parent e8de3a5a20
commit ebc5c3dd64
9 changed files with 55 additions and 6 deletions

View File

@ -78,6 +78,7 @@ pub struct MovieClipData<'gc> {
has_button_clip_event: bool,
flags: MovieClipFlags,
avm2_constructor: Option<Avm2Object<'gc>>,
avm2_constructor_ran: bool,
drawing: Drawing,
is_focusable: bool,
has_focus: bool,
@ -105,6 +106,7 @@ impl<'gc> MovieClip<'gc> {
has_button_clip_event: false,
flags: MovieClipFlags::empty(),
avm2_constructor: None,
avm2_constructor_ran: false,
drawing: Drawing::new(),
is_focusable: false,
has_focus: false,
@ -137,6 +139,7 @@ impl<'gc> MovieClip<'gc> {
has_button_clip_event: false,
flags: MovieClipFlags::empty(),
avm2_constructor: Some(constr),
avm2_constructor_ran: false,
drawing: Drawing::new(),
is_focusable: false,
has_focus: false,
@ -172,6 +175,7 @@ impl<'gc> MovieClip<'gc> {
has_button_clip_event: false,
flags: MovieClipFlags::PLAYING,
avm2_constructor: None,
avm2_constructor_ran: false,
drawing: Drawing::new(),
is_focusable: false,
has_focus: false,
@ -595,8 +599,6 @@ impl<'gc> MovieClip<'gc> {
if id == 0 {
//TODO: This assumes only the root movie has `SymbolClass` tags.
self.set_avm2_constructor(activation.context.gc_context, Some(proto));
self.allocate_as_avm2_object(&mut activation.context, self.into());
self.construct_as_avm2_object(&mut activation.context);
} else if let Some(Character::MovieClip(mc)) = library.character_by_id(id) {
mc.set_avm2_constructor(activation.context.gc_context, Some(proto))
} else {
@ -1576,6 +1578,8 @@ impl<'gc> MovieClip<'gc> {
log::error!("Got {} when constructing AVM2 side of display object", e);
}
}
self.0.write(context.gc_context).avm2_constructor_ran = true;
}
pub fn register_frame_script(
@ -1711,6 +1715,10 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
}
if !self.0.read().avm2_constructor_ran {
self.construct_as_avm2_object(context);
}
}
}
@ -2010,9 +2018,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
self.set_default_instance_name(context);
let vm_type = self.vm_type(context);
if vm_type == AvmType::Avm2 {
self.construct_as_avm2_object(context);
} else if vm_type == AvmType::Avm1 {
if vm_type == AvmType::Avm1 {
self.construct_as_avm1_object(
context,
display_object,

View File

@ -409,7 +409,7 @@ impl Player {
} else {
None
};
root.construct_frame(context);
root.post_instantiation(context, root, flashvars, Instantiator::Movie, false);
root.set_default_root_name(context);
context.levels.insert(0, root);

View File

@ -548,6 +548,7 @@ swf_tests! {
(as3_loaderinfo_quine, "avm2/loaderinfo_quine", 2),
(nan_scale, "avm1/nan_scale", 1),
(as3_nan_scale, "avm2/nan_scale", 1),
(as3_documentclass, "avm2/documentclass", 1),
}
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -0,0 +1,11 @@
package {
import flash.display.MovieClip;
public class Child extends MovieClip {
public function Child() {
trace("/// Child constructor");
trace("/// this.b");
trace(this.b);
}
}
}

View File

@ -0,0 +1,9 @@
package {
import flash.display.MovieClip;
public class Grandchild extends MovieClip {
public function Grandchild() {
trace("/// Grandchild constructor");
}
}
}

View File

@ -0,0 +1,13 @@
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
trace("/// Main constructor");
trace("/// this.a");
trace(this.a);
trace("/// this.a.b");
trace(this.a.b);
}
}
}

View File

@ -0,0 +1,9 @@
/// Grandchild constructor
/// Child constructor
/// this.b
[object Grandchild]
/// Main constructor
/// this.a
[object Child]
/// this.a.b
[object Grandchild]

Binary file not shown.

Binary file not shown.