This has some subtle problems: we cannot hold references to garbage-collected data in Futures, so we have to arrange for the AVM itself to forcibly root them for us. Then we get them back when our async code is ready to do something to the AVM.
Use the same code path for the global GotoFrame2 action and
MovieClip.gotoAndX, which properly handles out-of-range and invalid
values like NaN.
Fixes Disorderly hanging on game start
(https://www.newgrounds.com/portal/view/121896)
Ops and functions that take a movie clip path in String form have
a very forgiving syntax. These include:
* `SetTarget`
* `CloneSprite`
* `RemoveSprite`
* `swapDepths`
This change adds `Avm1::resolve_target_display_object` to parse
these paths correctly, along with `target_paths` test to test a
wide variety of formats.
This also applies to `GetVariable`/`SetVariable`, which accept
target paths to variables and is used by some SWF4/5 content.
(fixes#324, #337).
When setting a variable in a function-local scope, if that variable
has not been defined in the function scope, it should be defined in
the executing movieclip's scope. Previously it would get defined
in the function's scope. Changed Scope::overwrite to Scope::set,
and modified the behavior to stop traversing and define the value
when it hits a movie clip Target scope.
Also, modified With scopes to properly add onto the end of the scope
chain.
`idMap` is a strange property; it's only populated with nodes which had a given `id` *at the time of parsing*, and said nodes continue to be referenced even if the node is removed from the document. I have yet to find a way by which nodes can be deleted from `idMap`.
It also takes expandos, so this has to be a new retained object on the XML document. I originally considered not creating *another* `Object` impl and populating a regular `ScriptObject` with nodes, but that meant we couldn't lazy-instantiate their AVM1 side counterparts. Boo. :/
A lot of the arithmetic ops were still using SWFv4 style coercion
(`Value::into_number_v1`) even though they use full ECMA-262
coercion in SWF5+. This would cause `undefined` to turn into 0
isntead of NaN, for example.
Fixes disappearing player in Achievement Unlocked
(https://www.newgrounds.com/portal/view/474371)
It's possible even the older ops such as ActionAdd should do this,
too. Handcrafted bytecode will need to be used to test as you
cannot export these ops in newer SWF versions from the Flash IDE.
ActionAnd, ActionOr, and ActionNot were incorrectly comparing
to 0. This only works for SWF<4. Now they all go through the
Value::as_bool method to handle version specific behavior.
Value::from_bool_v1 was also renamed to Value::from_bool.
Functions now store their base clip (the code that contains the
executing bytecode). This is because `GotoFrame` and other actions
will execute on the clip the bytecode exists on, not on `this`.
(Note that `this.gotoAndStop` uses `GetMember` actions, which
worked correctly).
`Activation` now stores `target_clip`, and `Avm1::target_clip` and
`target_clip_or_root` grab this from the current stack frame.
Renamed `start_clip` to `base_clip` to match Flash conventions.
Removed `active_clip` as this was superfluous. Now you can use
`Avm1::target_clip_or_root`.
`UpdateContext` no longer contains `target_clip` etc.
Add a check and clear the stack if it isn't empty at the end of
`run_stack_till_empty`. This is probably a bug on our side
and we a good place for breakpoints.
When running an clip event handler (e.g. onEnterFrame), a stack
frame is pushed to get the property value. However, this frame
was causing an extra Undefined to be pushed on the operand stack in
`Avm1::retire_stack_frame`, which would blow out the stack.
Now this stack frame is popped after the property is resolved and
before the function is executed. The function will push its own
stack frame when it executes.
We implement `super` by way of a new `Object` impl which wraps arbitrary objects with a modified prototype chain. Specifically, the lowest layer of the prototype chain is omitted. This new `SuperObject` script is composable: a chain of two `SuperObject`s will go two levels of inheritance upwards while still maintaining non-prototype property access.
Add DisplayObject::slash_path to get the Flash 4-style slash path
to the clip. This fixes the tellTarget regression test and removes
the superfluous `target_path` from `UpdateContext`.
DisplayObject code no longer has to manage
UpdateContext::active_clip before calling out to children, because
each child still has access to its Gc pointer.
This was discovered almost by accident: @Dinnerbone noticed that `_global == null`, and surmised that `valueOf` was the culprit. However, this doesn't really make sense: `_global` is a bare object, so it shouldn't have a `valueOf` (and in practice, it doesn't).
The ultimate cause of such an odd comparison is as such:
1. Flash coerces the `_global` object to a numerical primitive by calling `valueOf`.
2. `_global.valueOf` is undefined. Flash handles calls to any uncallable value by literally just having it return `undefined`. In other words, all values are implicitly callable as empty functions.
3. `undefined` is then compared to `null`. These two values *are* equal under abstract equality (`==`). Hence, `_global == null`.
For comparison, modern ECMAScript engines throw errors on calls to uncallable values; and won't attempt to use an invalid `valueOf` to coerce objects. So none of this applies to, say, standard JavaScript in your browser.
Note that host objects that do so will *not* have access to their standard representation from within member functions - you will need to extend the interface to accomodate for them. This is due to long-standing limitations with type IDs and downcasting with types that bear lifetimes - it's entirely an unsafe operation and exposing such a facility to safe Rust is unsound. However, this will at least let us separate out several things from ScriptObject that don't need to be there for the time being.
`Object::function` now returns a pre-allocated function object. You may supply it an explicit prototype to have it linked into the function object (which is why we have to return a cell).