Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libemval.js
4150 views
1
// Copyright 2012 The Emscripten Authors. All rights reserved.
2
// Emscripten is available under two separate licenses, the MIT license and the
3
// University of Illinois/NCSA Open Source License. Both these licenses can be
4
// found in the LICENSE file.
5
6
// Number of handles reserved for non-use (0) or common values w/o refcount.
7
{{{
8
const EMVAL_RESERVED_HANDLES = 5;
9
const EMVAL_LAST_RESERVED_HANDLE = EMVAL_RESERVED_HANDLES * 2 - 1;
10
}}}
11
var LibraryEmVal = {
12
// Stack of handles available for reuse.
13
$emval_freelist: [],
14
// Array of alternating pairs (value, refcount).
15
// reserve 0 and some special values. These never get de-allocated.
16
$emval_handles: [
17
0, 1,
18
undefined, 1,
19
null, 1,
20
true, 1,
21
false, 1,
22
],
23
#if ASSERTIONS
24
$emval_handles__postset: 'assert(emval_handles.length === {{{ EMVAL_RESERVED_HANDLES }}} * 2)',
25
#endif
26
$emval_symbols: {}, // address -> string
27
28
$count_emval_handles__deps: ['$emval_freelist', '$emval_handles'],
29
$count_emval_handles: () => {
30
return emval_handles.length / 2 - {{{ EMVAL_RESERVED_HANDLES }}} - emval_freelist.length;
31
},
32
33
_emval_register_symbol__deps: ['$emval_symbols', '$AsciiToString'],
34
_emval_register_symbol: (address) => {
35
emval_symbols[address] = AsciiToString(address);
36
},
37
38
$getStringOrSymbol__deps: ['$emval_symbols', '$AsciiToString'],
39
$getStringOrSymbol: (address) => {
40
var symbol = emval_symbols[address];
41
if (symbol === undefined) {
42
return AsciiToString(address);
43
}
44
return symbol;
45
},
46
47
$Emval__deps: ['$emval_freelist', '$emval_handles', '$throwBindingError'],
48
$Emval: {
49
toValue: (handle) => {
50
if (!handle) {
51
throwBindingError(`Cannot use deleted val. handle = ${handle}`);
52
}
53
#if ASSERTIONS
54
// handle 2 is supposed to be `undefined`.
55
assert(handle === 2 || emval_handles[handle] !== undefined && handle % 2 === 0, `invalid handle: ${handle}`);
56
#endif
57
return emval_handles[handle];
58
},
59
60
toHandle: (value) => {
61
switch (value) {
62
case undefined: return 2;
63
case null: return 4;
64
case true: return 6;
65
case false: return 8;
66
default:{
67
const handle = emval_freelist.pop() || emval_handles.length;
68
emval_handles[handle] = value;
69
emval_handles[handle + 1] = 1;
70
return handle;
71
}
72
}
73
}
74
},
75
76
_emval_incref__deps: ['$emval_handles'],
77
_emval_incref: (handle) => {
78
if (handle > {{{ EMVAL_LAST_RESERVED_HANDLE }}}) {
79
emval_handles[handle + 1] += 1;
80
}
81
},
82
83
_emval_decref__deps: ['$emval_freelist', '$emval_handles'],
84
_emval_decref: (handle) => {
85
if (handle > {{{ EMVAL_LAST_RESERVED_HANDLE }}} && 0 === --emval_handles[handle + 1]) {
86
#if ASSERTIONS
87
assert(emval_handles[handle] !== undefined, `Decref for unallocated handle.`);
88
#endif
89
emval_handles[handle] = undefined;
90
emval_freelist.push(handle);
91
}
92
},
93
94
_emval_run_destructors__deps: ['_emval_decref', '$Emval', '$runDestructors'],
95
_emval_run_destructors: (handle) => {
96
var destructors = Emval.toValue(handle);
97
runDestructors(destructors);
98
__emval_decref(handle);
99
},
100
101
_emval_new_array__deps: ['$Emval'],
102
_emval_new_array: () => Emval.toHandle([]),
103
104
_emval_new_array_from_memory_view__deps: ['$Emval'],
105
_emval_new_array_from_memory_view: (view) => {
106
view = Emval.toValue(view);
107
// using for..loop is faster than Array.from
108
var a = new Array(view.length);
109
for (var i = 0; i < view.length; i++) a[i] = view[i];
110
return Emval.toHandle(a);
111
},
112
113
_emval_new_object__deps: ['$Emval'],
114
_emval_new_object: () => Emval.toHandle({}),
115
116
_emval_new_cstring__deps: ['$getStringOrSymbol', '$Emval'],
117
_emval_new_cstring: (v) => Emval.toHandle(getStringOrSymbol(v)),
118
119
_emval_new_u8string__deps: ['$Emval'],
120
_emval_new_u8string: (v) => Emval.toHandle(UTF8ToString(v)),
121
122
_emval_new_u16string__deps: ['$Emval'],
123
_emval_new_u16string: (v) => Emval.toHandle(UTF16ToString(v)),
124
125
_emval_get_global__deps: ['$Emval', '$getStringOrSymbol', '$emGlobalThis'],
126
_emval_get_global: (name) => {
127
if (!name) {
128
return Emval.toHandle(emGlobalThis);
129
}
130
name = getStringOrSymbol(name);
131
return Emval.toHandle(emGlobalThis[name]);
132
},
133
134
_emval_get_module_property__deps: ['$getStringOrSymbol', '$Emval'],
135
_emval_get_module_property: (name) => {
136
name = getStringOrSymbol(name);
137
return Emval.toHandle(Module[name]);
138
},
139
140
_emval_get_property__deps: ['$Emval'],
141
_emval_get_property: (handle, key) => {
142
handle = Emval.toValue(handle);
143
key = Emval.toValue(key);
144
return Emval.toHandle(handle[key]);
145
},
146
147
_emval_set_property__deps: ['$Emval'],
148
_emval_set_property: (handle, key, value) => {
149
handle = Emval.toValue(handle);
150
key = Emval.toValue(key);
151
value = Emval.toValue(value);
152
handle[key] = value;
153
},
154
155
$emval_returnValue__deps: ['$Emval'],
156
$emval_returnValue: (toReturnWire, destructorsRef, handle) => {
157
var destructors = [];
158
var result = toReturnWire(destructors, handle);
159
if (destructors.length) {
160
// void, primitives and any other types w/o destructors don't need to allocate a handle
161
{{{ makeSetValue('destructorsRef', '0', 'Emval.toHandle(destructors)', '*') }}};
162
}
163
return result;
164
},
165
166
_emval_equals__deps: ['$Emval'],
167
_emval_equals: (first, second) => {
168
first = Emval.toValue(first);
169
second = Emval.toValue(second);
170
return first == second;
171
},
172
173
_emval_strictly_equals__deps: ['$Emval'],
174
_emval_strictly_equals: (first, second) => {
175
first = Emval.toValue(first);
176
second = Emval.toValue(second);
177
return first === second;
178
},
179
180
_emval_greater_than__deps: ['$Emval'],
181
_emval_greater_than: (first, second) => {
182
first = Emval.toValue(first);
183
second = Emval.toValue(second);
184
return first > second;
185
},
186
187
_emval_less_than__deps: ['$Emval'],
188
_emval_less_than: (first, second) => {
189
first = Emval.toValue(first);
190
second = Emval.toValue(second);
191
return first < second;
192
},
193
194
_emval_not__deps: ['$Emval'],
195
_emval_not: (object) => {
196
object = Emval.toValue(object);
197
return !object;
198
},
199
200
$emval_lookupTypes__deps: ['$requireRegisteredType'],
201
$emval_lookupTypes: (argCount, argTypes) => {
202
var a = new Array(argCount);
203
for (var i = 0; i < argCount; ++i) {
204
a[i] = requireRegisteredType({{{ makeGetValue('argTypes', `i*${POINTER_SIZE}`, '*') }}},
205
`parameter ${i}`);
206
}
207
return a;
208
},
209
210
// Leave id 0 undefined. It's not a big deal, but might be confusing
211
// to have null be a valid method caller.
212
$emval_methodCallers: [undefined],
213
214
$emval_addMethodCaller__deps: ['$emval_methodCallers'],
215
$emval_addMethodCaller: (caller) => {
216
var id = emval_methodCallers.length;
217
emval_methodCallers.push(caller);
218
return id;
219
},
220
221
_emval_create_invoker__deps: [
222
'$emval_addMethodCaller', '$emval_lookupTypes',
223
'$createNamedFunction', '$emval_returnValue',
224
'$Emval', '$getStringOrSymbol',
225
],
226
_emval_create_invoker: (argCount, argTypesPtr, kind) => {
227
var GenericWireTypeSize = {{{ 2 * POINTER_SIZE }}};
228
229
var [retType, ...argTypes] = emval_lookupTypes(argCount, argTypesPtr);
230
var toReturnWire = retType.toWireType.bind(retType);
231
var argFromPtr = argTypes.map(type => type.readValueFromPointer.bind(type));
232
argCount--; // remove the extracted return type
233
234
#if !DYNAMIC_EXECUTION
235
var argN = new Array(argCount);
236
var invokerFunction = (handle, methodName, destructorsRef, args) => {
237
var offset = 0;
238
for (var i = 0; i < argCount; ++i) {
239
argN[i] = argFromPtr[i](args + offset);
240
offset += GenericWireTypeSize;
241
}
242
var rv;
243
switch (kind) {
244
case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}:
245
rv = Emval.toValue(handle).apply(null, argN);
246
break;
247
case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}:
248
rv = Reflect.construct(Emval.toValue(handle), argN);
249
break;
250
case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}:
251
// no-op, just return the argument
252
rv = argN[0];
253
break;
254
case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}:
255
rv = Emval.toValue(handle)[getStringOrSymbol(methodName)](...argN);
256
break;
257
}
258
return emval_returnValue(toReturnWire, destructorsRef, rv);
259
};
260
#else
261
var captures = {'toValue': Emval.toValue};
262
var args = argFromPtr.map((argFromPtr, i) => {
263
var captureName = `argFromPtr${i}`;
264
captures[captureName] = argFromPtr;
265
return `${captureName}(args${i ? '+' + i * GenericWireTypeSize : ''})`;
266
});
267
var functionBody;
268
switch (kind){
269
case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}:
270
functionBody = 'toValue(handle)';
271
break;
272
case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}:
273
functionBody = 'new (toValue(handle))';
274
break;
275
case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}:
276
functionBody = '';
277
break;
278
case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}:
279
captures['getStringOrSymbol'] = getStringOrSymbol;
280
functionBody = 'toValue(handle)[getStringOrSymbol(methodName)]';
281
break;
282
}
283
functionBody += `(${args})`;
284
if (!retType.isVoid) {
285
captures['toReturnWire'] = toReturnWire;
286
captures['emval_returnValue'] = emval_returnValue;
287
functionBody = `return emval_returnValue(toReturnWire, destructorsRef, ${functionBody})`;
288
}
289
functionBody = `return function (handle, methodName, destructorsRef, args) {
290
${functionBody}
291
}`;
292
293
var invokerFunction = new Function(Object.keys(captures), functionBody)(...Object.values(captures));
294
#endif
295
var functionName = `methodCaller<(${argTypes.map(t => t.name)}) => ${retType.name}>`;
296
return emval_addMethodCaller(createNamedFunction(functionName, invokerFunction));
297
},
298
299
_emval_invoke__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'],
300
_emval_invoke: (caller, handle, methodName, destructorsRef, args) => {
301
return emval_methodCallers[caller](handle, methodName, destructorsRef, args);
302
},
303
304
// Same as `_emval_invoke`, just imported into Wasm under a different return type.
305
// TODO: remove this if/when https://github.com/emscripten-core/emscripten/issues/20478 is fixed.
306
_emval_invoke_i64__deps: ['_emval_invoke'],
307
_emval_invoke_i64: '=__emval_invoke',
308
309
_emval_typeof__deps: ['$Emval'],
310
_emval_typeof: (handle) => {
311
handle = Emval.toValue(handle);
312
return Emval.toHandle(typeof handle);
313
},
314
315
_emval_instanceof__deps: ['$Emval'],
316
_emval_instanceof: (object, constructor) => {
317
object = Emval.toValue(object);
318
constructor = Emval.toValue(constructor);
319
return object instanceof constructor;
320
},
321
322
_emval_is_number__deps: ['$Emval'],
323
_emval_is_number: (handle) => {
324
handle = Emval.toValue(handle);
325
return typeof handle == 'number';
326
},
327
328
_emval_is_string__deps: ['$Emval'],
329
_emval_is_string: (handle) => {
330
handle = Emval.toValue(handle);
331
return typeof handle == 'string';
332
},
333
334
_emval_in__deps: ['$Emval'],
335
_emval_in: (item, object) => {
336
item = Emval.toValue(item);
337
object = Emval.toValue(object);
338
return item in object;
339
},
340
341
_emval_delete__deps: ['$Emval'],
342
_emval_delete: (object, property) => {
343
object = Emval.toValue(object);
344
property = Emval.toValue(property);
345
return delete object[property];
346
},
347
348
_emval_throw__deps: ['$Emval'],
349
_emval_throw: (object) => {
350
object = Emval.toValue(object);
351
throw object;
352
},
353
354
#if ASYNCIFY
355
_emval_await__deps: ['$Emval', '$Asyncify'],
356
_emval_await__async: true,
357
_emval_await: (promise) => {
358
return Asyncify.handleAsync(async () => {
359
var value = await Emval.toValue(promise);
360
return Emval.toHandle(value);
361
});
362
},
363
#endif
364
365
_emval_iter_begin__deps: ['$Emval'],
366
_emval_iter_begin: (iterable) => {
367
iterable = Emval.toValue(iterable);
368
return Emval.toHandle(iterable[Symbol.iterator]());
369
},
370
371
_emval_iter_next__deps: ['$Emval'],
372
_emval_iter_next: (iterator) => {
373
iterator = Emval.toValue(iterator);
374
var result = iterator.next();
375
return result.done ? 0 : Emval.toHandle(result.value);
376
},
377
378
_emval_coro_suspend__deps: ['$Emval', '_emval_coro_resume', '_emval_coro_reject'],
379
_emval_coro_suspend: (promiseHandle, awaiterPtr) => {
380
Emval.toValue(promiseHandle)
381
.then((result) => __emval_coro_resume(awaiterPtr, Emval.toHandle(result)),
382
(error) => __emval_coro_reject(awaiterPtr, Emval.toHandle(error)));
383
},
384
385
_emval_coro_make_promise__deps: ['$Emval'],
386
_emval_coro_make_promise: (resolveHandlePtr, rejectHandlePtr) => {
387
return Emval.toHandle(new Promise((resolve, reject) => {
388
{{{ makeSetValue('resolveHandlePtr', '0', 'Emval.toHandle(resolve)', '*') }}};
389
{{{ makeSetValue('rejectHandlePtr', '0', 'Emval.toHandle(reject)', '*') }}};
390
}));
391
},
392
393
_emval_from_current_cxa_exception__deps: ['$Emval', '__cxa_rethrow'],
394
_emval_from_current_cxa_exception: () => {
395
try {
396
// Use __cxa_rethrow which already has mechanism for generating
397
// user-friendly error message and stacktrace from C++ exception
398
// if EXCEPTION_STACK_TRACES is enabled and numeric exception
399
// with metadata optimised out otherwise.
400
___cxa_rethrow();
401
} catch (e) {
402
return Emval.toHandle(e);
403
}
404
},
405
};
406
407
addToLibrary(LibraryEmVal);
408
409