0.4.12
Highlights
- Faster programs:
- A promise reaction that answers
unknownoranyruns without a fiber and its 512 KB stack: a.then,.catchor.finallyhandler of that result costs 111 ns instead of 2,060 ns a reaction,finally605 ns instead of 8,464 and anawaitof anunknown133 ns instead of 2,007, and a round no longer maps and unmaps a stack (112 calls before). Of the 1,017 programs that hold such a body or link the dynamic promise unit, 943 are smaller by a median of 3,776 bytes and at most 4,336; 74 are larger, four of them by more than 3%, because a stackless body is larger than the fiber it replaces. - A union box that dies is freed without reporting to the dispose hook, and the dispose filter and the queued-disposal pointer are tested once per release: the 39 fully typed benchmarks are all smaller by 240 to 72 bytes (median 112), the 5,772 runtime-only programs are 144 bytes smaller at the median, and of 75 run-time workloads 73 are neutral, two are faster (
stream-buffered-drain4.6%,value-records3.1%) and none is slower. - Final fields and int32 slots stay in a class's static layout while no dynamic code in the program can observe the object.
&&and||overboolean | undefinedornumber | undefinedbuild their result in a stack box like??: the hot function of Queens calls neitherscr_union_new_boolnorscr_union_release, where it called three of each, and Queens runs 1.86 times faster (2,081.7 ms to 1,116.6 ms). The repository runtime suite's 52 measured workloads are byte-identical in size, print what Node prints and are 1.0% faster in geometric mean, and compile time is 0.97 times.- A nullable element array read is one guard and a direct null map instead of an out-of-line helper: towers runs 1.39 times faster with 223.0 instead of 315.9 instructions a move, and richards 1.06 times faster with 5.9% fewer instructions; every other runtime workload, compile time and program output are unchanged.
- An unannotated JavaScript
varthat every read sees stored takes the union of the sequence types its writes store, and a documentedArray.<number>parameter of a compiled npm package binds the storage its argument holds, so bcryptjs keeps its Blowfish key schedule in typed arrays instead of the checked-dynamic tier: 13.6 s to 3.6 s on the repro, with output identical to Node, and the runtime benchmark workloads are byte-identical.
- A promise reaction that answers
- Faster builds:
- The call of the program entry from
main, the entry's calls of the file initializers and an initializer's once-per-process calls arenoinline, so LLVM no longer inlines the whole program intomainand re-optimizes it once per level of the chain: the backend's time per program drops on sieve (1.49 times faster), mandelbrot (1.37), nbody (1.35) and json-build (1.17), and run time and output are unchanged.
- The call of the program entry from
- Node.js parity:
- All 14 zlib one-shot codecs and the stream writable side accept strings, Buffers, any TypedArray as its byte range, DataViews and ArrayBuffers, with Node's
Buffer.from(buffer.buffer, …)conversion and its"chunk"rejection of a bare ArrayBuffer; 48 zlib rows move frompartialtosupported. zlib.constantsis one record per program, built from the same table the member literals bake, so identity,typeof, key order, spread, destructuring and JSON match Node, and the declaredZlibConstantsinterface accepts the object; the row moves frompartialtosupported.- Every Brotli encoder and decoder parameter, zstd parameters with a dictionary and a pledged source size, and their validation errors are covered through the one-shots, callbacks, promises and streams; 24 zlib rows move from
partialtosupported, andBrotliOptionsandZstdOptionsstay partial until theinfooption lands. http.Server.maxHeadersCount,http.Server.maxRequestsPerSocket,http.Server'dropRequest',http.ClientRequest.maxHeadersCountandhttps.Server.maxHeadersCountfollow Node 24's limits: the 431 at the inclusive header-line cap, the client's response-header truncation, the 503 withConnection: closea dropped request gets while the socket stays open, and theKeep-Alive: max=<value>hint; the five rows move fromnot-implementedtopartial.- In JavaScript,
globalThis.X = class X {}(orglobal.X = …, in any module) makesX,new X(),X()andtypeof Xuse that value instead of throwingReferenceError: X is not defined, andvar c = void 0compiles where it failed with SC2001; a PNG decode loop that constructsImageDatathrough a polyfilled global runs 1.99 times faster than Node (151 to 171 ms against 337 ms for 2,000 decodes). - A bare reference to a global that TypeScript declares and erases (
declare varordeclare constin a script-style declaration file, or in adeclare globalblock) resolves through the global object, so a value assigned toglobalThis.documentbefore the read is whatdocumentevaluates to, as in Node; a declared global the program never writes keeps its previous static lowering and its compiled code is unchanged. a.length++,a.length--and compound assignment on a native array's length grow and truncate the array and throw Node'sRangeErrorfor a length that is not a valid array length, with the receiver and the length read saved before the right side runs; a postfix form yields the old length and a prefix form the new one.inover a brand-record union arm answers from the class's declared members, so a hoistedTextEncoderreportsencodeInto, andstdinandstdoutimported fromnode:processbind the streamsreadline.createInterfaceexpects.
- All 14 zlib one-shot codecs and the stream writable side accept strings, Buffers, any TypedArray as its byte range, DataViews and ArrayBuffers, with Node's
- TypeScript parity:
new ArrayBuffer(byteLength, { maxByteLength })lowers from TypeScript as it does from JavaScript.- An abstract declaration follows prototype semantics: a direct call with no implementation below evaluates its receiver and arguments and throws Node's
<callee> is not a functionTypeError, and an untyped lookup answers the nearest implementation (below, then above the re-declaration) orundefined. - A discarded negation of a void-typed call in unchecked JavaScript lowers as its operand, while consumed, checked and TypeScript forms keep their diagnostics; a JavaScript setter parameter that is optional only in JSDoc is admitted, while executable optional, default, rest and multiple parameters keep TS1051.
- The refusal descriptions of TS2629, TS2630 and TS2739 state the JavaScript behaviour the program has, without a change to the codes or to which programs are admitted.
- A literal array with a run of 65 or more equal items builds as a typed table: each row is a fresh value, and a column whose rows all agree is left out of the table and read from the row itself. A spread of empty array literals takes the target literal's element type, including within a conditional arm and two levels down.
- Element reads, writes and
.lengthon a JavaScript value that is a union of an array and a typed array dispatch on the storage the value holds, and the undefined-or-NaN element-read analysis coversnumber[]receivers.
- CommonJS and islands:
- A call of a module's own export through the exports object passes a receiver that holds the current value of every export the called function reads through
this, so a CommonJS module whose namespace functions reach their siblings throughthiscompiles where the previous build refused the call by name (SC1090) or, inside a package, threw where it ran; a detached call or.call(other)behaves as before, and the shapes the proof cannot settle, such asthis === exports, a write throughthis, or a member added toexportsby another module, stay refused by name. - The proof that a module export ignores its receiver follows a namespace member, an alias, a conditional,
??,||and&&, the last operand of a comma, the value thatObject.defineProperty,assign,freezeorsetPrototypeOfreturns, a re-export, a default export, an object or array literal member and a destructured binding, and refuses the call by name where the function it finds uses its ownthis: of 139 probes, 103 are refused by name and 29 match Node, against 66 and 29 with 42 answers that differed on the previous build, and no probe that matched Node is refused. A statement that gives a function its owncall,applyorbind, and a prototype that definescall, are refused by name.
- A call of a module's own export through the exports object passes a receiver that holds the current value of every export the called function reads through
- Fixes:
- A read of a field that several union arms declare reads over the stored union at the site's common type instead of a narrowed arm that traps, so a negated inexact type predicate matches Node where the previous build traps; the 0.4.11 native binary traps on the first read of the corpus program.
{ ...a, ...b }into one declared record enumerates in Node's order: each source keeps its own key order, a key that two sources set keeps its first position, and later keys append, soObject.keys,valuesandentries,for…in,JSON.stringify,util.inspect,console.logand a spread copy of the result agree with Node; a program that never observes the order of such a shape compiles to the same code.- The six http header-limit getters and setters are classified as never throwing, since they are plain storage with no accessor, coercion or listener, and
http.serverOnDropRequestas may-throw because it fires'newListener'before the add. - An
Immutableannotation is recognized through a re-export, an alias or a path alias of@scriptc/types, and a marker whose import cannot be followed is refused with SC7006 instead of compiling as if the annotation were absent. - An array built by pushing values read from
Immutabledata, and the result offilterover anImmutablearray, are accepted as fresh for a store into anImmutablefield (SC7004), and each checked element read is read as the array passed at its call site, so a plain array's.mapis no longer refused (SC7005) because an unrelatedImmutabletable of the same element type is indexed elsewhere.