Skip to content

Commit 014d977

Browse files
core: Skip instantiation for timeline objects placed before clip instantiation
Often, `MovieClip`s have children that are placed before the `MovieClip`'s AVM2 side is allocated and instantiated. These children will be instantiated in the `super()` constructor (specifically, the `Sprite.constructChildren` call), rather than in-line with normal frame construction. However, if the `super()` constructor is /never/ called, these children will never be instantiated. This commit adds a flag that is set on such early children. Children with this flag set will never have `construct_frame` called on them during normal frame construction. Also add test coverage
1 parent 09d7cda commit 014d977

File tree

5 files changed

+69
-5
lines changed

5 files changed

+69
-5
lines changed

core/src/display_object.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,14 @@ impl<'gc> DisplayObjectBase<'gc> {
794794
self.set_flag(DisplayObjectFlags::PLACED_BY_AVM2_SCRIPT, value);
795795
}
796796

797+
fn manual_frame_construct(&self) -> bool {
798+
self.contains_flag(DisplayObjectFlags::MANUAL_FRAME_CONSTRUCT)
799+
}
800+
801+
fn set_manual_frame_construct(&self, value: bool) {
802+
self.set_flag(DisplayObjectFlags::MANUAL_FRAME_CONSTRUCT, value);
803+
}
804+
797805
fn is_bitmap_cached_preference(&self) -> bool {
798806
self.contains_flag(DisplayObjectFlags::CACHE_AS_BITMAP)
799807
}
@@ -2192,6 +2200,18 @@ pub trait TDisplayObject<'gc>:
21922200
self.base().set_placed_by_avm2_script(value)
21932201
}
21942202

2203+
#[no_dynamic]
2204+
fn manual_frame_construct(&self) -> bool {
2205+
self.base().manual_frame_construct()
2206+
}
2207+
2208+
/// When this flag is set, the object will not be instantiated in-line with
2209+
/// normal frame construction by `MovieClip::construct_frame`.
2210+
#[no_dynamic]
2211+
fn set_manual_frame_construct(&self, value: bool) {
2212+
self.base().set_manual_frame_construct(value);
2213+
}
2214+
21952215
/// Whether this display object has been instantiated by the timeline.
21962216
/// When this flag is set, attempts to change the object's name from AVM2
21972217
/// throw an exception.
@@ -2864,7 +2884,7 @@ impl<'gc> DisplayObject<'gc> {
28642884
bitflags! {
28652885
/// Bit flags used by `DisplayObject`.
28662886
#[derive(Clone, Copy)]
2867-
struct DisplayObjectFlags: u16 {
2887+
struct DisplayObjectFlags: u32 {
28682888
/// Whether this object has been removed from the display list.
28692889
/// Necessary in AVM1 to throw away queued actions from removed movie clips.
28702890
const AVM1_REMOVED = 1 << 0;
@@ -2925,6 +2945,18 @@ bitflags! {
29252945
/// i.e. attachMovie, createEmptyMovieClip, duplicateMovieClip.
29262946
// TODO [KJ] Can this be merged with PLACED_BY_AVM2_SCRIPT?
29272947
const PLACED_BY_AVM1_SCRIPT = 1 << 15;
2948+
2949+
/// Whether this object was placed by the timeline on a `MovieClip`
2950+
/// before the `MovieClip` had its AVM2 object constructed. Such objects
2951+
/// are only instantiated by `Sprite.constructChildren`, which is
2952+
/// usually called when `super()` is called in a `Sprite` subclass.
2953+
/// However, if `super()` (and therefore `Sprite.constructChildren()`)
2954+
/// is never called, the object will never be instantiated. We mark all
2955+
/// objects placed by the timeline on a load frame with this flag to
2956+
/// ensure that `MovieClip::construct_frame` does not instantiate them
2957+
/// (they need to be instantiated "manually" by
2958+
/// `Sprite.constructChildren`).
2959+
const MANUAL_FRAME_CONSTRUCT = 1 << 16;
29282960
}
29292961
}
29302962

core/src/display_object/movie_clip.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,12 @@ impl<'gc> MovieClip<'gc> {
14091409
child.set_parent(context, Some(self.into()));
14101410
child.set_place_frame(self.current_frame());
14111411

1412+
// If this MC hasn't had an `object2` allocated yet, this
1413+
// child will be constructed by `Sprite.constructChildren`,
1414+
// not by the timeline
1415+
let has_object2_allocated = self.object2().is_none();
1416+
child.set_manual_frame_construct(has_object2_allocated);
1417+
14121418
// Apply PlaceObject parameters.
14131419
child.apply_place_object(context, place_object);
14141420
if let Some(name) = &place_object.name {
@@ -2485,12 +2491,28 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
24852491
let running_construct_frame = self
24862492
.0
24872493
.contains_flag(MovieClipFlags::RUNNING_CONSTRUCT_FRAME);
2488-
// The supercall constructor for display objects is responsible
2489-
// for triggering construct_frame on frame 1.
24902494
for child in self.iter_render_list() {
2491-
if running_construct_frame && child.object2().is_none() {
2492-
continue;
2495+
// Under some conditions, we won't run `construct_frame` on
2496+
// a not-yet-constructed child
2497+
if child.object2().is_none() {
2498+
// Avoid running recursively- if `Sprite.constructChildren`
2499+
// was constructing this clip's children, and somehow
2500+
// a child's construction triggered another `construct_frame`
2501+
// on this clip, Flash avoids running `construct_frame` on
2502+
// the non-constructed children of this clip.
2503+
if running_construct_frame {
2504+
continue;
2505+
}
2506+
2507+
// The supercall constructor for display objects is responsible
2508+
// for triggering construct_frame on frame 1 (see above comment).
2509+
// However, if the child has already been constructed, we run
2510+
// `construct_frame` like normal.
2511+
if child.manual_frame_construct() {
2512+
continue;
2513+
}
24932514
}
2515+
24942516
child.construct_frame(context);
24952517
}
24962518
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Main allocated
2+
Frame constructed (theSprite=null, theShape=null)
3+
Frame exited
4+
Frame entered
5+
Frame constructed (theSprite=null, theShape=null)
6+
Frame exited
7+
Frame entered
8+
Frame constructed (theSprite=null, theShape=null)
9+
Frame exited
629 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
num_frames = 3

0 commit comments

Comments
 (0)