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(frame_id) = write.queued_script_frame {
let is_fresh_frame = write.queued_script_frame != write.last_queued_script_frame;
write.last_queued_script_frame = Some(frame_id);
write.queued_script_frame = None;
write
// If we are already executing frame scripts, then we shouldn't
// run frame scripts recursively. This is because AVM2 can run
// gotos, which will both queue and run frame scripts for the
// whole movie again. If a goto is attempting to queue frame
// scripts on us AGAIN, we should allow the current stack to
// wind down before handling that.
if !write
.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 {
while let Some(fs) = write.frame_scripts.get(index) {
if fs.frame_id == frame_id {
let callable = fs.callable;
drop(write);
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.last_queued_script_frame = Some(frame_id);
write.queued_script_frame = None;
write
.flags
.insert(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT);
if is_fresh_frame {
while let Some(fs) = write.frame_scripts.get(index) {
if fs.frame_id == frame_id {
let callable = fs.callable;
drop(write);
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
.flags
.remove(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT);
write
.flags
.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_gotoandplay, "avm2/movieclip_gotoandplay", 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_scene, "avm2/movieclip_next_scene", 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