Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/cli/test/native-link-info.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
minimum_os: "14.0",
},
program: { object, entry_symbol: "main" },
runtime_abi: { version: 43, marker: "scr_runtime_abi_v43" },
runtime_abi: { version: 44, marker: "scr_runtime_abi_v44" },
runtime_pack: {
kind: "precompiled",
package: "@scriptc/runtime-darwin-arm64",
Expand Down Expand Up @@ -165,6 +165,6 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
() => null,
(failure: { stderr?: string }) => failure,
);
expect(error?.stderr).toContain("scr_runtime_abi_v43");
expect(error?.stderr).toContain("scr_runtime_abi_v44");
});
});
4 changes: 2 additions & 2 deletions packages/compiler/src/backend/runtime-abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* of the link contract: a program object keeps this symbol undefined and the
* matching runtime defines it, so incompatible manual links fail before the
* program can start. */
export const RUNTIME_ABI_VERSION = 43 as const;
export const RUNTIME_ABI_MARKER = "scr_runtime_abi_v43" as const;
export const RUNTIME_ABI_VERSION = 44 as const;
export const RUNTIME_ABI_MARKER = "scr_runtime_abi_v44" as const;

/** Object consumption is useful today, but the complete scr_* surface may
* still change before 1.0. The versioned marker prevents accidental mixing;
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/backend/runtime-pack-targets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async function fixture(target: NativeTargetSpec, workers = false) {
object_format: target.objectFormat,
minimum_os: target.minimumOs,
},
runtime_abi: { version: 43, marker: "scr_runtime_abi_v43" },
runtime_abi: { version: 44, marker: "scr_runtime_abi_v44" },
compiler: { command: "fixture", identity: "fixture", target: target.llvmTriple },
macros: { executable: [], excluded: ["SCR_LIB"], sanitizer: "external-toolchain-required" },
flavors: {
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/backend/runtime-pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async function fixture(target: NativeTargetSpec = MACOS_ARM64_TARGET) {
object_format: target.objectFormat,
minimum_os: target.minimumOs,
},
runtime_abi: { version: 43, marker: "scr_runtime_abi_v43" },
runtime_abi: { version: 44, marker: "scr_runtime_abi_v44" },
compiler: {
command: "clang",
identity: "fixture clang",
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler/src/frontend/lowering/lower-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,10 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
if (!argNode || (ts.isIdentifier(argNode) && argNode.text === "undefined")) {
return { kind: "libCall", fn: "sym.newAnon", args: [], type: SYMBOL_T, loc };
}
const desc = lowerer.lowerExpr(argNode);
let desc = lowerer.lowerExpr(argNode);
// A number or boolean description is its ToString.
if (desc.type.kind === "f64" || desc.type.kind === "bool")
desc = lowerer.ensureString(desc, argNode);
if (desc.type.kind !== "string") {
lowerer.noLowering(
`Symbol with a '${lowerer.fmt(desc.type)}' description`,
Expand Down
7 changes: 5 additions & 2 deletions packages/compiler/test/llvm-runtime-abi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,13 @@ describe("LLVM backend declares match scr_runtime.h prototypes", () => {
#include "scr_runtime.h"

/* LLVM emits: %ScrBytes = type { i64, i64, i32, ptr, ptr, i8, i8, i8, i8, ptr }.
* Keep every ABI fact used by its field-index GEPs explicit here. */
* Keep every ABI fact used by its field-index GEPs explicit here. The C
* struct continues with fields only the runtime reads. */
_Static_assert(sizeof(size_t) == 8, "LLVM ScrBytes expects 64-bit size_t");
_Static_assert(sizeof(void *) == 8, "LLVM ScrBytes expects 64-bit pointers");
_Static_assert(sizeof(ScrBytesElem) == 4, "LLVM ScrBytes elem field is i32");
_Static_assert(_Alignof(ScrBytes) == 8, "LLVM ScrBytes alignment changed");
_Static_assert(sizeof(ScrBytes) == 56, "LLVM ScrBytes size changed");
_Static_assert(sizeof(ScrBytes) == 64, "ScrBytes size changed");
_Static_assert(offsetof(ScrBytes, rc) == 0, "LLVM ScrBytes.rc offset changed");
_Static_assert(offsetof(ScrBytes, len) == 8, "LLVM ScrBytes.len offset changed");
_Static_assert(offsetof(ScrBytes, elem) == 16, "LLVM ScrBytes.elem offset changed");
Expand All @@ -370,7 +371,9 @@ _Static_assert(offsetof(ScrBytes, is_buffer) == 40, "LLVM ScrBytes.is_buffer off
_Static_assert(offsetof(ScrBytes, is_data_view) == 41, "LLVM ScrBytes.is_data_view offset changed");
_Static_assert(offsetof(ScrBytes, external) == 42, "LLVM ScrBytes.external offset changed");
_Static_assert(offsetof(ScrBytes, untransferable) == 43, "LLVM ScrBytes.untransferable offset changed");
_Static_assert(offsetof(ScrBytes, properties_traced) == 44, "ScrBytes.properties_traced must stay in padding");
_Static_assert(offsetof(ScrBytes, shared) == 48, "LLVM ScrBytes.shared offset changed");
_Static_assert(offsetof(ScrBytes, properties) == 56, "ScrBytes.properties must follow the LLVM prefix");
`,
);
const driver = resolveCc();
Expand Down
4 changes: 2 additions & 2 deletions packages/compiler/test/native-codegen-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ done:
expect(await symbols(helperObject, ["-u"])).toEqual(await symbols(clangObject, ["-u"]));
expect(await symbols(helperObject, ["-gU"])).toEqual(await symbols(clangObject, ["-gU"]));
expect(await symbols(helperObject, ["-gU"])).toEqual(["0000000000000000 T _main"]);
expect(await symbols(helperObject, ["-u"])).toContain("_scr_runtime_abi_v43");
expect(await symbols(helperObject, ["-u"])).toContain("_scr_runtime_abi_v44");

const clangExe = join(dir, "clang-program");
const linkDriver = join(dir, "clang-driver.c");
Expand Down Expand Up @@ -712,7 +712,7 @@ done:
.split("\n");
expect(undefinedSymbols).toContain("_sf_scale");
expect(undefinedSymbols).toContain("_sf_callback_mix");
expect(undefinedSymbols).toContain("_scr_runtime_abi_v43");
expect(undefinedSymbols).toContain("_scr_runtime_abi_v44");
});

test("helper and clang object paths retain the same outbound FFI C ABI", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/7753-typed-array-named-properties.js"
],
"diags": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/8321-typed-array-constructor-kind.js"
],
"diags": []
}
2 changes: 1 addition & 1 deletion packages/runtime-darwin-arm64/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ async function build() {
package: packageManifest.name,
version: packageManifest.version,
target: RUNTIME_PACK_MATRIX.target,
runtime_abi: { version: 43, marker: "scr_runtime_abi_v43" },
runtime_abi: { version: 44, marker: "scr_runtime_abi_v44" },
compiler: {
command: compiler,
identity: compilerVersion,
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-pack-common/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ async function build() {
package: packageManifest.name,
version: packageManifest.version,
target: matrix.target,
runtime_abi: { version: 43, marker: "scr_runtime_abi_v43" },
runtime_abi: { version: 44, marker: "scr_runtime_abi_v44" },
compiler: {
command: compiler,
identity: compilerVersion,
Expand Down
13 changes: 12 additions & 1 deletion packages/runtime/src/scr_bytes.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ ScrBytes *scr_bytes_take_data(uint8_t *data, size_t len) {
b->external = false;
b->untransferable = false;
b->shared = NULL;
b->properties = NULL;
b->properties_traced = false;
#ifdef SCR_RC_AUDIT
scr_live_bytes++;
#endif
Expand Down Expand Up @@ -304,6 +306,7 @@ void scr_bytes_release(ScrBytes *b) {
if (!b || b->rc == SIZE_MAX) return; /* NULL: an uninitialized `let` local */
if (--b->rc == 0) {
scr_weak_dispose_inline(b);
if (b->properties) scr_dyn_release(b->properties);
if (b->backing) {
scr_bytes_release(b->backing); /* a view: data points into the owner */
} else if (b->shared) {
Expand Down Expand Up @@ -740,6 +743,8 @@ ScrBytes *scr_bytes_subarray(ScrBytes *b, double start, double end) {
v->external = false;
v->untransferable = false;
v->shared = owner->shared;
v->properties = NULL;
v->properties_traced = false;
#ifdef SCR_RC_AUDIT
scr_live_bytes++;
#endif
Expand Down Expand Up @@ -883,7 +888,9 @@ ScrBytes *scr_bytes_from_dyn(ScrBytesElem elem, const ScrDyn *value, bool from)
if (value->kind == SCR_DYN_BYTES) return scr_bytes_convert(elem, value->v.bytes);
if (!from && scr_buffer_storage_is(value)) return scr_array_buffer_view(elem, value, scr_dyn_undefined(), scr_dyn_undefined());
if (!from && (value->kind == SCR_DYN_NUM || value->kind == SCR_DYN_BOOL ||
value->kind == SCR_DYN_STR || value->kind == SCR_DYN_NULL || value->kind == SCR_DYN_UNDEF)) {
value->kind == SCR_DYN_STR || value->kind == SCR_DYN_NULL || value->kind == SCR_DYN_UNDEF ||
value->kind == SCR_DYN_SYMBOL || value->kind == SCR_DYN_BIGINT)) {
/* ToIndex: a Symbol or BigInt length throws ToNumber's TypeError. */
double number;
if (!scr_dyn_number_coerce_js(value, &number)) return NULL;
return scr_bytes_new(elem, number);
Expand Down Expand Up @@ -1009,6 +1016,8 @@ ScrBytes *scr_dataview_new(ScrBytes *src, double byte_off, bool has_len, double
v->external = false;
v->untransferable = false;
v->shared = owner->shared;
v->properties = NULL;
v->properties_traced = false;
#ifdef SCR_RC_AUDIT
scr_live_bytes++;
#endif
Expand Down Expand Up @@ -2900,6 +2909,8 @@ static ScrBytes *scr_pool_take(size_t len) {
v->external = false;
v->untransferable = false;
v->shared = NULL;
v->properties = NULL;
v->properties_traced = false;
#ifdef SCR_RC_AUDIT
scr_live_bytes++;
#endif
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime/src/scr_bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@ typedef struct ScrBytes {
bool is_data_view; /* DataView is distinct from a numeric typed array. */
bool external; /* Root aliases foreign memory; releasing it never frees data. */
bool untransferable; /* A Buffer pool slab: its ArrayBuffer cannot be transferred. */
/* The cycle collector's last trace of `properties` from the one checked
* value that held this view (see scr_dyn_trace_v). In padding, so the
* offsets compiled code uses hold. */
bool properties_traced;
ScrSharedBytes *shared; /* Neutral backing, borrowed by views from their local owner. */
/* Named properties a program adds to the view (`ta.foo = 1`,
* Object.defineProperty): a checked object, NULL until the first one.
* Runtime-only and last, so compiled code's field indices hold. */
struct ScrDyn *properties;
} ScrBytes;

/* Copies out of and into shared storage, made of relaxed atomic accesses
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/scr_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ static void scr_enable_utf8_console_output(void) {
}
#endif

void scr_runtime_abi_v43(void) {}
void scr_runtime_abi_v44(void) {}

void scr_init(void) {
#ifdef SCR_WORKERS
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/scr_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void scr_init(void);
/* Program objects emitted by the bundled LLVM helper reference this symbol.
* Its versioned spelling makes a mismatched manual runtime link fail before
* the program can start. */
void scr_runtime_abi_v43(void);
void scr_runtime_abi_v44(void);
void scr_runtime_workers_v8(void);

/* ── the trap funnel (scr_console.c; scr_library.c under -DSCR_LIB) ──────
Expand Down
6 changes: 6 additions & 0 deletions packages/runtime/src/scr_dyn.h
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,9 @@ void scr_dyn_install_iterator_symbol(ScrDyn *key);
void scr_dyn_install_has_instance_symbol(ScrDyn *key);
void scr_dyn_install_async_iterator_symbol(ScrDyn *key);
void scr_dyn_install_async_dispose_symbol(ScrDyn *key);
void scr_dyn_install_species_symbol(ScrDyn *key);
/* Records each well-known symbol the symbol unit creates. */
void scr_dyn_install_well_known_symbol(ScrDyn *key);
void scr_dyn_install_to_string_tag_symbol(ScrDyn *key);
/* True when key is Symbol.asyncDispose (registered by the symbol unit when
* the program names it); handle symbol_get ops outside that unit use it. */
Expand Down Expand Up @@ -1046,6 +1049,9 @@ ScrDyn *scr_bytes_construct(const ScrDyn *callee, const ScrDyn *args, const ScrS
extern SCR_TL bool (*scr_dyn_global_construct_fn)(const ScrDyn *callee, const ScrDyn *args,
ScrDyn **result);
ScrDyn *scr_dyn_bytes_key_get(const ScrDyn *value, const ScrStr *key);
/* A typed array's, Buffer's or DataView's own named property (+1), or NULL
* without an exception when it has none. */
ScrDyn *scr_bytes_named_get(const ScrDyn *view, const char *key, size_t key_len);
/* The Buffer-flavored twin (stream chunks): string coercion/toString
* decode utf8 instead of joining elements. */
ScrDyn *scr_dyn_new_buffer(const ScrBytes *b);
Expand Down
14 changes: 14 additions & 0 deletions packages/runtime/src/scr_dyn_invoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -1712,6 +1712,20 @@ static ScrDyn *scr_dyn_invoke_impl(

if (recv->kind == SCR_DYN_BYTES) {
ScrBytes *bytes = recv->v.bytes;
/* An own property of the view shadows the prototype's method. */
ScrDyn *member = bytes->properties ? scr_bytes_named_get(recv, method, strlen(method)) : NULL;
if (!member && scr_exc_pending()) return NULL;
if (member) {
ScrDyn *result = NULL;
if (!scr_dyn_is_callable(member)) dyn_throw_not_fn(what);
else {
scr_dyn_this_push_dyn(recv);
result = scr_dyn_call(member, args, argc, what);
scr_dyn_this_pop();
}
scr_dyn_release(member);
return result;
}
if (bytes->is_data_view) {
static const char *const kinds[] = { "Uint8", "Int8", "Uint16", "Int16", "Uint32", "Int32", "Float32", "Float64", "BigUint64", "BigInt64", "Float16" };
const bool get = !strncmp(method, "get", 3);
Expand Down
79 changes: 77 additions & 2 deletions packages/runtime/src/scr_inspect.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,52 @@ ScrStr *scr_insp_regex(ScrRegex *re) {
return ib_take(&out);
}

/* Named properties of a typed array, Buffer or DataView (its
* ScrBytes.properties table): slot k is the k-th string-keyed entry, then
* the k-th symbol key. */
static size_t insp_view_property_slots(const ScrDyn *props) {
if (!props) return 0;
return props->v.obj.len + (props->symbol_keys ? props->symbol_keys->v.arr.len : 0);
}

static ScrStr *insp_accessor_tag(const ScrDynEntry *ent);
static void insp_key_into(InspBuf *b, const char *key, size_t len);

/* Slot k's `key: value` entry when it is enumerable, else NULL. */
static ScrStr *insp_view_property(ScrDyn *props, size_t k, double recurse, double depth) {
const ScrDynEntry *ent;
ScrStr *key = NULL;
if (k < props->v.obj.len) {
ent = &props->v.obj.entries[k];
} else {
ScrDyn *symbol = props->symbol_keys->v.arr.items[k - props->v.obj.len];
ent = scr_dyn_own_symbol_entry(props, symbol);
if (ent && ent->enumerable) key = scr_insp_dyn(symbol, recurse, depth);
}
if (!ent || !ent->enumerable) return NULL;
ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse, depth);
InspBuf eb = {0};
if (key) ib_bytes(&eb, key->data, key->len);
else insp_key_into(&eb, ent->key, ent->key_len);
ib_cstr(&eb, ": ");
ib_bytes(&eb, val->data, val->len);
scr_str_release(key);
scr_str_release(val);
return ib_take(&eb);
}

static size_t insp_view_property_count(ScrDyn *props) {
size_t count = 0;
for (size_t k = 0; k < insp_view_property_slots(props); k++) {
if (k < props->v.obj.len) count += props->v.obj.entries[k].enumerable;
else {
const ScrDynEntry *ent = scr_dyn_own_symbol_entry(props, props->symbol_keys->v.arr.items[k - props->v.obj.len]);
count += ent && ent->enumerable;
}
}
return count;
}

/* Buffer: the custom-inspect hex form `<Buffer aa bb ...>`, capped at
* INSPECT_MAX_BYTES (50); renders fully at any depth (custom inspection
* runs before the depth check in Node). u8 element kind only (the
Expand All @@ -816,6 +862,17 @@ ScrStr *scr_insp_buffer(ScrBytes *b) {
remaining > 1 ? "s" : "");
ib_bytes(&out, more, (size_t)n);
}
/* Named properties follow as `, key: value`, rendered as the members of
* an object at depth 0 (Buffer's custom inspect). */
bool first = true;
for (size_t k = 0; k < insp_view_property_slots(b->properties); k++) {
ScrStr *entry = insp_view_property(b->properties, k, 1, 2);
if (!entry) continue;
if (!first || b->len) ib_cstr(&out, ", ");
first = false;
ib_bytes(&out, entry->data, entry->len);
scr_str_release(entry);
}
ib_char(&out, '>');
return ib_take(&out);
}
Expand Down Expand Up @@ -899,6 +956,12 @@ static ScrStr *scr_insp_data_view(const ScrBytes *b, double recurse, double dept
entry = ib_take(&eb);
scr_insp_entry(entry, false);
scr_str_release(entry);
for (size_t k = 0; k < insp_view_property_slots(b->properties); k++) {
entry = insp_view_property(b->properties, k, recurse + 1, depth);
if (!entry) continue;
scr_insp_entry(entry, false);
scr_str_release(entry);
}
ScrStr *base = scr_str_new("", 0);
ScrStr *b0 = scr_str_new("DataView {", 10);
ScrStr *b1 = scr_str_new("}", 1);
Expand Down Expand Up @@ -1537,7 +1600,10 @@ static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char
ScrStr *scr_insp_typed_array(ScrBytes *b, double recurse, double depth) {
char prefix[48];
int pn = snprintf(prefix, sizeof prefix, "%s(%zu) [", scr_bytes_elem_name(b->elem), b->len);
if (b->len == 0) {
/* Named properties follow the elements as `key: value` entries. */
ScrDyn *props = b->properties;
size_t nprops = insp_view_property_count(props);
if (b->len == 0 && nprops == 0) {
InspBuf out = {0};
ib_bytes(&out, prefix, (size_t)pn);
ib_char(&out, ']');
Expand All @@ -1561,10 +1627,19 @@ ScrStr *scr_insp_typed_array(ScrBytes *b, double recurse, double depth) {
scr_insp_entry(m, true);
scr_str_release(m);
}
size_t entries = shown + (more ? 1 : 0);
for (size_t k = 0; nprops && k < insp_view_property_slots(props); k++) {
ScrStr *entry = insp_view_property(props, k, recurse + 1, depth);
if (!entry) continue;
/* Node's grouping pads by the element at the entry's output position. */
scr_insp_entry(entry, entries < b->len);
scr_str_release(entry);
entries++;
}
ScrStr *base = scr_str_new("", 0);
ScrStr *b0 = scr_str_new(prefix, (size_t)pn);
ScrStr *b1 = scr_str_new("]", 1);
ScrStr *out = scr_insp_end(base, b0, b1, recurse + 1, true, more);
ScrStr *out = scr_insp_end(base, b0, b1, recurse + 1, true, nprops ? entries > 100 : more);
scr_str_release(base);
scr_str_release(b0);
scr_str_release(b1);
Expand Down
Loading
Loading