core: Prevent recursive execution of frame scripts on the same movie clip.

To be clear, recursive execution of frame scripts between *different* movie clips is still allowed.
This commit is contained in:
David Wendt 2022-01-08 16:36:47 -05:00 committed by Mike Welsh
parent 85d98d8c07
commit e926af7796
5 changed files with 53 additions and 25 deletions

View File

@ -1798,37 +1798,52 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
if let Some(avm2_object) = avm2_object { if let Some(avm2_object) = avm2_object {
if let Some(frame_id) = write.queued_script_frame { if let Some(frame_id) = write.queued_script_frame {
let is_fresh_frame = write.queued_script_frame != write.last_queued_script_frame; // If we are already executing frame scripts, then we shouldn't
// run frame scripts recursively. This is because AVM2 can run
write.last_queued_script_frame = Some(frame_id); // gotos, which will both queue and run frame scripts for the
write.queued_script_frame = None; // whole movie again. If a goto is attempting to queue frame
write // scripts on us AGAIN, we should allow the current stack to
// wind down before handling that.
if !write
.flags .flags
.insert(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT); .contains(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT)
{
let is_fresh_frame =
write.queued_script_frame != write.last_queued_script_frame;
if is_fresh_frame { write.last_queued_script_frame = Some(frame_id);
while let Some(fs) = write.frame_scripts.get(index) { write.queued_script_frame = None;
if fs.frame_id == frame_id { write
let callable = fs.callable; .flags
drop(write); .insert(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT);
if let Err(e) = Avm2::run_stack_frame_for_callable(
callable, if is_fresh_frame {
Some(avm2_object), while let Some(fs) = write.frame_scripts.get(index) {
&[], if fs.frame_id == frame_id {
context, let callable = fs.callable;
) { drop(write);
log::error!("Error occured when running AVM2 frame script: {}", e); if let Err(e) = Avm2::run_stack_frame_for_callable(
callable,
Some(avm2_object),
&[],
context,
) {
log::error!(
"Error occured when running AVM2 frame script: {}",
e
);
}
write = self.0.write(context.gc_context);
} }
write = self.0.write(context.gc_context);
index += 1;
} }
index += 1;
} }
}
write write
.flags .flags
.remove(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT); .remove(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT);
}
} }
} }

View File

@ -356,6 +356,7 @@ swf_tests! {
(as3_movieclip_goto_during_frame_script, "avm2/movieclip_goto_during_frame_script", 1), (as3_movieclip_goto_during_frame_script, "avm2/movieclip_goto_during_frame_script", 1),
(as3_movieclip_gotoandplay, "avm2/movieclip_gotoandplay", 5), (as3_movieclip_gotoandplay, "avm2/movieclip_gotoandplay", 5),
(as3_movieclip_gotoandstop, "avm2/movieclip_gotoandstop", 5), (as3_movieclip_gotoandstop, "avm2/movieclip_gotoandstop", 5),
(as3_movieclip_gotoandstop_queueing, "avm2/movieclip_gotoandstop_queueing", 2),
(as3_movieclip_next_frame, "avm2/movieclip_next_frame", 5), (as3_movieclip_next_frame, "avm2/movieclip_next_frame", 5),
(as3_movieclip_next_scene, "avm2/movieclip_next_scene", 5), (as3_movieclip_next_scene, "avm2/movieclip_next_scene", 5),
(as3_movieclip_play, "avm2/movieclip_play", 5), (as3_movieclip_play, "avm2/movieclip_play", 5),

View File

@ -0,0 +1,12 @@
///Parent frame 1
///this.gotoAndStop(2);
///this.child.gotoAndStop(3);
///Child frame 3
///this.gotoAndStop(2);
///this.parent.gotoAndStop(5);
///End child frame 3
///End parent frame 1
///Parent frame 5
///this.child.gotoAndStop(5);
///Child frame 5
///End parent frame 5