-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathwasmWrapper.ts
140 lines (125 loc) · 3.04 KB
/
wasmWrapper.ts
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import wasmLoader, {
GlueModule,
PostRunModule,
PreRunModule,
} from "./WasmModule.js";
export { GlueModule } from "./WasmModule.js";
type Deletable = {
delete(): void;
};
type Scope = Deletable[];
const scopeStack: Scope[] = [];
function pushScope() {
scopeStack.push([]);
}
function currentScope() {
return scopeStack[scopeStack.length - 1];
}
function popScope() {
const scope = scopeStack.pop();
if (scope) {
for (const v of scope) {
v.delete();
}
} else {
console.warn("wasm scope underflow");
}
}
let loaded: PreRunModule | undefined;
let wasmModule: PostRunModule | undefined;
/**
* Returns a promise that will resolve after the main method has finished
*/
function getWasm() {
if (wasmModule) {
return Promise.resolve(wasmModule);
} else {
return new Promise<GlueModule>((res, rej) => {
if (wasmModule) {
res(wasmModule);
} else {
loaded = loaded || wasmLoader();
loaded.then((module) => {
if (!wasmModule) {
module.callMain();
module.setConstructCallback((v) => currentScope()?.push(v));
wasmModule = module;
// remove `then` property to resolve promise without creating an
// endless loop
(loaded as any)["then"] = undefined;
}
res(wasmModule);
});
}
});
}
}
/**
* get the scope for debugging and testing
* @param idx the scope index to get from the top
*/
export function __getCurrentWasmScope(idx = 0) {
return scopeStack[scopeStack.length - 1 - idx];
}
/**
* get the scope stack size for debugging and testing
*/
export function __getCurrentWasmScopeStackSize() {
return scopeStack.length;
}
/**
* persist a value from the current scope so it will stay alive after the scope
* is closed
* @param value: the wasm value to be persisted
*/
export function persistWasmValue<V>(value: V) {
const scope = currentScope();
if (scope) {
const idx = scope.indexOf((value as any)["__glue_instance"]);
if (idx != -1) {
scope.splice(idx, 1);
} else {
console.error(
`could not persist value: value not found in current scope.`
);
}
} else {
console.error(`persisting a value outside of the current scope.`);
}
return value;
}
/**
* delete persisted values
*/
export function deletePersistedValue(value: any) {
(value as Deletable).delete();
}
/**
* Opens a scope and calls `callback` inside
* @param callback
*/
export function withWasmScope<R>(callback: () => R) {
pushScope();
let result: R;
try {
result = callback();
} catch (error) {
if (typeof error === "number") {
throw new Error(wasmModule!.getExceptionMessage(error));
} else {
throw error;
}
} finally {
popScope();
}
return result;
}
/**
* Calls `callback` asynchronously inside a wasm scope with the wasm module as
* an argument
* @param callback
*/
export async function withWasm<R>(callback: (module: GlueModule) => R) {
const glue = await getWasm();
return withWasmScope(() => callback(glue));
}