Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libaddfunction.js
6162 views
1
/**
2
* @license
3
* Copyright 2020 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
addToLibrary({
8
// This gives correct answers for everything less than 2^{14} = 16384
9
// I hope nobody is contemplating functions with 16384 arguments...
10
$uleb128EncodeWithLen__internal: true,
11
$uleb128EncodeWithLen: (arr) => {
12
const n = arr.length;
13
#if ASSERTIONS
14
assert(n < 16384);
15
#endif
16
// Note: this LEB128 length encoding produces extra byte for n < 128,
17
// but we don't care as it's only used in a temporary representation.
18
return [(n % 128) | 128, n >> 7, ...arr];
19
},
20
#if WASM_JS_TYPES
21
// Converts a signature like 'vii' into a description of the wasm types, like
22
// { parameters: ['i32', 'i32'], results: [] }.
23
$sigToWasmTypes__internal: true,
24
$sigToWasmTypes: (sig) => {
25
#if ASSERTIONS && !WASM_BIGINT
26
assert(!sig.includes('j'), 'i64 not permitted in function signatures when WASM_BIGINT is disabled');
27
#endif
28
var typeNames = {
29
'i': 'i32',
30
'j': 'i64',
31
'f': 'f32',
32
'd': 'f64',
33
'e': 'externref',
34
#if MEMORY64
35
'p': 'i64',
36
#else
37
'p': 'i32',
38
#endif
39
};
40
var type = {
41
parameters: [],
42
results: sig[0] == 'v' ? [] : [typeNames[sig[0]]]
43
};
44
for (var i = 1; i < sig.length; ++i) {
45
#if ASSERTIONS
46
assert(sig[i] in typeNames, 'invalid signature char: ' + sig[i]);
47
#endif
48
type.parameters.push(typeNames[sig[i]]);
49
}
50
return type;
51
},
52
#endif
53
$wasmTypeCodes__internal: true,
54
// Note: using template literal here instead of plain object
55
// because jsify serializes objects w/o quotes and Closure will then
56
// incorrectly mangle the properties.
57
$wasmTypeCodes: `{
58
'i': 0x7f, // i32
59
#if MEMORY64
60
'p': 0x7e, // i64
61
#else
62
'p': 0x7f, // i32
63
#endif
64
'j': 0x7e, // i64
65
'f': 0x7d, // f32
66
'd': 0x7c, // f64
67
'e': 0x6f, // externref
68
}`,
69
70
$generateTypePack__internal: true,
71
$generateTypePack__deps: ['$uleb128EncodeWithLen', '$wasmTypeCodes'],
72
$generateTypePack: (types) => uleb128EncodeWithLen(Array.from(types, (type) => {
73
var code = wasmTypeCodes[type];
74
#if ASSERTIONS
75
assert(code, `invalid signature char: ${type}`);
76
#endif
77
return code;
78
})),
79
80
#if !WASM2JS || WASM == 2
81
// Wraps a JS function as a wasm function with a given signature.
82
$convertJsFunctionToWasm__deps: [
83
'$uleb128EncodeWithLen',
84
#if WASM_JS_TYPES
85
'$sigToWasmTypes',
86
#endif
87
'$generateTypePack'
88
],
89
$convertJsFunctionToWasm: (func, sig) => {
90
#if ASSERTIONS && !WASM_BIGINT
91
assert(!sig.includes('j'), 'i64 not permitted in function signatures when WASM_BIGINT is disabled');
92
#endif
93
#if WASM_JS_TYPES
94
// If the type reflection proposal is available, use the new
95
// "WebAssembly.Function" constructor.
96
// Otherwise, construct a minimal wasm module importing the JS function and
97
// re-exporting it.
98
if (WebAssembly.Function) {
99
return new WebAssembly.Function(sigToWasmTypes(sig), func);
100
}
101
#endif
102
103
// Rest of the module is static
104
var bytes = Uint8Array.of(
105
0x00, 0x61, 0x73, 0x6d, // magic ("\0asm")
106
0x01, 0x00, 0x00, 0x00, // version: 1
107
0x01, // Type section code
108
// The module is static, with the exception of the type section, which is
109
// generated based on the signature passed in.
110
...uleb128EncodeWithLen([
111
0x01, // count: 1
112
0x60 /* form: func */,
113
// param types
114
...generateTypePack(sig.slice(1)),
115
// return types (for now only supporting [] if `void` and single [T] otherwise)
116
...generateTypePack(sig[0] === 'v' ? '' : sig[0])
117
]),
118
// The rest of the module is static
119
0x02, 0x07, // import section
120
// (import "e" "f" (func 0 (type 0)))
121
0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00,
122
0x07, 0x05, // export section
123
// (export "f" (func 0 (type 0)))
124
0x01, 0x01, 0x66, 0x00, 0x00,
125
);
126
127
// We can compile this wasm module synchronously because it is very small.
128
// This accepts an import (at "e.f"), that it reroutes to an export (at "f")
129
var module = new WebAssembly.Module(bytes);
130
var instance = new WebAssembly.Instance(module, { 'e': { 'f': func } });
131
var wrappedFunc = instance.exports['f'];
132
return wrappedFunc;
133
},
134
#endif // !WASM2JS && WASM != 2
135
136
$freeTableIndexes: [],
137
138
// Weak map of functions in the table to their indexes, created on first use.
139
$functionsInTableMap: undefined,
140
141
$getEmptyTableSlot__deps: ['$freeTableIndexes', '$wasmTable'],
142
$getEmptyTableSlot: () => {
143
// Reuse a free index if there is one, otherwise grow.
144
if (freeTableIndexes.length) {
145
return freeTableIndexes.pop();
146
}
147
#if ASSERTIONS
148
try {
149
#endif
150
// Grow the table
151
return wasmTable['grow']({{{ toIndexType('1') }}});
152
#if ASSERTIONS
153
} catch (err) {
154
if (!(err instanceof RangeError)) {
155
throw err;
156
}
157
abort('Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.');
158
}
159
#endif
160
},
161
162
$updateTableMap__deps: ['$getWasmTableEntry'],
163
$updateTableMap: (offset, count) => {
164
if (functionsInTableMap) {
165
for (var i = offset; i < offset + count; i++) {
166
var item = getWasmTableEntry(i);
167
// Ignore null values.
168
if (item) {
169
functionsInTableMap.set(item, i);
170
}
171
}
172
}
173
},
174
175
$getFunctionAddress__deps: ['$updateTableMap', '$functionsInTableMap', '$wasmTable'],
176
$getFunctionAddress: (func) => {
177
// First, create the map if this is the first use.
178
if (!functionsInTableMap) {
179
functionsInTableMap = new WeakMap();
180
updateTableMap(0, {{{ from64Expr('wasmTable.length') }}});
181
}
182
return functionsInTableMap.get(func) || 0;
183
},
184
185
/**
186
* Add a function to the table.
187
* 'sig' parameter is required if the function being added is a JS function.
188
*/
189
$addFunction__docs: '/** @param {string=} sig */',
190
$addFunction__deps: ['$getFunctionAddress',
191
'$functionsInTableMap', '$getEmptyTableSlot',
192
'$setWasmTableEntry',
193
#if !WASM2JS || WASM == 2
194
'$convertJsFunctionToWasm',
195
#endif
196
#if ASSERTIONS >= 2
197
'$getWasmTableEntry', '$wasmTable',
198
#endif
199
],
200
201
$addFunction: (func, sig) => {
202
#if ASSERTIONS
203
assert(typeof func != 'undefined');
204
#endif // ASSERTIONS
205
// Check if the function is already in the table, to ensure each function
206
// gets a unique index.
207
var rtn = getFunctionAddress(func);
208
if (rtn) {
209
return rtn;
210
}
211
212
// It's not in the table, add it now.
213
214
#if ASSERTIONS >= 2
215
// Make sure functionsInTableMap is actually up to date, that is, that this
216
// function is not actually in the wasm Table despite not being tracked in
217
// functionsInTableMap.
218
for (var i = 0; i < wasmTable.length; i++) {
219
assert(getWasmTableEntry(i) != func, 'function in Table but not functionsInTableMap');
220
}
221
#endif
222
223
var ret = getEmptyTableSlot();
224
225
#if WASM2JS && WASM != 2
226
setWasmTableEntry(ret, func);
227
#else
228
// Set the new value.
229
try {
230
// Attempting to call this with JS function will cause table.set() to fail
231
setWasmTableEntry(ret, func);
232
} catch (err) {
233
if (!(err instanceof TypeError)) {
234
throw err;
235
}
236
#if ASSERTIONS
237
assert(typeof sig != 'undefined', 'Missing signature argument to addFunction: ' + func);
238
#endif
239
var wrapped = convertJsFunctionToWasm(func, sig);
240
setWasmTableEntry(ret, wrapped);
241
}
242
#endif
243
244
functionsInTableMap.set(func, ret);
245
246
return ret;
247
},
248
249
$removeFunction__deps: ['$functionsInTableMap', '$freeTableIndexes',
250
'$getWasmTableEntry', '$setWasmTableEntry'],
251
$removeFunction: (index) => {
252
functionsInTableMap.delete(getWasmTableEntry(index));
253
setWasmTableEntry(index, null);
254
freeTableIndexes.push(index);
255
},
256
});
257
258