ActionsExecutionOrder

From Gnash Project Wiki

Jump to: navigation, search

Contents

Background

Current gnash action execution handling has been found to be overly complex and semantically incorrect. The action_execution_order* tests and the goto_* tests reveal this.

This page is here to help with redesign the relevant parts.

Current implementation

In Gnash we implement a global action queue that gets filled with events and actions (ExecutableCode) during sprites advancement and gets processed at the end, just before displaying the stage characters.

Actions and events are queued by sprite_instance::execute_frame_tags(N, flags) in the order the triggering tag appears in the SWF.

Triggering tags are:

  • DOACTION - queues the actions
  • PLACEOBJECT - queues the LOAD event
  • REMOVEOBJECT - queues UNLOAD

Order of sprites fram-tag execution depend on the order sprites are pushed into a global list of "live" characters. This order is a last-placed-first-advanced one.

Expected behaviour

References

Verified behaviour

  1. Within the same frame, (1)actions in DoInitAction could reference movieclips placed before and after it.(2)all action scripts in DoAction tags can reference all movieClips placed before or after the DoAction tags. Tested with movieclip_destruction_test1.swf and action_execution_order_test1.swf
  2. Within the same frame: (1)If PlaceObject is after DoAction, then OnLoad is also called after executing actionscripts in DoAction. Otherwise before DoAction.(2)If RemoveObject is after DoAction, then OnUnload is also called after executing actionscripts in DoAction. Otherwise before DoAction. Tested with action_execution_order_test3.swf and action_execution_order_test10.swf
  3. Within the same timeline, (1)if PlaceObject(mc1) is before PlaceObject(mc2), then frame0 actions of mc1 should be executed before frame0 actions of mc2. Otherwise, after mc2. (2)for other frame(frameNum>0) actions, first placed last executed Tested with action_execution_order_test2.swf and action_execution_order_test10.swf.
  4. For all movieClips within the same timeline: (1)onClipInitialize: first placed first called(before onClipConstruct); (2)OnClipConstruct: first placed first called(before OnLoad); (3) OnLoad: first added first called; (4)OnUnload: first removed first called; (5) OnEnterFrame: first placed last called; Tested with action_execution_order_test4.swf
  5. For nested movieClips: (1) parent onLoad called first; (2) child unload called first; (3)child onEnterFrame called first; (4)child with a lower depth unloaded first compared to those with higer depths at the same level; Tested with action_execution_order_test5.swf
  6. System events order for movieClips: (1)onClipInitialize-->onClipConstruct-->onClipLoad[onLoad]-->onClipEnterFrame[onEnterFrame]-->onClipUnload[onUnload]; (2)within the same frame, all onClipInitializes happen before all onClipConstructs, all onClipConstructs happen before all onClipLoads. Tested with init_action_test.swf
  7. For passing-by MovieClips: (1) if jumping forward, actions order are determined by tags from frame(currentframe+1) to target frame; (2) if jumping backward, actions order are determined by tags from frame0 to frame(currentframe-1).Tested with action_execution_order_test6.swf
  8. target frame actions for non-more-existing-targets and unloaded targets should not be executed.Tested with goto3.swf
  9. a summary of all behaviours: (1)Actions order for statically placed movieClips is determined by the order of DisplayList control tags; (2)Actions for non-more-existing target won't/shouldn't be executed. (3)Actions for unloaded characters might be executed.
 Note: 
 (1)non-more-existing-targets: targets cann't be accessed from AS(including hard references, 
    soft references, getInstanceAtDepth()...), or ideally destroyed targets.  
 (2)unloaded targets: unloaded but still alive targets, and still accessible from AS. 
 (3)Any questions or related behaviour need to be tested, please add it here.
 Quick record for a few odd behaviours(not fully tested):
 (1)onClipInitialize, onClipConstruct, onClipLoad won't be triggered for passing-by moveclips
   when onClipUnload is not defined.(this one is explainable now by distinguishing 
   isDestroyed and isUnloaded)
 (2)user defined onLoad won't be triggered when allEventFlags == zero.
 (3)user defined onConstruct might be triggered even when no onClipConstruct defined.

Actions ordering model

Actions order priority(for a single advancement of the playhead)

LEVEL1 init_actions, initialize_handlers order of execution depends on order of DoInitAction and PlaceObject tags
LEVEL2 construct handlers depends on PlaceObject
LEVEL3 enterframe handlers depends on PlaceObject(reverse order)
LEVEL4 do_actions, load handlers, unload handlers depends on DoAction, PlaceObject and RemoveObject


  1. advance the global instance list, queue all actions according to the DLIST tags order or instance order or depth order.
  2. scan the actions queue,
    1. execute actions up to LEVEL1;
    2. execute actions up to LEVEL2;
    3. execute actions up to LEVEL3;
    4. execute actions up to LEVEL4;

Note. (1)lower level action blocks(if any) always take precedence of higher level action blocks; (2)execute actions in any level might trigger queuing new action blocks of other levels.(3)function block is uninterruptable(4)See discussion for other considerations.

To inspect

  1. how is the order of other events determined?(eg. Can you find a way to trigger a 'construct' event for _root?) Still not so sure.
  2. Actions that are not listed in the above table are other event handlers, and they should be triggered by certain events. They are also in LEVEL4 I guess.

Test cases

These are the current testcases for checking action execution order. They are from both Gnash and swfdec testsuite.

testsuite/misc-ming.all/action_execution_order_test.swf

  • Gnash succeds.
  • Swfdec succeeds.

Tests that in the frame placing a movieClip, actions of _root is executed before actions of the movieClip; and in later frames, actions of _root is executed after actions of that movieClip.

testsuite/misc-ming.all/action_execution_order_test1.swf

  • Gnash succeds.
  • Swfdec succeeds.

Tests that within the same frame, ActionScripts in DoAction tags can referance movieClip placed after DoAction tags.

testsuite/misc-ming.all/action_execution_order_test2.swf

  • Gnash succeeds.
  • Swfdec succeeds.

Tests that execution of frame0 action in child sprites does not depend on their depth, but only on the order PLACEOBJECT tags are found in the SWF.

testsuite/misc-ming.all/action_execution_order_test3.swf

  • Gnash succeds.
  • Swfdec succeeds.

Tests that when tag PlaceObject2 is after tag DoAction, then onClipLoad is also called after executing ActionScripts in tag DoAction; when tag RemoveObject is after tag DoAction, the situation is similar.

testsuite/misc-ming.all/action_execution_order_test4.swf

  • Gnash succeeds.
  • Swfdec fails one test but has one unexpected success (swfdec GIT 20th Aug 2007)

Tests behaviour (4)

testsuite/misc-ming.all/action_execution_order_test5.swf

  • Gnash succeeds.
  • Swfdec succeeds (swfdec GIT 20th Aug 2007)

Tests behaviour (5)

testsuite/misc-ming.all/action_execution_order_test6.swf

  • Gnash fails.
  • Swfdec fails.

Tests behaviour (6)

testsuite/misc-ming.all/action_execution_order_extend_test.swf

  • Gnash succeeds.
  • Swfdec succeeds.

Tests onClipEvents and user defined events, and extends its complexity as much as possible in future.

testsuite/misc-ming.all/goto_frame_test.swf

  • Gnash succeds.
  • Swfdec succeeds.

Tests that the _currentframe property of a movieClip is set immediately after executing the 'GotoFrame' instruction.

testsuite/misc-ming.all/consecutive_goto_frame_test.swf

  • Gnash succeeds.
  • Swfdec succeeds.

Tests all target frame actions should be executed in a signle advancement.

testsuite/misc-ming.all/multi_doactions_and_goto_frame_test.swf

  • Gnash succeds.
  • Swfdec succeeds.

Tests that all actions in current frame should be executed before jumping to the target frame.

testsuite/misc-swfc.all/action_execution_order_test10.swf

  • Gnash succeeds.
  • Swfdec succeeds.

Tests that:

   (1) user defined onContruct should not be triggered.
   (2) user defined onLoad should not be triggered in this case(when allEventFlags == zero).
   (3) If DoAction is before RemoveObject2, then actions in DoAction should be executed before
       onUnload, otherwise after onUnload.

swfdec/test/trace/children.swf

  • Gnash succeeds.
  • Swfdec succeeds.

What does this test verify ?

swfdec/test/trace/order.swf

  • Gnash succeeds.
  • Swfdec succeeds.

What does this test verify?

swfdec/test/trace/goto1.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/goto2.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/goto3.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/goto4.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/goto5.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/gotoframe.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/event-order.swf

  • Gnash succeeds.
  • Swfdec succeeds.

What does this test verify?

swfdec/test/trace/doaction-after-placeobject.swf

  • Gnash succeeds.
  • Swfdec succeeds.

swfdec/test/trace/doaction-before-placeobject.swf

  • Gnash succeeds.
  • Swfdec succeeds.

Check even order with onFoo and onClipFoo events (user-defined vs. placeobject-defined)

Other implementations

Implementation in Swfdec

Swfdec 0.4.2 implements action execution using 2 lists.

  • The instance list (SwfdecPlayer::movies) Whenever a new instance is created (either by executing PlaceObject tags or by executing Actionscript), the new instance is prepended to the list. Whenever an instance stops existing, it is removed from this list.
  • An action queue (SwfdecPlayer::actions, actions are added via swfdec_player_add_action). The name is unfortunate, since the action queue is used execute lots of different deferred functions these days, not just actions. Every call to swfdec_player_add_action appends an action to this queue. "Performing actions" is done in swfdec_player_perform_actions, the first action is removed from the queue and executed.

Whenever the movie iterates, the SwfdecPlayer performs step 1 and after that performs all pending actions in the action list until none are left anymore. Since every step will add new actions to the action list, it is guaranteed that every step will be taken in the right order.

Step 1: iterate

The function swfdec_player_iterate is called. It walks the instance list. It calls swfdec_sprite_movie_iterate for every sprite instance. This function increments the current frame by 1 if the movie is not stopped and queues 2 actions:

  • queueing of the onEnterFrame call
  • performing A Goto to the current frame using swfdec_sprite_movie_goto. This function does 2 things:
    • queue the performance of the DisplayList tags
    • flag all child instances that will not exist in the frame it'll go to for removal (SwfdecMovie::will_be_removed)

Step 2: performing DisplayList tags

In this step, 2 different functions may be executed:

  • the onEnterFrame call will be queued again if the movie is not going to be removed.
  • actions are performed if the current frame changed.

Both of these will only be executed if the instance has not been scheduled for removal above. Performing actions is done in swfdec_sprite_movie_do_goto_frame. The following actions from the current frame are performed in the order they appear in the SWF file:

DoAction (SWFDEC_SPRITE_ACTION_SCRIPT)

Queues the execution of the script.

PlaceObject Add (SWFDEC_SPRITE_ACTION_ADD)

Creates a new instance of the given object. This includes performing a swfdec_sprite_movie_do_goto_frame to the first frame if the instance is a sprite and it queues an onLoad event of the new instance.

PlaceObject Update (SWFDEC_SPRITE_ACTION_UPDATE)

Updates the various properties of a given instance. Does not queue any events.

RemoveObject (SWFDEC_SPRITE_ACTION_REMOVE)

Queues an onUnload event, if the instance has an onUnload event. Otherwise the instance is immediately removed from the player.

Step 3: performing actions

In this step a lot of things can happen in a lot of orders. This includes onEnterFrame events that were queued, onLoad and onUnload events from performing DisplayList tags and there's also actions queued from performing the Goto of new sprite instances. The order in which these happen depends of course on how they were added in the previous steps.

Step 4: reacting to actions

Since the actions in the last step can queue more functions (like performing gotos or placing new instances), there may be lots more actions to be executed. So the performing of actions may continue for a while.

When the action queue is empty, swfdec_player_iterate calls cleanup actions. These actions include removing the instances that had their onUnload event executed or performing sound related stuff (like repositioning the streaming sound). After this, the iteration is finished.