Repository navigation
Conversation
…rved
A JavaScript function with parameters outside an ES module was refused as
soon as it read `arguments`, because Node maps a sloppy-mode function's
arguments object onto its parameters. The hidden array scriptc passes is
exactly Node's unmapped object, so the refusal now stays only where the
difference can be observed:
- strict code ('use strict' file or function prologue, class bodies) and
non-simple parameter lists get Node's unmapped object, so they compile;
- sloppy simple-parameter functions compile when no parameter whose index
some read can reach is written (closures included);
- in both, `arguments` must be used only as `arguments.length`, an rvalue
`arguments[i]`, a spread, `f.apply(x, arguments)`,
`Array.prototype.slice.call(arguments)` or `Array.from(arguments)`.
Writes, escapes and other members keep a named refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lru-cache 11 and glob 13 point their exports at one minified esbuild bundle and ship the per-file build next to it. Auto-selection refused the bundle as minified, and naming the package compiled the bundle, where every diagnostic lands on one enormous line. An opted-in package's minified main/exports target now resolves to the sibling without '.min' when the source maps prove the two are one program: the bundle's sources, including dependencies it inlined at build time, are exactly the sources the per-file graph loads (relative edges and the installed dependencies Node would load), byte for byte, and both export the same names. Both resolution worlds read the same respelled package.json. Otherwise the minified target stays, and auto-selection names the mismatch. lru-cache 11.5.3 now joins the program from dist/esm/node/index.js. glob 13.0.6 stays refused with current dependency releases, because its bundle inlined older minimatch and lru-cache releases; with the bundled releases installed, its per-file build compiles up to the same lru-cache blocker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
main's coverage report now prints the dynamic-tier statement count; the refusals the fixture pins are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Summary
Two tickets from the zero-cost JavaScript compatibility plan (T1 and T2). Both replace sites that today end in a refusal, so programs that compiled before produce the same IR, LLVM and executables (evidence below).
T1: sloppy-mode
argumentsA JavaScript function with parameters outside an ES module was refused as soon as it read
arguments("parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)"). Node maps a sloppy-mode function'sargumentsonto its parameters, so writing one shows through the other. The hidden array scriptc passes is a snapshot of the actual arguments, which is exactly Node's unmapped object. The refusal now stays only where the difference can be observed.argumentsmust be used only asarguments.length, an rvaluearguments[i],...arguments,f.apply(thisArg, arguments),Array.prototype.slice.call(arguments[, n])orArray.from(arguments[, fn]). None of these can write the object or let it escape.'use strict'file or function prologue, anything inside a class body) and functions with non-simple parameter lists (defaults, rest, destructuring) get Node's unmapped object, so that condition is enough.arguments[k]reaches parameter k, the other forms reach every parameter. Writes in nested closures count (the existingparamWrittenInBody). node-fetch's transpiledfunction Body(body) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; … body = …; }compiles because only index 1 is read.writing 'arguments' …,'arguments' as a value …,'arguments.callee' …,writing parameter 'a' while reading it through 'arguments' in a sloppy-mode function (mapped 'arguments' aliases its parameters). In JavaScript files these are runtime fences, as before.f.apply(x, arguments)on an untyped receiver (var emit = process.emitin signal-exit) is accepted as Function.prototype.apply. A typed receiver whoseapplyis not the standard library's is an escape.lowering/sloppy-arguments.tsand runs only at the two existing non-module fence sites (appendArgumentsParameterandjsArgumentsFunctionType), after the ES module and class-method checks.T2: minified package entries
lru-cache 11 and glob 13 point every export at one minified esbuild bundle (
dist/esm/index.min.js) and ship the per-file build next to it. Auto-selection refused them ("its shipped JS looks minified"); naming the package compiled the bundle, where every diagnostic lands on one enormous line.An opted-in package's minified
main/exportstarget now resolves to the sibling without.minwhen the source maps prove that the two are the same program (frontend/npm-static-minified.ts):node_modules/minimatch/src/index.ts) must match the installedminimatchthe per-file build imports;The respelling (
npmStaticRespellMinifiedText) runs on the package.json text in both resolution worlds, the tsgo host's fs shadow andresolve.ts, so they read the same document and answer the same file. A document without.min.comes back as the same string, and only.min.target values are replaced. Verification lives innpm-static-minified.ts, which has no AST dependency; the parsing it needs comes from the load through a host (npm-static-minified-syntax.ts). When verification fails, the minified target stays and auto-selection names the mismatch, for exampleits shipped JS looks minified, and its per-file build cannot replace it: the bundle inlines a brace-expansion/src/index.ts that differs from the installed one its per-file build loads. Explicitly naming such a package compiles the bundle as before.Packages (Linux sandbox, Node 24.15)
"main" is
origin/mainb14377864. "+pending" is a local, unpushed merge ofopt/cjs-interop(#1287) andopt/npm-small-fixes(#1285) on top of it, because glob@7 and node-fetch@2 stop at their fixes first.require(fixed by #1287)argumentsinsync.jsglobSyncfs.realpath'svar origRealpathSync = fs.realpathSync(builtin function as a value, T6)punycode(fixed by #1285)arguments_interopDefault(require('stream'))("'Require' is typed by @types/node but has no scriptc lowering", T3)import fetchargumentsmodule.exports = exports = fetchdefault import (T3)processEmit'sargumentsfencedargumentsfencemodule.exports = functioninsideif (!processOk(process))(T3)dist/esm/node/index.jsand its imports join the programdist/esm/node/index.js:693:[Symbol.toStringTag] = 'LRUCache'computed class field (T7)brace-expansionthat differs from the installed one; named: unchanged[Symbol.toStringTag]field (T7)Zero-cost evidence
Pattern signatures: T1 changes only functions in non-module JavaScript that read
argumentsand have parameters (previously refused, or fenced at run time in JavaScript files). T2 changes only opted-in packages whosemain/exportsname a.min.file that looks minified.Measured in a Linux sandbox on the final commits against
b14377864(both trees built with their own runtime pack and LLVM helper), comparing.llfiles and release executables directly (the zero-cost gate tool is not on main yet):benchmarks/builds/{log-summary,inventory-report}(--emit=llvmandSCRIPTC_NO_CACHE=1release builds from one shared path)@scriptc/experimental:intern-tablewas built from a shared path with that package linked in;published-graphsandregion-buildswere built in each tree, where the only LLVM difference is the tree path (header line and the program-path literal) and the executables are byte-identicalb14377864'spackages/compiler/src/native/cli.tsbuilt with--emit=llvmby each compiler--emit=irfrom one shared path: all 669.js/.cjs/.mjsprograms and every 20th.tsprogram (115).tsprograms)No existing corpus program changed, which is expected: none had a sloppy-mode function with parameters reading
arguments(they were refused) or an npm package with a minified entry.Tests
arguments-sloppy-unaliased.cjs(length, constant and computed indexes, written parameters no read reaches, nested shadowing, spread, apply including an untyped receiver,Array.prototype.slice.call,Array.fromwith a map function, arrows, object-literal methods, function values, the glob@7, node-fetch and signal-exit shapes),arguments-sloppy-unmapped.cjs(defaults, destructuring, function-level and class-body strict code),arguments-sloppy-strict-file.cjs(file prologue: parameter writes never show througharguments). Existingargumentscorpus (1703,3034,arguments-arrow-capture) unchanged.coverage-js-sloppy-arguments.txt: each observable form keeps its named fence (parameter write read througharguments,arguments[0] = …, passingarguments,arguments.callee, a write in a strict function).tests/harness/npm-static.test.ts): a synthetic tshy-style package whose verified minified entry compiles from its per-file build and byte-matches Node; a bundle that inlined an older dependency stays refused with the reason; the published lru-cache 11.5.3 joins fromdist/esm/node/index.js; the published glob 13.0.6 with the lockfile's newer dependencies stays refused with a named mismatch; the published node-fetch 2.7.0 and signal-exit 3.0.7 keep noargumentsfence. lru-cache, glob 13, node-fetch 2 and signal-exit 3 are added as root devDependencies (onlylru-cache@11.5.3is new to the lockfile).pnpm lint,pnpm format:check,pnpm test:ts7 --baselines-only(order baselines recorded for the three new corpus programs; existing baselines unchanged),order-parity.test.ts(62 passed),pnpm node-compat:check, harnessnpm-static+coverage+diagnostics+code-quality+self-hosting-frontend-metadata(333 passed),packages/compiler/src/frontendunit tests (770 passed), differential corpus-t argument(31 passed;5443-crypto-random-arguments.jsfails because the sandbox's Node 24.15 itself crashes on it, a pinned-24.22 baseline failure), sanitized lane for the new programs (3 passed),scriptc coveragereports everyargumentscorpus program 100% static, self-hosted--emit=irbuild ofpackages/compiler/src/native/cli.tswith no diagnostics,self-hosting-native-driver.test.ts(passed). Docs:NEXT_DIST_DIR=.next-check pnpm checkpassed (25 route tests) before a one-word wording fix.Notes for review
argumentsaccepts any use, soArray.isArray(arguments)istrue,util.inspectprints an array, andarguments[3] = 1with one argument makesarguments.length4 (Node: 1). The newly admitted CommonJS functions refuse those uses instead. The limitations page now says this.[].slice.call(arguments)throwsTypeError: Cannot read properties of undefined (reading 'call')in every file kind; the[].sliceread does not lower to the prototype method. It stays an escape in the new analysis.useDefineForClassFields) would differ; the source maps cannot show that.Record<string, unknown>document. That made the self-hosted build ofpackages/compiler/src/native/cli.tsreport 301 errors in untouched lowering files (ClassInfo values turning intounknown), even with the function unreached. Respelling the text instead keeps the self-hosted build clean. The trigger looks like a self-hosting limitation worth its own investigation; the bisect is in the notes.🤖 Generated with Claude Code