Commit graph

526 commits

Author SHA1 Message Date
Andreas Kling
b81269e78b Libraries: Clean up UTF-16 source text paths
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.
2026-06-22 19:51:25 +02:00
Andreas Kling
b6bef6b688 Libraries: Use UTF-16 for JS-visible runtime strings
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.
2026-06-22 19:51:25 +02:00
Andreas Kling
7025dd1fa7 Libraries: Parse JS strings from UTF-16
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.
2026-06-22 19:51:25 +02:00
Andreas Kling
ee37bb5a9c LibJS: Remove primitive string UTF-8 paths
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.
2026-06-22 19:51:25 +02:00
Andreas Kling
13969b6bd4 LibJS: Store primitive strings as UTF-16
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.
2026-06-22 19:51:25 +02:00
Andreas Kling
950c460efd LibJS: Keep more string construction in UTF-16
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.
2026-06-22 16:10:40 +02:00
Andreas Kling
595ae25e74 LibJS: Use Utf16StringBuilder for JS string construction
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.
2026-06-22 16:10:40 +02:00
sideshowbarker
d7c08964cb LibJS: Throw rather than crashing on a deep prototype-chain get
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
2026-06-20 23:34:33 +02:00
sideshowbarker
a237fbc24f LibJS: Add a regression test for binding environment type confusion
Issue #3622 documents a now-no-longer reproducible type-confusion/OOB
bug. This just adds the POC from that issue as a regression test.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/3622
2026-06-20 23:34:19 +02:00
Shannon Booth
c982385ffe LibTextCodec: Remove unused PDFDocEncoding
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.
2026-06-20 21:56:43 +02:00
Andreas Kling
be5320b67c LibJS: Evaluate ToNumber for out-of-bounds typed array writes
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.
2026-06-17 21:24:33 +02:00
Andreas Kling
a3db2d1986 LibJS: Support source text access in JSON.parse revivers
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.
2026-06-17 21:24:33 +02:00
Sam Atkins
abfd5ab860 Tests: Make JSON.stringify deep serialization test more consistent
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.
2026-06-11 14:47:27 +12:00
Andreas Kling
6ecfcd3e68 LibWeb+LibJS: Cache decoded JS bytecode sidecars
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.
2026-06-06 09:15:09 +02:00
Andreas Kling
afb0fa2413 LibJS: Hash encoded source identity in bytecode cache
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.
2026-06-03 14:11:23 +02:00
aplefull
52f45aef4e LibWeb+LibJS: Don't crash when serializing deeply nested values
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.
2026-06-03 13:00:01 +01:00
Shannon Booth
9e58eb24de Tests/LibJS: Remove unneeded GC::Root use in PrimitiveString test 2026-05-29 17:21:17 +02:00
aplefull
105098131b Tests+Meta: Reimport WebKit regexp tests
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.
2026-05-26 10:00:22 +02:00
aplefull
b3bd8884ae Tests+Meta: Reimport V8 regexp tests
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.
2026-05-26 10:00:22 +02:00
aplefull
c0a8dd4536 LibRegex: Reject invalid /v class set expressions in negated classes
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.
2026-05-26 10:00:22 +02:00
aplefull
201c129973 LibRegex: Emit progress check for optional iterations in bounded quants
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.
2026-05-26 10:00:22 +02:00
Timothy Flynn
edaed9adfa LibJS: Protect DateParser from non-ASCII input
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)
2026-05-22 17:30:36 +02:00
Andreas Kling
19c1cae030 LibJS: Preserve strict unresolvable assignment references
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.
2026-05-22 14:30:35 +02:00
Andreas Kling
8b64f0079b LibJS: Allow parenthesized dynamic import under new
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.
2026-05-22 14:30:35 +02:00
Andreas Kling
01df2663af LibJS: Test mapped bytecode cache installation
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.
2026-05-22 10:54:44 +02:00
Andreas Kling
fb5b78bfea LibJS: Track async module evaluation order
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
54d0dc1a85 LibJS: Defer Promise jobs during module execution
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
790386e114 LibJS: Preserve Promise job ordering across calls
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
a9e54bd745 LibJS: Preserve resolved assignment bindings
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
eeda29d9ed LibJS: Hoist sloppy labelled function declarations
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
8a64784d46 LibJS: Check strict destructured parameter names
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
2f99653b33 LibJS: Resolve super constructor before arguments
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
c23b944340 LibJS: Reject parenthesized nested assignment patterns
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
99d3b27b17 LibJS: Allow async as a for-await-of target
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
45bd4087c4 LibJS: Reject unparenthesized await before exponentiation
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
72876d8b93 LibJS: Reject unconditional reserved words as labels
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
62ac304099 LibJS: Avoid naming parenthesized assignment functions
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
22e1df58d3 LibJS: Delete through optional chain property references
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
4629154c61 LibJS: Iterate Set difference over copied data
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
ca6cf670c2 LibJS: Materialize lazy function prototypes for own keys
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
3c0923e827 LibJS: Avoid TypedArray ArrayBuffer bounds truncation
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
84029461f8 LibJS: Tighten ISO-like Date parsing edge 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.
2026-05-22 01:56:57 +02:00
Andreas Kling
5da24e18b1 LibJS: Avoid huge asIntN modulo for small negatives
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
8b5d46d65b LibJS: Round toFixed digits exactly
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
94fea5ad2b LibJS: Re-export imported module namespaces indirectly
`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.
2026-05-22 01:56:57 +02:00
Andreas Kling
bdecc7f558 LibJS: Cache entry modules before loading imports
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
410f0fdbb0 LibJS: Yield delegated iterator results directly
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
6ee59d8bcb LibJS: Scope directive prologue octal tracking
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
6c7e642b67 LibJS: Reject non-of for-await loops
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.
2026-05-22 01:56:57 +02:00
Andreas Kling
bf530ad2af LibJS: Fix destructured primitive string const loops
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.
2026-05-22 01:56:57 +02:00