Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
Produce JS-visible string results as UTF-16 at their source, including
numeric formatting, BigInt and BigFraction formatting, URI encoding,
console formatting, parser errors, regular expression errors, Intl and
Temporal records, LibUnicode locale boundaries, and LibWeb bindings.
Handle fractional radix formatting through the UTF-16 builder view.
Thread UTF-16 string input through JSON, script parsing, Date parsing,
Intl option parsing, Temporal parsing, and the helper library boundaries
that feed those parsers. Preserve ASCII fast paths where the source data
is known to be ASCII.
Move the remaining LibJS primitive string users to UTF-16 views and
strings. Remove the primitive string UTF-8 accessors and byte-string
coercion paths so new callers cannot rely on the old storage model.
Keep primitive string storage in Utf16String and remove the UTF-8
storage path from PrimitiveString. ASCII strings still use compact
Utf16String ASCII storage, while UTF-16 becomes the only owned
representation.
Use a typed RegExp cache key instead of serializing the UTF-16
pattern and flags into a String. This avoids another StringBuilder
path for data that is already naturally represented as UTF-16 plus
flag bits.
Also make legacy unescape operate on UTF-16 code units and build its
result with Utf16StringBuilder. This preserves direct non-ASCII input
and %uXXXX escapes, including lone surrogate code units, without
routing the result through UTF-8 storage.
Accept Utf16View patterns at the LibRegex compile boundary and pass
UTF-16 or ASCII storage directly into the Rust regex parser. This keeps
JavaScript regular expression construction from converting patterns
through UTF-8 when LibRegex can consume the same UTF-16 representation
used by LibJS.
Update RegExp construction, HTML pattern validation, the regex fuzzer,
and LibRegex tests to use the UTF-16 compile API.
Build JSON.stringify, Date ISO strings, and Temporal string results with
Utf16StringBuilder when the result is consumed as a JavaScript string.
Keep UTF-8 conversion only at callers that explicitly need bytes outside
LibJS.
Port more UTF-16 string construction sites to Utf16StringBuilder.
This covers JSON serialization, URI escaping, RegExp replacement, date
and Temporal formatting, Uint8Array hex conversion, and stack string
formatting. Keep byte-oriented debug, bytecode, parser, and print
plumbing on StringBuilder.
Add a checked JS string length sum helper and use it for accumulation
paths that append JS-observable string pieces.
Port straightforward JS string construction sites to Utf16StringBuilder.
Make ASCII appends explicit with append_ascii(), and leave byte-output,
formatted-output, and printer plumbing on StringBuilder where that API
still matches the caller.
Add a small checked JS string length product helper. Use it for
String.prototype.repeat() before constructing the repeated result. Keep
the maximum string length policy in LibJS while allowing the UTF-16
builder to remain purpose-built and infallible. Cover overflow from the
final repeated code-unit length in the repeat tests.
Problem: Converting an object with a pathologically-deep prototype chain
to a primitive was segfaulting.
Cause: Object::internal_get implements [[Get]] by recursing into the
prototype’s [[Get]] (parent->internal_get) when the property isn’t an
own property. For a sufficiently deep prototype chain, that C++
recursion exhausts the native stack, and segfaults. The bytecode
interpreter’s call-stack limit doesn’t cover this native recursion.
Fix: Before recursing into the prototype in Object::internal_get, check
VM::did_reach_stack_space_limit(), and throw a CallStackSizeExceeded
InternalError — the same way the interpreter and other recursive runtime
operations guard the native stack. The deep-chain get now throws a
catchable call-stack-size-exceeded error, rather than crashing.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/3584
Previously, these were stored in a vector that was linearly scanned.
For large sites this vector could contain hundreds of entries, so
a HashMap gives a significant speedup.
The JSON.parse-with-source proposal (now part of ES2026) gives a
reviver a third "context" argument. For a primitive value that was
not modified by an earlier reviver call, the context has a "source"
property holding the matched JSON source text; for objects, arrays,
and forward-modified values it is an empty object.
We already had JSON.rawJSON and JSON.isRawJSON, but the reviver only
received two arguments. Implement the missing half by building a JSON
Parse Record snapshot while parsing: each primitive records the
trimmed raw token from simdjson, and arrays and objects record their
child records keyed by index and property name. InternalizeJSONProperty
threads the matching record down the tree, creates the context object,
and only attaches "source" when the record's stored value still equals
the live value (SameValue), which suppresses source for values a
reviver replaced or appended.
The record values live in heap storage the GC does not scan, and a
reviver can detach the originals from the object graph mid-walk, so
the snapshot's values are kept rooted for the duration of the walk.
Closes the six json-parse-with-source test262 failures and adds
test-js coverage for primitive source text and forward modification.
The Rust bytecode generator only passes local variable names to C++ now,
and no C++ code observes declaration kind metadata from LocalVariable.
Store local names directly as Utf16FlyString values and remove the stale
C++ wrapper type.
The Rust bytecode generator now owns basic block construction.
The old C++ BasicBlock class no longer has any users.
Label no longer needs to translate from BasicBlock.
Remove the now-empty Label.cpp from the build as well.
Remove the empty AsmInterpreter wrapper and the VM::run_bytecode()
trampoline now that the bytecode interpreter only enters AsmInt. Move
the stack-limit check and generated assembly entry call into
run_executable(), then drop the stale wrapper source file and includes.
Remove VM helpers that became unused after bytecode execution stopped
using the generic interpreter path. The AsmInt entry path now owns these
transitions directly.
Add a JSObjectStorage heap partition and route heap-backed property
storage (both named and indexed element buffers) through it.
These buffers are directly shaped by script-visible object and array
operations, so keeping them separate from the general heap makes a
corruption primitive less useful against unrelated allocations.
Move owned ArrayBuffer and SharedArrayBuffer data blocks into the
ArrayBuffer heap partition. Keep unowned and host storage explicit, so
Wasm memory and external LibWeb buffers stay outside this partition.
Introduce DataBlock::OwnedBackingStore as the LibJS-owned byte storage
representation. Expose byte spans instead of a ByteBuffer object, giving
ArrayBuffer one allocation boundary that can later grow toward guarded
or caged storage.
Let callers that need ByteBuffer data copy from backing-store bytes.
Keep TransferArrayBuffer zero-copy by moving the DataBlock directly
instead of materializing a ByteBuffer in between.
Update the Wasm typed-array test helper to compare viewed byte ranges
after ArrayBuffer stops exposing ByteBuffer identity.
The `CopyDataProperties` AO is used to implement object spread syntax.
The generic path calls `[[OwnPropertyKeys]]` to materialize a key list
and then performs a separate lookup through ``DescriptorArray::find()`
for every key.
For ordinary objects we now instead walk the shape in insertion order
and read each value directly by its storage offset, avoiding both the
key-list allocation and the per-key descriptor lookups. The fast path
is guarded to ordinary objects with no intrinsic accessors, no exotic
indexed access, packed-or-simpler indexed storage, and no excluded
values, falling back to the generic algorithm otherwise.
This is roughly 2x faster on a tight object spread microbenchmark.
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
Deeply nested structures passed to JSON.stringify or structuredClone
would cause a lot of recursion, eventually causing a crash.
We now throw an InternalError instead, same as other browser engines.
Check the cached prototype transition before converting the target
prototype shape. A cache hit means the target was already converted when
the transition was created, so this skips a redundant call in the hot
path.
When cloning a non-dictionary shape for prototype tracking, share the
descriptor array instead of copying it. Normal shape transitions already
use the same sharing model, with each shape keeping its own property
count boundary for lookups.
DateParser is a GenericLexer, which is not particularly UTF-8 aware.
Other engines do not accept ASCII input, so let's just reject non-ASCII
strings outright.
While we are here, let's avoid the std ctype header. It is explicitly
undefined behavior to provide code points to isspace, isalpha, etc. that
do not fit in an unsigned char. Although this isn't reachable any longer
let's just nip it in the bud and use our safe AK character types.
Just to demonstrate, the following returns different values for me on
different systems and with different compilers:
isdigit(0xff01)
isalpha(0xff01)
isspace(0xff01)
* Replace std::pair with a named struct, which we've always preferred.
* Remove needless use of std::ignore.
* Use concepts from AK instead of type traits from the STL.
This class was effectively upside-down compared to the way we typically
organize our classes.
* Put public section above private section
* Put methods above members
* Add newlines between methods
Give scripts and modules an explicit list of their shared function data
records, and teach Rust materialization to populate that list directly.
This gives bytecode cache installation a stable set of functions to
update when replacing executable backing storage.
Store the spec's [[AsyncEvaluationOrder]] value instead of a boolean for
pending async module evaluation. This lets AsyncModuleExecutionFulfilled
sort available parent modules by the order assigned during module graph
evaluation before executing them.
This matches ECMA-262 module graph ordering and fixes TLA parent
completion order, including graphs where a dynamic import later reuses a
TLA dependency.
Avoid draining the Promise job queue from VM::run_executable while a
SourceTextModule is still executing its body. Dynamic import queues the
load/link/evaluate continuation as a Promise job, and running that job
before ModuleEvaluation unwinds can re-enter Link() while the entry
module is still evaluating.
Track module execution depth so standalone script execution still drains
jobs at the same boundary, but module jobs are drained by
VM::run(SourceTextModule) after Link/Evaluate returns. Add coverage for
an entry module dynamically importing itself during evaluation.
Only drain the standalone Promise job queue when unwinding the outermost
bytecode execution. Draining after every nested run_executable() return
let sibling Promise reactions run before current-job reactions.
Plain async function returns were relying on that extra drain because
return values were compiled as implicit awaits. Resolve completed async
functions through the Promise resolving function instead, while keeping
the async generator return-await path.
Add Promise ordering coverage and update the asm inline-call regression
to check the pre-drain state.
Set.prototype.difference must call other.has for the entries in the
copied resultSetData, not for a live iteration over the receiver. If
other.has mutates the receiver, newly-added entries must not be visited.
Snapshot the copied result keys before calling user code so receiver
mutations cannot change which entries the operation tests. Add coverage
for the mutation case from the staging Set difference tests.
Normal functions create their prototype lazily. [[GetOwnProperty]]
already materialized it, but [[OwnPropertyKeys]] read the current shape
directly, so Object.getOwnPropertyNames(...) omitted "prototype".
Materialize the lazy prototype before enumerating own keys. This keeps
the fast path for functions whose keys are never observed while matching
the ordinary function property list expected by engines and test262.
InitializeTypedArrayFromArrayBuffer used Checked<u32> for view bounds.
Valid ToIndex values at 2**32 could wrap to zero before the
out-of-range check, so constructors accepted views that should throw.
Keep the bounds math in size_t until after the spec range checks. Narrow
only when storing the values in the current TypedArray slots. Add LibJS
coverage for ArrayBuffer byteOffset and length wraparound cases.
Accept single-digit minute and second components for space-separated
ISO-like dates. This matches other engines and the SpiderMonkey test.
Keep T-separated date-times stricter by rejecting bare hour offsets like
-07, while still allowing the non-standard space-separated form.
Return negative BigInts unchanged when their magnitude already fits in
the requested signed width. This matches BigInt.asIntN wrapping without
allocating the enormous modulo value first.
Update coverage to expect the result produced by other engines for these
large bit counts instead of an internal OOM.
Number.prototype.toFixed was using generic fixed-format double printing
after the spec steps. That loses the exact binary64 value for large
fractionDigits, so high precision output rounded from a nearby decimal.
Compute the scaled integer n with BigInteger arithmetic from the exact
binary decomposition of the double, then format that integer according
to the toFixed placement rules. Add coverage for high precision
fractional values and the signed Number.MIN_VALUE case.
Host-loaded modules are inserted into the VM module registry before
their dependencies are walked, but entry modules passed directly to
`VM::run()` were not. That made an entry module self-import parse a
second Module Record and observe the second copy after evaluation.
Register named entry modules in the same registry before dependency
loading runs, so self-imports and namespace access go through the
original Module Record. Add a test-js helper for running modules
directly and cover the self-import pre-evaluation binding state.
Sync yield* must suspend with the delegated IteratorResult object itself
on non-terminal steps. We were reading value and yielding that value,
causing Generator.prototype.next() to create a fresh result object.
Add a bytecode op for this path so ordinary yield keeps wrapping its
value, and teach generator resume to return the prebuilt result object.
This also avoids touching value before the delegated iterator is done.
Add regression coverage and update the yield* bytecode baseline.
ArraySetLength deletes array index properties in descending order. When
a non-configurable index cannot be deleted, the length is restored to
that index plus one, and lower indexes are not touched.
The dictionary indexed storage path already restored the final length,
but removed configurable entries below the failing index too. Keep those
entries when truncation stops early and add sparse array coverage.
Keep the premade normal-function property layout when creating dynamic
functions with a custom [[Prototype]], but transition the shape to that
prototype instead of replacing it with %Function.prototype%.
Let Function subclasses and bound functions created from their instances
participate in instanceof checks, and keep bound super() construction
passing the derived new.target through.
Generator and async generator instances should use
OrdinaryCreateFromConstructor with %GeneratorPrototype% and
%AsyncGeneratorPrototype%. When `.prototype` was nullish, we created a
null-prototype object, and when it was a primitive, we boxed it with
ToObject.
Use GetPrototypeFromConstructor for the generating function and cover
fallbacks for nullish and primitive prototype values.
ClassDefinitionEvaluation calls MakeConstructor with false for the
writablePrototype argument, so class constructors get a non-writable,
non-enumerable, non-configurable prototype property. We were using the
ordinary function attributes instead, which also allowed static class
elements named "prototype" to redefine the property.
Add LibJS coverage for the constructor and prototype descriptors, and
for static methods/accessors that attempt to define "prototype".
Track private identifiers that eval code references outside a local
class body and pass them through EvalDeclarationInstantiation data.
This lets direct eval validate those names against the caller private
environment before execution, while indirect eval and missing names
still throw SyntaxError.
Add coverage for direct eval access from methods, field initializers,
static methods, and the missing-private-name SyntaxError path.
Use a runtime SetFunctionName bytecode operation when object literal
property keys are not known until evaluation. This lets anonymous
function and class expressions, methods, and accessors receive names
from numeric, computed, and Symbol property keys.
Store inferred ECMAScript function names on each function object instead
of mutating shared function data. That keeps repeated evaluations with
different computed keys from leaking names across closures, while still
using the per-instance name for stack metadata.
Add regression coverage for computed object property names, repeated
computed-key evaluations, and preserving unnamed functions that are only
referenced by a computed property value.
Clear the lazy normal-function prototype hook when an ECMAScript
function is turned into a method. Normal object and class methods are
not constructors and should not grow an own prototype property when
queried, while generator methods keep their real prototype property.
Add browser-compatible caller and arguments reflection hooks for
ordinary sloppy ECMAScript functions. Keep strict, arrow, method,
generator, async, class, bound, and built-in functions on the
restricted Function.prototype accessors.
Respect the ECMA-262 forbidden-extension boundaries by never
exposing a caller frame outside the ordinary sloppy subset. This
matches V8 and JSC on the affected test262 caller tests and covers
the active function.arguments object case.