forked from dylan-sutton-chavez/edge-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnative.js
More file actions
123 lines (105 loc) · 5.38 KB
/
native.js
File metadata and controls
123 lines (105 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
Native module loading + dispatch. `nativeTable` indexed by `baseId` from `register_native_module`; entries are wasmpdk fns or JS handlers, dispatched by `host_call_native`.
*/
export const nativeTable = [];
export function resetNativeTable() {
nativeTable.length = 0;
}
/* Build the 6 `env.edge_*` imports for wasm-pdk plugins; bridges guest <-> compiler memory. */
export function makeGuestEnv(compilerExports) {
const compMem = () => new Uint8Array(compilerExports.memory.buffer);
const compView = () => new DataView(compilerExports.memory.buffer);
return (guestExports) => {
const gMem = () => new Uint8Array(guestExports.memory.buffer);
const gView = () => new DataView(guestExports.memory.buffer);
const stage = (ptr, len) => {
const c = compilerExports.wasm_alloc(Math.max(1, len));
if (len) compMem().set(gMem().subarray(ptr, ptr + len), c);
return c;
};
return {
edge_op: (op, recv, name_ptr, name_len, argv_ptr, argc, out) => {
const cName = stage(name_ptr, name_len);
const cArgv = compilerExports.wasm_alloc(Math.max(4, argc * 4));
const cOut = compilerExports.wasm_alloc(4);
for (let i = 0; i < argc; i++) {
compView().setUint32(cArgv + i * 4, gView().getUint32(argv_ptr + i * 4, true), true);
}
const ret = compilerExports.host_edge_op(op, recv, cName, name_len, cArgv, argc, cOut);
if (ret === 0 && out) gView().setUint32(out, compView().getUint32(cOut, true), true);
return ret;
},
edge_encode: (tag, ptr, len) =>
compilerExports.host_edge_encode(tag, stage(ptr, len), len),
edge_decode: (h, out_tag, dst, dst_max) => {
const cTag = compilerExports.wasm_alloc(4);
const cBuf = compilerExports.wasm_alloc(Math.max(1, dst_max));
const ret = compilerExports.host_edge_decode(h, cTag, cBuf, dst_max);
gView().setUint32(out_tag, compView().getUint32(cTag, true), true);
if (ret > 0) gMem().set(compMem().subarray(cBuf, cBuf + ret), dst);
return ret;
},
edge_release: (h) => compilerExports.host_edge_release(h),
edge_throw: (kind, msg_ptr, msg_len) => {
compilerExports.host_edge_throw(kind, stage(msg_ptr, msg_len), msg_len);
},
edge_take_error: (out_kind, dst, dst_max) => {
const cKind = compilerExports.wasm_alloc(4);
const cBuf = compilerExports.wasm_alloc(Math.max(1, dst_max));
const ret = compilerExports.host_edge_take_error(cKind, cBuf, dst_max);
if (ret >= 0) {
gView().setUint32(out_kind, compView().getUint32(cKind, true), true);
if (ret > 0) gMem().set(compMem().subarray(cBuf, cBuf + ret), dst);
}
return ret;
},
};
};
}
/* Built-in Path A fallback: instantiate guest, walk exports, annotate each fn with its guest's `__edge_alloc` + `__edge_memory`. */
async function builtinWasmPdkLoader(module, ctx) {
const envFactory = makeGuestEnv(ctx.compilerExports);
// Forward reference: the getter captures `instance` lazily. It's only read when env functions fire during VM execution, by which point `instance` is bound.
const env = envFactory({ get memory() { return instance.exports.memory; } });
// WebAssembly.instantiate(Module, ...) returns the Instance directly, not {module, instance}.
const instance = await WebAssembly.instantiate(module, { env });
if (typeof instance.exports.__edge_alloc !== 'function') {
throw new Error(
`native module missing '__edge_alloc(size: u32) -> *mut u8';` +
` see /reference/wasm-abi for the contract`
);
}
const names = [];
const fns = [];
for (const [k, v] of Object.entries(instance.exports)) {
if (k === 'memory' || typeof v !== 'function') continue;
// Keep convention exports (__fn_/__class_/__const_), drop ABI internals like __edge_alloc.
if (k.startsWith('__') && !k.startsWith('__class_') && !k.startsWith('__const_') && !k.startsWith('__fn_')) continue;
names.push(k);
v.__edge_alloc = instance.exports.__edge_alloc;
v.__edge_memory = instance.exports.memory;
v.__edge_kind = 'wasmpdk';
fns.push(v);
}
return { kind: 'wasmpdk', names, fns };
}
/* Try custom loaders first; built-in Path A is the implicit fallback. */
export async function loadNativeModule(_spec, bytes, ctx) {
const module = await WebAssembly.compile(bytes);
for (const loader of ctx.loaders) {
if (loader.match(module)) {
const result = await loader.load(module, ctx);
// Tag each fn with its dispatch kind so host_call_native picks the right path.
for (const fn of result.fns) fn.__edge_kind = result.kind;
annotateNames(result);
return result;
}
}
const result = await builtinWasmPdkLoader(module, ctx);
annotateNames(result);
return result;
}
/* Pair each fn with its declared name so deferred dispatch can route by name on the main thread. */
function annotateNames({ names, fns }) {
for (let i = 0; i < fns.length; i++) fns[i].__edge_name = names[i];
}