Skip to content

Feat/js engine conformance sweep - #97

Merged
codymullins merged 1059 commits into
mainfrom
feat/js-engine-conformance-sweep
Jul 7, 2026
Merged

Feat/js engine conformance sweep#97
codymullins merged 1059 commits into
mainfrom
feat/js-engine-conformance-sweep

Conversation

@codymullins

Copy link
Copy Markdown
Collaborator

No description provided.

codymullins and others added 30 commits June 4, 2026 21:16
Ignore IDEOGRAPHIC SPACE
…l-demo-video

GPU Surface Overlays + Native Shell MCP support
Optimize JsLexer/JsParser with ref struct and JsTokenKind
Remove the home-rolled IDiagnostics sink. Logging now uses ILogger with
source-generated [LoggerMessage] methods. Spans and metrics move to a static
StarlingTelemetry facade (ActivitySource + Meter) in Starling.Common.

Layering and shape:
- Libraries depend only on Microsoft.Extensions.Logging.Abstractions. Concrete
  providers (console, OpenTelemetry, in-memory DevTools) register at the host.
- Leaf classes take ILogger<T> in the constructor with a NullLogger<T> default,
  so they stay new-able without a container.
- Only the four composition points that mint several child-category loggers keep
  an ILoggerFactory: StarlingEngine, Painter, StarlingHttpClient, WebviewPanel.
  Engine also needs it for the dynamic "Starling.engine.js" console category.
- Static classes use CreateLogger(typeof(X)), since T cannot be a static type.
- Classes that only emit spans or metrics carry no logger at all (the HTML parse
  chain, BlockLayout, InlineLayout, ImageSharpBackend).

DI:
- AddStarlingEngine(IServiceCollection) registers Painter, StarlingEngine, and
  BrowserSession. The Avalonia host calls it and resolves BrowserSession from the
  container. Tests, bench, and the headless CLI still construct directly.

Also fix every empty catch in src so caught exceptions are logged (debug or trace
for best-effort paths, warning or error otherwise).

Solution builds clean with no warnings. Tests pass across Net, Layout, Css,
Paint, Html, AngleSharp, Bindings, Js, Common, and the Engine capture-double set.
refactor(diag): replace IDiagnostics with ILogger and StarlingTelemetry
Move regexp back in-tree, drop the submodule
Fix bugs and add missing features in the managed SVG decoder, and add a
test suite modeled on the resvg test suite.

Bug fixes:
- Circle and ellipse rendered at half their radius (EllipsePolygon takes a
  full width/height, not a radius).
- Recursive use/pattern/mask references overflowed the stack or fanned out
  exponentially. Added a depth limit plus reference-cycle detection.

New features:
- display:none, visibility, switch, transform-origin, paint-order,
  mix-blend-mode.
- Gradient attribute inheritance through href, and objectBoundingBox patterns.
- clip-path and clip-rule, mask (luminance), filters (feGaussianBlur, feFlood,
  feOffset), text and tspan, and markers.
- XML internal-DTD entity expansion (external fetch stays blocked).

Tests:
- Authored conformance tests grouped by SVG 1.1 chapter (shapes, painting,
  structure, paint servers, masking, text, filters).
- A data-driven corpus test with one case per vendored resvg file. The corpus
  data under testdata/spec/resvg is not committed here; the test reads it from
  the test output directory.
The Jint JS backend ships in builds, so its binaries (Jint, BSD-2-Clause)
and Jint's only dependency (Acornima, BSD-3-Clause) require their copyright
notice, conditions, and disclaimer to travel with binary distributions.

- Add verbatim upstream license texts under third-party-licenses/.
- List both packages in THIRD_PARTY_NOTICES.md.
- Copy LICENSE, NOTICE, THIRD_PARTY_NOTICES.md, and third-party-licenses/
  into the output of the distributable apps (Starling.Headless, Starling.Gui)
  so they ship on build and publish.
Add the resvg test suite's 1,679 SVG files as the conformance corpus that
ResvgCorpusTests decodes. The upstream reference PNGs (~10 MB) are not vendored
because the decode test never pixel-compares against them; this keeps the
corpus around 1 MB of SVG text.

Credit resvg (MIT, Copyright (c) 2018 Reizner Evgeniy) in THIRD_PARTY_NOTICES.md
with thanks for openly sharing an excellent static-SVG test suite. results.csv,
LICENSE, and a README are kept for attribution and reference.
Reflection test asserting the renderer-neutral contract surface in
Starling.Paint (the DisplayList scene records, the backend/compositor
interfaces, and the GPU hand-off DTOs) exposes no SixLabors type. Seven
known GPU leaks (GpuPaintTexture, GpuPaintDeviceContext) are allow-listed
with the step that removes each; the list must shrink to empty.
…tInterpolation (Step 1)

Move the 13 pure CSS Color 4 conversion methods (sRGB<->Oklab/Lab/HSL/HWB,
hue interpolation, sRGB<->linear) out of the ImageSharp adapter into a neutral
Starling.Paint.Gradients.GradientInterpolation. Pure double math, no Rgba32 or
SixLabors dependency, so a second backend can interpolate stops identically on
the CPU. Verbatim move; Rgba32 packing (ToStraightAlpha/SampleStops) stays in
the adapter and calls the neutral class. Output is byte-identical.
…ory (Step 2)

Introduce IPaintBackendFactory (Kind/CreateBackend/CreateMeasurer) and
ImageSharpPaintBackendFactory, and dispatch PaintBackendSelector.Create/
CreateMeasurer through a single FactoryFor(kind) switch. The factory pairs the
backend with its measurer so layout and raster share a font stack, and is the
registration point a second backend plugs into. Public Create/CreateMeasurer
signatures are unchanged, so Painter is untouched; behavior is identical.
… Slice (Step 3)

Add ShapedRun.CanReuseAtSize(double) virtual (default false; ImageSharp run
returns Font.Size == size) and use it at the three backend text-draw sites
instead of reading the concrete Font.Size — the reuse decision lives on the run,
ready for a non-ImageSharp run to answer false and take a glyph path.

Also clamp ImageSharpShapedRun.Slice's character-range substring to the string
bounds so a non-1:1 (glyphs != chars) run can never read out of range — exact
glyph slicing, best-effort text, never a crash. Adds a regression test.
…Step 4)

Move the box-shadow / conic-gradient LRU layer caches and the deferred-disposal
bag out of the 2,637-line ImageSharpBackend.cs into a same-class partial,
Backend/ImageSharp/ImageSharpBackend.Caches.cs, so the ImageSharp-only cache
vocabulary reads as one clearly-adapter concern. These were already nested
private (maximally encapsulated, excluded from the neutral-seam guard); the
partial split keeps identical behavior and visibility while shrinking the main
adapter file. Output unchanged.
Split GpuPaintTexture into a neutral DTO (native texture/view nint handles +
size + a Starling PaintTextureFormat enum + an opaque IDisposable owner) and a
new adapter-internal ImageSharpGpuTexture that owns the SixLabors
WebGPURenderTarget and reflects out its native handles. ImageSharpBackend
constructs the adapter texture and hands the compositor a neutral GpuPaintTexture;
GpuBlendEngine already consumed only the nint handles, so it is unchanged.

The neutral-seam guard test loses its five GpuPaintTexture exceptions; only the
two GpuPaintDeviceContext leaks remain (removed in Step 6).
Replace the SixLabors-typed GpuPaintDeviceContext at the seam with a neutral
GpuPaintDevice(nint Device, nint Queue). The compositor's IGpuLayerTextureCache
now exposes GpuDevice (the native handle pair) instead of ImageSharpContext, and
RenderTexture takes GpuPaintDevice. The reflection bridge to ImageSharp's WebGPU
device context moves to an adapter-internal ImageSharpGpuContext that caches one
context per device handle and is torn down by the existing device-state cleanup.
GpuBlendEngine no longer holds an ImageSharp context.

The neutral-seam guard test's allow-list is now empty: every contract type in
Starling.Paint.Backend / .Compositor / .DisplayList is free of SixLabors.
… (Step 7)

Investigated making ShapedRun.Glyphs the raster input. Two findings make a
glyph-by-id draw path the wrong move: (1) ImageSharp.Drawing's public API does
not expose OpenType glyph indices (GlyphMetrics.GlyphId is internal), and (2)
glyph ids are not portable across shaping engines, so a non-ImageSharp backend
(skrifa/HarfBuzz) re-shapes the source text with its own ids regardless. The
real renderer-neutral text contract is therefore the source text + FontSpec on
the draw item, with ShapedGlyph positions for layout — which already exists.

Document this on ShapedGlyph and in the measurer (positions authoritative, ids
best-effort/non-portable). Structural readiness for a second backend's own text
path is already in place via ShapedRun.CanReuseAtSize (Step 3) and the neutral
GlyphShapedRun. No behavioral change.
…p 8)

The agents assumed SvgImageDecoder's public surface was already neutral, but its
Decode/DecodeText took a SixLabors Color for the SVG currentColor keyword, and
Starling.Engine/ImageFetcher constructed a SixLabors.ImageSharp.Color to call it
— a real cross-assembly leak. Change the parameter to neutral CssColor and move
the sRGB->ImageSharp conversion inside the decoder. ImageFetcher now passes the
CssColor it already has and references no SixLabors type (0 refs).

The decoder's internals stay ImageSharp (correctly — it is the adapter's SVG
rasterizer); only its public contract is neutralized. Full resvg corpus and SVG
conformance suites stay green.
Reading a namespace export whose live cell still holds the TDZ sentinel
(module not yet evaluated to the declaration) throws ReferenceError instead
of leaking the sentinel; the namespace captures its realm at build time.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
With any default present, default/pattern parameter bindings are declared up
front in the uninitialized state, so self/forward default references
(`(x = x)`, `(a = b, b)`) throw ReferenceError; the actual parameter binding
runs in declaration-init mode (the missing piece last attempt — stores were
routing through TDZ-checked writes and throwing on their own
initialization). language 98.19 -> 98.33, floor 98.2.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
IsLetDeclarationStart only looked for an Identifier lookahead; the lexer
always classifies `yield` as its keyword token, so `let yield = 4` misparsed
as the identifier `let` plus a stray token. language 98.33 -> 98.35.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
….1.1.2.5)

A getter side effect can delete the with-binding between the compound read
and write; the strict write must throw ReferenceError without re-creating
the property.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
Two rules: (1) at statement-list level the `let` head COMMITS to a
LexicalDeclaration for identifier/yield/[/{ lookaheads — inside a generator
`let\nyield` therefore hits the binding-identifier rejection instead of an
ASI rescue; (2) in single-statement positions a sloppy declaration-looking
`let` is parsed as a FORCED ExpressionStatement (falling into
ParseStatement re-detected the declaration and wrongly accepted
`do let x = 1; while (false)`). language 98.36 -> 98.43.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
Update expressions on a resolvable const local emit the read (TDZ check +
coercion) then ThrowConstAssignment; for-head let/const bindings are now
marked lexical/const in the loop scope (they previously skipped both TDZ
and const semantics — `for (const i = 0; …; i++)` never threw).

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
…rototype

Rewrite JsArray around an authoritative uint length decoupled from the
dense backing: growing length materializes nothing (new Array(1e9) is
O(1)), deleted elements become real holes (HasOwn false, skipped by
iteration), far-sparse and attribute-deviating elements live in the
property bag, and getOwnPropertyNames now reports index keys, length,
then the rest in spec order. ArraySetLength runs both observable
coercions and clamps shrinks at the highest non-configurable element.

Rewrite Array.prototype/statics generically: lengths are LengthOfArrayLike
longs (huge array-likes no longer busy-loop 2^31 reads), length reads
precede callable checks, mutators use throwing Set/DeletePropertyOrThrow
(push on a non-writable length is a TypeError), sort collects only
present elements with a stable merge sort (inconsistent comparators
can't crash the BCL introsort) and deletes the hole tail, flat/flatMap
follow FlattenIntoArray, from/of honor a constructor this, and at /
@@unscopables / element-wise toLocaleString are installed. ArrayCreate
paths reject lengths above 2^32-1 with RangeError.

OrdinarySet's receiver tail now follows §10.1.9.2: the receiver's own
descriptor comes from [[GetOwnProperty]] and exotic receivers get a
value-only [[DefineOwnProperty]], so writing length through a Proxy
reaches ArraySetLength instead of silently failing.

built-ins/Array 83.03% -> 98.55% (947+59T -> 86 fails); built-ins/Object
92.20% -> 94.58% as a side effect.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
Object.assign follows CopyDataProperties ([[OwnPropertyKeys]] order with
symbols, ToObject targets, getters fire, throwing writes). defineProperties
and Object.create accept any coercible props object and honor symbol-keyed,
enumerable-checked descriptors. keys/values/entries snapshot the key list
(getters that mutate the object mid-walk no longer crash the host) and read
through the vm. freeze/seal follow SetIntegrityLevel: preventExtensions
runs first and a false answer (Proxy trap) is a TypeError, attribute
changes are partial defines. Object.preventExtensions throws on a false
trap answer. getOwnPropertySymbols routes through [[OwnPropertyKeys]] so
proxy invariants fire. hasOwn coerces the object before the key.
fromEntries is strict-iterator-only with IteratorClose on abrupt entries.
Object.groupBy lands. Annex B __defineGetter__/__defineSetter__/
__lookupGetter__/__lookupSetter__ land as partial accessor defines and
trap-aware chain walks.

Primitive wrappers (Boolean/Number/BigInt/Symbol) now hold their value in
a host slot instead of a visible '__primitiveValue' own property.
Object.prototype.toLocaleString is Invoke(O, 'toString') with the original
receiver. Object.prototype.toString classifies proxied arrays via IsArray.
BigInt.prototype gets @@toStringTag. Builtins define length before name.

built-ins/Object 94.66% -> 98.34%; built-ins/Array holds at 99.54%.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
The §15.2.5 check ran only inside the local-resolution branch; hoist it and
thread detection through the parent compiler chain so nested-closure writes
resolve the same immutable binding. (An eval-context nested shape still
bypasses it — logged for follow-up.)

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
…prototype%

Non-configurable redefine validation now matches §10.1.6.3: writable may
transition true→false, a non-writable data slot pins its value by
SameValue (NaN redefines pass, -0/+0 flips reject), and non-configurable
accessor redefines require identical get/set. The partial-define path
compares values with SameValue too. %Object.prototype% is an
immutable-prototype exotic object (§10.4.7). String wrappers enumerate
out-of-range index keys numerically before 'length'.

built-ins/Object 98.34% -> 99.66%.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
Script-top capture analysis is real now: block-nested lexicals at global
scope get cells when captured, so a hoisted block function no longer
snapshots a TDZ sentinel (and closures see later writes). Globally-bound
names (vars, top-level functions and lexicals) are stripped so no cell
shadows the global binding.

Array tail: %Object.prototype.toString% is captured as an intrinsic for
the Array toString fallback, ArraySetLength's RangeError comes from the
RUNNING realm (new JsVm.ActiveOnThread), the array iterator throws
TypeError on an out-of-bounds typed array, and toLocaleString method
lookups use GetV receivers (primitives stay primitive).

Object tail: generic descriptors keep the current property kind in the
array-index partial-define merge, %GeneratorFunction% /
%AsyncGeneratorFunction% / %AsyncFunction% constructors exist with
dynamic-source compilation and prototype round-trips, generator/async
function objects inherit from their kind's prototype (toStringTag),
typed arrays on resizable buffers reject [[PreventExtensions]] (freeze
throws), and string wrappers order bag indices before 'length'.

JsObject.OwnPropertyKeys routes through the virtual Keys so host objects
that only override Keys (storage, DOM collections) surface exotic keys.

built-ins/Array 99.54% -> 100.00%; built-ins/Object 99.66% -> 100.00%.
Gates: Js.Tests 2258/0, Engine 181/0, Bindings 436/0.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
…bles, spec RegExpBuiltinExec, RegExp.escape, lookbehind/quantifier capture semantics

Regex engine (src/Starling.RegExp):
- Generated exact Unicode 17 property tables (UnicodePropertyData.g.cs):
  all Scripts/Script_Extensions/General_Category values + binaries with
  every tested alias spelling; case-sensitive Name=Value validation
- Reversed-program lookbehind: right-to-left matching, correct capture
  positions, no start-point scan; captures propagate out of positive
  lookarounds (shared numbering, group 0 preserved)
- RepeatMatcher semantics: per-iteration capture reset (ResetCaptures)
  and the empty-iteration progress guard (MarkPos/ProgressJmp) with
  nullable-body loops routed to the backtracking matcher
- Simple case folding (scf) for u/v ignore-case literal comparison
- Group names always parse escapes in Unicode mode (\u{...}, pairs)
- In-class \0<digit> rejected under u/v; huge bounded quantifiers
  compile as unbounded tails instead of exploding
- Emoji_Keycap_Sequence enumerates as class-set strings so && and --
  work; bare skin-tone modifiers no longer start RGI ZWJ units
- Binary-search char-class membership for the big property tables

JS intrinsics (RegExpCtor, StringCtor.fromCodePoint):
- RegExpBuiltinExec: observable lastIndex Get/ToLength/Set-throw order,
  spec ToString of the argument
- §22.2.4.1 constructor: IsRegExp protocol (source/flags via Get),
  plain-call short-circuit, flags override, coercion order
- RegExp.escape (§22.2.5.1) with ControlEscape/otherPunctuators/
  surrogate hex escaping
- @@match/@@matchAll/@@search/@@split/@@replace: flags-string reads,
  SpeciesConstructor per §7.3.24, observable ToString/ToLength/ToUint32
  coercions, SameValue lastIndex save/restore, throwing lastIndex Sets
- Match results: null-prototype groups object, d-flag indices array
  gets its own groups property, unclosed $< stays literal
- String.fromCodePoint encodes lone surrogates (UTF16EncodeCodePoint)

.NET regex backend: rewrite JS-divergent constructs for ECMAScript mode
(\s/\S/\W/\D classes, '.' line terminators, $ end anchor, multiline
anchors via lookarounds) and fall back to the Pike VM for in-class \S/\W/\D,
ignore-case \W, forward/in-class backreferences, and nullable loop bodies.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
…onformance-sweep

Unicode 17 property tables + RegExp spec conformance (property-escapes,
lookbehind, RepeatMatcher captures, RegExpBuiltinExec, RegExp.escape).

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
built-ins/RegExp 61.8%->98.8% and the bucket 89.83%->93.64% after the
Unicode 17 property tables + RegExp spec merge.

Claude-Session: https://claude.ai/code/session_017bx26FXvX899HzMXo9cWAs
Copilot AI review requested due to automatic review settings July 7, 2026 15:01
@codymullins
codymullins marked this pull request as draft July 7, 2026 15:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

…mance-sweep

# Conflicts:
#	.github/workflows/ci.yml
#	AGENTS.md
#	Directory.Build.props
#	Directory.Packages.props
#	README.md
#	THIRD_PARTY_NOTICES.md
#	bench/README.md
#	bench/Starling.HtmlParserBench/HtmlParserComparisonBench.cs
#	bench/Starling.HtmlParserBench/Starling.HtmlParserBench.csproj
#	bench/Starling.JsEngineBench/Scripts/README.md
#	bench/Starling.JsEngineBench/Starling.JsEngineBench.csproj
#	bench/Starling.JsEngineBench/StarlingFeatureBench.cs
#	bench/Starling.JsEngineBench/StarlingScalingBench.cs
#	bench/engine-comparison.md
#	bench/html-parser-comparison.md
#	browser-plan/09_JS_ENGINE.md
#	browser-plan/anglesharp-backend-plan.md
#	src/Starling.AppHost/AppHost.cs
#	src/Starling.Bindings/PerformanceBinding.cs
#	src/Starling.Dom/Starling.Dom.csproj
#	src/Starling.Engine/Engine.cs
#	src/Starling.Engine/JsEngineSelector.cs
#	src/Starling.Gui/MainWindow.cs
#	src/Starling.Gui/Starling.Gui.csproj
#	src/Starling.Headless/Starling.Headless.csproj
#	src/Starling.Js.Hosting/Starling.Js.Hosting.csproj
#	src/Starling.Js/Bytecode/Disassembler.cs
#	src/Starling.Js/Bytecode/JsCompiler.Classes.cs
#	src/Starling.Js/Bytecode/JsCompiler.Modules.cs
#	src/Starling.Js/Bytecode/JsCompiler.cs
#	src/Starling.Js/Bytecode/Opcode.cs
#	src/Starling.Js/Intrinsics/ArrayBufferCtor.cs
#	src/Starling.Js/Intrinsics/ArrayCtor.cs
#	src/Starling.Js/Intrinsics/BigIntCtor.cs
#	src/Starling.Js/Intrinsics/BooleanCtor.cs
#	src/Starling.Js/Intrinsics/DataViewCtor.cs
#	src/Starling.Js/Intrinsics/DateCtor.cs
#	src/Starling.Js/Intrinsics/ErrorCtor.cs
#	src/Starling.Js/Intrinsics/FunctionCtor.cs
#	src/Starling.Js/Intrinsics/GeneratorIntrinsics.cs
#	src/Starling.Js/Intrinsics/IntlObj.cs
#	src/Starling.Js/Intrinsics/IntrinsicHelpers.cs
#	src/Starling.Js/Intrinsics/IteratorIntrinsics.cs
#	src/Starling.Js/Intrinsics/NumberCtor.cs
#	src/Starling.Js/Intrinsics/ObjectCtor.cs
#	src/Starling.Js/Intrinsics/PromiseCtor.cs
#	src/Starling.Js/Intrinsics/ReflectObj.cs
#	src/Starling.Js/Intrinsics/RegExpCtor.cs
#	src/Starling.Js/Intrinsics/RegExpStringIterator.cs
#	src/Starling.Js/Intrinsics/StringCtor.cs
#	src/Starling.Js/Intrinsics/SymbolCtor.cs
#	src/Starling.Js/Intrinsics/TypedArrayCtors.cs
#	src/Starling.Js/Lex/JsLexer.cs
#	src/Starling.Js/Modules/ModuleLoader.cs
#	src/Starling.Js/Parse/JsParser.Modules.cs
#	src/Starling.Js/Parse/JsParser.Patterns.cs
#	src/Starling.Js/Parse/JsParser.Scoping.cs
#	src/Starling.Js/Parse/JsParser.Strict.cs
#	src/Starling.Js/Parse/JsParser.cs
#	src/Starling.Js/Runtime/AbstractOperations.cs
#	src/Starling.Js/Runtime/JsArray.cs
#	src/Starling.Js/Runtime/JsArrayBuffer.cs
#	src/Starling.Js/Runtime/JsFunction.cs
#	src/Starling.Js/Runtime/JsModuleNamespace.cs
#	src/Starling.Js/Runtime/JsObject.cs
#	src/Starling.Js/Runtime/JsPromise.cs
#	src/Starling.Js/Runtime/JsRealm.cs
#	src/Starling.Js/Runtime/JsRegExp.cs
#	src/Starling.Js/Runtime/JsRuntime.cs
#	src/Starling.Js/Runtime/JsStringObject.cs
#	src/Starling.Js/Runtime/JsTypedArray.cs
#	src/Starling.Js/Runtime/JsVm.cs
#	src/Starling.Js/Runtime/Regex/DotNetRegexMatcher.cs
#	src/Starling.Js/Runtime/Regex/IRegexMatcher.cs
#	src/Starling.RegExp/RegexCharClass.cs
#	src/Starling.RegExp/RegexCompiler.cs
#	src/Starling.RegExp/RegexInstruction.cs
#	src/Starling.RegExp/RegexParser.cs
#	src/Starling.RegExp/RegexPikeVm.cs
#	src/Starling.RegExp/RegexProgram.cs
#	tasks/wpt/PLAN-98.md
#	tests/Starling.BindingSurface.Tests/IdlSurfaceManifestTests.cs
#	tests/Starling.BindingSurface.Tests/Starling.BindingSurface.Tests.csproj
#	tests/Starling.Bindings.Tests/README.md
#	tests/Starling.Engine.Tests/GithubRuntimeTests.cs
#	tests/Starling.Gui.Headless.Tests/ProgrammaticInputTests.cs
#	tests/Starling.Gui.Headless.Tests/SidebarBuildInfoTests.cs
#	tests/Starling.Gui.Headless.Tests/TodoInteractionTests.cs
#	tests/Starling.Js.Test262.Tests/README.md
#	tests/Starling.Js.Test262.Tests/Starling.Js.Test262.Tests.csproj
#	tests/Starling.Js.Test262.Tests/Test262Corpus.cs
#	tests/Starling.Js.Test262.Tests/Test262Runner.cs
#	tests/Starling.Js.Test262.Tests/Test262Tests.cs
#	tests/Starling.Js.Tests/Intrinsics/NumberTests.cs
#	tests/Starling.Js.Tests/Parse/JsParserModuleTests.cs
#	tests/Starling.Js.Tests/Runtime/JsErrorPositionTests.cs
#	tests/Starling.Js.Tests/Runtime/JsOperatorsGapTests.cs
#	tests/Starling.Js.Tests/Runtime/JsVmTests.cs
#	tests/Starling.RegExp.Tests/IdentifierPropertyTests.cs
#	tests/Starling.Wpt.Tests/README.md
#	tools/Starling.IdlGen/README.md
@codymullins
codymullins marked this pull request as ready for review July 7, 2026 15:17
@codymullins
codymullins merged commit 546a493 into main Jul 7, 2026
4 of 5 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
@codymullins
codymullins deleted the feat/js-engine-conformance-sweep branch July 7, 2026 15:25
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants