Previously, throw and return completions would not be executed inside
the generator. This is incorrect, as throw and return need to perform
unwinds which can potentially execute more code inside the generator,
such as finally blocks.
This is done by also passing the completion type alongside the passed
in value. The continuation block will immediately extract and type and
value and perform the appropriate operation for the given type.
For normal completions, this is continuing as normal.
For throw completions, it will perform `throw <value>`.
For return completions, it will perform `return <value>`, which is a
`Yield return` in this case due to being inside a generator.
This also refactors GeneratorObject to properly send across the
completion type and value to the generator inside of trying to operate
on the completions itself.
This is a prerequisite for yield*, as it performs special iterator
operations when receiving a throw/return completion and does not
complete the generator like the regular yield would.
There's still more work to be done to make GeneratorObject::execute
be closer to the spec. It's mostly a restructuring of the existing
GeneratorObject::next_impl.
Unwind contexts need to be preserved as we exit and re-enter a
generator.
For example, this would previously crash when returning from the try
statement after yielding as we lost the unwind context when yielding,
but still have a LeaveUnwindContext instruction from running
`perform_needed_unwinds` when generating the return statement.
```js
function* a() {
try {
return (yield 1);
} catch {}
}
iter = a();
iter.next();
iter.next();
```
This means that rather than this:
```
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false,
false, true, FunctionAddress);
```
We now have this:
```
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, FunctionAddress, Arithmetic,
Comparison, Increment);
```
Which is a lot more readable. :^)
Co-authored-by: Ali Mohammad Pur <mpfard@serenityos.org>
We were mistakenly treating these as `for (x of obj)`. By reorganizing
the code a little bit, we actually support both kinds of iteration with
less duplication. :^)
Fixes 17 tests in test262.
The optimization passes are not stable, which makes test262 flaky.
Address this by introducing a new OptimizationLevel::None and making it
the default.
This removes all the flakiness from test262 in my testing.
We can enable optimizations by default again once they have been made
stable. :^)
This gives us better debug output when analysing calls to `undefined`
and also fixes multiple test-js cases expecting an
`(evaluated from $Expression)` in the error message.
This also refactors out the generation of that string, to avoid code
duplication with the AST interpreter.
Intrinsics, i.e. mostly constructor and prototype objects, but also
things like empty and new object shape now live on a new heap-allocated
JS::Intrinsics object, thus completing the long journey of taking all
the magic away from the global object.
This represents the Realm's [[Intrinsics]] slot in the spec and matches
its existing [[GlobalObject]] / [[GlobalEnv]] slots in terms of
architecture.
In the majority of cases it should now be possibly to fully allocate a
regular object without the global object existing, and in fact that's
what we do now - the realm is allocated before the global object, and
the intrinsics between both :^)
The basic idea is that a global object cannot just come out of nowhere,
it must be associated to a realm - so get it from there, if needed.
This is to enforce the changes from all the previous commits by not
handing out global objects unless you actually have an initialized
realm (either stored somewhere, or the VM's current realm).
- Prefer VM::current_realm() over GlobalObject::associated_realm()
- Prefer VM::heap() over GlobalObject::heap()
- Prefer Cell::vm() over Cell::global_object()
- Prefer Wrapper::vm() over Wrapper::global_object()
- Inline Realm::global_object() calls used to access intrinsics as they
will later perform a direct lookup without going through the global
object
This is a continuation of the previous six commits.
The global object is only needed to return it if the execution context
stack is empty, but that doesn't seem like a useful thing to allow in
the first place - if you're not currently executing JS, and the
execution context stack is empty, there is no this value to retrieve.