Commit graph

30 commits

Author SHA1 Message Date
Andreas Kling
f2df6b2531 LibJS: Add ClassBlueprint data structures
Introduce ClassBlueprint and ClassElementDescriptor structs that will
replace the AST-backed class construction path. ClassBlueprint stores
pre-compiled function data indices and element metadata, following the
same pattern as SharedFunctionInstanceData for NewFunction.

Add Vector<ClassBlueprint> to Executable for storage.

No behavioral change.
2026-02-11 23:57:41 +01:00
Andreas Kling
6b0003b057 LibJS: Pre-create SharedFunctionInstanceData in NewFunction
Replace the FunctionNode const& stored on the NewFunction bytecode
instruction with an index into a table of pre-created
SharedFunctionInstanceData objects on the Executable.

During bytecode compilation, we now eagerly create
SharedFunctionInstanceData for each function that will be
instantiated by NewFunction, and store it on both the FunctionNode
(for caching) and the Executable (for GC tracing).

At runtime, NewFunction simply looks up the SharedFunctionInstanceData
by index and calls create_from_function_data() directly, bypassing
the AST entirely. This removes one of the main reasons the AST had
to stay alive after compilation.

The instantiate_ordinary_function_expression() helper in
Interpreter.cpp is removed as its non-trivial code path (creating a
scope for named function expressions) was dead code -- it was only
called when !has_name(), so the has_own_name branch never executed.
2026-02-11 23:57:41 +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
81bee185e6 LibJS: Replace source map HashMap with sorted Vector
Bytecode source map entries are always added in order of increasing
bytecode offset, and lookups only happen during error handling (a cold
path). This makes a sorted vector with binary search a better fit than
a hash map.

This change reduces memory overhead and speeds up bytecode generation
by avoiding hash table operations during compilation. Lookups remain
fast via binary search, and since source_range_at() is only called
when generating stack traces, the O(log n) lookup is acceptable.
2026-01-26 19:37:42 +01:00
Andreas Kling
4d92c4d71a LibJS: Skip initializing constant slots in ExecutionContext
Every function call allocates an ExecutionContext with a trailing array
of Values for registers, locals, constants, and arguments. Previously,
the constructor would initialize all slots to js_special_empty_value(),
but constant slots were then immediately overwritten by the interpreter
copying in values from the Executable before execution began.

To eliminate this redundant initialization, we rearrange the layout from
[registers | constants | locals] to [registers | locals | constants].
This groups registers and locals together at the front, allowing us to
initialize only those slots while leaving constant slots uninitialized
until they're populated with their actual values.

This reduces the per-call initialization cost from O(registers + locals
+ constants) to O(registers + locals).

Also tightens up the types involved (size_t -> u32) and adds VERIFYs to
guard against overflow when computing the combined slot counts, and to
ensure the total fits within the 29-bit operand index field.
2026-01-19 10:48:12 +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
e7e3c21123 LibJS: Shift PropertyLookupCache::types when finding a new slot
This regressed in c258282094 and broke
the polymorphic lookup cache, which was caught by the pic-* tests in
our MicroBench suite.
2025-12-21 13:00:35 -06:00
Andreas Kling
c258282094 LibJS: Shrink PropertyLookupCache by putting types in its own array
We have so many inline caches that this kind of thing becomes profitable
on complex pages. Also the memory access pattern is slightly nicer for
polymorphic caches.

Reduces memory usage on my x.com home feed by 4.9 MiB.
2025-12-21 10:06:04 -06:00
Andreas Kling
138b8f9607 LibJS: Shrink PropertyLookupCache::Entry size by tweaking layout
Reorder members and use u32 instead of Optional<u32> for things that
didn't actually need the "empty" state other than for assertions.

Reduces memory usage on my x.com home feed by 9.9 MiB.
2025-12-21 10:06:04 -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
f3fb02e6ef LibJS: Mark exception_handlers_for_offset() COLD 2025-12-09 21:44:13 -06:00
Andreas Kling
9f822345bf LibJS: Flatten Operand to 32-bit index in bytecode instruction stream
While we're in the bytecode compiler, we want to know which type of
Operand we're dealing with, but once we've generated the bytecode
stream, we only ever need its index.

This patch simplifies Operand by removing the aarch64 bitfield hacks
and makes it 32-bit on all platforms. We keep 3 type bits in the high
bits of the index while compiling, and then zero them out when
flattening the final bytecode stream.

This makes bytecode more compact on x86_64, and avoids bit twiddling
on aarch64. Everyone wins something!

When stringifying bytecode for debugging output, we now have an API in
Executable that can look at a raw operand index and tell you what type
of operand it was, based on known quantities of each type in the stack
frame.
2025-12-09 21:44:13 -06:00
Andreas Kling
6671cbef41 LibJS: Precompute the number of regs/constants/locals in Executable
Instead of doing it again on every call. Minor bump in call performance.
2025-10-30 08:54:45 +01:00
Andreas Kling
fb05063dde LibJS: Let bytecode instructions know whether they are in strict mode
This commits puts the strict mode flag in the header of every bytecode
instruction. This allows us to check for strict mode without looking at
the currently running execution context.
2025-10-29 21:20:10 +01:00
Luke Wilde
d4deafe5fe LibJS: Track current shape dictionary generation in PropertyLookupCache
When an object becomes too big (currently 64 properties or more), we
change its shape to a dictionary and don't do any further transitions.

However, this means the Shape of the object no longer changes, so the
cache invalidation check of `current_shape != cache.shape` is no longer
a valid check.

This fixes that by keeping track of a generation number for the Shape
both on the Shape object and in the cache, allowing that to be checked
instead of the Shape identity. The generation is incremented whenever
the dictionary is mutated.

Fixes stale cache lookups on Gmail preventing emails from being
displayed.

I was not able to produce a reproduction for this, plus the generation
count was over the 20k mark on Gmail.
2025-10-24 15:35:04 +02:00
Andreas Kling
dfa796a4e4 LibJS+LibWeb: Use GC::Weak instead of AK::WeakPtr for GC-allocated types
This makes some common types like JS::Object smaller (by 8 bytes) and
yields a minor speed improvement on many benchmarks.
2025-10-17 17:22:16 +02:00
Aliaksandr Kalenik
d45f8a3081 LibJS: Add inline caching for adding new own properties to objects
We already had IC support in PutById for the following cases:
- Changing an existing own property
- Calling a setter located in the prototype chain

This was enough to speed up code where structurally identical objects
(same shape) are processed in a loop:
```js
const arr = [{ a: 1 }, { a: 2 }, { a: 3 }];
for (let obj of arr) {
    obj.a += 1;
}
```

However, creating structurally identical objects in a loop was still
slow:
```js
for (let i = 0; i < 10_000_000; i++) {
    const o = {};
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```

This change addresses that by adding a new IC type that caches both the
source and target shapes, allowing property additions to be fast-pathed
by directly jumping to the shape that already includes the new property.
2025-09-17 12:44:44 +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
0efa98a57a LibJS+LibWeb+WebContent: Port JS::PropertyKey to UTF-16
This has quite a lot of fall out. But the majority of it is just type or
UDL substitution, where the changes just fall through to other function
calls.

By changing property key storage to UTF-16, the main affected areas are:
* NativeFunction names must now be UTF-16
* Bytecode identifiers must now be UTF-16
* Module/binding names must now be UTF-16
2025-08-05 07:07:15 -04:00
ayeteadoe
2e2484257d LibJS: Enable EXPLICIT_SYMBOL_EXPORT and annotate minimum symbol set 2025-07-22 11:51:29 -04:00
ayeteadoe
539a675802 LibJS: Revert Enable EXPLICIT_SYMBOL_EXPORT
This reverts commit c14173f651. We
should only annotate the minimum number of symbols that external
consumers actually use, so I am starting from scratch to do that
2025-07-22 11:51:29 -04:00
ayeteadoe
c14173f651 LibJS: Enable EXPLICIT_SYMBOL_EXPORT 2025-06-30 10:50:36 -06:00
Andreas Kling
a677d96b8f LibJS: Make GetById cache polymorphic
Instead of monomorphic (1 shape), GetById inline caches are now
polymorphic (4 shapes).

This improves inline cache hit rates greatly on most web JavaScript.
For example, Speedometer 2.1 sees 88% -> 97% cache hit rate improvement.

1.71x speedup on MicroBench/pic-get-own.js
1.82x speedup on MicroBench/pic-get-pchain.js
2025-05-07 00:27:11 +02:00
Aliaksandr Kalenik
db480b1f0c LibJS: Preserve information about local variables declaration kind
This is required for upcoming change where we want to emit ThrowIfTDZ
for assignment expressions only for lexical declarations.
2025-05-06 12:06:23 +02:00
Aliaksandr Kalenik
3f04d18ef7 LibJS: Add new operand type for function arguments
This allows us to directly access passed arguments instead of copying
them to register/local first using GetArgument instruction.
2025-04-26 11:02:29 +02:00
Andreas Kling
46a5710238 LibJS: Use FlyString in PropertyKey instead of DeprecatedFlyString
This required dealing with *substantial* fallout.
2025-03-24 22:27:17 +00:00
Andreas Kling
37c7eb14fe LibJS: Let GetGlobal cache module environment lookups when possible 2025-03-20 12:51:21 -05:00
Shannon Booth
f87041bf3a LibGC+Everywhere: Factor out a LibGC from LibJS
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:

 * JS::NonnullGCPtr -> GC::Ref
 * JS::GCPtr -> GC::Ptr
 * JS::HeapFunction -> GC::Function
 * JS::CellImpl -> GC::Cell
 * JS::Handle -> GC::Root
2024-11-15 14:49:20 +01:00
Timothy Flynn
93712b24bf Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Renamed from Userland/Libraries/LibJS/Bytecode/Executable.h (Browse further)