From d8265b220922c8d756a230de7423f3facfa51c37 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Wed, 21 Oct 2026 12:35:58 -0500 Subject: [PATCH] Link scr_bytes_dyn.c into libraries --- packages/compiler/src/backend/library-pack.ts | 1 + .../compiler/src/backend/native/contracts.ts | 3 + .../compiler/src/backend/native/library.ts | 1 + packages/compiler/src/index.ts | 2 + .../compiler/src/library/library-cache.ts | 2 + .../runtime-pack-common/library-matrix.mjs | 1 + packages/runtime/src/scr_bytes.c | 60 +++++++++++++++++++ packages/runtime/src/scr_bytes_io.c | 60 ------------------- 8 files changed, 70 insertions(+), 60 deletions(-) diff --git a/packages/compiler/src/backend/library-pack.ts b/packages/compiler/src/backend/library-pack.ts index 8f6dbb221..805198df1 100644 --- a/packages/compiler/src/backend/library-pack.ts +++ b/packages/compiler/src/backend/library-pack.ts @@ -33,6 +33,7 @@ export async function compilePackedLibrary( assert: options.assert ?? false, inspect: options.inspect ?? false, dynInvoke: options.dynInvoke ?? false, + ...(options.dynBuffer === true ? { dynBuffer: true } : {}), ...(options.dynTier === true ? { dynTier: true } : {}), dc: false, dynAsync: false, diff --git a/packages/compiler/src/backend/native/contracts.ts b/packages/compiler/src/backend/native/contracts.ts index a3c0a62fe..06a41c9b2 100644 --- a/packages/compiler/src/backend/native/contracts.ts +++ b/packages/compiler/src/backend/native/contracts.ts @@ -321,6 +321,9 @@ export interface LibArchiveOptions { threadInstances?: boolean; /** IR-detected link gates (the compileC precedent, refusal-narrowed). */ dynInvoke?: boolean; + /** Buffer.prototype methods on checked-dynamic values: scr_bytes_dyn.c, + * with dynInvoke. */ + dynBuffer?: boolean; /** See CompileOptions.dynTier: the scr_js_* units. */ dynTier?: boolean; regex?: boolean; diff --git a/packages/compiler/src/backend/native/library.ts b/packages/compiler/src/backend/native/library.ts index 3907fd5d8..f04e9926c 100644 --- a/packages/compiler/src/backend/native/library.ts +++ b/packages/compiler/src/backend/native/library.ts @@ -112,6 +112,7 @@ export async function compileLibArchive(opts: LibArchiveOptions): Promise ...(opts.assert && opts.bigint ? ["scr_bigint_assert.c"] : []), ...(opts.emitter ? ["scr_events_emitter.c", "scr_dyn_handle.c"] : []), ...(opts.dynInvoke ? ["scr_dyn_invoke.c"] : []), + ...(opts.dynInvoke && opts.dynBuffer ? ["scr_bytes_dyn.c"] : []), ...(opts.dynTier === true ? ["scr_js_value.c", "scr_js_object.c"] : []), ...(opts.zlib ? ["scr_zlib.c"] : []), ...(opts.copying ? ["scr_copying.c"] : []), diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index 461a32c90..08457f86b 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -1706,6 +1706,7 @@ function libraryNativeFeatures(mod: IrModule, backend: "llvm"): EarlyLibraryNati copying: features.copying, textDecoderLegacy: features.legacyTextDecoder, dynInvoke: features.dynInvoke, + ...(features.dynBuffer ? { dynBuffer: true } : {}), ...(features.dynTier ? { dynTier: true } : {}), ...(mod.lib?.identity !== undefined ? { buildId: mod.lib.identity.buildId } : {}), }; @@ -1800,6 +1801,7 @@ async function compileLibraryNative( copying: features.copying, textDecoderLegacy: features.textDecoderLegacy, dynInvoke: features.dynInvoke, + ...(features.dynBuffer === true ? { dynBuffer: true } : {}), ...(features.dynTier === true ? { dynTier: true } : {}), }; if (packTarget !== null) await compilePackedLibrary(archiveOptions, packTarget); diff --git a/packages/compiler/src/library/library-cache.ts b/packages/compiler/src/library/library-cache.ts index 356056dd8..3e77ccced 100644 --- a/packages/compiler/src/library/library-cache.ts +++ b/packages/compiler/src/library/library-cache.ts @@ -63,6 +63,8 @@ interface EarlyLibraryCacheStamp { export interface EarlyLibraryNativeFeatures { backend: "llvm"; dynInvoke: boolean; + /** Buffer.prototype methods on checked-dynamic values (scr_bytes_dyn.c). */ + dynBuffer?: boolean; dynTier?: boolean; regex: boolean; assert: boolean; diff --git a/packages/runtime-pack-common/library-matrix.mjs b/packages/runtime-pack-common/library-matrix.mjs index 667f9a34f..d9878ec7c 100644 --- a/packages/runtime-pack-common/library-matrix.mjs +++ b/packages/runtime-pack-common/library-matrix.mjs @@ -15,6 +15,7 @@ export function withLibraryRuntimeFlavors(matrix) { "scr_zlib.c", "scr_copying.c", "scr_dyn_invoke.c", + "scr_bytes_dyn.c", "scr_js_value.c", "scr_js_object.c", ]); diff --git a/packages/runtime/src/scr_bytes.c b/packages/runtime/src/scr_bytes.c index 69e26e5d7..7a6a76cf9 100644 --- a/packages/runtime/src/scr_bytes.c +++ b/packages/runtime/src/scr_bytes.c @@ -3957,3 +3957,63 @@ ScrStr *scr_bytes_join(const ScrBytes *b, const ScrStr *separator) { scr_arr_release(values); return out; } + +/* ── the checked-dynamic Buffer compare/equals validators ────────────── + * Node's argument ladders for buf.equals / buf.compare / Buffer.compare + * over dyn-boxed arguments (the invalid-input probes: string needles, + * '0' offsets, null/object range args). A well-typed dyn still computes + * the real answer — validation, not a constant fence. */ + +/* A bytes payload or the API's own ERR_INVALID_ARG_TYPE (borrowed). */ +static ScrBytes *scr_bytes_chk_u8(const ScrDyn *d, const char *argname) { + if (!scr_dyn_bytes_is(d, SCR_BYTES_U8)) { + scr_dyn_arg_type_fail(argname, "an instance of Buffer or Uint8Array", d); + return NULL; + } + return d->v.bytes; +} + +double scr_buffer_compare_chk(const ScrDyn *a, const ScrDyn *b) { + ScrBytes *b1 = scr_bytes_chk_u8(a, "buf1"); + if (!b1) return 0; + ScrBytes *b2 = scr_bytes_chk_u8(b, "buf2"); + if (!b2) return 0; + return scr_bytes_compare(b1, b2, 0, 0, 0, 0, 0); +} + +bool scr_bytes_equals_chk(const ScrBytes *recv, const ScrDyn *other) { + ScrBytes *o = scr_bytes_chk_u8(other, "otherBuffer"); + if (!o) return false; + return scr_bytes_equals(recv, o); +} + +/* One offset slot: undefined takes the Node default, non-numbers throw + * ERR_INVALID_ARG_TYPE "of type number", numbers run validateOffset. */ +static bool scr_bytes_chk_off(const ScrDyn *d, const char *name, double max, + double dflt, double *out) { + if (d->kind == SCR_DYN_UNDEF) { + *out = dflt; + return true; + } + if (d->kind != SCR_DYN_NUM) { + scr_dyn_arg_type_fail(name, "of type number", d); + return false; + } + *out = d->v.num; + return scr_bytes_validate_off(name, *out, max); +} + +double scr_bytes_compare_chk(const ScrBytes *src, const ScrDyn *target, + const ScrDyn *ts, const ScrDyn *te, + const ScrDyn *ss, const ScrDyn *se) { + ScrBytes *t = scr_bytes_chk_u8(target, "target"); + if (!t) return 0; + double tsv, tev, ssv, sev; + if (!scr_bytes_chk_off(ts, "targetStart", 9007199254740991.0, 0, &tsv)) return 0; + if (!scr_bytes_chk_off(te, "targetEnd", (double)t->len, (double)t->len, &tev)) return 0; + if (!scr_bytes_chk_off(ss, "sourceStart", 9007199254740991.0, 0, &ssv)) return 0; + if (!scr_bytes_chk_off(se, "sourceEnd", (double)src->len, (double)src->len, &sev)) return 0; + /* Every slot validated or defaulted above; nargs 4 revalidates the + * now-known-good numbers (a no-op) and keeps one comparison core. */ + return scr_bytes_compare(src, t, 4, tsv, tev, ssv, sev); +} diff --git a/packages/runtime/src/scr_bytes_io.c b/packages/runtime/src/scr_bytes_io.c index c852388e9..eb3661fc6 100644 --- a/packages/runtime/src/scr_bytes_io.c +++ b/packages/runtime/src/scr_bytes_io.c @@ -1368,66 +1368,6 @@ ScrBytes *scr_buffer_from_dyn_ref_pooled(const ScrDyn *value, const ScrStr *enco return scr_buffer_from_dyn_pooled(value, encoding); } -/* ── the checked-dynamic Buffer compare/equals validators ────────────── - * Node's argument ladders for buf.equals / buf.compare / Buffer.compare - * over dyn-boxed arguments (the invalid-input probes: string needles, - * '0' offsets, null/object range args). A well-typed dyn still computes - * the real answer — validation, not a constant fence. */ - -/* A bytes payload or the API's own ERR_INVALID_ARG_TYPE (borrowed). */ -static ScrBytes *scr_bytes_chk_u8(const ScrDyn *d, const char *argname) { - if (!scr_dyn_bytes_is(d, SCR_BYTES_U8)) { - scr_dyn_arg_type_fail(argname, "an instance of Buffer or Uint8Array", d); - return NULL; - } - return d->v.bytes; -} - -double scr_buffer_compare_chk(const ScrDyn *a, const ScrDyn *b) { - ScrBytes *b1 = scr_bytes_chk_u8(a, "buf1"); - if (!b1) return 0; - ScrBytes *b2 = scr_bytes_chk_u8(b, "buf2"); - if (!b2) return 0; - return scr_bytes_compare(b1, b2, 0, 0, 0, 0, 0); -} - -bool scr_bytes_equals_chk(const ScrBytes *recv, const ScrDyn *other) { - ScrBytes *o = scr_bytes_chk_u8(other, "otherBuffer"); - if (!o) return false; - return scr_bytes_equals(recv, o); -} - -/* One offset slot: undefined takes the Node default, non-numbers throw - * ERR_INVALID_ARG_TYPE "of type number", numbers run validateOffset. */ -static bool scr_bytes_chk_off(const ScrDyn *d, const char *name, double max, - double dflt, double *out) { - if (d->kind == SCR_DYN_UNDEF) { - *out = dflt; - return true; - } - if (d->kind != SCR_DYN_NUM) { - scr_dyn_arg_type_fail(name, "of type number", d); - return false; - } - *out = d->v.num; - return scr_bytes_validate_off(name, *out, max); -} - -double scr_bytes_compare_chk(const ScrBytes *src, const ScrDyn *target, - const ScrDyn *ts, const ScrDyn *te, - const ScrDyn *ss, const ScrDyn *se) { - ScrBytes *t = scr_bytes_chk_u8(target, "target"); - if (!t) return 0; - double tsv, tev, ssv, sev; - if (!scr_bytes_chk_off(ts, "targetStart", 9007199254740991.0, 0, &tsv)) return 0; - if (!scr_bytes_chk_off(te, "targetEnd", (double)t->len, (double)t->len, &tev)) return 0; - if (!scr_bytes_chk_off(ss, "sourceStart", 9007199254740991.0, 0, &ssv)) return 0; - if (!scr_bytes_chk_off(se, "sourceEnd", (double)src->len, (double)src->len, &sev)) return 0; - /* Every slot validated or defaulted above; nargs 4 revalidates the - * now-known-good numbers (a no-op) and keeps one comparison core. */ - return scr_bytes_compare(src, t, 4, tsv, tev, ssv, sev); -} - /* new Buffer(number, encoding) — the deprecated ctor's string arm with a * non-string first argument: Node's exact ERR_INVALID_ARG_TYPE (the * throwing path never fires DEP0005, so compiled silence matches). */