This allows us to remove the conditionals on implementations of `from_path` that need to call this function, as the function is now always guaranteed to be there, even if it's just a no-op/`Err` generator.
During the small period of time when a player is created but has no root movie, a temporary empty movie is installed with an assumed stage size and framerate of 550x400@12fps. This is Flash default for new projects, so it seemed appropriate. User ActionScript cannot see these values, and I'm not even sure JavaScript can, either.
Calling loadMovieNum with a variable parameter compiles into a
GetURL2 call with a `_level` window target parameter. Previously
this triggered Ruffle to try to navigate to the SWF. Now it
properly loads the SWF inside the current movie.
Holding a `Ref` on a garbage-collected object inherently extends any borrow locks on that object. Since ABC files are references already, taking a `Ref` to them only helps to skip the refcount update. This is less useful than expected: in most situations, using `abc_ref` causes double-borrow panics. The few methods that can use it are going to be fragile in the face of future refactors, so I'm nipping the problem in the bud now.
For good measure, most of the other methods in `value` for retrieving pool primitives now also use `TranslationUnit` instead of `AbcFile`. This is the result of a handful of cascading changes throughout the project, and itself caused a few more.
Interface methods are specifically not allowed to be called: as a result, they don't get a method body. Existing code assumed a 1:1 relationship between methods and bodies, which causes spurious errors.
This is inspired by Dinnerbone's similar PR on the AVM1 side, where the Action half of that VM's `Executable` was reduced from 128 bytes to 16 by shoving it in a `Gc`. This won't be as dramatic but should still save some memory.
In fact, it should save a *lot* of memory in bytecode execution, where thanks to the previous commit's rebase, we now need to clone the current method once *for each instruction executed*. That is terrible, but should stop now.
This also results in a far reduced role for `ReturnValue`, since I also took the liberty of removing most of it's use. Furthermore, I also made it apply equally to native and AVM2 code, which ensures all native implementations of methods don't double-borrow.
In AVM1, `ReturnValue` was actually removed entirely, because it's not needed. I attempted to do the same, but the fact that we're currently embedding `ScriptObjectData` in native objects means that we need it for virtual properties. Otherwise, virtual property implementations will see locked objects, which is bad.
While some code that references pool multinames has zero as a valid index, we cannot validate exactly what the zero index is for a given index. Hence, callers instantiating multinames must check for zero and substitute the correct zero-value interpretation for their given type. If zero is an invalid value, it should ideally throw a different error than what's provided here.
This commit breaks the build: we still need to tell `Avm2` how to turn ABC traits into our own internal `Trait<'gc>`, `Class<'gc>`, and `Method<'gc>` types. We also need something to track which traits have already been instantiated, because `callstatic` would otherwise reinstantiate the trait in a different scope. (In fact, I think it *does* do exactly that right now...)
The intention is to completely replace all usage of `Avm2XYZEntry` with `Class`, `Trait`, and `Method`. This will allow runtime-provided global class traits to coexist with those provided by user code.
Inspired by Dinnerbone's PR doing the exact same thing to AVM1.
On AVM2 we have a bit of a subtle issue: the base implementation of `set_property_local` and `init_property_local` *must* return `ReturnValue`s to avoid double-borrows. Each implementation of `TObject` must resolve them before returning.
This function has vague documentation about enabling locale-specific formatting in subclasses. As far as I can tell, none of the objects I implemented so far do anything different than `toString`, so I just have it use the same `TObject` property I set up for `toString`.
This was originally something *way* more evil: mixed inheritance between ES3 and ES4 classes. It didn't pan out due to fundamental limitations of the two object models. How the hell did Brendan Eich/Adobe/TC-39 expect ES4 classes to be adopted in already-existing codebases?!
We still reuse the `FunctionObject` machinery internally. If necessary, we may want to split this into a separate `ClassObject` if some internal `TObject` method needs replacing for classes.
In practice not many movies will care about this, because the `AS3` namespace is open by default. You could opt-out of that, and I suppose that was there for using existing ES3 code in AS3 projects. ES4 would have had a similar ES4 namespace, which "JavaScript 2.0" code would need to opt into. Of course, ES4/JS2 never happened, so we just have this weird historical quirk here.
The reason for this is that, in AVM2, `toString` and `valueOf` are not defined on the classes or prototypes of `Function` or `Class`. Instead, they use the `Object.prototype` versions of those functions. Ergo, string and primitive coercion are inherent object methods (the ones that get `[[DoubleSquareBrackets]]` in the ECMA standards). In Ruffle, our equivalent to `[[DoubleSquareBrackets]]` methods are methods on the `TObject` trait, so we're adding them there.
This mechanism will make implementing boxed value types (ala AVM1's `BoxedObject`) easier, too.
We also add some reasonable defaults for `ScriptObject` and `FunctionObject` which will appear on objects, functions, and classes.
Private names now return `false`, and we run any names through trait lookups. This also means any namespace resolution can fail now, in case we need to throw a `VerifyError`.
I have... significant reservations with the way object enumeration happens in AVM2. For comparison, AVM1 enumeration works like this: You enumerate the entire object at once, producing a list of property names, which are then pushed onto the stack after a sentinel value. This is a properly abstract way to handle property enumeration.
In AVM2, they completely replaced this with index-based enumeration. What this means is that you hand the object an index and it gives you back a name or value. There's also an instruction that will give you the next index in the object.
The only advantage I can think of is that it results in less stack manipulation if you want to bail out of iteration early. You just jump out of your loop and kill the registers you don't care about. The disadvantage is that it locks the object representation down pretty hard. They also screwed up the definition of `hasnext`, and thus the VM is stuck enumerating properties from 1. This is because `hasnext` and `hasnext2` increment the index value before checking the object. Code generated by Animate 2020 (which I suspect to be the final version of that software that generates AVM2 code) initializes the index at hero, and then does `hasnext2`, hence we have to start from one.
I actually cheated a little and added a separate `Vec` for storing enumerant names. I strongly suspect that Adobe's implementation has objects be inherently slot-oriented, and named properties are just hashmap lookups to slots. This would allow enumerating the slots to get names out of the object.