Declare the WebAssembly W5 and stream-slot entry points in the runtime headers

The backend can emit scr_wasm_memory_construct, scr_wasm_table_construct,
scr_wasm_global_construct, scr_wasm_construct_without_new,
scr_wasm_module_imports, scr_wasm_module_exports,
scr_wasm_module_custom_sections and scr_wasm_value_of (#1410), and
scr_stream_dyn_native, scr_stream_pipe_dyn and scr_stream_unpipe_dyn
(#1524). Their prototypes were in scr_wasm.c or nowhere, so the runtime
ABI guard failed. They are now in scr_wasm.h and scr_stream.h, and
scr_wasm.c no longer repeats the header's declarations.

The speculative-hoisting test relied on `v instanceof Num` in a
JavaScript function failing, which constructor-function instanceof now
compiles (#1512). Its hoisted function reads the prototype through
Reflect.getPrototypeOf instead, which still fences.
This commit is contained in:
Chris Tate
2026-10-20 12:50:19 -05:00
parent 73a595692f
commit 97bf98cd89
4 changed files with 20 additions and 22 deletions
@@ -9,7 +9,9 @@ import type { IrModule } from "../src/ir/ir.js";
// list above the constructor. The initializer lowers speculatively first,
// which hoists the constructor and the helper it calls. Those bodies are
// kept, so their failing statements must keep runtime fences; dropping
// them left `isNum` without its return (an SC9002 trap at load).
// them left `isNum` without its return (an SC9002 trap at load). isNum's
// return reads the prototype through Reflect.getPrototypeOf, which has no
// lowering, so it fences.
const source = `
function clone(config) {
var P = (Num.prototype = { constructor: Num, toString: null }),
@@ -20,7 +22,7 @@ function clone(config) {
x.v = isNum(v) ? 0 : v * unit;
}
function isNum(v) {
return v instanceof Num || (!!v && v._isNum === true);
return Reflect.getPrototypeOf(v) === P || (!!v && v._isNum === true);
}
P._isNum = true;
return Num;
@@ -59,6 +61,6 @@ test("speculatively hoisted JavaScript functions keep their statement fences", a
const mod = await lower();
const isNum = mod.functions.find((fn) => fn.name.endsWith("_isNum"));
expect(isNum).toBeDefined();
expect(fenceCodes(isNum!.body)).toEqual(["SC1090"]);
expect(fenceCodes(isNum!.body)).toEqual(["SC2020"]);
expect(mod.functions.flatMap((fn) => fenceCodes(fn.body))).not.toContain("SC9002");
});
+7
View File
@@ -136,6 +136,13 @@ ScrStream *scr_stream_pipe_general(ScrStream *src, ScrStream *dst, bool end);
/* A boxed native stream out of a checked-dynamic pipeline stage (+1), or
* NULL with `message` thrown as the not-supported Error. */
ScrStream *scr_stream_unbox_dyn(const struct ScrDyn *value, const ScrStr *message);
/* Checked-dynamic stream slots (scr_stream_dyn.c): the native stream a
* value boxes with the requested side (1 readable, 2 writable; +1, or
* NULL), and readable.pipe()/unpipe() for a destination that is not a
* native stream. */
ScrStream *scr_stream_dyn_native(const struct ScrDyn *value, double side);
void scr_stream_pipe_dyn(ScrStream *src, struct ScrDyn *src_box, struct ScrDyn *dest, bool end);
void scr_stream_unpipe_dyn(ScrStream *src, struct ScrDyn *dest);
ScrStream *scr_stream_new_duplex_dyn(struct ScrDyn *opts);
ScrStream *scr_stream_new_transform_dyn(struct ScrDyn *opts);
ScrStream *scr_stream_new_passthrough_dyn(struct ScrDyn *opts);
-19
View File
@@ -90,25 +90,6 @@
#define SCR_WASM_ASAN 1
#endif
ScrPromise *scr_wasm_compile(ScrDyn *bytes);
bool scr_wasm_validate(ScrDyn *bytes);
ScrPromise *scr_wasm_instantiate(ScrDyn *source, ScrDyn *imports);
ScrDyn *scr_wasm_module_new(ScrDyn *bytes);
ScrDyn *scr_wasm_instance_new(ScrDyn *module, ScrDyn *imports);
bool scr_wasm_is(const ScrDyn *value, double kind);
ScrDyn *scr_wasm_error_new(double kind, ScrDyn *message, ScrDyn *options);
ScrDyn *scr_wasm_function(double kind);
ScrDyn *scr_wasm_namespace(void);
ScrDyn *scr_wasm_global_view(void);
ScrDyn *scr_wasm_memory_construct(ScrDyn *descriptor);
ScrDyn *scr_wasm_table_construct(ScrDyn *descriptor, ScrDyn *value);
ScrDyn *scr_wasm_global_construct(ScrDyn *descriptor, ScrDyn *value);
ScrDyn *scr_wasm_construct_without_new(double kind, ScrDyn *arg0, ScrDyn *arg1);
ScrDyn *scr_wasm_module_imports(ScrDyn *module);
ScrDyn *scr_wasm_module_exports(ScrDyn *module);
ScrDyn *scr_wasm_module_custom_sections(ScrDyn *module, ScrDyn *name);
ScrDyn *scr_wasm_value_of(ScrDyn *value);
/* Entry points of translated code. */
/* Sanitized builds do not declare it noreturn: AddressSanitizer would
* call __asan_handle_no_return before every call, which warns on the
+8
View File
@@ -19,5 +19,13 @@ ScrDyn *scr_wasm_error_new(double kind, ScrDyn *message, ScrDyn *options);
ScrDyn *scr_wasm_function(double kind);
ScrDyn *scr_wasm_namespace(void);
ScrDyn *scr_wasm_global_view(void);
ScrDyn *scr_wasm_memory_construct(ScrDyn *descriptor);
ScrDyn *scr_wasm_table_construct(ScrDyn *descriptor, ScrDyn *value);
ScrDyn *scr_wasm_global_construct(ScrDyn *descriptor, ScrDyn *value);
ScrDyn *scr_wasm_construct_without_new(double kind, ScrDyn *arg0, ScrDyn *arg1);
ScrDyn *scr_wasm_module_imports(ScrDyn *module);
ScrDyn *scr_wasm_module_exports(ScrDyn *module);
ScrDyn *scr_wasm_module_custom_sections(ScrDyn *module, ScrDyn *name);
ScrDyn *scr_wasm_value_of(ScrDyn *value);
#endif /* SCR_WASM_H */