Merge main

This commit is contained in:
Chris Tate
2026-10-20 17:46:26 -05:00
49 changed files with 308 additions and 52 deletions
+6
View File
@@ -219,6 +219,12 @@ jobs:
Move-Item "$expanded\clang+llvm-22.1.8-x86_64-pc-windows-msvc" $llvm
echo "LLVM_DIR=$llvm\lib\cmake\llvm" >> $env:GITHUB_ENV
echo "$llvm\bin" >> $env:GITHUB_PATH
# A musl CLI ships tsc-ts's musl executable, and these jobs run on glibc
# runners, where pnpm installs only the glibc platform packages.
- name: Let pnpm install musl platform packages
if: endsWith(matrix.cli_target, '-musl')
run: |
printf 'supportedArchitectures:\n libc: [current, musl]\n' >> pnpm-workspace.yaml
- run: pnpm install --frozen-lockfile
- name: Build native packages (POSIX)
if: matrix.platform != 'windows-x64'
+67 -2
View File
@@ -6,6 +6,73 @@ All notable changes to scriptc will be documented in this file.
<!-- release:start -->
## 0.4.0
### Breaking changes
- **tsc-ts is the default TypeScript 7 checker** on Linux and macOS. scriptc installs it with the compiler (`tsc-ts` 0.0.5 and its platform package for the host). Programs get the same diagnostics and generated code as with tsgo, and compiling takes less checker memory. `SCRIPTC_CHECKER=tsgo` selects tsgo, which stays the default on Windows. A build cache entry made with one checker isn't reused with the other.
- **`--npm-static=auto`, the default, compiles more packages:** packages typed by their DefinitelyTyped twin (`@types/<name>`), and untyped dependencies that another package's JavaScript imports. A program whose dependencies were refused before can now compile them, and a twin whose declarations disagree with the package's JavaScript fails the build at the use. Use `--npm-static <pkg,…>` to name the packages to compile.
- **Program type errors are reported as type errors** instead of as refusals of the npm package that contains them.
### Highlights
- **WebAssembly:** builds translate every WebAssembly module whose bytes they can see into native code, and `WebAssembly.compile`, `instantiate`, `validate`, `new WebAssembly.Module` and `new WebAssembly.Instance` run them. There is no interpreter. `WebAssembly.Memory`, `Table` and `Global`, `Module.imports`, `Module.exports` and `Module.customSections`, and function references from tables are supported, and modules can be sent to workers. `scriptc coverage` lists the modules a program loads, with `--wasm <file.wasm>` for modules loaded from paths the sources don't spell out.
- **Async functions that suspend only at their `await`s run as stackless coroutines** instead of on fibers with their own stack, so a suspended call keeps only the values live across its awaits.
- **Discriminated unions of records are the record pointer itself,** with no box around each value.
- **The dynamic tier computes with NaN-boxed values:** numbers, booleans, `undefined` and `null` between operators no longer allocate. `scriptc coverage --explain-dynamic` lists the functions that run in the dynamic tier and why, and `--tier=static-only` keeps a program fully static.
- **npm packages:** esbuild's bundled CommonJS chunks, CommonJS cycles and lazily initialized ES modules compile statically, as do classic CommonJS packages (late and JSON `require`s, require cycles), class hierarchies across a package's modules, and the packages unblocked by type-only imports, `@types` packages, `punycode` and `constants`. chalk 5 and the jose root entry compile.
### Features
- **`@scriptc/experimental` adds `ThreadSlots`,** one value per key and thread on shared objects, read without a lock.
- **`node:test` adds `mock.timers`, `getTestContext`, `t.assert.snapshot` and `t.assert.fileSnapshot`,** the `TestContext` values `filePath`, `attempt`, `passed`, `workerId` and `tags`, module-level `before` and `after` hooks, the `SuiteContext` and hook contexts, and unnamed `test` and `describe` calls. A timed-out test cancels its running subtests.
- **`node:worker_threads` adds `BroadcastChannel`,** `setEnvironmentData`/`getEnvironmentData`, `receiveMessageOnPort`, `markAsUntransferable`, `markAsUncloneable`, `threadName`, `resourceLimits.stackSizeMb`, the `stdout`/`stderr`/`stdin` options and streams, and `worker[Symbol.asyncDispose]`.
- **`node:net` adds `SocketAddress` and `BlockList`,** and `net.connect` resolves host names.
- **`node:http` pools keep-alive sockets in `Agent`s,** uses `globalAgent` for default requests, reads `http.request` options at run time, and speaks HTTP over Unix domain sockets. `fetch` tunnels through `HTTP_PROXY`/`HTTPS_PROXY` with `CONNECT`.
- **`DisposableStack`, `Float16Array`, `Error.isError`, `Promise.try` and the regular expression `v` flag** are supported.
- **Iterator helpers** run on generators, `Map` and `Set` iterators, string and typed array iterators, `Iterator.from` and untyped iterators.
- **`Object.groupBy` and `Map.groupBy`** group Sets, Maps and generators.
- **`v8`, `stream/web`, `dns/promises`, `EvalError` and `graceful-fs`** are available, and `Temporal` is reported as absent, as in Node 24.
- **`util.promisify`** works over the crypto callback functions, dns lookups, `child_process.exec`'s callback forms and callback-style functions.
- **`events.once`** takes an `AbortSignal` and works over streams.
- **`AsyncLocalStorage` stores** carry into timer, immediate, `process.nextTick` and microtask callbacks.
- **JavaScript classes compile like TypeScript classes** when their layout or JSDoc types allow it.
- **Classes that extend `EventEmitter` or a stream class can be used as values,** and so can `EventEmitter`, the stream classes and the error classes themselves. Constructor functions can inherit from them.
- **Class instances can be frozen and their fields redefined.**
- **Method overrides may differ from the method they override** in parameter and result types, as long as each value converts.
- **Library profiles add the `cstring` class.**
- **An experimental young-object nursery** for compiled classes and records is available at compile time with `SCRIPTC_YOUNG_NURSERY=1`.
### Performance
- **Dense arrays have head room,** so `shift`, `splice(0, n)` and `unshift` runs cost amortized O(1), and `pop` of a packed array is inline.
- **Index loops over arrays and typed arrays are versioned on one bounds guard,** with stable lengths proven and typed-array memory tagged for alias analysis.
- **Retain and release pairs are removed** through private-array precision and last-use moves, borrowed string slices go into stack buffers, and records, tuples and spread updates stay in frame storage.
- **SHA-384 and SHA-512 use AVX2, AVX-512VL or the arm64 SHA-512 instructions,** and MD5's block function is unrolled.
- **String character tests, optional-string `switch`es, `String.fromCharCode` and long-string `length` are faster,** and `Buffer.write` widens ASCII to UTF-16 sixteen bytes per step.
- **The cycle collector walks dead cycles once,** frees large dead cycle sets in address order and no longer creates self-capture cycles on the release fast path.
- **Workers return their memory** while the event loop waits and when they finish, and default-region memory is committed as it is used.
- **Lifetime regions carry across `await`,** and freed region chunks are reused.
- **Large closure environments compile in linear time,** and two frontend scans no longer repeat.
- **Programs keep stack frames only when they can read `stack`.** Before, a computed-key read on a dynamic value kept frames in every function; tsc-ts's own check is back to 0.3.6's speed.
- **The child-process runtime unit links only into programs that spawn.**
### Fixes
- **Node 24 parity:** `util.format`, `util.types` and `util.inspect`, `URLSearchParams`, `querystring` (null-prototype results), `string_decoder`, `os`, `events` and `timers/promises` match Node 24. Class instances, iterators, iterator helper objects and holes in arrays print as Node prints them, and `%d`/`%i` format dynamic arguments as Node does.
- **Silent wrong output is fixed** for object identity, `Object.prototype` values, `Date.parse` (V8's date grammar), stored builtins, constructor returns, `arr && arr[0]`, ES5 class names, builtin function values, uninitialized `let` writes, module-export `this` and `1 ** Infinity`.
- **Strings compare in UTF-16 code-unit order.**
- **Dates format with Node's local time zone names,** convert to local time once for their local getters, and follow `TZ` changes. `Date.prototype.toJSON` gives `null` for invalid dates.
- **Errors:** thrown Maps, Sets, typed arrays, functions and arrays keep their identity. Errors list their own name where they were created, errors held as `any` have their own `stack` accessor and convert with `Error.prototype.toString`, and errors that cross into dynamic code carry Node's error properties.
- **`matchAll` rows and `exec` results have `index`, `input` and `groups`,** and named groups keep their null prototype through optional chains.
- **Unhandled rejections are reported after each timer and immediate callback,** and async generator completions settle on Node's microtask turns.
- **Missing `Record` and index-signature keys read as `undefined`,** and generic functions that read a missing element return `undefined`.
- **Sockets:** `'data'` listeners get strings after `setEncoding`, socket and stream chunks are Buffers, and an idle pooled http socket that receives data is destroyed.
- **Diagnostic code frames window long source lines.**
- **npm crashes, internal errors and false TDZ errors** found while compiling popular packages are fixed.
<!-- release:end -->
## 0.3.6
### Features
@@ -38,8 +105,6 @@ All notable changes to scriptc will be documented in this file.
- **`Object.setPrototypeOf` returns primitive receivers,** and `createHmac` without a key throws Node's error.
- **Boxed records that hold an async function link the dynamic promise unit.**
<!-- release:end -->
## 0.3.5
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-darwin-arm64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for darwin-arm64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-darwin-x64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for darwin-x64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-linux-arm64-gnu",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for linux-arm64-gnu",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-linux-arm64-musl",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for linux-arm64-musl",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-linux-x64-gnu",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for linux-x64-gnu",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-linux-x64-musl",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for linux-x64-musl",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/cli-win32-x64-msvc",
"version": "0.3.6",
"version": "0.4.0",
"description": "Native scriptc compiler for win32-x64-msvc",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "scriptc",
"version": "0.3.6",
"version": "0.4.0",
"description": "Compile ordinary TypeScript and JavaScript to small, fast native executables and WebAssembly modules that run without Node.js",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/compiler",
"version": "0.3.6",
"version": "0.4.0",
"description": "The scriptc compiler — TypeScript/JavaScript frontend, typed IR, LLVM backend",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
@@ -36,7 +36,7 @@ export function emitStreamLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlV
host.emitPendingCheck();
return out;
}
if (e.fn === "stream.dynNative") {
if (e.fn === "stream.dynNative" || e.fn === "stream.dynNativePipe") {
// Checked-dynamic stream slots: the boxed native stream (+1) or NULL.
if (e.type.kind !== "union")
throw new InternalCompilerError(`llvm emitter bug: ${e.fn} result is not a union`);
@@ -47,11 +47,16 @@ export function emitStreamLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlV
throw new InternalCompilerError(`llvm emitter bug: ${e.fn} union lacks its arms`);
const value = host.emitExpr(e.args[0]!);
const side = host.emitExpr(e.args[1]!);
host.declare(`declare ptr @scr_stream_dyn_native(ptr, double)`);
// A pipe or unpipe destination in programs with constructor-function
// streams can be one of those streams too (scr_dyn_builtin_stream.c),
// as stream.unboxDyn does there.
const fn =
e.fn === "stream.dynNativePipe" && host.usesDynBuiltinStream
? "scr_dyn_builtin_stream_dyn_native"
: "scr_stream_dyn_native";
host.declare(`declare ptr @${fn}(ptr, double)`);
const raw = B.tmp();
B.line(
`${raw} = call ptr @scr_stream_dyn_native(ptr ${value.name}, double ${side.name}) ; +1 or NULL`,
);
B.line(`${raw} = call ptr @${fn}(ptr ${value.name}, double ${side.name}) ; +1 or NULL`);
return host.wrapNullable(raw, raw, def!.arms[streamTag]!, streamTag, e.type, nullTag);
}
if (e.fn === "stream.onData") {
@@ -58,11 +58,16 @@ export function nativeStreamClass(side: "r" | "w"): IrType {
/** The native stream of a checked-dynamic value (+1, typed as the side's
* class) or null: the stream.dynNative libCall, which tests the value's
* typed capsule without throwing. */
function dynNativeStream(lowerer: Lowerer, value: IrExpr, side: "r" | "w"): IrExpr {
function dynNativeStream(
lowerer: Lowerer,
value: IrExpr,
side: "r" | "w",
fn: "stream.dynNative" | "stream.dynNativePipe" = "stream.dynNative",
): IrExpr {
const native = nativeStreamClass(side);
return {
kind: "libCall",
fn: "stream.dynNative",
fn,
args: [value, numLit(side === "r" ? 1 : 2, value.loc)],
type: { kind: "union", unionId: lowerer.unions.intern([native, { kind: "nullT" }]) },
loc: value.loc,
@@ -268,8 +273,11 @@ export function lowerForAwaitDynStream(
}
/** A statement that runs `onNative` with the native stream a
* checked-dynamic value boxes (typed as the side's class), or `otherwise`
* for any other value. */
* checked-dynamic pipe or unpipe destination stands for (typed as the
* side's class), or `otherwise` for any other value. The destination's
* stream is a boxed native one or a constructor-function stream's
* (stream.dynNativePipe): only the runtime's pipe reads it, while member
* calls on a slot keep a constructor-function stream's own methods. */
export function dynNativeBranch(
lowerer: Lowerer,
value: IrExpr,
@@ -279,7 +287,7 @@ export function dynNativeBranch(
): IrStmt {
const loc = value.loc;
const nativeType = nativeStreamClass(side);
const nullable = dynNativeStream(lowerer, value, side);
const nullable = dynNativeStream(lowerer, value, side, "stream.dynNativePipe");
const nativeOrNull = lowerer.declareHiddenLocal("%streamNative", nullable.type);
const unionId = nullable.type.kind === "union" ? nullable.type.unionId : "";
const tag = lowerer.armTag(unionId, nativeType);
@@ -7228,7 +7228,7 @@ function wideningAddedProperties(
if ((prop.flags & ts.SymbolFlags.Optional) === 0 || prop.declarations.length === 0) continue;
if (prop.declarations.some((d) => d.parent === literal)) continue;
if ((ctx.checker.getTypeOfSymbol(prop).flags & ts.TypeFlags.Undefined) === 0) continue;
added.add(prop.name);
added.add(ts.symbolName(prop));
}
return added;
}
@@ -2689,6 +2689,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
* (1) or writable (2) side — or null for any other value. Never throws;
* the result union is `%Readable | null` or `%Writable | null`. */
"stream.dynNative": { argTypes: [DYN, F64], result: VOID },
/** stream.dynNative for a pipe or unpipe destination: also a
* constructor-function stream's runtime stream, in programs that link
* that unit, unless it overrides write() or end(). */
"stream.dynNativePipe": { argTypes: [DYN, F64], result: VOID },
/** readable.pipe(destination, end) into a checked-dynamic destination
* that is not a native stream: [source, the source boxed, destination,
* end]. Node's pipe listener protocol over the destination's members
+5 -1
View File
@@ -170,6 +170,7 @@ const STREAM_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
"stream.errored",
"stream.prop",
"stream.dynNative",
"stream.dynNativePipe",
"stream.pipeDyn",
"stream.unpipeDyn",
"stream.stdioWritable",
@@ -209,6 +210,7 @@ const READABLE_DYN_ASYNC_PREFIXES: readonly string[] = ["readable.fromGen", "rea
/** Entry points that link the dyn-invoke unit. */
const DYN_INVOKE_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
"stream.dynNative",
"stream.dynNativePipe",
"stream.pipeDyn",
"stream.unpipeDyn",
"dyn.arrayProtoCall",
@@ -568,7 +570,9 @@ const RUNTIME_FEATURE_RULES = {
dynTier: rule({ prefixes: ["js."] }),
/** Checked-dynamic stream slots test values and pipe into JavaScript
* stream objects (scr_stream_dyn.c). */
streamDyn: rule({ fns: ["stream.dynNative", "stream.pipeDyn", "stream.unpipeDyn"] }),
streamDyn: rule({
fns: ["stream.dynNative", "stream.dynNativePipe", "stream.pipeDyn", "stream.unpipeDyn"],
}),
/** The stream unit emits through the emitter unit, also when the
* program constructs no stream (a pipeline of functions). */
emitter: rule({
+1 -1
View File
@@ -5233,7 +5233,7 @@ function validateFunction(
}
return true;
}
if (e.fn === "stream.dynNative") {
if (e.fn === "stream.dynNative" || e.fn === "stream.dynNativePipe") {
const def = e.type.kind === "union" ? unions.get(e.type.unionId) : undefined;
const side = e.args[1];
const className =
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 2,
"compilerVersion": "0.3.6",
"compilerVersion": "0.4.0",
"coverage": [
"Entries are projected mechanically from the compiler's own decision tables: the diagnostics registry, the unsupported-syntax dispatch tables, the stdlib and node-builtin lowering tables, and the supported-builtin-module list. Nothing is hand-maintained; the manifest regenerates byte-identically from the source tree at this version.",
"Absence from this manifest means 'not projected', never 'unsupported'. Surfaces lowered through dedicated code paths rather than tables are not yet projected: console, JSON, Promise/async and the timer surface, the net/http/tls/https/dgram/dns/assert/test/stream/readline module member surfaces, template literals, the regex slice, global functions (parseInt, parseFloat, isNaN, isFinite), and the process surface outside its ambient slice.",
@@ -0,0 +1,7 @@
{
"order": [
"<repo>/tests/corpus/5820-node-stream-dyn-constructor-overrides/sources.js",
"<repo>/tests/corpus/5820-node-stream-dyn-constructor-overrides/main.ts"
],
"diags": []
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/experimental",
"version": "0.3.6",
"version": "0.4.0",
"description": "Experimental scriptc APIs, with Node.js implementations: lifetime regions for ProgramLifetime objects, intern tables shared between threads, and per-thread slots on shared objects",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-darwin-arm64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Pinned LLVM code-generation helper for scriptc on macOS arm64",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-darwin-x64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Pinned LLVM code-generation helper for scriptc on macOS x64",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-linux-arm64-gnu",
"version": "0.3.6",
"version": "0.4.0",
"description": "Pinned LLVM code-generation helper for scriptc on Linux arm64",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-linux-arm64-musl",
"version": "0.3.6",
"version": "0.4.0",
"description": "Statically linked LLVM code-generation helper for scriptc on Linux arm64 musl",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-linux-x64-gnu",
"version": "0.3.6",
"version": "0.4.0",
"description": "Pinned LLVM code-generation helper for scriptc on Linux x64",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-linux-x64-musl",
"version": "0.3.6",
"version": "0.4.0",
"description": "Statically linked LLVM code-generation helper for scriptc on Linux x64 musl",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/llvm-win32-x64-msvc",
"version": "0.3.6",
"version": "0.4.0",
"description": "Pinned LLVM code-generation helper for scriptc on Windows x64",
"license": "Apache-2.0 WITH LLVM-exception",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-android-arm64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc library runtime pack for Android arm64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-darwin-arm64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for macOS arm64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-darwin-x64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for macOS x64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-ios-arm64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc library runtime pack for iOS arm64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-ios-simulator-arm64",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc library runtime pack for iOS simulator arm64",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-linux-arm64-gnu",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for Linux arm64 glibc",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-linux-arm64-musl",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for Linux arm64 musl",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-linux-x64-gnu",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for Linux x64 glibc",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-linux-x64-musl",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for Linux x64 musl",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-wasm32-wasi",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for WASI Preview 1",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime-win32-x64-msvc",
"version": "0.3.6",
"version": "0.4.0",
"description": "Precompiled scriptc runtime pack for Windows x64 MSVC",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/runtime",
"version": "0.3.6",
"version": "0.4.0",
"description": "scriptc native runtime sources and vendored dependencies",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+4
View File
@@ -1192,6 +1192,10 @@ ScrDyn *scr_dyn_builtin_stream_object(struct ScrStream *s);
ScrDyn *scr_dyn_builtin_stream_view(struct ScrStream *s);
struct ScrStream *scr_dyn_builtin_stream_check(const ScrDyn *value, double kind);
struct ScrStream *scr_dyn_builtin_stream_unbox(const ScrDyn *value, const ScrStr *message);
/* stream.dynNative in those programs: scr_stream_dyn_native's contract,
* also taking constructor-function streams that keep the runtime's write
* and end (+1 or NULL). */
struct ScrStream *scr_dyn_builtin_stream_dyn_native(const ScrDyn *value, double side);
bool scr_dyn_class_is_key(const ScrDyn *value, const ScrStr *type_key);
bool scr_dyn_class_is(const ScrDyn *value, const char *type_key);
/* Calls a dyn value: a non-function kind throws the catchable TypeError
@@ -1673,6 +1673,31 @@ ScrStream *scr_dyn_builtin_stream_check(const ScrDyn *value, double kind) {
return ok ? scr_stream_retain(s) : NULL;
}
/* stream.dynNative (checked-dynamic stream slots, scr_stream_dyn.c) in
* programs that link this unit: a constructor-function stream with the
* requested side (1 readable, 2 writable) answers its runtime stream (+1),
* so a pipe into it, and an unpipe of it, are the runtime's own, unless it
* overrides write() or end(), which the runtime's pipe would bypass: that
* one is NULL and pipes through its JavaScript members. Other values take
* scr_stream_dyn_native. Never throws. */
ScrStream *scr_dyn_builtin_stream_dyn_native(const ScrDyn *value, double side) {
ScrStream *s = value->kind == SCR_DYN_OBJ ? bcs_of(value) : NULL;
if (s) {
const ScrVt *vt = s->vt;
bool readable = vt->pre >= scr_readable_vt.pre && vt->pre <= scr_readable_vt.post;
bool writable = vt->pre >= scr_writable_vt.pre && vt->pre <= scr_writable_vt.post;
bool duplex = vt->pre >= scr_duplex_vt.pre && vt->pre <= scr_duplex_vt.post;
if (!(duplex || (side == 1 ? readable : writable))) return NULL;
ScrDyn *object = (ScrDyn *)value;
if (side == 2 && (bcs_overrides(object, "write") || bcs_overrides(object, "end"))) {
if (scr_exc_pending()) scr_exc_clear();
return NULL;
}
return scr_stream_retain(s);
}
return scr_stream_dyn_native(value, side);
}
/* stream.unboxDyn (an untyped pipe destination or pipeline stage) in
* programs that link this unit: a constructor-function stream answers its
* runtime stream (+1), unless it overrides write() or end(), which the
+44 -3
View File
@@ -306,9 +306,50 @@ static void scr_wasm_throw_trap(int code) {
/* AddressSanitizer intercepts siglongjmp and warns when it cannot tell the
* extent of the running stack, as on the runtime's async fibers. Sanitized
* builds therefore jump with the compiler's builtin pair, which no runtime
* intercepts, and unpoison the abandoned frames themselves. */
#ifdef SCR_WASM_ASAN
* builds therefore jump with a pair no runtime intercepts and unpoison the
* abandoned frames themselves: the compiler's builtin pair, or on AArch64,
* where the compiler has no builtin setjmp lowering, the pair below. */
#if defined(SCR_WASM_ASAN) && defined(__aarch64__)
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
/* x19-x28, the frame and link registers, sp and d8-d15: the registers the
* AArch64 procedure call standard has a callee preserve. */
typedef uint64_t ScrWasmJmp[21];
__attribute__((naked, noinline, returns_twice)) static int scr_wasm_setjmp(ScrWasmJmp buf) {
__asm__ volatile("stp x19, x20, [x0, #0]\n"
"stp x21, x22, [x0, #16]\n"
"stp x23, x24, [x0, #32]\n"
"stp x25, x26, [x0, #48]\n"
"stp x27, x28, [x0, #64]\n"
"stp x29, x30, [x0, #80]\n"
"mov x16, sp\n"
"str x16, [x0, #96]\n"
"stp d8, d9, [x0, #104]\n"
"stp d10, d11, [x0, #120]\n"
"stp d12, d13, [x0, #136]\n"
"stp d14, d15, [x0, #152]\n"
"mov w0, #0\n"
"ret\n");
}
/* Returns 1 from the scr_wasm_setjmp call that filled buf. */
__attribute__((naked, noinline, noreturn)) static void scr_wasm_longjmp(ScrWasmJmp buf) {
__asm__ volatile("ldp x19, x20, [x0, #0]\n"
"ldp x21, x22, [x0, #16]\n"
"ldp x23, x24, [x0, #32]\n"
"ldp x25, x26, [x0, #48]\n"
"ldp x27, x28, [x0, #64]\n"
"ldp x29, x30, [x0, #80]\n"
"ldr x16, [x0, #96]\n"
"mov sp, x16\n"
"ldp d8, d9, [x0, #104]\n"
"ldp d10, d11, [x0, #120]\n"
"ldp d12, d13, [x0, #136]\n"
"ldp d14, d15, [x0, #152]\n"
"mov w0, #1\n"
"ret\n");
}
#define SCR_WASM_SETJMP(buf) scr_wasm_setjmp(buf)
#define SCR_WASM_LONGJMP(buf) scr_wasm_longjmp(buf)
#elif defined(SCR_WASM_ASAN)
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
typedef void *ScrWasmJmp[5];
#define SCR_WASM_SETJMP(buf) __builtin_setjmp(buf)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/threads",
"version": "0.3.6",
"version": "0.4.0",
"description": "Zero-copy publication of immutable object graphs to worker threads (scriptc), with a deep-freezing Node.js implementation",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@scriptc/types",
"version": "0.3.6",
"version": "0.4.0",
"description": "Contract types for scriptc: type-only annotations (Int32, NoThrow, Immutable) that scriptc checks and optimizes, with no runtime semantics",
"license": "Apache-2.0",
"homepage": "https://scriptc.dev",
+9
View File
@@ -1374,6 +1374,11 @@ packages:
'@ts-morph/common@0.11.1':
resolution: {integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==}
'@tsc-ts/darwin-arm64@0.0.5':
resolution: {integrity: sha512-yP64b2VpAKWG92KepBqxs1P89wpwiheYwNMk4EmmhSl6aw/ImIBrdHYNjvao3sg6FHZCXTc+PB6Vukh6hyQiJQ==}
cpu: [arm64]
os: [darwin]
'@tsc-ts/darwin-x64@0.0.5':
resolution: {integrity: sha512-CEeHOFfpx5H9V3WSq+V7XGU8tfc06nRN818auiRVwuwPHvpti9gHq0HYkvf/43PH1pIGcgf68sQYdIxJhrQDKQ==}
cpu: [x64]
@@ -3442,6 +3447,9 @@ snapshots:
mkdirp: 1.0.4
path-browserify: 1.0.1
'@tsc-ts/darwin-arm64@0.0.5':
optional: true
'@tsc-ts/darwin-x64@0.0.5':
optional: true
@@ -4800,6 +4808,7 @@ snapshots:
dependencies:
'@scriptc/threads': 0.3.6
optionalDependencies:
'@tsc-ts/darwin-arm64': 0.0.5
'@tsc-ts/darwin-x64': 0.0.5
'@tsc-ts/linux-arm64-gnu': 0.0.5
'@tsc-ts/linux-arm64-musl': 0.0.5
@@ -0,0 +1,35 @@
// Constructor-function streams in NodeJS.ReadableStream / WritableStream
// slots: member calls run the stream's own pause, resume and on, and a pipe
// into one, and an unpipe of it, are the runtime's.
import { Readable } from "node:stream";
import { collector, recordingSource } from "./sources.js";
function control(stream: NodeJS.ReadableStream): void {
stream.pause();
stream.resume();
stream.on("end", () => {});
}
function attach(src: Readable, dst: NodeJS.WritableStream): void {
src.pipe(dst);
}
function detach(src: Readable, dst: NodeJS.WritableStream): void {
src.unpipe(dst);
}
const recording = recordingSource();
control(recording);
console.log("recorded", recording.calls.join(","));
const sink = collector();
sink.on("pipe", () => console.log("pipe event"));
sink.on("unpipe", () => console.log("unpipe event"));
const source = new Readable({ read() {} });
attach(source, sink);
source.push("a");
setImmediate(() => {
detach(source, sink);
source.push("b");
setImmediate(() => console.log("collected", sink.chunks.join(","), source.isPaused()));
});
@@ -0,0 +1,39 @@
// Constructor-function streams that override stream methods, and a plain
// JavaScript sink.
import { Readable, Writable } from "node:stream";
import util from "node:util";
/** A Readable whose pause, resume and on record their calls before
* running Readable's own. */
function Recording() {
Readable.call(this, { read() {} });
this.calls = [];
}
util.inherits(Recording, Readable);
for (const name of ["pause", "resume", "on"]) {
Recording.prototype[name] = function (...args) {
this.calls.push(name);
return Readable.prototype[name].apply(this, args);
};
}
/** @returns {any} */
export function recordingSource() {
return new Recording();
}
/** A Writable without overrides, piped into natively. */
function Collector() {
Writable.call(this);
this.chunks = [];
}
util.inherits(Collector, Writable);
Collector.prototype._write = function (chunk, _encoding, done) {
this.chunks.push(String(chunk));
done();
};
/** @returns {any} */
export function collector() {
return new Collector();
}
@@ -32,9 +32,12 @@ fs.writeFileSync(file, "file contents\n");
const stream = fs.createReadStream(file);
const release = (): void => console.log("released");
stream.on("data", (chunk: Buffer | string) => describe("file", chunk));
stream.on("close", release);
stream.on("error", release);
stream.on("end", () => {
// The second read starts once the first stream has closed its file, so the
// order of its 'close' and the second stream's data does not depend on
// which file operation finishes first.
stream.on("close", () => {
release();
const encoded = fs.createReadStream(file, { encoding: "utf8" });
encoded.on("data", (chunk: Buffer | string) => describe("file utf8", chunk));
encoded.on("close", () => {
@@ -0,0 +1 @@
{ "plain": 3490, "san": 8050 }