Age | Commit message (Collapse) | Author |
|
Unwind contexts now remember the lexical and variable environments in
effect when they were created. If an exception is caught, we revert
to those environments in the running execution context.
|
|
|
|
Same as NonnullRefPtrVector: weird semantics, questionable benefits.
|
|
We use generators in bytecode to approximate async functions, but the
code generated by AwaitExpressions did not have the value processing
paths that Yield requires, eg the `generator.throw()` path, which is
used by AsyncFunctionDriverWrapper to signal Promise rejections.
|
|
|
|
This works similar to `break`
The `try-finally-continue` still do not pass with this, likely because
of binding issues.
|
|
This uses a newly added instruction `ScheduleJump`
This instruction tells the finally proceeding it, that instead of
jumping to it's next block it should jump to the designated block.
|
|
|
|
Note the couple of cases using MUST are just debugging statements.
|
|
|
|
DeprecatedFlyString relies heavily on DeprecatedString's StringImpl, so
let's rename it to A) match the name of DeprecatedString, B) write a new
FlyString class that is tied to String.
|
|
This is still not perfect, as we now actually crash in the
`try-finally-continue` tests, while we now succeed all
`try-catch-finally-*` tests.
Note that we do not yet go through the finally block when exiting the
unwind context through a break or continue.
|
|
We are already doing this in a good manner via the generated code,
doing so in the execution loop as well will cause us to pop contexts
multiple times, which is not very good.
|
|
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
|
|
This pass tries to eliminate repeated lookups of variables by name, by
remembering where these where last loaded to.
For now the lookup cache needs to be fully cleared with each call or
property access, because we do not have a way to check if these have any
side effects on the currently visible scopes.
Note that property accesses can cause getters/setters to be called, so
these are treated as calls in all cases.
|
|
Otherwise debug prints will show the wrong block until we preform a jump
|
|
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 is quite helpful, when reporting internal errors.
|
|
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 is unused.
|
|
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).
|
|
An executable is generated for the top-level script and for each
function. Strict mode can only be changed with the first statement of
the top-level script and each function, which corresponds directly to
Executable.
|
|
|
|
It makes no sense to require passing a global object and doing a stack
space check in some cases where running out of stack is highly unlikely,
we can't recover from errors, and currently ignore the result anyway.
This is most commonly in constructors and when setting things up, rather
than regular function calls.
|
|
For example, a try/catch block with no finally. The try block and catch
block do not need to unwind to a finally block, so the unwind context
is no longer needed when we jump to the catch block.
If we threw an exception in a catch block of a try/catch, there will be
no handler or finalizer and the unit would continue on as if nothing
happened.
This would subsequently crash with the `m_saved_exception.is_null()`
assertion failure when we next call a non-native function.
|
|
Now we emit CreateVariable and SetVariable with the appropriate
initialization/environment modes, much closer to the spec.
This makes a whole lot of things like let/const variables, function
and variable hoisting and some other things work :^)
|
|
|
|
This commit removes all exception related code:
Remove VM::exception(), VM::throw_exception() etc. Any leftover
throw_exception calls are moved to throw_completion.
The one method left is clear_exception() which is now a no-op. Most of
these calls are just to clear whatever exception might have been thrown
when handling a Completion. So to have a cleaner commit this will be
removed in a next commit.
It also removes the actual Exception and TemporaryClearException classes
since these are no longer used.
In any spot where the exception was actually used an attempt was made to
preserve that behavior. However since it is no longer tracked by the VM
we cannot access exceptions which were thrown in previous calls.
There are two such cases which might have different behavior:
- In Web::DOM::Document::interpreter() the on_call_stack_emptied hook
used to print any uncaught exception but this is now no longer
possible as the VM does not store uncaught exceptions.
- In js the code used to be interruptable by throwing an exception on
the VM. This is no longer possible but was already somewhat fragile
before as you could happen to throw an exception just before a VERIFY.
|
|
|
|
This allows us to use TRY in these functions :^).
|
|
Because we now push an execution context when creating the "normal"
interpreter without valid environments we have to check for that case
as well when running the bytecode interpreter.
|
|
This was effectively replaced by correct use of completions, including
UpdateEmpty semantics.
|
|
|
|
Note that this is just a shallow API change.
|
|
This is the same as what the AST interpreter does.
|
|
And use it to _correctly_ implement state saving for generators.
Prior to this, we were capturing the caller frame, which is completely
irrelevant to the generator frame.
|
|
To support situations like this:
function foo() { throw 1; }
try {
foo();
} catch (e) {
}
Each unwind context now keeps track of its origin executable.
When an exception is thrown, we return from run() immediately if the
nearest unwind context isn't in the current executable.
This causes a natural unwind to the point where we find the
catch/finally block(s) to jump into.
|
|
We were missing some "break" statements, causing us to actually finish
executing everything within "try" blocks before actually jumping to the
"catch" and/or "finally" blocks.
|
|
This will allow us to trigger bytecode executable dumps when generating
bytecode inside LibJS as well, not just in clients like js and test-js.
|
|
I forgot to consider the bytecode Interpreter when adding a Realm to the
ExecutionContext. This should make it a lot less crashy again :^)
|
|
This is where the spec wants to have it. Requires a couple of hacks as
currently everything that needs a Realm actually has a GlobalObject, so
we need to go via the Interpreter.
|
|
Also pass a Realm reference to the Bytecode::Interpreter constructor,
just like we pass the GlobalObject.
|
|
The test262 tests under RegExp/property-escapes/generated will invoke
Reflect.apply with up to 10,000 arguments at a time. In LibJS, when the
call stack reached VM::call_internal, we transfer those arguments from
a MarkedValueList to the execution context's arguments Vector.
Because these types differ (MarkedValueList is a Vector<Value, 32>), the
arguments are copied rather than moved. By changing the arguments vector
to a MarkedValueList, we can properly move the passed arguments over.
This shaves about 2 seconds off the following test262 test (from 15sec):
RegExp/property-escapes/generated/General_Category_-_Decimal_Number.js
|
|
"Records" in the spec are basically C++ classes, so let's drop this
mouthful of a suffix.
|
|
This struct represents what the ECMAScript specification calls an
"execution context" so let's use the same terminology. :^)
|
|
These represent the outermost scope in the environment record
hierarchy. The spec says they should be a "composite" of two things:
- An ObjectEnvironmentRecord wrapping the global object
- A DeclarativeEnvironmentRecord for other declarations
It's not yet clear to me how this should work, so this patch only
implements the first part, an object record wrapping the global object.
|
|
To better follow the spec, we need to distinguish between the current
execution context's lexical environment and variable environment.
This patch moves us to having two record pointers, although both of
them point at the same environment records for now.
|
|
This patch makes the following name changes:
- ScopeObject => EnvironmentRecord
- LexicalEnvironment => DeclarativeEnvironmentRecord
- WithScope => ObjectEnvironmentRecord
|
|
This commit adds a bunch of passes, the most interesting of which is a
pass that merges blocks together, and a pass that places blocks that
flow into each other next to each other, and a very simply pass that
removes duplicate basic blocks.
Note that this does not remove the jump at the end of each block in that
pass to avoid scope creep in the passes.
|
|
This counter is increased each time a synchronous execution sequence
completes, and will allow us to emulate the abstract operations
AddToKeptObjects & ClearKeptObjects efficiently.
|