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.
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
PDFDocEncoding has no remaining users left. Remove the decoder and
label plumbing. Retarget the lazy source-code decoding regression
test to Windows-1252 so it still covers non-UTF source decoding.
The interpreter's fast path for PutByValue on a typed array treated an
out-of-bounds index as a silent no-op and returned without touching the
value. That is observably wrong: TypedArraySetElement evaluates
ToNumber(value) before checking the index, so a value with a valueOf
side effect must still have that side effect run even when the store is
ultimately discarded.
Fall back to the slow path on an out-of-bounds or otherwise invalid
index instead of reporting success. The slow path runs the full
TypedArraySetElement algorithm, which performs the coercion and then
discards the write. Direct assignment now matches Reflect.set, which
already went through the slow path.
Fixes the staging/sm typed array out-of-bounds ToNumber test262 case
and adds a test-js regression covering direct assignment, Reflect.set,
and Reflect.defineProperty.
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.
This test verifies that we throw an exception instead of overflowing the
stack, but some machines have a large enough stack that this wouldn't
fill it. Raise the value by 10x, which does now throw an exception for
me.
Add a ref-counted decoded bytecode cache backing so bytecode cache
materialization can create fresh script or module records from a shared
decoded sidecar without passing around one-shot raw blob ownership.
Keep that backing in ExecutableBacking for records materialized from
bytecode cache sidecars, so the immutable decoded data stays alive for
as long as the installed record needs it.
Cover the shared backing path with a bytecode-cache test that
materializes and runs two scripts from one decoded backing.
Switch bytecode cache source identity from decoded UTF-16 source text to
the original encoded response bytes plus the effective source encoding.
Store the decoded source length in the cache blob header so warm loads
can build lazy SourceCode objects without decoding the source before
checking the sidecar.
This removes the main-thread decoded_source_text_info pass from valid
warm-cache script and module loads. The source is only decoded on cache
miss, or when a rejected sidecar falls back to source compilation.
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.
This commit updates Meta/import-webkit-regexp-tests.py and re-imports
WebKit tests.
Removed pcre-test-1 from SKIP_TESTS because we pass it.
Removed everything except slow.js from XFAIL_TESTS. We pass everything
except slow.js, which still hits the step limit.
Moved overflow.js to SKIP_FILES because it tests WebKit's behaviour of
rejecting regexps with huge quantifiers. Ladybird, Chrome, and Firefox
just clamp them.
This commit fixes assertEquals, assertThrows/assertDoesNotThrow in
compatibility shim - we were failing some tests purely because of a
bad shim.
Two test files are moved from XFAIL_TESTS to SKIP_FILES:
1. es6/regexp-tostring.js: checks for V8-specific error message wording
2. es6/unicode-regexp-ignore-case-noi18n.js: tests V8's non-i18n build
behavior, it doesn't case-fold non-ASCII characters, but we do it
correctly.
The regexp-unicode-sets.js test is now included and passes.
All files that were previously in XFAIL_TESTS and SKIP_TESTS are now
passing, so both lists are cleared.
Some string literals were incorrectly accepted inside negated classes
because the negated class check computed the actual result string set
instead of applying the structural rules.
This commit replaces class_set_expression_strings with a structural
class_set_expression_may_contain_strings check. Also removes some
incorrect tests.
Optional iterations in bounded quantifiers like {2,3} only emitted a
zero-width progress check when the outer min was 0. It made patterns
like /(?:a*?){2,3}/ match empty string even on non-empty input.
RepeatMatcher step 2.b states that the check must fire
whenever min reaches 0, which corresponds to every optional iteration.
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)
Resolve strict global-looking assignment targets before evaluating their
right-hand side instead of lowering them directly to SetGlobal. Preserve
the original unresolvable reference for PutValue so a RHS-created global
cannot hide the ReferenceError.
Add a runtime test for that ordering and update the bytecode expectation
for a strict top-level assignment that now uses SetResolvedBinding.
Parenthesized import calls are valid callees for new expressions. The
unparenthesized form remains a syntax error, but the grouped expression
must parse and then fail at runtime if the produced Promise is not a
constructor.
Add syntax coverage for both forms.
Cover installing bytecode cache blobs into live scripts and modules,
including stable shared-function-data identity, lazy function cleanup,
template object cache preservation, and top-level-await module
executable replacement.
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.
Resolve dynamic identifier assignment targets before evaluating the
right hand side, then store through that saved environment record. This
matches the ECMA-262 ordering for simple assignment and var initializers
when a with binding is deleted or direct eval creates a nearer var.
Add runtime coverage for with and direct-eval cases and refresh bytecode
expectations for dynamic assignments that now snapshot the binding
before storing.
Treat labelled FunctionDeclaration nodes as declaration-bearing items
when collecting block lexical and top-level var scoped declarations.
This lets Annex B declaration instantiation initialize them before
execution and copy block functions back to the var binding.
Add LibJS coverage for function bodies, direct eval, blocks, switch
cases, and duplicate labelled declarations.
Run deferred parameter validation whenever the surrounding context is
already strict, not only when a function body contains a Use Strict
Directive. Destructured parameter names are collected while parsing the
parameter list, so eval and arguments bindings in patterns otherwise
escaped the strict-mode early error for functions and methods.
Add parser coverage for sloppy destructuring remaining accepted, strict
source being rejected, and class methods applying their strict context.
Match ECMA-262's SuperCall evaluation order by resolving the super
constructor before evaluating the argument list. This preserves the
constructor across argument-side prototype mutations and still lets
abrupt argument evaluation happen before the constructor check.
Add LibJS coverage and update bytecode baselines for the new saved
super-constructor operand. The test262 superCallOrder staging test now
passes.
Reject parenthesized object and array literals when a destructuring
assignment element is refined into a nested assignment pattern. These
grouped forms do not cover AssignmentPattern, while parenthesized simple
assignment targets such as identifiers and member expressions remain
valid.
Add local syntax coverage for the invalid nested patterns and the valid
simple target cases.
Apply the `async of` grammar exclusion only to ordinary for-of
statements. The for-await-of production excludes `let` at the start of
the left-hand side, but does not exclude `async of`, so `async` remains
a valid assignment target there.
Add local parser coverage for async functions and async generators.
Parse await through the unary-expression path so the existing
exponentiation early error also applies to AwaitExpression. This keeps
parenthesized await expressions valid while rejecting await on the
left-hand side of **.
Add parser coverage for the async-function constructor case.
Reject labels whose string value is an unconditional ReservedWord,
while keeping await and yield on their contextual identifier paths.
This makes super labels a syntax error, matching ECMA-262 and V8.
Add coverage for the rejected super label form.
NamedEvaluation for assignment only applies when the left hand side is
an IdentifierRef. Parenthesized identifiers are still assignment
targets, but are not IdentifierRefs, so `(fn) = function () {}` must
leave the function anonymous.
Carry the parser's parenthesized-LHS bit into assignment codegen. Use
it when setting pending_lhs_name for plain and logical assignment, and
cover parenthesized assignment targets in the function-name tests.
The delete operator needs the Reference produced by an optional chain
when the chain ends in a property access. We evaluated the chain for
side effects and returned true, so delete object?.property reported
success without removing the property.
Emit delete-specific optional-chain bytecode. It short-circuits to true
for nullish optional hops, but performs DeleteById or DeleteByValue
when the final property reference is reached. Add runtime coverage for
named, computed, and short-circuited optional deletes.
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.
`import * as ns; export { ns }` was being recorded as a local
export, so two star exports of matching namespace objects resolved
through different fixture modules and became ambiguous.
Emit the same indirect namespace export metadata used by `export * as`
for both normal compilation and bytecode-cache materialization. Add a
module test that merges two matching namespace re-exports.
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.
Reset the legacy octal string marker at the start of each directive
prologue scan. Otherwise an octal string parsed later in an outer
function body can leak into a nested function and be treated as part of
that nested function's directive prologue.
Add coverage for a nested strict function following a non-directive
octal string in the outer function body.
Reject for-await loop heads that fall through to normal for-loop or
for-in parsing. ECMA-262 only defines for-await with for-of productions,
and V8 rejects these forms as syntax errors.
Add parser coverage for the valid for-await-of form and the invalid
C-style and for-in forms.
Mark binding-pattern identifiers with their declaration kind so local
destructuring assignments use the normal TDZ and const assignment path.
This makes local const destructuring match the environment-backed path.
Also teach GetById to expose primitive string virtual index properties
before boxing, matching StringGetOwnProperty and GetByValue. Together
these fix the SpiderMonkey for-in/of const declaration coverage and the
lexical destructuring TDZ test.