Change VideoStream to use a VideoDecoder trait instead of an enum.
This will make it a little easier as more codecs are added, and
allow us to easily enable/disable codecs behind features.
Currently properties added using `addProperty` overwrite existing
stored properties. However, there are some cases where the original
stored value can still be retrieved, which indicates that Flash
Player doesn't overwrite these properties internally.
As a solution, unify `Property::Stored` and `Property::Virtual` to
a single struct. This allows to simultaneously store regular data
and getter/setter on the same property. It also simplifies some
logic in `ScriptObject`.
The last usage of it was in `Player`, which anyway should operate
only on newly created objects that don't have any virtual properties
nor watchers. So it is safe to replace with `define_value`, that
also cannot fail.
`__proto__` seems to behave much like a regular data property. So
simply remove the `prototype` field of `ScriptObject` in favor of
storing the prototype in the general properties hash map.
It was regressed in #4822 when changed to use `flat_map`. But
currently Rust emits suboptimal code for such case. For now use
`Vec::with_capacity` manually to avoid unnecessary re-allocations.
The former native initializer call was incorrect and would cause spurious coercion failures. Since existing code assumes the error fork of `coerce_to_object` means undefined or null, this was causing coercions and parameter typechecks against primitives to fail in the most general case.
Attempting to fix this by providing the primitive value as native initializer argument instead causes a stack overflow. This is because native initializer arguments still have to be typechecked, and in the most general case this means... coercing the primitive value we're already coercing into an object... into another object. This won't work.
Since primitive initializers aren't going to do anything that native coercions need to care about, I'm just removing the initializer call.
Instead, the following terms are used:
* Static classes, to refer to `Class<'gc>`. Shortened to "class" in contexts where this is not ambiguous.
* Class objects, to refer to objects that represent a particular class. Also shortened to "class" in non-ambiguous contexts.
Downstream of this, the `base_constr` (referring to the class that a currently called trait has been pulled from) is now called `subclass_object` and several `TObject` methods have also been renamed.
This is limited by the fact that we currently cannot store type metadata in static tables. I don't think it's necessary to do so as of yet as pretty much every actual parameter type I *could* shove in here turned out to be optional and broke tests if it wasn't. Still, it's probably useful enough for new classes to include.
This is very wrong: Strictly speaking, we should not be instantiating anything that needs a scope when we install the trait. We just create a slot for it to go into. Script initializers are responsible for providing a scope stack to instantiate traits into.
This pushes a few more ancillary changes:
* `has_proto_in_chain` no longer checks interfaces (since it exists to serve `instanceof`, which does not respect them)
* Interfaces no longer live on prototypes. They now live on class constructors.
Because we have stuff running in early globals, we stlil need a more elaborate version of this function that *does* take `fn_proto`. We also can't pull `scope` from the activation since this gets called to install traits.
This also includes protections on `String` to prevent it from overwriting itself. All user-constructed primitive objects start out boxing `Value::Undefined` and future constructor implementations should check what's already been boxed before overwriting it with user arguments.
For various reasons, this is kind of incomplete:
1. We need to support native event subclasses, so we can't just pull one class from `activation` (yet).
2. We can't run native instance initializers without overwriting the native object.
I expect these to be fixed in a future PR where I start adding native event types.
This also makes it more difficult to accidentally build a class without calling it's initializer. Native/builtin class initializers should also be running now, too.
The only minor bit of jank is that we need a class initialized bit to flag classes we've already run, because our current lazy-init design for traits causes classes to be constructed twice. This is temporary and I intend to remove it along with lazy-init traits.
This effectively turns calling `ArrayObject`'s associated methods into an alternate constructor path. We even make sure to run instance initializers. You also no longer have to grab the `array` system constructor, so we get to remove that code.
Native initializers are a separate, parallel initialization chain intended for all object construction that is not directly triggered by `Op::Construct` and friends. This allows us to implement classes that cannot be directly constructed by user code, but can be constructed by native code or supercalled into from non-native.
All native object allocation in the project now pulls prototypes or constructors as necessary from the following sources:
* System prototype or constructor lists
* Instance `constr`s
This also resulted in the removal of a few unnecessary prototype accesses.
Fixes the `as3_virtual_properties` test, where the old approach would accidentally grab a `Getter` and then think there's no setter (there is, you just have to look further down the list).
This is a temporary fix for failing tests; it is invalid in the sense that attempting to grab loader events will now fail. We can't actually correctly init the loader without two planned changes:
1. Moving all object initialization into the object helper functions (e.g. `LoaderInfoObject::from_movie`)
2. A notion of "native-only" constructors separate from the existing constructor chain
Once those are in place, loaders should be proper event dispatchers again.
This also incurred a large number of ancillary changes, as it turns out nearly every native object is currently pulling a prototype and sticking it into an object. Right now, I have it instead pulling the constructor out of the prototype, but a future PR will also remove `system_prototypes` as well.
Other ancillary changes include:
* `Domain` now supports partial initialization to avoid an order-of-events issue. Accessing domain memory on a partially-initialized `Domain` will panic.
* `Domain` construction requires a full `activation` now, except for `global_scope` which needs to be initialized later with valid domain memory before user code runs.
* Pretty much every native object constructor now takes a proto/constr pair
* Trait lookup was rewritten to handle this. It's still buggy - seven tests don't work
* `TObject.construct` now actually does the full object construction dance. This allows `ClassObject` to implement the ES4 object construction pathway directly while `FunctionObject` maintains ES3 compatibility.
This is a tentative commit; there are still seven failing tests that I need to fix.
* The properties parameter is evaluated last. This is observable by
the order the `toString`/`valueOf` methods of the parameters are
called.
* Only `null` in the property list parameter configures all properties,
as opposed to `undefined`, `null`, numbers and booleans previously.
* Objects in the property list parameter are not handled specially.
Instead, they're also coerced to string and split by comma.
* tests: add tests for scroll
* avm1: implement scroll, maxscroll, bottomScroll
* chore: fmt
* docs: note that scroll is 1-based
* fix: non-word wrapped text with manual breaks is scrollable
* chore: move magic number to const
* chore: avoid mut with extra if
* chore: moving clamping behaviour into core
* refactor: eagerly compute line data
* fix: make scroll work when text is aligned right
* chore: clippy
* docs: add more information about line_data
* tests: add more test cases for scroll
The current version just doesn't make any sense.
The fixed version is akin to the `target.starts_with("_level") && target.len() > 6` line a bit earlier in this file.