Commit graph

19 commits

Author SHA1 Message Date
pwespi
6c471c5ef7 LibJS: Do not allow reassignment to local const variable 2026-02-09 21:06:46 +01:00
Andreas Kling
5abe40874a LibJS: Remove LeaveUnwindContext opcode
LeaveUnwindContext popped the runtime unwind context stack. With the
stack being removed, all emission sites become dead code. Remove the
opcode and all its emissions.
2026-02-09 16:35:39 +01:00
Andreas Kling
e84a1fd6ad LibJS: Remove EnterUnwindContext opcode
EnterUnwindContext pushed an UnwindInfo and jumped to entry_point.
Without the stack push, it's just a Jump. Replace the single emission
site with a Jump and remove the opcode entirely.
2026-02-09 16:35:39 +01:00
Andreas Kling
7f89158d20 LibJS: Replace implicit environment stack with explicit registers
Replace the saved_lexical_environments stack in ExecutionContextRareData
with explicit register-based environment tracking. Environments are now
stored in registers and restored via SetLexicalEnvironment, making the
environment flow visible in bytecode.

Key changes:
- Add GetLexicalEnvironment and SetLexicalEnvironment opcodes
- CreateLexicalEnvironment takes explicit parent and dst operands
- EnterObjectEnvironment stores new environment in a dst register
- NewClass takes an explicit class_environment operand
- Remove LeaveLexicalEnvironment opcode (instead: SetLexicalEnvironment)
- Remove saved_lexical_environments from ExecutionContextRareData
- Use a reserved register for the saved lexical environment to avoid
  dominance issues with lazily-emitted GetLexicalEnvironment
2026-02-09 16:35:39 +01:00
Andreas Kling
a439dc8490 LibJS: Use explicit completion records for try/finally dispatch
Each finally scope gets two registers (completion_type and
completion_value) that form an explicit completion record. Every path
into the finally body sets these before jumping, and a dispatch chain
after the finally body routes to the correct continuation.

This replaces the old implicit protocol that relied on the exception
register, a saved_return_value register, and a scheduled_jump field
on ExecutionContext, allowing us to remove:

- 5 opcodes (ContinuePendingUnwind, ScheduleJump, LeaveFinally,
  RestoreScheduledJump, PrepareYield)
- 1 reserved register (saved_return_value)
- 2 ExecutionContext fields (scheduled_jump, previously_scheduled_jumps)
2026-02-09 08:51:12 +01:00
Andreas Kling
5cefa59116 LibJS: Fix evaluation order of computed property keys in object literals
The spec for PropertyDefinitionEvaluation requires that when evaluating
a property definition with a computed key (PropertyDefinition :
PropertyName : AssignmentExpression), the PropertyName is fully
evaluated (including ToPropertyKey, which calls ToPrimitive) before the
value's AssignmentExpression is evaluated.

Our bytecode compiler was evaluating the key expression first, then
the value expression, and only performing ToPropertyKey later inside
PutByValue at runtime. This meant user-observable side effects from
ToPrimitive (such as calling Symbol.toPrimitive or toString on the key
object) would fire after the value expression had already been
evaluated.

Fix this by using a new ToPrimitiveWithStringHint instruction that
performs ToPrimitive with string hint(!), and emitting it between the
key and value evaluations in ObjectExpression codegen.
After ToPrimitive, the key is already a primitive, so the subsequent
ToPropertyKey inside PutByValue becomes a no-op from the perspective
of user-observable side
effects.

Also update an existing test that was asserting the old (incorrect)
evaluation order, and add comprehensive new tests for computed property
key evaluation order.
2026-02-09 01:23:48 +01:00
dosisod
dab739771f LibJS: Reduce number of template literal op codes
There is no need to concat empty string literals when building template
literals. Now strings will only be concatenated if they need to be.

To handle the edge case where the first segment is not a string
literal, a new `ToString` op code has been added to ensure the value is
a string concatenating more strings.

In addition, basic const folding is now supported for template literal
constants (templates with no interpolated values), which is commonly
used for multi-line string constants.
2026-01-31 18:24:02 +01:00
Andreas Kling
505fe0a977 LibJS: Add shape caching for object literal instantiation
When a function creates object literals with simple property names,
we now cache the resulting shape after the first instantiation. On
subsequent calls, we create the object with the cached shape directly
and write property values at their known offsets.

This avoids repeated shape transitions and property offset lookups
for a common JavaScript pattern.

The optimization uses two new bytecode instructions:
- CacheObjectShape: Captures the final shape after object construction
- InitObjectLiteralProperty: Writes properties using cached offsets

Only "simple" object literals are optimized (string literal keys with
simple value expressions). Complex cases like computed properties,
getters/setters, and spread elements use the existing slow path.

3.4x speedup on a microbenchmark that repeatedly instantiates an object
literal with 26 properties. Small progressions on various benchmarks.
2026-01-10 00:56:51 +01:00
Luke Wilde
c4c9ac08ad LibJS: Follow the spec more closely for tagged template literals
This resolves a FIXME in its code generation, particularly for:
- Caching the template object
- Setting the correct property attributes
- Freezing the resulting objects

This allows archive.org to load, which uses the Lit library.

The Lit library caches these template objects to determine if a
template has changed, allowing it to determine to do a full template
rerender or only partially update the rendering. Before, we would
always cause a full rerender on update because we didn't return the
same template object.

This caused issues with archive.org's code, I believe particularly with
its router library, where we would constantly detach and reattach nodes
unexpectedly, ending up with the page content not being attached to the
router's custom element.
2026-01-06 23:25:36 +01:00
Andreas Kling
ee9e24d1dd LibJS: Add dedicated bytecode instruction for x|0 (ToInt32)
This operation is a very common technique to force a value to become
a 32-bit integer.
2025-12-15 08:57:00 -06:00
Andreas Kling
a62daf2a88 LibJS: Remove redundant Put*ByNumericId* instructions
These were helpful when PropertyKey instantiation happened in the
interpreter, but now that we've moved it to bytecode generation time,
we can use the basic Put*ById* instructions instead.
2025-12-11 14:34:45 -06:00
Andreas Kling
bad16dc0e0 LibJS: Cache fully-formed PropertyKeys in Executable
Instead of creating PropertyKeys on the fly during interpreter
execution, we now store fully-formed ones in the Executable.

This avoids a whole bunch of busywork in property access instructions
and substantially reduces code size bloat.
2025-12-11 14:34:45 -06:00
Andreas Kling
2e172b210a Revert "LibJS: Remove unnecessary ConcatString bytecode instruction"
This reverts commit 420187ba7c.

Caused 41 regressions in test262.
2025-12-10 09:17:11 -06:00
Andreas Kling
420187ba7c LibJS: Remove unnecessary ConcatString bytecode instruction
In favor of just using Add instead.
2025-12-09 21:44:13 -06:00
Andreas Kling
2a4c76fa29 LibJS: Mark Not and Typeof as non-throwing instructions 2025-12-09 21:44:13 -06:00
Luke Wilde
0eceee0a05 LibJS: Replace Array.fromAsync with a native JavaScript implementation
This allows us to use the bytecode implementation of await, which
correctly suspends execution contexts and handles completion
injections.

This gains us 4 test262 tests around mutating Array.fromAsync's
iterable whilst it's suspended as well.

This is also one step towards removing spin_until, which the
non-bytecode implementation of await uses.

```
Duration:
     -5.98s

Summary:
    Diff Tests:
        +4     -4 

Diff Tests:
    [...]/Array/fromAsync/asyncitems-array-add-to-singleton.js  -> 
    [...]/Array/fromAsync/asyncitems-array-add.js               -> 
    [...]/Array/fromAsync/asyncitems-array-mutate.js            -> 
    [...]/Array/fromAsync/asyncitems-array-remove.js            -> 
```
2025-11-30 11:54:54 +01:00
Andreas Kling
702977373c LibJS: Some Bytecode.def fixes
- Add missing @nothrow to a bunch of instructions.
- Rename fields that were colliding between base and derived class.
2025-11-30 11:53:56 +01:00
Andreas Kling
370b81f1b7 LibJS: Remove unused "Dump" instruction 2025-11-21 09:46:03 +01:00
Andreas Kling
003589db2d LibJS: Generate C++ bytecode instruction classes from a definition file
This commit adds a new Bytecode.def file that describes all the LibJS
bytecode instructions.

From this, we are able to generate the full declarations for all C++
bytecode instruction classes, as well as their serialization code.

Note that some of the bytecode compiler was updated since instructions
no longer have default constructor arguments.

The big immediate benefit here is that we lose a couple thousand lines
of hand-written C++ code. Going forward, this also allows us to do more
tooling for the bytecode VM, now that we have an authoritative
description of its instructions.

Key things to know about:

- Instructions can inherit from one another. At the moment, everything
  simply inherits from the base "Instruction".

- @terminator means the instruction terminates a basic block.

- @nothrow means the instruction cannot throw. This affects how the
  interpreter interacts with it.

- Variable-length instructions are automatically supported. Just put an
  array of something as the last field of the instruction.

- The m_length field is magical. If present, it will be populated with
  the full length of the instruction. This is used for variable-length
  instructions.
2025-11-21 09:46:03 +01:00