Repository navigation
Fix pi's small compile gaps - #1348
Merged
Merged
Conversation
pi's CLI setup replaces process.emitWarning to silence warnings. The assignment now stores the replacement: later process.emitWarning(...) calls apply it to their arguments, and runtime-built warnings (Node's own modules call process.emitWarning) reach it as (message, type[, code]) instead of the report and the 'warning' listeners. process.title writes already lowered; the corpus program pins both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under @types/node, process.getBuiltinModule("node:os") types as the
module namespace, which lowered as an unchecked dynamic value and failed.
A string-literal id naming a supported builtin now lowers to the namespace
value an `import * as` binding has, including pi's cast-and-probe spelling
`(process as P).getBuiltinModule?.("node:os") ?? null`; a nullable
namespace binding narrowed present keys the same member tables, and a
namespace is truthy. process.features read as a whole value (`"sea" in
process.features`) is an object holding none of the flags, matching the
existing member reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pi-ai consumes every provider stream with `for await` over an EventStream whose `async *[Symbol.asyncIterator](): AsyncIterator<T>` is an async generator method; that was SC1070. The loop now opens the iterator with the method call and drives the returned async generator like any other (break and throw close it). An `async *` declaration annotated with the protocol interfaces (AsyncIterator, AsyncIterableIterator, AsyncIterable) maps like AsyncGenerator. Boxing a class instance whose fields hold a promise now links the dynamic async unit (the materializer boxes the promise), which EventStream's own `this.isComplete(event)` calls need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Weak collections already lower to checked-dynamic storage as locals, but a class field of those types was fenced as a dynamic-typed field (pi's AgentSession keys entry ids by message in a WeakMap field). Declared, optional, and constructor-assigned fields of those types now store the native collection like the locals do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1309 let a checker-untyped receiver call a member named like a String.prototype-only method (`bold`, `italics`, ...), refusing only a string receiver at run time. TypeScript importing chalk's JavaScript source types the default export as a plain function, so `chalk.bold` is an `any` member on a typed receiver and still fenced; pi calls it in nine places. A checker-untyped member now takes the same path. The chalk 5 fixture pins root, aliased, and Chalk-instance `bold` calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node checks its builtins before node_modules for a bare core-module name, so an installed npm package named string_decoder (a transitive dependency of pi) never answers `import ... from "string_decoder"`. scriptc attributed the import to the package and refused it. The npm resolution now answers null for bare builtin names (prefix-only builtins such as node:test keep their bare names for packages), for ES imports and CommonJS requires alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three gaps on pi-tui's terminal start path:
- `if (process.stdin.setRawMode)` read the method as a value (SC2020).
The read now answers the stream object's member: the method on a TTY,
undefined otherwise, as on Node.
- process.stdin.setEncoding had no runtime method, and a string 'data'
listener was fenced. stdin now decodes with the shared StringDecoder
once per read (split multibyte sequences stay whole, end() flushes the
tail before 'end'), string listeners receive the text, and dynamic
listeners receive what Node delivers. A string listener with no
encoding set throws the setEncoding-state TypeError, as child streams
do.
- process.stdout.on("resize", cb) (once/off/removeListener, stderr too)
registers on the stream object, which listens for SIGWINCH and emits
when a TTY's size changed; the listener never keeps the loop alive.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
quickjs-wasi's JSException constructor runs `delete this.stack` so V8's own stack property stops shadowing the class's stack accessor; scriptc fenced the delete, so every guest exception reaching the host threw the fence error. Compiled errors keep the trace internally and class dispatch already reads the accessor, so the delete now evaluates its receiver and nothing else, for Error subclasses whose chain declares a stack getter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # packages/runtime/src/scr_async.h
The process.emitWarning replacement and the stdin encoding, string data listener and stdio 'resize' support changed runtime functions every program with the dyn-async or events unit links (the shared warning paths, stdin delivery, the process stream object) and grew their thread-local state. That code now compiles only into new runtime-pack variants, SCR_WARNING_REPLACE of scr_async_dyn.c and SCR_STDIO_EXTENDED of scr_events.c, which the link selects when the program assigns process.emitWarning or reaches setEncoding, a string 'data' listener or 'resize'. The default objects are byte-identical to before, so other executables do not change. The development and sanitizer builds pass the same defines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The fixture's class was rejected through a WeakMap-typed field, which this branch makes legal. A MapIterator-typed field is still refused, so the class stays rejected and the test still checks that a class reached only through a receiver probe reports its own diagnostic exactly once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JSON.stringify of the runtime-features record follows the declared field order in the self-hosted compiler and the literal's insertion order under Node. warningReplace and stdioExtended were declared before processEvents but built after it, so the two compilers serialized the record differently (self-hosting-runtime-features). The interface now lists them in the literal's order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 tasks done
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The pi coding agent diagnosis turned up a set of small gaps on pi's startup, provider-stream and terminal paths. Each fix is its own commit and lands with Node-differential corpus programs, which pass in both the plain and sanitized lanes. The optional-chain miscompile from the same diagnosis lands separately (
opt/optional-chain-fix).Changes
process.emitWarning. The assignment stores the replacement. Laterprocess.emitWarning(...)calls apply it to their own arguments. Warnings the runtime builds itself (util.deprecateand Node's own modules) reach it as(message, type[, code]), and no report is printed. Assigningprocess.titlealready worked; the corpus program pins both.process.getBuiltinModuleresults as module namespaces. Under@types/node, a string-literal id that names a supported builtin now lowers to the same namespace value animport * asbinding has. This includes pi's spelling(process as P).getBuiltinModule?.("node:os") ?? null. A nullable namespace binding that has been narrowed to present uses the same member tables, and a namespace is truthy. Readingprocess.featuresas a whole value gives an object with none of the flags set, which matches the existing reads of its members.for awaitover class instances whose[Symbol.asyncIterator]is an async generator method. This is pi-ai'sEventStream, which every provider stream goes through. Anasync *declaration annotated withAsyncIterator,AsyncIterableIteratororAsyncIterablemaps likeAsyncGenerator. Boxing a class instance whose fields hold a promise now links the dynamic async unit. Without that, linking failed on EventStream'sthis.isComplete(event).chalk.bold()from TypeScript. TypeScript that imports chalk's JavaScript source types the default export as a plain function, sochalk.boldis ananymember on a typed receiver. Compile chalk 5 through the dynamic object model #1309 only handled receivers that are themselves untyped, so this shape was still refused. A member the checker leaves untyped now takes the same path, which still refuses a string receiver at run time. The chalk 5 fixture now covers root, aliased andChalk-instanceboldcalls.string_decoder(orevents, ...) no longer capturesimport "string_decoder", because Node checks its builtins first. Builtins that only exist with thenode:prefix, such asnode:test, leave their bare names to packages.if (process.stdin.setRawMode)now answers the method on a TTY andundefinedotherwise.process.stdin.setEncodingdecodes with the shared StringDecoder: a multibyte character split across reads stays whole, and the buffered tail is flushed before'end'. String'data'listeners are supported.process.stdout.on/once/off("resize")works, backed by a SIGWINCH listener that does not keep the event loop alive and only emits when a TTY's size actually changed.delete this.stackon Error subclasses that declare a stack accessor. quickjs-wasi'sJSExceptiondoes this. Compiled errors keep the stack trace internally, so the delete only needs to evaluate its receiver.emitWarningreplacement (item 1) and the stdin encoding, string'data'listeners and'resize'support (item 7) are compiled only into two new runtime-pack variants:SCR_WARNING_REPLACEofscr_async_dyn.candSCR_STDIO_EXTENDEDofscr_events.c. The link selects them when the program assignsprocess.emitWarning, or reachessetEncoding, a string'data'listener or'resize'(the compiler'swarningReplace/stdioExtendedlink features). The default objects are byte-identical to main's, so the shared warning paths, stdin delivery and TLS layout of every other executable are unchanged. The development and sanitizer builds pass the same defines. The darwin matrix lists the variants too, so the macOS runtime pack has to be rebuilt to provide them.Not in this PR
[Symbol.toStringTag] = "LRUCache", from lru-cache). Fields named withsym:need to be materialized for the dynamic view,Object.prototype.toStringand inspect, and only methods support that today.node:sea, which pi's extension loader probes withprocess.getBuiltinModule("node:sea")?.isSea(), is not a supported builtin.this.decoder.decode(...)inside a method compiled with a dynamicthis(quickjs-wasihandleHostCall). The class's dynamic view cannot expose a TextDecoder record field. Fixing it needs a dynamic handle that holds the codec record by reference. A repro is in the results directory.globalThisin TypeScript (const { X } = globalThis as unknown as T), and the local typing ofevalCode()results in quickjs-wasi callers."stack" in eisfalsenatively for a classstackaccessor, where Node answerstrue.Validation
node24-url-authority. These are Node 24.15 vs 24.22 differences in the sandbox.pnpm lintandpnpm format:checkare clean.pnpm test:ts7 --baselines-onlyis complete. The runtime-pack, backend, executable-cache, runtime-features, validate and code-quality unit tests pass (941).packages/compiler/src/native/cli.tswith--emit=irreports no diagnostics.tests/harness/self-hosting-native-driver.test.tspassed on the pre-merge tree..o, all flavors) built from this branch are byte-identical to origin/main's; the only additions are thewarning-replaceandstdio-extendedvariant directories (and their-workerstwins).effectiveRuntimeFeatures(needed for self-hosting) and the matching test fixture. Those edits only touch variant selection and give the same answer for programs that do not set the features.🤖 Generated with Claude Code