Commit graph

36 commits

Author SHA1 Message Date
Linus Groh
57de5056b6 LibJS: Convert push_execution_context() to ThrowCompletionOr 2021-11-14 16:14:38 +00:00
Ali Mohammad Pur
070d2eaa51 LibJS+LibTest+js: Convert BC::Interpreter::run to ThrowCompletionOr<>
Note that this is just a shallow API change.
2021-11-12 13:01:59 +00:00
Ali Mohammad Pur
c604e95993 LibJS: Run the queued promise reaction jobs on bytecode interpreter exit
This is the same as what the AST interpreter does.
2021-11-12 13:01:59 +00:00
Ali Mohammad Pur
e4a7f1a696 LibJS: Make Bytecode::Interpreter return the popped frame
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.
2021-11-12 13:01:59 +00:00
Andreas Kling
3618ca2420 LibJS: Propagate exceptions across bytecode executable boundaries
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.
2021-10-25 12:57:21 +02:00
Andreas Kling
049b755123 LibJS: Make bytecode interpreter leave unwind context immediately
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.
2021-10-25 12:57:21 +02:00
Andreas Kling
c95dde971b LibJS: Move global "should dump bytecode" flag into LibJS
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.
2021-10-24 17:18:06 +02:00
Linus Groh
35cc579264 LibJS: Also set ExecutionContext::realm in Bytecode::Interpreter::run()
I forgot to consider the bytecode Interpreter when adding a Realm to the
ExecutionContext. This should make it a lot less crashy again :^)
2021-09-13 21:06:18 +01:00
Linus Groh
f29a82dd84 LibJS: Move the GlobalEnvironment from GlobalObject to Realm
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.
2021-09-12 11:10:20 +01:00
Linus Groh
2b8d5696ab LibJS: Allocate a Realm next to GlobalObject in Interpreter::create()
Also pass a Realm reference to the Bytecode::Interpreter constructor,
just like we pass the GlobalObject.
2021-09-12 11:10:20 +01:00
Timothy Flynn
66264f7c2a LibJS: Change ExecutionContext's arguments list to a MarkedValueList
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
2021-08-10 23:07:50 +02:00
Andreas Kling
44221756ab LibJS: Drop "Record" suffix from all the *Environment record classes
"Records" in the spec are basically C++ classes, so let's drop this
mouthful of a suffix.
2021-07-01 12:28:57 +02:00
Andreas Kling
c2ad599783 LibJS: Rename CallFrame => ExecutionContext
This struct represents what the ECMAScript specification calls an
"execution context" so let's use the same terminology. :^)
2021-06-24 19:28:00 +02:00
Andreas Kling
1f8b6ac3c3 LibJS: Begin implementing GlobalEnvironmentRecord
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.
2021-06-22 18:44:53 +02:00
Andreas Kling
1d20380859 LibJS: Split the per-call-frame environment into lexical and variable
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.
2021-06-22 18:44:53 +02:00
Andreas Kling
6c6dbcfc36 LibJS: Rename Environment Records so they match the spec :^)
This patch makes the following name changes:

- ScopeObject => EnvironmentRecord
- LexicalEnvironment => DeclarativeEnvironmentRecord
- WithScope => ObjectEnvironmentRecord
2021-06-21 23:49:50 +02:00
Ali Mohammad Pur
1414c7b049 LibJS: Add a basic pass manager and add some basic passes
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.
2021-06-15 22:06:33 +04:30
Idan Horowitz
6913f06b6f LibJS: Store and maintain an "execution generation" counter
This counter is increased each time a synchronous execution sequence
completes, and will allow us to emulate the abstract operations
AddToKeptObjects & ClearKeptObjects efficiently.
2021-06-12 18:39:23 +01:00
Ali Mohammad Pur
3234697eca LibJS: Implement generator functions (only in bytecode mode) 2021-06-11 00:30:09 +02:00
Andreas Kling
22c803d8e5 LibJS: Always keep the global object in bytecode VM register $1 2021-06-10 21:59:49 +02:00
Gunnar Beutner
67cc31a74f LibJS: Implement bytecode generation for try..catch..finally
EnterUnwindContext pushes an unwind context (exception handler and/or
finalizer) onto a stack.

LeaveUnwindContext pops the unwind context from that stack.

Upon return to the interpreter loop we check whether the VM has an
exception pending. If no unwind context is available we return from the
loop. If an exception handler is available we clear the VM's exception,
put the exception value into the accumulator register, clear the unwind
context's handler and jump to the handler. If no handler is available
but a finalizer is available we save the exception value + metadata (for
 later use by ContinuePendingUnwind), clear the VM's exception, pop the
unwind context and jump to the finalizer.

ContinuePendingUnwind checks whether a saved exception is available. If
no saved exception is available it jumps to the resume label. Otherwise
it stores the exception into the VM.

The Jump after LeaveUnwindContext could be integrated into the
LeaveUnwindContext instruction. I've kept them separate for now to make
the bytecode more readable.

> try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4
1:
[   0] EnterScope
[  10] EnterUnwindContext handler:@4 finalizer:@3
[  38] EnterScope
[  48] LoadImmediate 1
[  60] NewString 1 ("x")
[  70] Throw
<for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here>
2:
[   0] LoadImmediate 4
3:
[   0] EnterScope
[  10] LoadImmediate 3
[  28] ContinuePendingUnwind resume:@2
4:
[   0] SetVariable 0 (e)
[  10] EnterScope
[  20] LoadImmediate 2
[  38] LeaveUnwindContext
[  3c] Jump @3

String Table:
0: e
1: x
2021-06-10 21:59:46 +02:00
Gunnar Beutner
319a60043b LibJS: Let the bytecode interpreter set the VM's last value 2021-06-10 21:47:25 +02:00
Gunnar Beutner
d198e41f74 LibJS: Stop bytecode execution after we've encountered an exception 2021-06-09 20:04:11 +02:00
Gunnar Beutner
6a0d1fa259 LibJS: Store strings in a string table
Instead of using Strings in the bytecode ops this adds a global string
table to the Executable struct which individual operations can refer
to using indices. This brings bytecode ops one step closer to being
pointer free.
2021-06-09 17:42:52 +02:00
Andreas Kling
48a8022cf6 LibJS: Move Bytecode::Instruction::execute() to the Op.h header
..and make sure it always gets inlined in the interpreter loop.
2021-06-09 09:24:32 +02:00
Andreas Kling
b61f198d22 LibJS: Rename Bytecode::ExecutionUnit => Bytecode::Executable 2021-06-09 09:24:32 +02:00
Ali Mohammad Pur
01e8f0889a LibJS: Generate bytecode in basic blocks instead of one big block
This limits the size of each block (currently set to 1K), and gets us
closer to a canonical, more easily analysable bytecode format.
As a result of this, "Labels" are now simply entries to basic blocks.
Since there is no more 'conditional' jump (as all jumps are always
taken), JumpIf{True,False} are unified to JumpConditional, and
JumpIfNullish is renamed to JumpNullish.
Also fixes #7914 as a result of reimplementing the loop logic.
2021-06-09 09:07:29 +02:00
Andreas Kling
7cbe4daa7c LibJS: Move bytecode debug spam behind JS_BYTECODE_DEBUG :^) 2021-06-07 18:11:59 +02:00
Andreas Kling
e7d69c5d3c LibJS: Devirtualize and pack the bytecode stream :^)
This patch changes the LibJS bytecode to be a stream of instructions
packed one-after-the-other in contiguous memory, instead of a vector
of OwnPtr<Instruction>. This should be a lot more cache-friendly. :^)

Instructions are also devirtualized and instead have a type field
using a new Instruction::Type enum.

To iterate over a bytecode stream, one must now use
Bytecode::InstructionStreamIterator.
2021-06-07 18:11:59 +02:00
Andreas Kling
845f2826aa LibJS: Reset Bytecode::Interpreter's m_return_value when leaving run()
Otherwise it will cause complete unwind since all parent run() loops
will see the same m_return_value being non-empty and break out.
2021-06-07 18:11:59 +02:00
Andreas Kling
9330163b0b LibJS: Make sure the global CallFrame doesn't go out of scope
The Bytecode::Interpreter will push a global call frame if needed,
and it needs to make sure that call frame survives until the end
of the Interpreter::run() function.
2021-06-07 18:11:59 +02:00
Andreas Kling
80b1604b0a LibJS: Compile ScriptFunctions into bytecode and run them that way :^)
If there's a current Bytecode::Interpreter in action, ScriptFunction
will now compile itself into bytecode and execute in that context.

This patch also adds the Return bytecode instruction so that we can
actually return values from called functions. :^)

Return values are propagated from callee to caller via the caller's
$0 register. Bytecode::Interpreter now keeps a stack of register
"windows". These are not very efficient, but it should be pretty
straightforward to convert them to e.g a sliding register window
architecture later on.

This is pretty dang cool! :^)
2021-06-07 18:11:59 +02:00
Andreas Kling
2316a084bf LibJS: Create a global/outermost CallFrame for Bytecode::Interpreter
Note that we don't yet support nested calls using bytecode.
2021-06-07 18:11:59 +02:00
Andreas Kling
6ae9346cd3 LibJS: Add basic support for while loops in the bytecode engine
This introduces two new instructions: Jump and JumpIfFalse.
Jumps are made to a Bytecode::Label, which is a simple object that
represents a location in the bytecode stream.

Note that you may not always know the target of a jump when adding the
jump instruction itself, but we can just update the instruction later
on during codegen once we know where the jump target is.

The Bytecode::Interpreter now implements jumping via a jump slot that
gets checked after each instruction to see if a jump is pending.
If not, we just increment the PC as usual.
2021-06-07 18:11:59 +02:00
Andreas Kling
6da5d17416 LibJS: Add a VM accessor to Bytecode::Interpreter :^) 2021-06-07 18:11:59 +02:00
Andreas Kling
69dddd4ef5 LibJS: Start fleshing out a bytecode for the JavaScript engine :^)
This patch begins the work of implementing JavaScript execution in a
bytecode VM instead of an AST tree-walk interpreter.

It's probably quite naive, but we have to start somewhere.

The basic idea is that you call Bytecode::Generator::generate() on an
AST node and it hands you back a Bytecode::Block filled with
instructions that can then be interpreted by a Bytecode::Interpreter.

This first version only implements two instructions: Load and Add. :^)

Each bytecode block has infinity registers, and the interpreter resizes
its register file to fit the block being executed.

Two new `js` options are added in this patch as well:

`-d` will dump the generated bytecode
`-b` will execute the generated bytecode

Note that unless `-d` and/or `-b` are specified, none of the bytecode
related stuff in LibJS runs at all. This is implemented in parallel
with the existing AST interpreter. :^)
2021-06-07 18:11:59 +02:00