Skip to content

Releases

v0.4.12

Choose a tag to compare

@github-actions github-actions released this 11 Nov 19:50
cdf8a5b

0.4.12

Highlights

  • Faster programs:
    • A promise reaction that answers unknown or any runs without a fiber and its 512 KB stack: a .then, .catch or .finally handler of that result costs 111 ns instead of 2,060 ns a reaction, finally 605 ns instead of 8,464 and an await of an unknown 133 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-drain 4.6%, value-records 3.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 || over boolean | undefined or number | undefined build their result in a stack box like ??: the hot function of Queens calls neither scr_union_new_bool nor scr_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 var that every read sees stored takes the union of the sequence types its writes store, and a documented Array.<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.
  • 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 are noinline, so LLVM no longer inlines the whole program into main and 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.
  • 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 from partial to supported.
    • zlib.constants is 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 declared ZlibConstants interface accepts the object; the row moves from partial to supported.
    • 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 partial to supported, and BrotliOptions and ZstdOptions stay partial until the info option lands.
    • http.Server.maxHeadersCount, http.Server.maxRequestsPerSocket, http.Server 'dropRequest', http.ClientRequest.maxHeadersCount and https.Server.maxHeadersCount follow Node 24's limits: the 431 at the inclusive header-line cap, the client's response-header truncation, the 503 with Connection: close a dropped request gets while the socket stays open, and the Keep-Alive: max=<value> hint; the five rows move from not-implemented to partial.
    • In JavaScript, globalThis.X = class X {} (or global.X = …, in any module) makes X, new X(), X() and typeof X use that value instead of throwing ReferenceError: X is not defined, and var c = void 0 compiles where it failed with SC2001; a PNG decode loop that constructs ImageData through 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 var or declare const in a script-style declaration file, or in a declare global block) resolves through the global object, so a value assigned to globalThis.document before the read is what document evaluates 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's RangeError for 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.
    • in over a brand-record union arm answers from the class's declared members, so a hoisted TextEncoder reports encodeInto, and stdin and stdout imported from node:process bind the streams readline.createInterface expects.
  • 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 function TypeError, and an untyped lookup answers the nearest implementation (below, then above the re-declaration) or undefined.
    • 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 .length on 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 covers number[] 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 through this compiles 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 as this === exports, a write through this, or a member added to exports by 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 that Object.defineProperty, assign, freeze or setPrototypeOf returns, 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 own this: 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 own call, apply or bind, and a prototype that defines call, are refused by name.
  • 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, so Object.keys, values and entries, for…in, JSON.stringify, util.inspect, console.log and 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.serverOnDropRequest as may-throw because it fires 'newListener' before the add.
    • An Immutable annotation 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 Immutable data, and the result of filter over an Immutable array, are accepted as fresh for a store into an Immutable field (SC7004), and each checked element read is read as the array passed at its call site, so a plain array's `....
Read more

v0.4.11

Choose a tag to compare

@github-actions github-actions released this 10 Nov 21:01
15b4452

0.4.11

Highlights

  • Faster programs:
    • A read through an untyped value of a typed record or array no longer copies it: three React render loops execute 31% to 57% fewer instructions per render (294,339 to 197,439, 6,765,442 to 4,653,659 and 6,648,657 to 2,880,208 by callgrind), and the untyped-record-probes workload runs 72.8% faster, about 0.7 s instead of 0.18 s against 0.13 s in Node. Of 74 run-time workloads, 73 are neutral, one is faster and none is slower.
    • A write by a name known only at run time (v[k] = x) costs 134 instructions instead of 300 when the receiver is a class instance, 379 instead of 534 for a typed record, 349 instead of 528 for a parsed object and 319 instead of 501 for an object literal; k in v costs 112 instead of 278 for a class instance, an element read 109 instead of 174, a typeof test 87 instead of 125, and an object spread of three members 1,324 and 3,160 instead of 9,364. Executables that link the changed functions grow by 256 to 512 bytes.
    • A typed array read through an untyped value reads the element from the array itself: v[i] costs 508 instructions an item instead of 1,189 for an array of class instances and 346 instead of 1,026 for an array of numbers, with identical compiled code.
    • A property copied from a layout-mode object onto an array view takes the ordinary writable, enumerable, configurable write instead of the descriptor path: 300,000 Array subclass instances with three fields, each read once through a view, take 206 ms instead of 559 ms, against 18 ms in Node.
    • A type that carries the collector header only for the weak analysis is emitted without cycle-capable children, so it nominates nothing to the collector: a program of 200 plain records a round filed in a WeakMap<object, …> over 25 rounds costs 242,083 instructions a round instead of 349,827, against 217,117 before the weak analysis needed the header.
    • An Object.freeze, Object.seal or Object.preventExtensions call that can never run, under if (false), a const that is false or a comparison --define decides, no longer sets the program-wide integrity flag: a loop of five field stores and one object goes from 156 to 97 instructions an iteration, and its executable from 382,520 to 188,864 bytes.
    • A switch on the code unit inside a scanning loop and a for without an initializer take the UTF-16 units copy, at 7.7 instructions a character for non-ASCII text instead of 26.7 and 8.0 instead of 22.0 for a counter declared before the for, and a chain of five replaceAll calls with one-character literals becomes one scan at 410 instructions instead of 637.
    • A question about one member of a Node stream held in an untyped value no longer builds the stream's member view when the name is absent or the question is in or Reflect.has: typeof h.flush on a Writable costs 541 instructions instead of 33,952, and asked twice 867 instead of 73,587, with every answer unchanged.
    • The own-key questions a serializer asks about a value held under a closed record type, whether its prototype is Object.prototype, how many own keys it has and whether a key is a given name, are answered from the record's static layout and one runtime fact: 44 instructions a question instead of 985 for one member with Object.keys, 41 instead of 4,777 with Reflect.ownKeys and Object.keys(r).length, and 78 instead of 20,205 for 11 members. A record that kept a source object, a linked copy or a live capsule falls back to building the key array.
    • A write to an untyped record by a name known only at run time costs 100 instructions instead of 361, against 129 for the same write with the name in the source. A program with no such write is identical to its previous build.
    • A field append (this.text += s, obj.text += s, a record field) is linear instead of quadratic: at 10,000 characters an append costs 126 instructions instead of 2,225, and at 1,000,000 characters it costs 125 where the previous build does not finish. A local text += piece costs about 80 instructions instead of 146.
    • The first .length of a finished JSON document scans it 32 bytes a step and marks it ASCII instead of walking it through the index, at 155 additional instructions instead of 563.
    • parts.join(sep) on a string[] costs 21.5 instructions an element instead of 53.0 with an empty separator and 22.5 instead of 63.0 with a comma, and Array.from<string>({ length: 2000 }) 14.8 instead of 33.0. The join is a typed runtime entry of its own: the 1,745 programs that join a string array grow by 720 to 856 bytes, and the 3,138 programs that never call it shrink by 640 to 704 bytes.
    • A string | null, string | undefined or three-state union keyed in a Map or held in a Set stays the string pointer, so one such collection no longer boxes the union in every position of the program: a string | null field makes 0 union boxes per 1,000 iterations instead of 750, Set.has costs 91 instructions instead of 226, Map.get 90 instead of 251, Set.add 118 instead of 335 and Set.delete 112 instead of 286.
    • A chain of setImmediate callbacks no longer grows the immediate queue: 200,000 turns of a chain peak at 1,660 KB of maximum resident size instead of 6,524 KB, and a turn that queues nine immediates peaks at 1,644 KB instead of 48,508 KB.
  • Faster builds:
    • A statement that holds many values across steps that can throw compiles in linear time instead of quadratic release calls: a dynamic object literal of 494 members builds in 15.9 s instead of 52 minutes 43 s, and generated tables of 100, 200 and 400 strings take 0.17 s, 0.28 s and 0.67 s of native code generation instead of 6.6 s, 60.4 s and 535 s.
    • The TypeScript 7.1 symbol table keeps one parsed descriptor per source file instead of one per symbol: the compiler's own build holds 3,404 MB of live heap at 137 bytes per source byte instead of 3,572 MB at 144, and among the 1,443,719 references that carry a descriptor for 889 files no two descriptors for one node ID differ.
    • Every uncompiled import() in one statement is decided in one island round, so 29 programs take fewer lowerings (142 to 102 in all) and a large program's build time grows 1.0% instead of about 49%.
  • Node.js parity:
    • writeFileSync, appendFileSync, the fs/promises writes, the descriptor form and FileHandle.writeFile and appendFile accept any ArrayBufferView, including a view over WebAssembly memory, a subarray at an offset, a DataView and a typed array of any element, and write the bytes it covers; other data throws Node's ERR_INVALID_ARG_TYPE with the argument checks in Node's order. 200,000 descriptor-form writes of a 4 KiB view take 25.5 ms against 74.5 ms in Node.
    • new TextEncoder(label) evaluates and ignores its arguments as Node does, including spread arguments, so new TextEncoder("utf-8") compiles, and construction runs 1.63x faster than Node's (373 ms against 610 ms for 20 million encoder and decoder pairs).
    • Buffer.alloc(size, fill, encoding) and buf.fill(value, offset, end, encoding) with arguments that have no static type are filled by the kinds the values have at run time instead of being refused with SC2020.
    • Terminal members of a stream (isTTY, columns, rows, isRaw) read through an optional chain, off an alias of process.stdin or bound by a pattern answer the declared type plus undefined on a terminal, a pipe, a file, the null device and a closed descriptor: 501 of 619 cells match Node instead of 283, and the 34 cells that differed now match.
    • getColorDepth() and hasColors() on a process stream answer by Node's rules on a terminal and on a pipe: 115 of 132 rows match Node instead of 5.
    • A builtin global read by name off a stored global object (const root = globalThis; root.Headers) reads the value the bare name reads (2,687 of 3,480 cells match Node instead of 1,055), and a program that asks the global object about itself gets Node's answers.
    • process.hrtime reads mach_continuous_time scaled by the time base on macOS, the clock Node reads, so a Node island's load-time reading and the program's own reading share one origin.
  • TypeScript parity:
    • instanceof answers for every kind of untyped value instead of throwing: a Uint8Array or a URL tested against a program class answers false where the previous build reported SC2020, and v instanceof Error inside a chain of class tests answers instead of reporting SC1090. Over 1,294 cells of four corpus programs, 181 cells differed from Node on the previous build, every one a throw, and none differs now.
    • The arguments object answers as Node does for Array.isArray, Object.prototype.toString.call, arguments instanceof Array and JSON.stringify, and the builtin concat over it is refused by name: over a family of 147 cells, 114 match Node instead of 75.
    • Object.assign copies in its source's key order and a const bound to its result is its target, Object.getPrototypeOf of an instance held as its base class answers the instance's own class, and a record type whose objects create their keys in orders the compiler does not follow per object is reported once as the warning SC6014, which --no-key-order-warning leaves out.
    • A generic function over an interface held as an untyped value gets one instance per set of type arguments (292 of 292 cells match Node instead of 186, where the previous build threw TypeError: expected number at $, got string).
    • A generator, async or async generator function reports GeneratorFunction, AsyncFunction or AsyncGeneratorFunction, and a handled rejection is not reported when the same promise also crossed into an untyped position: 150 of 336 cells that ended in a build refusal or a run-time error become matches.
    • A function's body lowers when a destructured parameter's binding has no static type, and a Promise.all position whose type has no st...
Read more

v0.4.10

Choose a tag to compare

@github-actions github-actions released this 09 Nov 05:47
96e05f6

0.4.10

Highlights

  • Faster programs:
    • A function literal written where a function with more parameters or other parameter types is expected is lowered once with that slot's parameter list instead of being wrapped in a signature adapter: a loop that calls a two-parameter callback 12 million times takes 154 ms instead of 1,544 ms.
    • Typed-array conversion between integer kinds stores each element directly instead of routing it through a double: set() of 131,072 elements from a Uint8Array into a Uint16Array takes 1.9 ms instead of 61.2 ms, and the typed-array-convert workload runs at 0.65x Node's time instead of 1.29x.
    • Loop-bound versioning covers loops that write record fields, so advance in n-body drops its double counter and its double-to-integer round trip per index: scale 1 under --layouts=4 --runs=16 takes 132.5 ms instead of 158.4 ms.
    • new Error(message), Error(message) and super(message) into a builtin error base use the typed constructor when the message is a native string or undefined: a throw-and-catch new Error("too big") executable is 96,760 bytes instead of 307,392, and 774 of the 779 changed corpus executables are smaller.
    • A same-shape clone snapshots its source's own keys only when the source keeps keys the plain copy would not reproduce: a tree rewriter over Expr values built by literals runs in 311 ms instead of 3,769 ms once the program also parses one Expr and prints it.
    • A boxed function's name lookup sorts the module's closure-name table once and binary-searches it: with 5,178 named functions, 600,000 boxes take 166 ms instead of 1,349 ms.
    • A regular expression's character class is collected, sorted and merged into the class once: six patterns of two properties and 60 ranges each compile in 1.19 ms instead of 7.20 ms.
    • A start of a program with Node islands no longer parses the manifest or reads and hashes the island module tree: a warm --version of pi takes 560 ms and 157.7 MB instead of 1,050 ms and 171.6 MB, and makes 14,000 system calls instead of 232,000.
    • A regular expression anchored at the start is searched with one attempt when every path through the pattern meets a non-multiline ^ first, a non-ASCII subject is matched on the bytes its string holds instead of being converted to UTF-16, and s.slice(k) on a non-ASCII string no longer reads the whole string to find its end: a tokenizer over non-ASCII text takes 12.9 ms instead of 4,876 ms against Node's 90.3 ms, a loop over a 600,000-character text takes 0.30 s instead of 381 s, a slice of one takes 10.3 µs instead of 59.1 µs, and a regular-expression call on a new non-ASCII string takes 0.04 µs instead of 64 µs at 100,000 characters. The 260 programs that link the regular-expression unit grow by 832 to 18,880 bytes.
    • x instanceof C, new C() and C.member with C an untyped value, and a computed read on an untyped value, index a table from eight class values, sixty-four for computed reads, instead of walking every class of the program: a 240-class program takes 696 ms instead of 2,183 ms.
    • A list Object.values makes over a checked-dynamic object stays a native list through map, filter, flatMap, reduceRight and findLastIndex, and the callbacks are called directly: the catalog-tables workload takes 95.0 ms instead of 186.7 ms.
    • A record that is one arm of a union stays the same object in a checked conversion, so a write through the union value reaches the record: collecting a 300-record table takes 0.223 ms instead of 37.6 ms, and the stream-buffered-drain workload takes 44.7 ms instead of 59.8 ms.
    • A loop over rows of numbers or numeric class instances runs the rows as masked lanes, each lane running its own inner loop with the instance methods inlined: the value-objects mandelbrot workload takes 0.7571 of its former wall time, and every other runtime workload's generated code is unchanged.
    • A loop's carried values are packed only when the pack gains more than it costs, so a break-even pack stays scalar instead of adding insertion and extraction to the loop's carried dependency chain: a Mandelbrot sampling loop whose periodicity test is |dx| + |dy| < eps runs 17% faster with the same output, and the runtime benchmark set is neutral.
    • A program's start-up code is emitted first, ahead of everything else in the image, and its symbols and the runtime functions they call are ordered first at link time: a program that uses fx-ts reports 186 to 191 minor faults instead of 227 to 230, touches 16 text windows instead of 37, keeps 229 to 234 resident text pages instead of 538, peaks at 3.0 to 3.1 MB instead of 4.2 to 4.5 MB and takes 0.877 to 0.886 of its former wall time.
    • A program with island packages it does not use starts in 2.2 ms instead of 75.1 ms, one that uses them starts in 71.6 ms instead of 87.7 ms and cold in 60.5 ms instead of 76.0 ms, for executables 1.15% larger.
    • Math.random() draws from a per-thread xorshift128+ generator seeded once per thread from the system's secure source instead of asking the secure source for every number: 5,000,000 calls take 20 ms instead of 184 ms, a call takes 3.4 ns instead of 36 ns against Node's 11 ns, and the misc:math-random workload takes 3.0 ms instead of 36 ms. node:crypto and WebCrypto keep the secure source for every call.
    • A captured binding that is filled once before a closure over it is created and that nothing rebinds afterwards is captured by value instead of through a heap box, and a binding whose every capture is of that kind has no box: the compiler's own emitted module has 10,422 boxed captured bindings instead of 43,472, cyclic-graphs runs 12.3% faster, and the compiler's own executable is 106,875,664 bytes instead of 108,383,576.
    • A typed call of a method that an untyped write somewhere could replace is the declared call, direct or virtual, while the receiver has no own dynamic property and no prototype object of its class family exists: a loop of one-line method calls takes 4.3 to 4.4 ns per call and 58 instructions instead of 93.7 to 102.4 ns and 1,215, and its executable is 545,432 bytes instead of 572,528.
  • Faster builds:
    • A comment or whitespace edit reuses the cached frontend only when every recorded text read answers the same: a comment appended to a typed CLI that imports commander rebuilds in 0.372 s instead of 8.53 s. A build with a literal require.resolve is cached, so the compiler's unchanged rebuild takes 11.8 s instead of 1,143.6 s, and SCRIPTC_TIMING=1 reports why a build is not cached.
    • A build whose tsconfig.json lists the output's directory restores the executable from the cache, so the second and third builds take 0.39 s instead of 1.33 to 1.38 s.
    • A call chain of 12 links lowers in 37 lowerings and 1.2 s instead of 24,584 lowerings and 3.7 s, and a 30-link chain builds in 1.5 s where it previously exceeded 60 s at 19 links.
    • --emit-lowered <file> writes the lowered program a build produced, and --from-lowered <file> builds from it without starting a type checker or lowering anything: the compiler's own source builds in 311.7 s instead of 770.8 s, and a changed source, option or compiler build is refused by name with SC3002.
    • The canonical-type walk maps each object node once per depth instead of once per path, and the type mapper answers each type once per depth: scriptc coverage on json-schema-to-typescript finishes in 113 s at 2.0 GB where the previous build was stopped at 420 s, and pi's first lowering fixpoint takes 9.4 s instead of 147 to 153 s.
    • Lowering drops lifted functions the module no longer reaches: about 12,600 of the 54,700 functions in the self-hosted compiler's module had no caller, the phases after lowering take 8% less, and its executable is 41,520 bytes smaller.
    • A run of more than 64 consecutive literal items that differ only in numbers, strings and booleans is built from columns in the program image: a 20,000-row record table builds in 15.5 s instead of 1,114 s with a 1,465 KB executable instead of 7,519 KB.
    • A lowering pass that a later pass replaces ends where it asks for the next stream mode instead of lowering the rest of the program: the compiler's own lowering falls from a mean 656 s to 471 s, and its self-hosted build is 19.26% faster. A class that extends Array keeps its arrayFields and arraySpecies functions on the pending flag, so 20 corpus programs that emitted twice now emit once.
    • Lowering interns each finished function's long string literals, so one string is held per failure-message text: the compiler compiling its own source has a largest heap of 4,105 MB instead of 6,424 MB and builds 2.7% faster.
    • A typed program with a generator records the ambient receiver only when the module reads a generator's this, which removes a dynamic receiver read, 3 KB and 15 symbols from every such program: 111 of the 239 changed executables in a corpus build shrank, up to 4,920 bytes.
    • An npm-static build no longer computes diagnostics nothing reads, so its frontend loads fall by 19 to 56 s in five of six measured pairs (median 31 s).
    • A build sends the LLVM codegen helper's version, emit and merge-profiles commands to a resident server over a Unix socket and falls back to a process per build when no server answers; cold native builds of the Benchmarks Game programs run 3.9% to 8.8% faster, with executables identical to those of a build without the server.
    • Ending a stream program's push-down pass once both of its stream flags are set shortens a build of packages/coding-agent/src/cli.ts with 15 --npm-static packages from 1070.3 s to 882.5 s, 17.5% faster.
    • Nursery bypass statistics are kept per closure body, in a per-thread table keyed by the closure's function, instead of one family each for closures and for boxes holding closures, so a program's long-lived closures no longer keep its short-lived closure cycles in the nur...
Read more

v0.4.9

Choose a tag to compare

@github-actions github-actions released this 06 Nov 17:56
f589a56

0.4.9

Highlights

  • Faster programs:
    • Typed-array and dense number[] loops run as lane loops: elementwise stores and dense sums, counts and stores run whole vectors and finish the remainder with the scalar loop. The typed-array benchmark runs 1.83x faster, and integer sums and counts 1.56x.
    • Typed arrays copy in bulk where the element kinds allow it: construction, set() and TypedArray.from(view) between typed arrays move same-width integer pairs with one memmove and convert every other pair in one loop. A Uint8ClampedArray over a decoded buffer runs 1.9x faster, at 1.19x Node's time instead of 2.39x.
    • A callee called in a loop with an array parameter of one known length is compiled for that length, so the loop loses a per-element hole guard: n-body at 50,000,000 iterations is 5.0% faster.
    • Byte loops that count a constant product and byte loops that index downwards are versioned on their trip count: 1.31x and 5.98x on probes of those shapes (1,305 ms to 1,000 ms and 1,140 ms to 190 ms).
    • Dynamic computed access has a faster path: the fasta benchmark port runs at 1.73x Node's time instead of about 3.2x, a byte-array store falls from 63.3 to 20.4 ns per operation, and an indexed charCodeAt from 172.0 to 90.2 ns.
    • Buffer.write with a latin1 or ascii encoding uses the word-wise encoder: a 16 MB write is 55% faster, from 3.9x to 1.75x Node's time. Indexed scans of long non-ASCII texts are 47-51% faster.
    • Untyped process.stdout.write(text) and process.stderr.write(text) calls reach one runtime call: 10,000,000 writes to /dev/null take 1.38 s instead of 3.13 s. The opt-in --stdout-buffering keeps console output in the 64 KiB buffer when stdout is not a terminal: 10,000,000 console.log lines to a pipe take 0.54 s instead of 6.3 s.
    • The collector does less work at collection points and sizes its nursery window from what it collects: channel-messages is 12.4% faster and worker-messages 5.9% faster. Object chunks of 1 MiB or more come from huge pages, an async function's statement frame stays out of a group when nothing in it awaits, and depth-21 binary-trees runs at 0.84 of its former wall time with 199 MB of peak memory instead of 253 MB.
    • Large immutable number tables are built in the program image, so a small CLI program starts in 1.21 ms instead of 1.51 ms. An optional-element array read skips the runtime getter (13% faster, 35% fewer instructions), and 200,000 calls through builtin function values take 4.6 ms against Node's 75.3 ms.
    • --cpu-dispatch copies only the functions whose body has a loop: 1.305x on mandelbrot and 1.068x on n-body against the default build.
  • Faster builds:
    • A failing program's whole-slot relower runs on the pass whose result is kept, after the general-streams relower: a large program that takes the general-streams relower builds 1.22x faster (1,141.9 s to 932.5 s cold).
    • A build whose inputs did not change returns the executable already on disk, without reading a source, lowering, emitting LLVM, generating objects or linking: a no-change build takes 16.6 ms instead of 71.9 ms, 17.6 ms instead of 426.8 ms with an LLVM helper set, and 58.6 ms instead of 959 ms with a profile and an FFI manifest.
    • Runtime-pack selections and link-driver jobs are kept in the build cache: cold builds take 16.3 ms less on average over 369 paired builds.
    • Partition groups are numbered from the file name relative to the entry module, so a warm one-function edit that adds a module recompiles 3 of 64 partitions and the module-init pass is 19.5% faster.
    • A build's repeated frontend loads share one checker project, so parsed files carry between passes: a large multi-package program builds 15.9% faster (652.96 s to 549.05 s). Source parses are cached and declaration type queries batched (59,075 source-parser requests become 2,595), alias answers are memoized for one load (573,047 requests become 190,448), and npm-static package selection is memoized per build: in a separate pairing on the same program the build falls from 634.0 s to 404.6 s and the island reloads from 222.4 s to 188.1 s.
    • Lowering a large program reads each mapped symbol name once, builds union arm keys once per sort and answers the library-file question once per file: a large compiler build's coverage analysis falls 20.2%, from 1,360.20 s to 1,085.14 s.
    • A build of the compiler's own sources loads a 3.1-3.8 MB frontend graph instead of 15.7 MB, and one analyze and compile of a fixture falls from 111.9 s to 24.6 s. The bundled-library scan asks once per directory, 1.2 ms to 0.1 ms.
    • Windows release executables request no debug info: hello world is 146,432 bytes instead of 1,803,264 bytes. Analysis peak memory falls 8.7% on a 1000-module graph and from 1207 MB to 1053 MB on 3000 modules, and a macOS type check's peak memory falls 19.0-21.5% with compact class references.
    • Large shared buffers get a mapping of their own: a 16-checker type check's peak memory falls from 2856 MB to 2805 MB.
  • Node.js parity:
    • node:dns lowers lookup, lookupService, getServers and the default result-order functions with Node's argument ladder, error codes and error properties, and its address, hint and error-code constants come from the target libc.
    • node:tls accepts tls.createServer({ SNICallback }) without a default cert/key pair.
    • node:readline interfaces have prependListener, prependOnceListener, removeListener, off, removeAllListeners, listenerCount and eventNames, with removal by the registered function, and question(query, { signal }, callback) cancels on abort.
    • process.allowedNodeEnvironmentFlags is the target's NODE_OPTIONS flag names, process emits 'beforeExit' and has abort(), and ChildProcess reports spawnfile and spawnargs.
    • child_process accepts stdio arrays past fd 2, detached, options.signal and stdio: "inherit", and spawnSync's error carries Node's errno, code, syscall, path and spawnargs fields.
    • Buffer#toJSON returns Node's snapshot, Blob#stream() and Blob#textStream() return real ReadableStreams, URL.createObjectURL, URL.revokeObjectURL and buffer.resolveObjectURL share Node's table, and fetch sends Blob and File bodies as their bytes with the blob's type.
    • url.Url is Node's legacy constructor, strict-mode property writes name bigint and symbol receivers, util.format walks Error values for %j, and typed emits reach listeners registered through untyped values with errorMonitor first.
    • Native handles accept own properties, and a class instance's getter-backed fields read through its checked view.
    • process.report is answered by the Node island, node:test's context has t.runOnly and t.waitFor, and static builds read /etc/passwd for os.homedir and os.userInfo and throw Node's catchable SystemError.
    • The compatibility ledger records the implemented web globals, the Headers class and the node:diagnostics_channel surface against the programs that exercise them.
  • TypeScript parity:
    • Every dynamic-to-typed conversion asks one predicate, and an optional record field whose type maps to the checked-dynamic slot can be absent, so delete works and an omitted field is absent to in, Object.hasOwn, Object.keys, util.inspect, JSON.stringify and spread.
    • new Set(x) and new Map(x) seed from a checked-dynamic iterable, and a Map or Set arm of a class field's undefined-armed union lowers.
    • A function's own properties use one representation, a class instance passed to a callee that reads its fields keeps its record shape, and accessor literals for intersections, unions and mapped types keep their checked-identity view.
    • Method calls on a union receiver with a primitive arm lower, && spreads that drop the left operand's widened arm lower, Promise.all over a mixed armed-union array literal lowers, and Object.assign takes dynamic sources into index-signature records.
    • Overrides that append trailing optional or defaulted parameters, optional captures, computed keys and variadic function values lower.
    • Builtin members read as values compile: Function.prototype's call, apply and bind, RegExp.prototype's exec, test and toString, DataView.prototype, builtin functions carrying Node's name and length, and an http.request alias or a client chosen at run time.
    • Packages that declare their data shapes in declaration files compile, as do package module edges reached through literal require() and import() calls, a package's own JSON documents, and rest bindings from a CommonJS module's export names.
    • More JavaScript semantics compile in typed sources: BigInt operands of the arithmetic, shift and bitwise operators, symbol to string coercion, Object.prototype.__proto__ on dynamic receivers, rest bindings over index-signature sources, and delete through an erased assertion.
    • Five diagnostics are admitted for JavaScript sources, and a project that checks its JavaScript still reports them.
  • CommonJS and islands:
    • A constant-decided top-level if exports what its branch exports, a failed CommonJS initializer retries on the next require(), and a shared module's exported function exists before the module evaluates.
    • A local JavaScript import answers its runtime sibling through a typed sidecar, and shared exports publish between declarators.
    • An island's module tree stops at a type-only import, and type-only island edges ship as the no-ops Node erases.
    • A union type alias a package declares over Node data types lowers as the checked dynamic value, and a callable type from a types-only package stays the island's function handle.
  • Fixes:
    • A nested marker table is a brand marker, so branded primitives stay Map keys. A parameter that shadows a builtin module alias is no longer reported as a patched module, and a generator declared to return any lowers instead of reportin...
Read more

v0.4.8

Choose a tag to compare

@github-actions github-actions released this 03 Nov 21:11
3d1888a

0.4.8

Highlights

  • Faster programs:
    • Counted loops run two iterations at a time where the iterations are independent: a single-file mandelbrot port is 39% faster.
    • A class whose instances form a tree, each node owned by one parent, keeps its nodes in the group arena that holds the rest of the tree, at 16 bytes per node instead of 24: a class-based binary-trees program is 20% faster and uses 33% less peak memory.
    • A dictionary update fuses its second hash probe into the write that follows (4.5% faster on dictionary walks), and number and identity-word map keys hash with one multiply instead of two (6.9% faster on number-key maps).
    • A run of dynamic property reads on one object shares one guard, untyped code calls typed records' dynamic parameters through specialized calls, and dynamic class tables dispatch on an indexed class key.
    • Compact class fields store 32-bit references to arrays, maps, sets and instances, so programs that keep many objects use less memory.
    • musl executables use an allocator built for the program they link into: across the runtime benchmarks they are 2.9% faster, line-by-line reading is 39% faster, and an HTTP server's peak memory falls from 34.6 MiB to 2.4 MiB. musl executables also use musl's string comparison and a faster getenv.
    • x86-64-v3 builds keep baseline copies of the program's functions.
  • Faster builds:
    • The thin-LTO pre-link runs at the build's optimization level: a cold build of a large program is 2.4% faster, with the LLVM stage 10% faster and the same runtime speed.
    • The partition cache also covers builds that name their LLVM helper with SCRIPTC_LLVM_HELPER: a warm one-function edit of a large program rebuilds 13% faster in that setup.
    • A build shares one frontend services scope across its loads, memoizes type mappings per generic binding context, shares type-memo entries that read no type-parameter binding, and computes its preflight diagnostics once. Island rounds share their program facts across lowering passes.
    • LLVM partitions are sized from the host's threads as well as the module, the emission passes read their node scans from one recorded walk, and IR functions decode without a whole-document number reviver.
    • The LLVM helper's identity is checked once per install, the installed compiler package digest is reused, and edit builds keep less per-build bookkeeping. The native compiler's optimization profile is refreshed.
  • Node.js parity:
    • node:path, node:url and node:os match Node 24. node:buffer matches Node 24's Buffer members, encodings and module surface. node:zlib adds the zstd API.
    • util.inspect supports showHidden, numericSeparator, maxStringLength, maxArrayLength and the getters option, console.dir supports showHidden, and errors print from their stack in util.format, util.inspect and console.log.
    • child_process lowers the shell forms, typed spawnSync options including cwd and input, the cwd option of the exec and execFile callback forms, and child stdio pipes typed as node:stream streams.
    • node:http applies Node's HTTP/1 parser checks, answers ClientRequest listeners, reports late Transfer-Encoding errors, and lowers the server 'clientError' event and the URL-plus-options request form with protocol and shorthand headers.
    • process.stdin has its readable surface, readline accepts process.stderr output and runtime question options, and getEventListeners answers for EventTarget receivers.
    • ArrayBuffer transfer, detach and resizable or growable buffers compile. A cgroup CPU quota below one CPU is floored at one.
    • Node.js is pinned to 24.22.1.
  • JavaScript semantics (Test262):
    • More early errors are reported, RegExp coerces its constructor arguments, and the async-generator yield* delegation protocol is implemented.
    • Class values honor Symbol.hasInstance. Promise statics accept a foreign constructor. Iterator helpers run on a live Iterator.prototype.
    • BigInt typed arrays work in the checked-dynamic tier, and builtin global functions and classes have values in JavaScript sources.
    • Generator parameters bind at the call, higher-order functions honor thisArg, pattern defaults take their binding's name, and object literals accept __proto__ with any prototype value.
  • Node islands: coverage lowers once with the islands the build proves, packages that are not opted in keep their JavaScript out of the inferred surface, and npm-static packages are classified only from real loads.
  • Correctness: a record reached through an alias keeps writes made through either name when its shape widens under the shape overlay.
  • TypeScript: more record, union and generic patterns compile, among them overload sets held as values, class overload declarations, unions of a class and its subclasses, Object.assign over a narrowed record arm, and partial record views that gain keys through dynamic writes.

v0.4.7

Choose a tag to compare

@github-actions github-actions released this 31 Oct 05:03
6c60b0b

0.4.7

Highlights

  • Node.js parity:
    • Intl.Segmenter splits text into graphemes and words, and toLocaleString uses the default locale, on Linux x64 (ICU 78.3). Malformed UTF-8 in directory entries and command-line arguments decodes to U+FFFD, as in Node.
    • node:http emits Node's abort events, matches Node's outgoing header behavior, and checks http.request options before a socket opens. Sockets after setEncoding pipe decoded strings, untyped listen ports are validated, and net errors carry Node's system properties.
    • node:querystring and node:string_decoder match Node 24 for values known only at run time, and node:path matches Node 24's path tests. TextDecoder matches Node's errors, options and printing.
    • structuredClone copies Maps and Sets. SharedArrayBuffer, DataView, BigInt64Array, BigUint64Array, Proxy and Intl can be read as values. process.memoryUsage() counts ArrayBuffer storage.
    • import() of a specifier that cannot be resolved rejects with ERR_MODULE_NOT_FOUND. fs reads return Buffers in programs that can observe the difference. Values held from process.env read the live environment.
    • EventEmitter and stream listeners are called with the emitter as this, listeners that read arguments receive every argument, and the leak warning is delivered at exit. 'error' events accept non-Error payloads.
    • readline accepts listener values and ends lines at U+2028 and U+2029. fs.watch names the changed entry. assert.throws reports validation-object failures as Node does.
    • Executables ignore SIGPIPE and SIGXFSZ, as Node does. Timer deadlines hold on macOS, and intervals re-arm as in Node. Timeout delay warnings are emitted, and untyped timer delays convert with ToNumber.
    • process.emitWarning, process.kill, process.chdir and os.userInfo behave as in Node. Replacing process.stdout.write or process.stderr.write receives the arguments, Buffers and whole writes that Node passes.
    • JavaScript semantics from Test262: each evaluation of a class expression creates its own class, private static members check their brand, wrapper objects for strings, numbers and booleans work in JavaScript sources, and Set, Map and weak-collection methods can be read as values.
    • for...in runs over a value that may be null or undefined. A null or undefined destructuring source throws Node's TypeError. Functions held in records and arrays keep their names.
  • Node islands:
    • Sloppy-mode modules and modules that patch builtins run in Node islands automatically.
    • Islands can require CommonJS modules and hold builtin classes, and garbage cycles are collected across the program's heap and Node's.
    • Island modules whose load is deterministic are deferred until first use, and shared modules resolve by real path, so caches reached through symbolic links work.
  • Faster programs:
    • Untyped code calls methods of compiled class instances inline.
    • Integer arithmetic: sums built on unsigned shifts stay in 32 bits, and loops are versioned on the range of locals they write as remainders. Saturating loops that run together test their exit every fourth iteration.
    • Number-key map lookups test the direct index once and inline the probe in loops.
    • Streams: taking from a Readable's buffered queue is constant time, object-mode stream callbacks do less work per chunk and keep typed records unboxed, and a line goes to a single line listener directly.
    • fs streams read and write cached regular files on the loop thread, worker threads that allocate large blocks no longer reread the process's memory map, and constructors store their own fields without releasing a previous value.
    • --cpu-dispatch keeps x86-64-v3 code from running slower than the baseline.
    • Profile-guided builds compile the functions a profile never ran as cold code, and weigh profile matches by the profile's counts. The runtime profile is refreshed.
    • x86_64 musl executables use SSE2 memory routines and the runtime's rounding and libm definitions.
  • Faster builds:
    • The native compiler's optimization profile is refreshed: its code is 63.0 MB instead of 73.9 MB, so builds use less memory and run faster.
    • Optimized partitions of builds without a profile run a lighter simplification before they are linked, and builds that use a profile keep the full one. Partition placement stays stable when an edit adds a small helper, so more partitions stay cached.
    • A compile reads and stats each file once and reuses its type checker across module loads, and finished checker processes no longer stay as zombies.
    • The type checker executable is written once, and builds with an empty cache no longer hash the runtime pack's artifacts again.
    • Large modules decide in one pass whether fs reads must return Buffers.
  • Memory and size:
    • Programs return freed memory sooner: large strings are freed when they are released, writes of large shared views go out in chunks, and the main thread returns free pages when its event loop waits.
    • An HTTP response whose socket closes releases its completion callbacks, and the web view of an abandoned fetch body is released while its transfer runs.
    • Intl executables ship ICU data without the categories no Intl form reads (21.5 MB smaller), and the runtime pack is 24 MB smaller on Linux x64.
  • TypeScript: an import source clause from TypeScript 7.1 is reported as unsupported instead of failing the build, and more record, union and generic patterns compile: spreads of unions with an absent arm, literal-union keys of one field type, partial record assertions, records cast from unknown[] elements, and entries().filter in generic bodies.

v0.4.6

Choose a tag to compare

@github-actions github-actions released this 29 Oct 20:01
12e284a

0.4.6

Highlights

  • Node islands: programs can load Node.js in-process on first use and run npm packages that scriptc doesn't compile in Node islands.
    • Native values, closures and promises pass into islands by reference, and island handles, classes and collections can be used from compiled code.
    • Modules are shared between a program and its islands, and island modules are evaluated only when the program uses them.
    • Once Node loads, the runtime's event loop runs on Node's libuv, and programs exit cleanly with Node loaded.
  • New options:
    • --exceptions=rare unwinds through recursive functions instead of checking a status after each call.
    • --static builds static Linux executables (opt-in).
    • --large-pages moves executable code onto huge pages; --cpu-dispatch adds x86-64-v3 copies of hot functions.
    • Type patches replace a package's declaration files or supply missing declarations.
    • The shape overlay types JavaScript from facts the compiler proves (opt-in).
  • Faster programs:
    • Exceptions raise through cached unwind plans on x86-64 Linux, and every native function has an unwind table entry.
    • Ordinary objects are stored as a layout plus value slots; unions of classes are the instance pointer itself; class instances that stay in their frame use frame storage.
    • Object groups that die together are allocated in arenas, and the cycle collector does cheaper trial deletion and adapts its nursery trigger.
    • The speed runtime is optimized with a committed runtime profile, and Math functions are computed as V8 does, with libm linked only when needed.
    • Loops: short constant-count loops run together, for and while loops are versioned on the ranges their locals enter with, counted loops unroll by the period of a guard, and small leaf functions called from loops are inlined.
    • Typed arrays: header fields are read once at a speculative loop's entry, products under bitwise operators stay in 32 bits, and shared typed-array elements are read and written with plain accesses at a counted loop's counter.
    • Strings: UTF-16 indexes of long non-ASCII strings map in constant time, ASCII case converts eight bytes at a time, template literals allocate at their exact length, and strings iterate over their bytes.
    • Untyped code: class instance fields are read inline at dynamic property sites with two inline-cache entries, native arrays iterate without type-name scans, and numbers pass to directly called untyped functions as JavaScript values.
    • I/O: fs thread-pool completions and pool-thread writes make fewer syscalls; sockets, clients and agent entries register in constant time; fetch() reuses keep-alive connections; web streams, WritableStream and TransformStream run natively.
    • Large executables map their text with 2 MiB pages, and freed heap pages are returned to the system after the frontend is disposed.
  • Faster builds:
    • Small release and speed programs compile in balanced partitions, the entry module's top-level code gets its own partition, and unchanged partitions stay cached across edits.
    • Optimized executables that stay one partition generate code concurrently, and emission's workers stay busy.
    • The native compiler is laid out by context-sensitive profiles collected in two phases.
    • Type checking uses a shared tsc-ts server by default on Linux and macOS, and lib diagnostics are reused across options and module sets that cannot change them.
    • Rebuilds skip the helper version probe and the digests of unchanged files, and the CLI links without huge text pages, so cold builds use less memory.
  • Node.js parity:
    • Error.captureStackTrace, Node's navigator, builtin globals read through the global object, and unhandled rejections reported in programs without async work.
    • node:zlib is built on the zlib Node bundles; Response.clone and Request.clone work on typed values; fetch bodies have the members of a web ReadableStream; redirects whose chunked body has not ended are followed.
    • Classes that extend Array compile; strings spread into their code points; frozen checked records stay frozen; sloppy-mode this is the global object.
    • util.format runtime format strings, inspect.custom and colors, and untyped promisify.
  • TypeScript: scriptc reads TypeScript 7.1's binary AST format and API server beside 7.0's, and tsc-ts is updated to 0.0.10.

v0.4.5

Choose a tag to compare

@github-actions github-actions released this 25 Oct 20:51
4ee477d

0.4.5

Highlights

  • Fixes:
    • Release builds read a typed array's length without a header load that could read a freed view.
    • net.Socket write() returns Node's backpressure boolean and fires 'drain'.
    • Super calls in JavaScript method values call the declared base method.
    • Calling a non-function through an asserted callee throws Node's TypeError.
  • Faster programs:
    • Exceptions propagate by unwinding from directly called functions on Linux, instead of status checks after every call.
    • Typed-array loops prove their bounds and keep their views disjoint in more cases, including views over shared memory in worker programs; integer loops stay in integers across nested counted loops, pure helper calls, powers of two and byte reads.
    • Lone array callbacks run as loops with their bodies inlined, and row sums run two rows per pass.
    • Number-key maps look keys up on their integer view and write updates through the entry the read found; small number-key maps use a direct index.
    • Strings: base64 decodes eight characters at a time; Buffers fill, swap byte order and decode utf16le a word at a time; readline splits input in one scan; string scans over non-ASCII strings use their UTF-16 units.
    • Untyped code: property caches also cache absent properties, object literal members go through the store caches, calls on plain arrays skip the method ladder, and dynamic values dispose without key-cache walks.
    • JSON parses into discriminated unions, and dynamic values convert into nested unions, in one pass.
    • BigInt division settles quotients from the top limbs, and limb shifts are vectorized.
  • Faster builds:
    • Function bodies are emitted on up to eight workers, including in dev builds, and repeated work in LLVM module emission is cut.
    • Linux executables link with lld when the driver finds it, and unchanged executables are reused without selecting the runtime pack.
    • Dev builds reuse their frontend output after edits that keep token positions, and unchanged frontend inputs are checked by file identity before hashing.
    • Lowering fetches only the syntax trees it reads, and the large compiler tables are built on first use.
    • Release builds of the native compiler use a profile collected on the release commit.
    • Type checking can use a tsc-ts server shared by later builds (opt-in).
  • Node.js parity:
    • Crypto: RSA key generation lengths, RSA-PSS keys, encrypted private keys, X509Certificate fields and methods, and TLS certificates and tls.Server contexts.
    • zlib: Brotli compression and decompression.
    • Dates are mutable objects with Node's setters and conversions; URL hosts and paths parse as the URL Standard does, including non-ASCII hosts with UTS #46.
    • Math, JSON and Reflect are real objects in JavaScript; objects can inherit from JavaScript class prototypes; typed arrays held as untyped values support named properties and Symbol.species.
    • Error messages: JSON.parse syntax errors use V8's messages and positions; Object.getPrototypeOf of an Error answers Error.prototype.
    • util.inspect of dynamic classes, functions and %o arguments, and Symbol.toStringTag fields and getters, match Node.
  • npm packages: more npm and CommonJS dependencies compile statically, including namespace-style packages with guarded declarations and var exports, and untyped Maps, Sets and arrays are typed from their uses.

v0.4.4

Choose a tag to compare

@github-actions github-actions released this 24 Oct 05:00
f5972b0

0.4.4

Highlights

  • Fixes for 0.4.3:
    • Unchanged rebuilds hit the frontend cache again. An unchanged rebuild of a 23-module program takes 93 ms instead of 1.26 s, and of tsc-ts (dev) 1.6 s instead of 58.2 s.
    • The native compiler builds programs with several typed-array accesses at one masked index in a loop again, including tsc-ts's release build, which failed with SC3004.
    • x! returned from a function, used as an arrow function's result, or passed as an argument keeps null or undefined when the destination type holds it, as JavaScript does. x! operands of +, += and other conversions that hold undefined or null convert as Node does.
    • Effect 4 programs whose generators yield clients built by other generators run again.
    • The cache for untyped property access in programs that declare classes is reverted, fixing a crash in Effect 4 observability programs.
  • Faster:
    • Number compares and typed-array indexes use the exact integer of sums like i + 1: a heap sift-down over an Int32Array runs 17.5% faster.
    • glibc's per-thread heaps use huge pages: the VS Code type-check runs 3.4% faster.
    • Mid-size executables (over 1 MiB of LLVM text) build in at least eight partitions: the LLVM backend phase takes 34 to 74% less time for three.js programs of 1.4 to 7.8 MB.
    • JavaScript class instances are boxed at their exact class, and stores into nested string-keyed dictionaries keep class method calls direct.
    • --target-cpu selects an x86-64 microarchitecture level.
  • Node.js parity:
    • X25519 keys and crypto.diffieHellman; existsSync and fs.promises accept file: URLs; Buffer.from ranges over ArrayBuffers, the deprecated Buffer() constructor and Buffer as a value.
    • Dates are objects with identity; Promise.all over different entry types settles on Node's turns; completion callbacks run in their call's AsyncLocalStorage context.
    • TypedArray methods run on typed arrays held as untyped values, and unions of typed-array classes compile. Borrowed TypedArray.prototype.set calls compile and throw Node's TypeError for other targets.
    • Split CommonJS require bindings, __-prefixed exports, string-key destructuring of any values, and record key order for values JavaScript built.
  • npm packages: zod 4 and @hono/zod-validator values are projected at the declaration boundary.

v0.4.3

Choose a tag to compare

@github-actions github-actions released this 23 Oct 21:25
c70652d

0.4.3

Highlights

  • Faster checking and smaller builds with tsc-ts 0.0.9: scriptc checks with tsc-ts 0.0.9, and builds reuse the bundled declaration files' diagnostics instead of checking them again. Small IR builds are 10.6 to 16.0% faster and small executable builds 5.5 to 7.6% faster, with identical output. A native build of tsc-ts with this release checks 0.4 to 6.6% faster than with 0.4.2 and peaks 4 to 16% lower.
  • Integer arrays and loops:
    • Number arrays that only hold int32 values use 4-byte slots in locals, across direct calls and in module globals, and loop-carried locals written from their elements stay int32. A workload that walks arrays of cursors runs 54.5% faster.
    • Loops with two counters closing in on each other (for (let i = lo, j = hi; i < j; i++, j--)) and element copies through a[i]! run without per-element checks, and two-counter while loops run on integer counters.
    • Loops over typed arrays and charAt scans are versioned on their bounds, and ASCII strings are scanned as bytes.
  • Less work for untyped and dynamic code:
    • String-keyed record reads under ?? and ! no longer allocate: a dictionary-walking workload makes no union boxes instead of 363,359 and runs 12 to 15% faster.
    • Checked-dynamic method calls dispatch on method ids, observed class methods call natively while their slot is unchanged, and calls on bag-tracked class instances skip building their views.
  • Node.js parity:
    • crypto.getCipherInfo, AES-CCM, AES key wrap, the legacy createCipheriv modes, checkPrime/generatePrime, and Node's argument checks for generateKeyPair.
    • Streams: construct, writev, decodeStrings and defaultEncoding, including writev on Transform and PassThrough.
    • FinalizationRegistry, new.target, String.prototype.at on untyped values, Error.stackTraceLimit, spawnSync's env option, and class constructors called through call/apply throw Node's TypeError.
    • Records enumerate in the key order their object literal wrote, and objects converting to a union with an empty-record arm keep every field.
  • npm packages: zod 3 compiles natively and zod 4's core runs, minified packages such as marked compile, chalk 5 and hot-shots 8 build from built-in shims, and @hono/node-server 2.x is built in. User code types npm package declarations at a checked, identity-preserving boundary.
  • Builds: the cached build is reused after whitespace and comment edits, function bodies are emitted on forked workers, and the LLVM emitter reads its analyses from one recorded walk of each module.