Skip to content

PHP: ship intl as an optional ICU-backed side module#839

Merged
brandonpayton merged 12 commits into
integration/kd-6nz-php-extensionsfrom
explore-intl-side-module
Jul 12, 2026
Merged

PHP: ship intl as an optional ICU-backed side module#839
brandonpayton merged 12 commits into
integration/kd-6nz-php-extensionsfrom
explore-intl-side-module

Conversation

@mho22

@mho22 mho22 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Purpose

Ship PHP's intl extension as a runtime-optional C++ side module without
adding ICU's roughly 30 MB common-data payload to every base PHP launch. The
extension, its matching icu.dat, the host dynamic linker, fork replay, and
the package resolver must follow the same normal platform path on Node.js and
browser hosts.

This PR is the third constituent of stacked PHP extension Batch 3 (#894). It
targets integration/kd-6nz-php-extensions; merging this PR incorporates its
reviewed commits into that still-open aggregate and does not merge anything to
main.

What changed

  • Added reproducible ICU 74.2 and PIC libc++/libc++abi package outputs. Their
    archives, headers, pkg-config metadata, and ICU common data are declared,
    relocation-safe, producer-path-free, and validated through the package
    resolver.
  • Built all 70 PHP intl objects into an opt-in intl.so at ABI 19. Base
    php.wasm remains intl/ICU-free; icu.dat is a declared non-Wasm runtime
    file installed at /usr/lib/php/icu.dat through the normal package/VFS
    closure.
  • Made C++ side-module linking fail closed: weak self-import trampolines are
    allowed only for exports proven to be module-defined, while genuinely
    missing imports fail eagerly.
  • Made C++ exception tags process-owned and pointer-width-correct, and
    preserved validated side-module TLS state across fork() replay. Dynamic
    loading from pthread workers remains an explicit unsupported boundary rather
    than racing process-owned replay state.
  • Added a program runtime_files package contract with cache, archive,
    materialization, path-safety, and structured-metadata coverage. Related
    executable, side-module, and runtime-data outputs are selected atomically
    from one complete local, fetched, or installed-package tier.
  • Fixed shared VFS image staging so positive partial writes continue to
    completion and zero/negative/invalid progress fails loudly. Node's intl
    fixture rebases to a 256 MiB filesystem before adding ICU data; Node and
    browser fixtures verify the staged runtime bytes.

Review repairs

The original two-commit branch was rebuilt as a 12-commit, purpose-led serial
on the exact Batch 3 tip while preserving the original author's two commits
and attribution. The review removed masked build failures, incomplete intl
object discovery, checkout-local outputs, cache scanning, host-only ICU path
overrides, permissive missing-import stubs, per-module exception tags,
unrestored TLS state, unsafe package/archive/output paths, mixed-provenance
runtime closures, and silent VFS truncation.

Validation

  • Two clean ICU revision-6 builds and two clean libc++ revision-7 builds from
    distinct roots produced byte-identical declared outputs; producer-path scans
    were clean, and relocated ICU pkg-config metadata compiled and linked a
    consumer.
  • A fresh exact-tree PHP build produced all nine declared files byte-for-byte,
    linked all 70 intl objects, resolved the final import closure, retained ABI
    19, and matched the installed local package mirror.
  • Focused host dylink/resolver/fork coverage passed 52/52 before the final
    staging repair. Package manifest coverage passed 116 tests; focused
    resolver/runtime metadata passed 14; VFS write-helper coverage passed 5/5.
    The exact integrated candidate's focused closure/VFS/dylink/compiled-fork
    and PHP hello/curl set passed 90/90.
  • On the exact integrated candidate, PHP intl passed 5/5 on Node.js, including
    base-PHP exclusion, explicit loading, Locale, Collator, and parent/child
    fork replay. The complete real-Chromium PHP spec passed 2/2: the existing
    inline/file/session/SQLite/fileinfo/XML/OpenSSL/ZIP/curl scenarios and the
    new intl/ICU fork-replay scenario.
  • Kernel unit tests passed 1,135/1,135. The ABI snapshot/version check passed;
    this PR does not add another ABI epoch beyond the Batch 2 ABI-19 base.
  • A freshly built musl plus the candidate's ABI-19 dash fixture passed the full
    libc gate: 303 passed, 20 documented XFAIL, one known FLAKE-PASS, and zero
    failures, build failures, timeouts, or unexpected passes.
  • Host declarations and the PHP browser Vite production bundle passed.
  • A broad host run reached 1,184 passes but was not green in the isolated
    checkout: seven failures disappeared when the verified ABI-19 test mirrors
    were supplied, and the remaining two rootfs/lazy-exec fixture failures
    reproduced identically on exact base ec8b9e318 with the same fixtures.

The final aggregate Batch 3 gate and manual ./run.sh browser verification
remain aggregate work and are not claimed by this constituent review.

Boundaries

  • Reproducibility builds ran on macOS/aarch64; the Linux host-tool repair still
    relies on the package CI path for Linux execution evidence.
  • The i64 tag/TLS cases validate 64-bit pointer encoding but are not a full
    memory64 end-to-end runtime run.
  • Pthread-side dlopen remains truthfully unsupported by this process-owned
    model.
  • A separate TeX Live-only direct VFS writer is outside this PHP landing and is
    not claimed fixed.
  • Performance was not measured.

Nothing in this PR or Batch 3 is authorized to merge to main without
Brandon's explicit approval.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Phase B-1 matrix build status — pr-839-staging

ABI v19. 71 built, 0 failed, 71 total.

Package Arch Status Sha
icu wasm32 built 89ba9e6e
libcurl wasm32 built 608a2cd0
libcxx wasm32 built b4e54a56
libcxx wasm64 built 3787dc5e
libiconv wasm32 built 99619c53
libpng wasm32 built c11fe7a3
libxml2 wasm32 built d37e55b0
libzip wasm32 built 1548aa2c
openssl wasm32 built 3bef3b47
openssl wasm64 built dd55f594
sqlite wasm32 built acaab402
sqlite wasm64 built d8c4bd83
zlib wasm32 built c2644c11
zlib wasm64 built 0a92e1fd
bc wasm32 built a59b6b69
bzip2 wasm32 built 949ff502
coreutils wasm32 built 39a3cc51
curl wasm32 built e8f5ddda
dash wasm32 built 3d50460e
diffutils wasm32 built 85b92dde
dinit wasm32 built 221fe8bc
fbdoom wasm32 built 47c37ec5
file wasm32 built 15fcd9b9
findutils wasm32 built b9c0c6e3
gawk wasm32 built f6930ef0
git wasm32 built 0012c03b
grep wasm32 built 93876756
gzip wasm32 built 6599b79a
hello wasm32 built 65f94017
kandelo-sdk wasm32 built eebd138c
kernel wasm32 built b697326f
less wasm32 built e5fd4d44
lsof wasm32 built c6f6fc76
m4 wasm32 built 93e9875c
make wasm32 built 49c532f3
mariadb wasm32 built bd8b7ee5
mariadb wasm64 built 7b3053c7
modeset wasm32 built 80aa3f6a
msmtpd wasm32 built 5bca0f0b
nano wasm32 built ad03e346
ncurses wasm32 built e67397eb
netcat wasm32 built fb699a1f
nginx wasm32 built 2c296da1
php wasm32 built 8e2b6815
posix-utils-lite wasm32 built 3830adcc
ruby wasm32 built bf41a46b
sed wasm32 built 43edede7
spidermonkey wasm32 built 608a829f
tar wasm32 built 2d39220c
tcl wasm32 built d0336feb
unzip wasm32 built 990b360f
userspace wasm32 built 1f3f8b39
vim wasm32 built 678b22ef
wget wasm32 built f0503b24
xz wasm32 built 7ef7ea7e
zip wasm32 built 55ee3121
zstd wasm32 built db024284
bash wasm32 built e5c92cbf
mariadb-test wasm32 built cbb09e63
mariadb-vfs wasm32 built bfeb6b22
mariadb-vfs wasm64 built a351a119
nethack wasm32 built 5df5f220
node wasm32 built 6e2568ea
spidermonkey-node wasm32 built e9571a92
vim-browser-bundle wasm32 built f74d5f17
nethack-browser-bundle wasm32 built 86775d63
rootfs wasm32 built d7a30a3f
shell wasm32 built aa96fffe
lamp wasm32 built aadd9846
node-vfs wasm32 built ff759795
wordpress wasm32 built e676ab95

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

mho22 added a commit that referenced this pull request Jul 2, 2026
## Why

- The ICU package's Stage-1 HOST build compiles the native data tools
  (genrb/pkgdata/icupkg/…) with the dev-shell clang++. On the Nix Linux
  CI runner those tools link the GNU C++/GCC runtime dynamically, but
  `libstdc++.so.6` is not on the runner's loader path, so they abort at
  exec with "error while loading shared libraries: libstdc++.so.6:
  cannot open shared object file".
- Stage 2's `make` invokes icupkg/pkgdata to package the ICU common
  data, so an unrunnable host tool fails the ICU build itself — which
  reddened `lib-matrix-build (icu, wasm32)` and every job that depends
  on icu (`matrix-build (wasm32, php)`, `lamp`, `wordpress`) on PR #839.
- macOS did not surface this: local clang links a self-contained libc++,
  so a macOS from-source build (or a reused macOS cache) never exercises
  the Linux host-tool loader linkage.

## What

- **packages/registry/icu/build-icu.sh** — on Linux, pass
  `-static-libstdc++ -static-libgcc` as the Stage-1 host LDFLAGS so the
  C++/GCC runtime is folded into each data tool and no runtime `.so` is
  needed. The flags are Linux-guarded because macOS clang links libc++
  and rejects them; `runConfigureICU` re-exports the pre-set LDFLAGS to
  configure, so setting them here reaches the host tool link.
- **packages/registry/icu/build.toml** — bump revision 3 → 4 so CI
  rebuilds and re-stages icu from source.

## Validation

- `bash -n packages/registry/icu/build-icu.sh` passes.
- Linux from-source validation is deferred to CI (matrix-build rebuilds
  icu from source on the Nix Linux runner); it was not run locally
  because this Mac cannot reproduce the Linux host-tool linkage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mho22 mho22 marked this pull request as ready for review July 2, 2026 21:57
mho22 and others added 12 commits July 12, 2026 05:05
Introduce the initial ICU and PIC libc++ package outputs, C++ weak-self-import coverage, and the PHP intl side-module link path while keeping ICU data outside the base PHP executable. Follow-up commits in this serial reconcile the ABI-19 build, fail-closed linker behavior, reproducibility, runtime closure, fork replay, and host/browser validation.

This commit intentionally makes no standalone completion or validation claim.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ICU's native Stage-1 data tools must execute during the cross build. On the Nix Linux runner, link their libstdc++ and libgcc runtimes statically so Stage 2 does not depend on loader paths for host runtime shared libraries; keep the flags Linux-only because the macOS toolchain rejects them.

Linux execution evidence remains the responsibility of the later package rebuild/CI validation in this serial.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ICU previously left its common data and pkg-config files outside the declared library output contract, embedded the resolver temporary prefix in libicuuc.a, and relied on undeclared native build tools. The libcxx revision provenance also pointed at a fork commit that did not contain the published PIC recipe.

Add a backward-compatible outputs.files contract with parse, cache-key, missing-output, and accepted-output coverage. Declare ICU data/pkg-config outputs and host probes, build from a clean verified source tree, and compile the fallback data path as /usr/lib/php so clean archives are stable. Keep existing package keys unchanged when files is absent, and mark the ICU/libcxx producer revisions as unpublished Automattic work.

Validation: the four focused runtime-file xtask tests passed; build-deps check reported 10 consistent tools across four consumers; the six ICU tools matched the dev-shell versions. Two clean ICU rev5 builds produced byte-identical four archives, 197 headers, and icu.dat; resolver validation accepted all declared outputs and rewrote the three .pc prefixes to the canonical cache path. Two clean libcxx rev6 builds in distinct cache locations produced byte-identical four archives and 1,680 headers.

Scope limits: the clean producer comparisons were run on macOS/aarch64 in the repository dev shell. The repaired Linux host-tool build is not re-claimed here beyond the original PR CI evidence, and performance was not measured.
The original intl linker change synthesized delayed stubs for every missing
env function. That can hide real ABI gaps on unexecuted paths, while name-only
self-import detection can also mistake an imported-function re-export for a
definition and recurse through its own trampoline.

Preserve Batch 2 transactional and fork-replay behavior. Supply a trampoline
only when the export section proves the same-name function index is
module-defined, keep main-module interposition first, and leave every other
unresolved import as an eager instantiation failure. At this point in the
serial, provide the wasm-exceptions C++ tag required by the intl side module.

Validation: host type generation passed; host/test/dylink.test.ts passed 37/37,
including genuine self-definition, missing import, imported-function re-export,
C++ tag, rollback, and fork-replay cases. The built ABI-19 intl.so had zero
unresolved env/GOT symbols under this rule, and all four real PHP intl
Node/kernel cases passed after the stricter index check.

Scope limits: this intermediate commit creates the C++ tag at the side-module
linker boundary. The later process-state replay commit in this same landing
serial deliberately supersedes that ownership model with one process-owned tag
and adds cross-side identity coverage. Browser behavior is validated later;
performance was not measured.
The original recipe was based on an older PHP tree, linked into the package source directory, and masked make failures with `|| true`. On the reconciled ABI-19 recipe that could publish a partial module: the first strict attempt exposed that only one newline-delimited object had been consumed.

Preserve PHP 8.3.15, GNU libiconv, the Batch 2 extensions, forced shared-libc symbols, and fork instrumentation. Add ICU/libcxx as direct resolver dependencies, build every generated shared_objects_intl target without invoking the unsupported libtool link, then link all PIC objects and declared ICU/libcxx archives into OUT_DIR/intl.so. Reserve PHP revision 13 for the serial zip(11), curl(12), intl(13) Batch 3 order. Make the Node data fixture prefer an explicit resolved ICU file and restrict its cache fallback to the current ICU version/revision.

Validation: a clean ABI-19 source resolve built 70 intl PIC objects and a 5,860,812-byte side module; php, php-fpm, opcache, phar, and zend_test were byte-identical across two clean temporary builds. The link consumed both libcxx PIC archives, and static inspection found all 3,040 intl imports resolved by PHP, the side module, or linker-owned objects. The focused Node/kernel suite passed 4/4: base PHP excludes intl, explicit loading succeeds, Locale reads icu.dat, and Collator applies locale rules.

Scope limits: this evidence is for #839 on the ABI-19 Batch 2 base. The final #647 -> #648 -> #839 serial tree and real browser path are validated separately before recommendation. This adds no kernel/process ABI shape, so no ABI bump or snapshot change is included; performance was not measured.
Instantiation alone did not prove that the host supplied the C++ exception tag with the expected payload signature, and synthetic fork-state tests did not prove that ICU common-data state survives replay of a real PHP side module.

Make the WAT fixture throw and catch an i32 payload through env.__cpp_exception. Add a PHP case that loads intl and icu.dat before pcntl_fork, calls an ICU-backed Locale API in the replayed child, calls Locale and Collator again in the parent, and verifies wait/exit status.

Validation: with the ABI-19 sysroot present, host/test/dylink.test.ts passed 37/37. packages/registry/php/test/php-intl.test.ts passed 5/5 against the resolver-built PHP/intl/ICU artifacts; the fork-replay case completed in both branches and reaped a zero-status child.

Scope limits: this commit records Node/kernel evidence. The equivalent Chromium path is added and run only on the exact landed #647 -> #648 -> #839 serial tree; it is not claimed here.
libc++abi archive members retained the absolute checkout path because both PIC
and non-PIC CMake builds lacked compiler prefix maps. ICU metadata was likewise
rewritten from the resolver temporary prefix to one producer cache path, so
fetched or moved archives still pointed back to the build machine.

Map the entire worktree, including assembled LLVM sources, both build variants,
the smoke source, and sysroots, to /usr/src/kandelo in libcxx compiler paths and
advance it to revision 7. Rewrite ICU pkg-config prefix fields to
${pcfiledir}/../.. and advance it to revision 6, keeping headers and libraries
relative to each extracted package tree.

Validation: two libcxx revision-7 builds from distinct checkout/output roots
produced byte-identical PIC and non-PIC archives, and producer-path scans were
clean. Two ICU revision-6 builds from distinct roots produced byte-identical
declared archives and icu.dat. After moving an ICU prefix, pkg-config resolved
the relocated include/library paths and a consumer compile/link succeeded.

Scope limits: the reproducibility and relocation checks ran on macOS/aarch64 in
the repository dev shell. Linux host-tool execution, the final PHP package,
Node/browser runtime behavior, and aggregate gates are validated by later
commits; performance was not measured.
Problem: program manifests could describe only executable outputs, leaving
required data blobs outside cache keys, archives, local mirrors,
fetched-artifact validation, and VFS installation metadata. Existing archive
collection could also follow external/cyclic symlinks, and unvalidated
package/output paths could escape or collide in the mirror.

Design: add a program-only runtime_files contract with normalized artifact and
absolute guest paths, mode metadata, length-framed cache-key fields, exact
nested-artifact lookup, collision checks, and regular-file validation on
build/cache/fetch paths. Archive staging dereferences only contained symlinks
and rejects escapes, cycles, specials, empty declared trees, and incomplete
closures. Local and fetched materialization use one package-scoped mirror
layout and a structured repository-build query.

Compatibility: manifests with no runtime_files retain their historical cache
keys. The previously additive library outputs.files section is versioned and
length-framed now, before its first published consumer, while older output
sections retain their encoding for cache compatibility.

Focused evidence: pkg_manifest tests passed 112/112; archive_stage tests passed
8/8; runtime-filtered tests passed 19/19; delimiter framing, fetched/source
mirror parity, incomplete-archive fallback/fetch-only failure, nested paths,
collisions, and symlink safety are covered. The full xtask run reached 336
passed and the same 7 pre-existing malformed-Wasm/filename fixture failures
observed on the base.

Limits: shell helper syntax and host resolver precedence passed, but the full
aggregate package build, published-archive round trip, full host/libc gate, and
browser gate have not run. The metadata helper is a repository
build/materialization API, not a guest or runtime host API.
Problem: replay instantiated a fresh side module without restoring its live TLS-base global, and each module could receive a different C++ exception-tag identity. Pthread workers also have separate instances, tables, and tags, so allowing them to race process dlopen or inherit the process archive could corrupt replay.

Design: keep process-owned pointer-width-correct exception/longjmp tags, archive and validate each module's exact positive TLS base, restore only that global over the fork-copied live TLS bytes, and arbitrate process dlopen against pthread fork with a host-private atomic record. Pthread dlopen now fails through dlerror; pthread fork returns ENOTSUP once the process archive is nonempty.

Focused evidence: host declaration generation passed; dylink plus binary-resolver Vitest ran 33 tests passed with 16 compiler-dependent skips; the compiled fork/dlopen replay file ran 3/3, including mutated C++ thread_local state plus throw/catch and the real pthread boundary.

Limits: the i64 cases exercise 64-bit pointer encoding only, not a memory64 end-to-end runtime. The full host, libc, ABI-classifier, and real browser aggregate gates have not yet run. The archive-size change is intended to remain host-private and transient; the ABI check is still required before landing.
Problem: intl.so needs an ICU data blob at runtime, but the original branch relied on cache scanning and host-only path overrides. That made local, fetched, Node, VFS, and browser executions observe different closures and allowed a present extension to run without its matching data.

Design: PHP copies the exact resolver-selected ICU data into its program archive as a declared runtime file at /usr/lib/php/icu.dat. Repository image/test builders query structured package metadata for the guest path and mode, fail when intl exists without its closure, and exercise the same resolver-selected bytes on Node and browser. The PHP build exports __tls_base for fork-safe side-module replay and tracks the loader source as a cache-key input.

Focused evidence: runtime metadata resolved to php/icu.dat, /usr/lib/php/icu.dat, mode 0644; the local mirror is byte-identical to both reproducible ICU rev6 outputs; shell syntax, host declarations, and the PHP browser Vite bundle passed. The dev middleware served all 30,782,896 ICU bytes and returned 400 for dot, encoded-dot, and malformed-percent artifact paths. The base-PHP focused case passed against an ABI-19 base-manifest image.

Limits: the four intl runtime cases intentionally remain unvalidated on the stale cached intl.so, which the repaired host correctly rejects because it does not export mutable __tls_base. A fresh PHP rebuild, artifact inspection, Node intl/fork tests, real Chromium run, and full aggregate gates are still required. The focused base-manifest rootfs omitted unrelated lazy package entries and is not canonical browser evidence.
Reject unsafe manifest and dependency path components before registry, cache, and archive interpolation, and make package.toml's top-level schema fail closed so a misspelled runtime_files declaration cannot disappear silently.

Expose every program output and runtime file in structured runtime metadata. Resolve the complete set from one local, fetched, or installed-package root, allowing whole-tier fallback while refusing mixed partial closures. Route the PHP Node, VFS, and browser consumers through that selected closure.

Validation:
- pkg_manifest focused suite: 116 passed
- runtime metadata focused test: 1 passed
- binary resolver/runtime metadata Vitest: 14 passed
- live build-deps manifest check: passed
- host declaration build: passed
- PHP browser Vite production build: passed with ABI-19 kernel/rootfs fixtures
- ABI snapshot/version check: passed
- full xtask suite: 342 passed; the same 7 pre-existing archive/program fixture failures remain

The full host, libc, PHPT, and manual browser suites were not run for this focused repair. Performance was not measured.
Make the shared VFS image writer honor positive partial writes until all bytes are staged, reject zero/negative/invalid progress, and close its descriptor on every exit path. Route the browser-demo wrappers through that shared implementation.

Give the Node PHP intl fixture a 256 MiB rebased filesystem before adding the 30.8 MB ICU data file and verify the staged bytes by size and SHA-256. Preserve the browser fixture's proven 256 MiB growable restore and byte-check its injected side-module/runtime fixtures without re-reading the large PHP executable on every invocation.

Validation:
- focused VFS image-helper tests: 5 passed
- exact ABI-19 PHP intl Node suite: 5 passed, including ICU data and fork replay
- exact ABI-19 Chromium intl/fork case: 1 passed
- host declaration build: passed
- PHP browser Vite production build: passed

The full browser app typecheck was attempted and remains blocked by pre-existing unrelated type errors. Its production build was attempted and remains blocked by absent coreutils/node/nc binary assets in this isolated worktree. The full host, libc, and manual browser suites were not run. Performance was not measured.
@brandonpayton brandonpayton force-pushed the explore-intl-side-module branch from 9aaf2aa to 5ec4d89 Compare July 12, 2026 09:56
@brandonpayton brandonpayton changed the title Load PHP intl as a runtime-optional C++ side module PHP: ship intl as an optional ICU-backed side module Jul 12, 2026
@brandonpayton brandonpayton changed the base branch from main to integration/kd-6nz-php-extensions July 12, 2026 09:56
@brandonpayton brandonpayton merged commit e6d080f into integration/kd-6nz-php-extensions Jul 12, 2026
2 checks passed
brandonpayton added a commit that referenced this pull request Jul 12, 2026
The original recipe was based on an older PHP tree, linked into the package source directory, and masked make failures with `|| true`. On the reconciled ABI-19 recipe that could publish a partial module: the first strict attempt exposed that only one newline-delimited object had been consumed.

Preserve PHP 8.3.15, GNU libiconv, the Batch 2 extensions, forced shared-libc symbols, and fork instrumentation. Add ICU/libcxx as direct resolver dependencies, build every generated shared_objects_intl target without invoking the unsupported libtool link, then link all PIC objects and declared ICU/libcxx archives into OUT_DIR/intl.so. Reserve PHP revision 13 for the serial zip(11), curl(12), intl(13) Batch 3 order. Make the Node data fixture prefer an explicit resolved ICU file and restrict its cache fallback to the current ICU version/revision.

Validation: a clean ABI-19 source resolve built 70 intl PIC objects and a 5,860,812-byte side module; php, php-fpm, opcache, phar, and zend_test were byte-identical across two clean temporary builds. The link consumed both libcxx PIC archives, and static inspection found all 3,040 intl imports resolved by PHP, the side module, or linker-owned objects. The focused Node/kernel suite passed 4/4: base PHP excludes intl, explicit loading succeeds, Locale reads icu.dat, and Collator applies locale rules.

Scope limits: this evidence is for #839 on the ABI-19 Batch 2 base. The final #647 -> #648 -> #839 serial tree and real browser path are validated separately before recommendation. This adds no kernel/process ABI shape, so no ABI bump or snapshot change is included; performance was not measured.
brandonpayton added a commit that referenced this pull request Jul 12, 2026
Instantiation alone did not prove that the host supplied the C++ exception tag with the expected payload signature, and synthetic fork-state tests did not prove that ICU common-data state survives replay of a real PHP side module.

Make the WAT fixture throw and catch an i32 payload through env.__cpp_exception. Add a PHP case that loads intl and icu.dat before pcntl_fork, calls an ICU-backed Locale API in the replayed child, calls Locale and Collator again in the parent, and verifies wait/exit status.

Validation: with the ABI-19 sysroot present, host/test/dylink.test.ts passed 37/37. packages/registry/php/test/php-intl.test.ts passed 5/5 against the resolver-built PHP/intl/ICU artifacts; the fork-replay case completed in both branches and reaped a zero-status child.

Scope limits: this commit records Node/kernel evidence. The equivalent Chromium path is added and run only on the exact landed #647 -> #648 -> #839 serial tree; it is not claimed here.
@brandonpayton brandonpayton deleted the explore-intl-side-module branch July 12, 2026 09:56
brandonpayton added a commit that referenced this pull request Jul 12, 2026
The #839 side-module repair gave each pthread fork exclusive ownership of the process dlopen lock. In the existing two-thread, 16-round concurrent-fork fixture, one thread therefore received ENOTSUP and left its peer blocked at the next barrier.

Encode the unchanged host-private slot as an exclusive negative dlopen writer or a positive pthread-fork reader count. Concurrent empty-archive pthread forks can now snapshot in parallel, while dlopen remains excluded until every parent completes unwind, SYS_FORK, and rewind. Keep child reset, archive rejection, error releases, and ownership diagnostics explicit.

Validation: the exact pre-#839 base passed the concurrent fixture while the prior aggregate timed out. On this repair, fork-from-thread plus dlopen/fork replay passed 5/5. Full host Vitest reached 1,251 passes, 2 expected failures, and 95 skips; its only two failures are the lazy-rootfs memory64/posix-utils fixtures reproduced on the exact base. Host declarations and the ABI consistency check passed. Browser behavior was not rerun for this host-only arbitration follow-up, and performance was not measured.
brandonpayton pushed a commit that referenced this pull request Jul 13, 2026
PHP intl needs reproducible ICU and libc++ inputs plus fail-closed C++ side-module linking before the host/guest replay contract can advance safely.

Build PIC ICU/libc++ outputs without producer paths, declare non-Wasm runtime files explicitly, complete the initial intl.so link path, reject unresolved C++ imports, and cover exceptions and fork replay. This compatible setup intentionally precedes ABI 36; the next commit owns the incompatible replay-memory and pthread-fork contract, while the following PHP commit owns final ICU runtime-data materialization.

Source-era package and focused runtime evidence is retained as review provenance, but the final ABI-38 artifact rebuild and complete Node/browser gate apply at the super-batch tip.

Source-PR: #839
Source-PR: #894
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
brandonpayton added a commit that referenced this pull request Jul 13, 2026
Side-module replay-control memory and pthread fork arbitration change the host/guest continuation contract and unsupported-operation behavior. Advance ABI 35 to 36 and regenerate ABI evidence atomically.

Source-PR: #839

Source-PR: #894
brandonpayton added a commit that referenced this pull request Jul 13, 2026
intl.so needs the exact resolver-selected ICU data blob at runtime. Cache scanning and host-only path overrides made local, fetched, Node, VFS, and browser executions observe different closures and could leave a present extension without matching data.

Copy the selected ICU data into the PHP program archive as declared /usr/lib/php/icu.dat state, keep general runtime files in one artifact tier, query structured package metadata from image/test builders, and reject incomplete VFS staging. The PHP consumers now follow the ABI-36 side-module replay contract established by the preceding epoch.

Source-era evidence covers reproducible ICU/libc++ outputs, runtime metadata and byte identity, complete PHP artifacts, focused Node/browser intl behavior, and fail-closed stale-artifact handling. Those results are provenance rather than a claim about an ABI-19 artifact; final ABI-38 archives and broad gates remain the super-batch tip's responsibility.

Pthread-side dynamic loading remains truthfully unsupported.

Source-PR: #839
Source-PR: #894
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants