Commit graph

81 commits

Author SHA1 Message Date
Andreas Kling
c0f38c82d8 LibJS: Fix evaluation order in array destructuring assignment
Per AssignmentRestElement and AssignmentElement in the specification,
the DestructuringAssignmentTarget reference must be evaluated before
iterating or stepping the iterator. We were doing it in the wrong
order, which caused observable differences when the target evaluation
has side effects, and could lead to infinite loops when the iterator
never completes.

Add Generator::emit_evaluate_reference() to evaluate a member
expression's base and property into ReferenceOperands without performing
a load or store, then use the pre-evaluated reference for the store
after iteration completes.
2026-02-15 23:21:46 +01:00
Andreas Kling
ecadf3ce21 LibJS: Inline AsyncIteratorClose with proper Await in for-await-of
The AsyncIteratorClose bytecode op calls async_iterator_close() which
uses synchronous await() internally. This spins the event loop while
execution contexts are on the stack, violating the microtask checkpoint
assertion in LibWeb.

Replace AsyncIteratorClose op emissions in for-await-of close handlers
with inline bytecode that uses the proper Await op, allowing the async
function to yield and resume naturally through the event loop.

For the non-throw path (break/return/continue-to-outer): emit
GetMethod, Call, Await, and ThrowIfNotObject inline.

For the throw path: wrap the close steps in an exception handler so
that any error from GetMethod/Call/Await is discarded and the original
exception is rethrown, per spec step 5.
2026-02-12 11:37:43 +01:00
Andreas Kling
1f71a11131 LibJS: Remove emit_todo() from AssignmentExpression codegen
The else branch already throws ReferenceError and switches to a dead
basic block, so the emit_todo() in the PutValue section is unreachable.
Return early after the throw and replace emit_todo() with
VERIFY_NOT_REACHED().
2026-02-12 11:37:43 +01:00
Andreas Kling
322ad1363e LibJS: Throw ReferenceError for invalid assignment targets like foo()=x
CallExpression is accepted as an assignment target for web compatibility
(Annex B), but must throw ReferenceError at runtime. We were incorrectly
throwing TypeError with a TODO message.

Replace emit_todo() calls in three codegen paths (simple assignment,
compound assignment/update, and for-in/of) with proper ReferenceError
using the "Invalid left-hand side in assignment" message, matching the
behavior of V8 and JSC.
2026-02-12 11:37:43 +01:00
Andreas Kling
9a7f836d27 LibJS: Call IteratorClose on abrupt completion in for-of loops
When a for-of or for-await-of loop exits via break, return, throw,
or continue-to-outer-loop, we now correctly call IteratorClose
(or AsyncIteratorClose) to give the iterator a chance to clean
up resources.

This uses a synthetic FinallyContext that wraps the LHS assignment
and loop body, reusing the existing try/finally completion record
machinery. The ReturnToFinally boundary is placed between Break
and Continue so that continue-to-same-loop bypasses the close
(zero overhead on normal iteration) while all other abrupt exits
route through the iterator close dispatch chain.

for-in (enumerate) does not require iterator close per spec.
2026-02-12 11:37:43 +01:00
Andreas Kling
94cef3228f LibJS: Make IteratorClose/AsyncIteratorClose take Operand for value
Change the completion_value field from Optional<Value> to Operand
in both IteratorClose and AsyncIteratorClose bytecode instructions.

This allows passing a dynamic value from a register, which is needed
for iterator close on abrupt completion where the exception value
is not known at codegen time.
2026-02-12 11:37:43 +01:00
Andreas Kling
7281091fdb LibJS: Make bytecode generation infallible
Remove CodeGenerationError and make all bytecode generation functions
return their results directly instead of wrapping them in
CodeGenerationErrorOr.

For the few remaining sites where codegen encounters an unimplemented
or unexpected AST node, we now use a new emit_todo() helper that emits
a NewTypeError + Throw sequence at compile time (preserving the runtime
behavior) and then switches to a dead basic block so subsequent codegen
for the same function can continue without issue.

This allows us to remove error handling from all callers of the
bytecode compiler, simplifying the code significantly.
2026-02-12 11:37:43 +01:00
Andreas Kling
de1b6d4f07 LibJS: Convert unreachable codegen error sites to VERIFY
Replace CodeGenerationError returns with VERIFY_NOT_REACHED() or
VERIFY() at sites that are provably unreachable:

- Non-computed member expression fallbacks in emit_load_from_reference,
  emit_store_to_reference, and emit_delete_reference (member expression
  properties are always computed, identifier, or private identifier)
- Two non-computed member expression fallbacks in AssignmentExpression
- Default case in compound assignment switch (all 15 AssignmentOp values
  are handled)
- BindingPattern Empty/Expression name+alias pair (computed property
  names always require an alias)
- Two assignment+destructuring combinations in for-in/of body evaluation
  (is_destructuring is only set for VariableDeclaration lhs, which
  always has VarBinding or LexicalBinding kind, never Assignment)
2026-02-12 11:37:43 +01:00
Andreas Kling
35674df48a LibJS: Handle BigInt literal keys in class field initializer naming
When a class field has a BigInt literal key like `128n = class {}`,
the anonymous class should get the name "128". The codegen path
handles Identifier, StringLiteral, and NumericLiteral keys but was
missing BigInt keys, causing the name to be empty.

Parse the BigInt literal value at codegen time and convert it to a
decimal string for both the field_name (anonymous function naming)
and class_field_initializer_name (eval("arguments") checking) paths.
2026-02-11 23:57:41 +01:00
Andreas Kling
e308e73120 LibJS: Move SharedFunctionInstanceData creation out of FunctionNode
Add static factory methods create_for_function_node() on
SharedFunctionInstanceData and update all callers to use them instead
of FunctionNode::ensure_shared_data().

This removes the GC::Root<SharedFunctionInstanceData> cache from
FunctionNode, eliminating the coupling between the RefCounted AST
and GC-managed runtime objects. The cache was effectively dead code:
hoisted declarations use m_functions_to_initialize directly, and
function expressions always create fresh instances during codegen.
2026-02-11 23:57:41 +01:00
Andreas Kling
ec2f4e4a7b LibJS: Wire NewClass to ClassBlueprint
Replace the ClassExpression const& reference in the NewClass
instruction with a u32 class_blueprint_index. The interpreter now
reads from the ClassBlueprint stored on the Executable and calls
construct_class() instead of the AST-based create_class_constructor().

Literal field initializers (numbers, booleans, null, strings, negated
numbers) are used directly in construct_class() without creating an
ECMAScriptFunctionObject, avoiding function creation overhead for
common field patterns like `x = 0` or `name = "hello"`.

Set class_field_initializer_name on SharedFunctionInstanceData at
codegen time for statically-known field keys (identifiers, private
identifiers, string literals, and numeric literals). For computed
keys, the name is set at runtime in construct_class().

ClassExpression AST nodes are no longer referenced from bytecode.
2026-02-11 23:57:41 +01:00
Andreas Kling
6decb93dd7 LibJS: Populate ClassBlueprint during codegen
Build a ClassBlueprint from ClassExpression elements at codegen time:

- Methods/getters/setters: register SharedFunctionInstanceData from
  the method's FunctionExpression
- Field initializers with literal values (numbers, booleans, null,
  strings, negated numbers): store the value directly, avoiding
  function creation entirely
- Field initializers with non-literal values: wrap in
  ClassFieldInitializerStatement and create SharedFunctionInstanceData
- Static initializers: create SharedFunctionInstanceData from the
  function body
- Constructor: register SharedFunctionInstanceData from the
  constructor's FunctionExpression

Add public accessors to ClassMethod::function() and
StaticInitializer::function_body() for codegen access.

The blueprint is registered but not yet used by NewClass (dual path).

No behavioral change.
2026-02-11 23:57:41 +01:00
Andreas Kling
6e7830da71 LibJS: Give try/catch/finally blocks own completion registers
Each of the three blocks in a TryStatement (try body, catch body,
finally body) needs its own CompletionRegisterScope so that
break/continue inside any of them carries the block's own
completion value rather than leaking a value from a surrounding
statement or a different block.

Previously, statements inside these blocks would update the
enclosing scope's completion register (e.g. a for-loop's
register), and if break/continue fired with no prior expression
value, the enclosing register's stale value would leak through
as the completion value instead of undefined.

Each block now allocates a fresh register initialized to
undefined and uses it as the current completion register during
body generation. This matches the pattern already used by loops
and switch statements.
2026-02-11 14:29:36 +01:00
Andreas Kling
479b89aa6d LibJS: Fix UpdateEmpty completion value semantics for loops/switch/if
When a loop or switch body produces an abrupt completion (break or
continue) with an empty value, the ES spec requires UpdateEmpty to
replace the empty value with the last non-empty completion value V.

The bytecode compiler was failing to do this because it only updated
the completion register after body codegen, guarded by
!is_current_block_terminated(). When break/continue terminated the
block, the update was skipped.

Fix this with three changes:

1. Introduce a CompletionRegisterScope that tells
   ScopeNode::generate_bytecode to eagerly emit Mov instructions
   into the completion register after each value-producing
   statement. This ensures the register is up to date before any
   break or continue fires.

2. Give IfStatement its own CompletionRegisterScope (initialized
   to undefined) during branch evaluation. This models the spec's
   UpdateEmpty(stmtCompletion, undefined) for if-statements: when
   break/continue fires inside an if-branch, the scoped jump
   propagation sees that the if's completion register differs from
   the loop's and emits a Mov, correctly replacing the eagerly
   written value with undefined. Without this, code like
   { 3; if (true) { break; } else { } } would incorrectly carry
   the value 3 instead of undefined through the break.

3. Capture loop body results and emit a fallback Mov for
   non-ScopeNode bodies (e.g. bare expression statements like
   do x=1; while(false)) that don't participate in the eager
   CompletionRegisterScope update mechanism.

For labelled break/continue that cross loop boundaries, the jump
codegen now propagates the inner completion register to the target
scope's completion register before emitting the jump.

Also fix ForStatement to use a proper completion register
(previously it returned the body result directly, which was wrong
for empty bodies and break-with-no-value cases).
2026-02-11 14:29:36 +01:00
Andreas Kling
720fd567b1 LibJS: Collapse handler/finalizer into single exception handler target
After replacing the runtime unwind context stack with explicit
completion records for try/finally dispatch, the distinction between
"handler" (catch) and "finalizer" (finally) in the exception handler
table is no longer meaningful at runtime.

handle_exception() checked handler first, then finalizer, but they
did the exact same thing (set the PC). When both were present, the
finalizer was dead code.

Collapse both fields into a single handler_offset (now non-optional,
since an entry always has a target), remove the finalizer concept
from BasicBlock, UnwindContext, and ExceptionHandlers, and simplify
handle_exception() to a direct assignment.
2026-02-09 16:35:39 +01:00
Andreas Kling
cbca493b28 LibJS: Remove BlockBoundaryType::Unwind
With LeaveUnwindContext gone, the Unwind boundary type has no purpose.
Remove it from the enum and all start/end boundary calls.
2026-02-09 16:35:39 +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
Andreas Kling
bef09b899c LibJS: Fix object rest destructuring with MemberExpression target
When the rest element in an object destructuring assignment targets a
MemberExpression (e.g. `({a, ...t.rest} = obj)`), we were incorrectly
storing the original source object to the reference instead of the
rest object produced by CopyObjectExcludingProperties.

For example, `({a, ...t.rest} = {a:1, b:2, c:3})` would set t.rest
to `{a:1, b:2, c:3}` instead of the correct `{b:2, c:3}`.

The fix is to pass the result of CopyObjectExcludingProperties
to emit_store_to_reference instead of the original RHS.
2026-02-09 01:23:48 +01:00
Andreas Kling
7997267942 LibJS: Remove outdated FIXME comments about ToPropertyKey ordering
The FIXME comments suggested that ToPropertyKey was called at the wrong
time for computed super property access. However, extensive testing
shows that both Ladybird and V8 implement the correct ordering according
to the ECMA262 specification.

Remove the outdated FIXME comments and add comprehensive test coverage
for super property computed keys with Symbol.toPrimitive to prevent
regressions.
2026-02-09 01:23:48 +01:00
Andreas Kling
3eb03b4817 LibJS: Preserve this binding for tagged with identifiers
Route tagged template identifier lookup through
GetCalleeAndThisFromEnvironment only when the identifier is non-local.
Keep local and global identifiers on Identifier::generate_bytecode so
TDZ checks and ordinary undefined-this behavior stay intact.

Expand runtime coverage with a tagged-template TDZ regression case,
sequential with-binding calls, and getter-returned tag functions.
2026-02-09 01:23:48 +01:00
Andreas Kling
0c843b04e3 LibJS: Remove stale FIXME about sloppy-mode this in CallExpression
For non-Reference calls (e.g. (0, fn)(), (cond ? fn : x)()), the
codegen correctly passes undefined as the thisValue, matching step 2b
of EvaluateCall in the spec. OrdinaryCallBindThis then coerces
undefined to the global object in sloppy mode at runtime. Replace the
stale FIXME with a clarifying comment.

Also add comprehensive tests for this-value behavior in non-Reference
call patterns (comma, ternary, logical, assignment, nullish coalescing)
in both sloppy and strict mode.
2026-02-08 20:59:20 +01:00
Andreas Kling
b7091ba35a LibJS: Remove stale FIXME about NamedEvaluation in assignment
The FIXME claimed that IsAnonymousFunctionDefinition + NamedEvaluation
was missing for simple assignment expressions like `x = function() {}`.
However, the code directly below the FIXME already implements this
correctly via emit_named_evaluation_if_anonymous_function.
2026-02-08 20:59:20 +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
dosisod
2c3077b878 LibJS: Dead code elimination for always truthy/falsey conditions
This improves and expands the ability to do dead code elimination on
conditions which are always truthy or falsey.

The following cases are now optimized:
* `if (true){}` -> Only emit `if` block, ignore `else`
* `if (false){}` -> Only emit `else if`/`else` block
* `while (false){}` -> Ignore `while` loop entirely
* `for (x;false;){}` -> Only emit `x` (if it exists), skip `for` block
* Ternary -> Directly return left/right hand side if condition is const
2026-01-31 18:22:40 +01:00
dosisod
ac8cc6d24b LibJS: Constant fold LogicalExpression
Logical expressions like `true || false` are now constant folded. This
also allows for dead code elimination if we know the right-hand side of
the expression will never be evaluated (such as `false && f()` or
`true || f()`).

In the test suites, the values are now being constant folded at compile
time. To ensure that the actual evaluation logic is being called
properly, I had to duplicate the tests and call them via a function so
the compiler would not optimize the evaluation logic away.

This also demotes `NaN` and `Infinity` identifiers to `nan` and
`inf` double literals, which will further help with const folding.
2026-01-22 08:47:18 +01:00
dosisod
5a8d71fb02 LibJS: Optimize double boolean not (!!) operation
This is a common way to convert a value to a boolean. Instead of doing
a boolean conversion and 2 negate operations, we replace this with a
single `ToBoolean` op code.
2026-01-22 08:45:42 +01:00
Andreas Kling
986e30a140 LibJS: Fix object literal shape caching for numeric string keys
Numeric string keys like "0" are converted to numeric property keys and
stored in indexed storage rather than shape-based storage. The shape
caching optimization introduced in 505fe0a977 didn't account for this,
causing properties with numeric keys to be lost on subsequent calls.

The fix excludes object literals with numeric string keys from the
shape caching fast path by checking if any key would become a numeric
property index.
2026-01-21 10:49:34 +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
Aliaksandr Kalenik
711100e3b7 LibJS: Avoid unnecessary NewArray in yield* iterator method calls
Use `Op::Call` directly instead of creating a single-element array and
using `CallWithArgumentArray` when calling iterator methods (`next`,
`throw`, `return`) in `yield*` expressions.
2025-12-28 19:12:15 +01:00
Andreas Kling
ece0b72e3c LibJS: Don't set [[HomeObject]] for non-method object properties
This fixes an issue where we'd incorrectly retain objects via the
[[HomeObject]] slot. This common pattern was affected:

    Object.defineProperty(o, "foo", {
        get: function() { return 123; }
    });

Above, the object literal would get assigned to the [[HomeObject]]
slot even though "get" is not a "method" per the spec.

This frees about 30,000 objects on my x.com home feed.
2025-12-17 12:50:17 -06: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
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
aa5ee7866f LibJS: Move CallBuiltin argument count check to bytecode compiler
No need to check this at runtime, we have all the necessary info already
when generating bytecode.

Also mark the "yes, we are indeed calling the builtin" path [[likely]]
since it's exceedingly rare for anyone to replace the global functions.
2025-12-09 21:44:13 -06:00
Luke Wilde
a63b0cfaba LibJS: Introduce NativeJavaScriptBackedFunction
This hosts the ability to compile and run JavaScript to implement
native functions. This is particularly useful for any native function
that is not a normal function, for example async functions such as
Array.fromAsync, which require yielding.

These functions are not allowed to observe anything from outside their
environment. Any global identifiers will instead be assumed to be a
reference to an abstract operation or a constant. The generator will
inject the appropriate bytecode if the name of the global identifier
matches a known name. Anything else will cause a code generation error.
2025-11-30 11:54:54 +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
Aliaksandr Kalenik
646457099c LibJS: Avoid IteratorRecord GC-allocation in GetIterator instruction
With this change, `GetIterator` no longer GC-allocates an
`IteratorRecord`. Instead, it stores the iterator record fields in
bytecode registers. This avoids per-iteration allocations in patterns
like: `for (let [x] of array) {}`.

`IteratorRecord` now inherits from `IteratorRecordImpl`, which holds the
iteration state. This allows the existing iteration helpers
(`iterator_next()`, `iterator_step()`, etc.) operate on both the
GC-allocated and the register-backed forms.

Microbenchmarks:
1.1x array-destructuring-assignment-rest.js
1.226x array-destructuring-assignment.js
2025-11-02 20:05:47 +01:00
Andreas Kling
b47f8f94fe LibJS: Split PutBy* instructions into specialized per-kind variants
This allows the compiler to fold away lots of unused code for each kind.

1.10x speed-up on MicroBench/pic-add-own.js :^)
2025-10-11 20:08:58 +02:00
Andreas Kling
e7a3c4dbad LibJS: Rename Bytecode::Op::PropertyKind => Bytecode::PutKind
This is only used to specify how a property is being added to an object
by Put* instructions, so let's call it PutKind.

Also add an enumeration X macro for it to prepare for upcoming
specializations.
2025-10-11 20:08:58 +02:00
Aliaksandr Kalenik
e81833423b LibJS: Add PutByNumericId and change PutById to be string key only
Previously, PutById constructed a PropertyKey from the identifier,
which coerced numeric-like strings to numbers. This moves that decision
to bytecode generation: the bytecode generator now emits PutByNumericId
for numeric keys and PutById for string keys. This removes per-execution
parsing from the interpreter.

1.4x speedup on the following microbenchmark:
```js
const o = {};
for (let i = 0; i < 10_000_000; i++) {
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```
2025-09-13 20:02:28 +02:00
Luke Wilde
18c0739bbb LibJS: Copy base object of LHS of assignment to preserve eval order
Previously, the given test would create an object with the test
property that pointed to itself.

This is because `temp = temp.test || {}` overwrote the `temp` local
register, and `temp.test = temp` used the new object instead of the
original one it fetched.

Allows https://www.yorkshiretea.co.uk/ to load, which was failing in
Gsap library initialization.
2025-09-02 12:59:52 +01:00
Andreas Kling
996ea109b3 LibJS: Allocate context up front when calling with argument array
This necessitated splitting CallWithArgumentArray into three variants,
one for each call type (call, construct and direct eval).
2025-08-31 15:24:37 +02:00
Timothy Flynn
70db474cf0 LibJS+LibWeb: Port interned bytecode strings to UTF-16
This was almost a no-op, except we intern JS exception messages. So the
bulk of this patch is porting exception messages to UTF-16.
2025-08-14 10:27:08 +02:00
Timothy Flynn
cf61171864 LibJS: Port remaining bytecode identifiers to UTF-16 2025-08-14 10:27:08 +02:00