vercel-labs / vercel-labs/scriptc
Out-of-bounds array read/write throws RangeError instead of undefined (clean build, runtime abort)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 4.9k
- Forks
- 125
- Avg merge
- 2h 14m
- Merged PRs (30d)
- 95
Description
Out-of-bounds array access throws RangeError where Node evaluates to undefined. The build is clean; the divergence only shows up at runtime, and it hits the most ordinary JS idioms there are — destructuring a short array, probing arr[i] before checking length, growing an array by index.
This one cost me the most time while porting a real CLI: six separate sites, each compiling without a diagnostic and then aborting on the first run.
Repro
1. Destructuring past the end
export {}
function main() {
const a = ['x']
const [first, second] = a
console.log(`first=${first} second=${second}`)
}
main()
$ node --experimental-strip-types oob.ts
first=x second=undefined
$ scriptc build oob.ts -o oob && ./oob
scriptc: RangeError: array index 1 out of bounds (length 1)
# exit 134
2. Indexed read past the end
export {}
function main() {
const a: string[] = ['x']
console.log(`read=${a[1]}`)
}
main()
Node: read=undefined — scriptc: RangeError: array index 1 out of bounds (length 1).
3. Write past the end (array growth)
export {}
function main() {
const a: string[] = []
a[2] = 'z'
console.log(`len=${a.length} v=${a[2]}`)
}
main()
Node: len=3 v=z — scriptc: RangeError: array index 2 out of bounds (length 0).
Expected
Per the README's "What compiles behaves byte-for-byte like Node", an out-of-range read should evaluate to undefined and an out-of-range write should extend the array.
Why it is easy to hit
These are the shapes real code uses, and none of them look risky:
const [src, selector] = positionals // optional second CLI argument
const peek = () => toks[pos] // recursive-descent parser at EOF
if (bytes[0] === 0xef && bytes[1] === 0xbb) // BOM sniff on an empty body
while (grid[r][c] !== undefined) c++ // HTML table grid fill
argv[index - 1] === flag // look-behind at index 0
Under noUncheckedIndexedAccess the checker already types these as T | undefined, so the source is honest about the undefined case — the runtime just never produces it.
Suggestions
Ideally the read lowers to a bounds test yielding undefined, and the write extends. If matching Node here is genuinely out of scope for the value representation, a compile-time diagnostic would still be a large improvement over a runtime abort — the alternative today is discovering each site by running the binary.
Environment
- scriptc 0.0.17,
@scriptc/compiler0.0.17 - macOS 26.5.2, arm64, Node v24.18.0
- Static build (no
--dynamic), default backend
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the three TypeScript reproductions in the issue and compare their native output with Node: destructuring and indexed reads should produce undefined, while an indexed write should grow the array. Trace the compiler and runtime handling of array indexing, then verify all three cases no longer abort and preserve the expected length and values.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100