Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libemval.js
6172 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
#if !SUPPORT_BIG_ENDIAN
105
_emval_new_array_from_memory_view__deps: ['$Emval'],
106
_emval_new_array_from_memory_view: (view) => {
107
view = Emval.toValue(view);
108
// using for..loop is faster than Array.from
109
var a = new Array(view.length);
110
for (var i = 0; i < view.length; i++) a[i] = view[i];
111
return Emval.toHandle(a);
112
},
113
_emval_array_to_memory_view__deps: ['$Emval'],
114
_emval_array_to_memory_view: (dst, src) => {
115
dst = Emval.toValue(dst);
116
src = Emval.toValue(src);
117
dst.set(src);
118
},
119
#else
120
_emval_new_array_from_memory_view__deps: ['$Emval'],
121
_emval_new_array_from_memory_view: (view) => {
122
view = Emval.toValue(view);
123
const dv = new DataView(view.buffer, view.byteOffset);
124
const reader = {
125
Int8Array: dv.getInt8,
126
Uint8Array: dv.getUint8,
127
Int16Array: dv.getInt16,
128
Uint16Array: dv.getUint16,
129
Int32Array: dv.getInt32,
130
Uint32Array: dv.getUint32,
131
BigInt64Array: dv.getBigInt64,
132
BigUint64Array: dv.getBigUint64,
133
Float32Array: dv.getFloat32,
134
Float64Array: dv.getFloat64,
135
}[view[Symbol.toStringTag]];
136
var a = new Array(view.length);
137
for (var i = 0; i < view.length; i++) a[i] = reader.call(dv, i * view.BYTES_PER_ELEMENT, true);
138
return Emval.toHandle(a);
139
},
140
_emval_array_to_memory_view__deps: ['$Emval'],
141
_emval_array_to_memory_view: (dst, src) => {
142
dst = Emval.toValue(dst);
143
src = Emval.toValue(src);
144
const dv = new DataView(dst.buffer, dst.byteOffset);
145
const writer = {
146
Int8Array: dv.setInt8,
147
Uint8Array: dv.setUint8,
148
Int16Array: dv.setInt16,
149
Uint16Array: dv.setUint16,
150
Int32Array: dv.setInt32,
151
Uint32Array: dv.setUint32,
152
BigInt64Array: dv.setBigInt64,
153
BigUint64Array: dv.setBigUint64,
154
Float32Array: dv.setFloat32,
155
Float64Array: dv.setFloat64,
156
}[dst[Symbol.toStringTag]];
157
for (var i = 0; i < src.length; i++) writer.call(dv, i * dst.BYTES_PER_ELEMENT, src[i], true);
158
},
159
#endif
160
161
_emval_new_object__deps: ['$Emval'],
162
_emval_new_object: () => Emval.toHandle({}),
163
164
_emval_new_cstring__deps: ['$getStringOrSymbol', '$Emval'],
165
_emval_new_cstring: (v) => Emval.toHandle(getStringOrSymbol(v)),
166
167
_emval_new_u8string__deps: ['$Emval'],
168
_emval_new_u8string: (v) => Emval.toHandle(UTF8ToString(v)),
169
170
_emval_new_u16string__deps: ['$Emval'],
171
_emval_new_u16string: (v) => Emval.toHandle(UTF16ToString(v)),
172
173
_emval_get_global__deps: ['$Emval', '$getStringOrSymbol'],
174
_emval_get_global: (name) => {
175
if (!name) {
176
return Emval.toHandle(globalThis);
177
}
178
name = getStringOrSymbol(name);
179
return Emval.toHandle(globalThis[name]);
180
},
181
182
_emval_get_module_property__deps: ['$getStringOrSymbol', '$Emval'],
183
_emval_get_module_property: (name) => {
184
name = getStringOrSymbol(name);
185
return Emval.toHandle(Module[name]);
186
},
187
188
_emval_get_property__deps: ['$Emval'],
189
_emval_get_property: (handle, key) => {
190
handle = Emval.toValue(handle);
191
key = Emval.toValue(key);
192
return Emval.toHandle(handle[key]);
193
},
194
195
_emval_set_property__deps: ['$Emval'],
196
_emval_set_property: (handle, key, value) => {
197
handle = Emval.toValue(handle);
198
key = Emval.toValue(key);
199
value = Emval.toValue(value);
200
handle[key] = value;
201
},
202
203
$emval_returnValue__deps: ['$Emval'],
204
$emval_returnValue: (toReturnWire, destructorsRef, handle) => {
205
var destructors = [];
206
var result = toReturnWire(destructors, handle);
207
if (destructors.length) {
208
// void, primitives and any other types w/o destructors don't need to allocate a handle
209
{{{ makeSetValue('destructorsRef', '0', 'Emval.toHandle(destructors)', '*') }}};
210
}
211
return result;
212
},
213
214
_emval_equals__deps: ['$Emval'],
215
_emval_equals: (first, second) => {
216
first = Emval.toValue(first);
217
second = Emval.toValue(second);
218
return first == second;
219
},
220
221
_emval_strictly_equals__deps: ['$Emval'],
222
_emval_strictly_equals: (first, second) => {
223
first = Emval.toValue(first);
224
second = Emval.toValue(second);
225
return first === second;
226
},
227
228
_emval_greater_than__deps: ['$Emval'],
229
_emval_greater_than: (first, second) => {
230
first = Emval.toValue(first);
231
second = Emval.toValue(second);
232
return first > second;
233
},
234
235
_emval_less_than__deps: ['$Emval'],
236
_emval_less_than: (first, second) => {
237
first = Emval.toValue(first);
238
second = Emval.toValue(second);
239
return first < second;
240
},
241
242
_emval_not__deps: ['$Emval'],
243
_emval_not: (object) => {
244
object = Emval.toValue(object);
245
return !object;
246
},
247
248
$emval_lookupTypes__deps: ['$requireRegisteredType'],
249
$emval_lookupTypes: (argCount, argTypes) => {
250
var a = new Array(argCount);
251
for (var i = 0; i < argCount; ++i) {
252
a[i] = requireRegisteredType({{{ makeGetValue('argTypes', `i*${POINTER_SIZE}`, '*') }}},
253
`parameter ${i}`);
254
}
255
return a;
256
},
257
258
// Leave id 0 undefined. It's not a big deal, but might be confusing
259
// to have null be a valid method caller.
260
$emval_methodCallers: [undefined],
261
262
$emval_addMethodCaller__deps: ['$emval_methodCallers'],
263
$emval_addMethodCaller: (caller) => {
264
var id = emval_methodCallers.length;
265
emval_methodCallers.push(caller);
266
return id;
267
},
268
269
_emval_create_invoker__deps: [
270
'$emval_addMethodCaller', '$emval_lookupTypes',
271
'$createNamedFunction', '$emval_returnValue',
272
'$Emval', '$getStringOrSymbol',
273
],
274
_emval_create_invoker: (argCount, argTypesPtr, kind) => {
275
var GenericWireTypeSize = {{{ 2 * POINTER_SIZE }}};
276
277
var [retType, ...argTypes] = emval_lookupTypes(argCount, argTypesPtr);
278
var toReturnWire = retType.toWireType.bind(retType);
279
var argFromPtr = argTypes.map(type => type.readValueFromPointer.bind(type));
280
argCount--; // remove the extracted return type
281
282
#if DYNAMIC_EXECUTION
283
var captures = {'toValue': Emval.toValue};
284
var args = argFromPtr.map((argFromPtr, i) => {
285
var captureName = `argFromPtr${i}`;
286
captures[captureName] = argFromPtr;
287
return `${captureName}(args${i ? '+' + i * GenericWireTypeSize : ''})`;
288
});
289
var functionBody;
290
switch (kind){
291
case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}:
292
functionBody = 'toValue(handle)';
293
break;
294
case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}:
295
functionBody = 'new (toValue(handle))';
296
break;
297
case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}:
298
functionBody = '';
299
break;
300
case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}:
301
captures['getStringOrSymbol'] = getStringOrSymbol;
302
functionBody = 'toValue(handle)[getStringOrSymbol(methodName)]';
303
break;
304
}
305
functionBody += `(${args})`;
306
if (!retType.isVoid) {
307
captures['toReturnWire'] = toReturnWire;
308
captures['emval_returnValue'] = emval_returnValue;
309
functionBody = `return emval_returnValue(toReturnWire, destructorsRef, ${functionBody})`;
310
}
311
functionBody = `return function (handle, methodName, destructorsRef, args) {
312
${functionBody}
313
}`;
314
315
var invokerFunction = new Function(Object.keys(captures), functionBody)(...Object.values(captures));
316
#else
317
var argN = new Array(argCount);
318
var invokerFunction = (handle, methodName, destructorsRef, args) => {
319
var offset = 0;
320
for (var i = 0; i < argCount; ++i) {
321
argN[i] = argFromPtr[i](args + offset);
322
offset += GenericWireTypeSize;
323
}
324
var rv;
325
switch (kind) {
326
case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}:
327
rv = Emval.toValue(handle).apply(null, argN);
328
break;
329
case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}:
330
rv = Reflect.construct(Emval.toValue(handle), argN);
331
break;
332
case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}:
333
// no-op, just return the argument
334
rv = argN[0];
335
break;
336
case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}:
337
rv = Emval.toValue(handle)[getStringOrSymbol(methodName)](...argN);
338
break;
339
}
340
return emval_returnValue(toReturnWire, destructorsRef, rv);
341
};
342
#endif
343
var functionName = `methodCaller<(${argTypes.map(t => t.name)}) => ${retType.name}>`;
344
return emval_addMethodCaller(createNamedFunction(functionName, invokerFunction));
345
},
346
347
_emval_invoke__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'],
348
_emval_invoke: (caller, handle, methodName, destructorsRef, args) => {
349
return emval_methodCallers[caller](handle, methodName, destructorsRef, args);
350
},
351
352
// Same as `_emval_invoke`, just imported into Wasm under a different return type.
353
// TODO: remove this if/when https://github.com/emscripten-core/emscripten/issues/20478 is fixed.
354
_emval_invoke_i64: '_emval_invoke',
355
356
_emval_typeof__deps: ['$Emval'],
357
_emval_typeof: (handle) => {
358
handle = Emval.toValue(handle);
359
return Emval.toHandle(typeof handle);
360
},
361
362
_emval_instanceof__deps: ['$Emval'],
363
_emval_instanceof: (object, constructor) => {
364
object = Emval.toValue(object);
365
constructor = Emval.toValue(constructor);
366
return object instanceof constructor;
367
},
368
369
_emval_is_number__deps: ['$Emval'],
370
_emval_is_number: (handle) => {
371
handle = Emval.toValue(handle);
372
return typeof handle == 'number';
373
},
374
375
_emval_is_string__deps: ['$Emval'],
376
_emval_is_string: (handle) => {
377
handle = Emval.toValue(handle);
378
return typeof handle == 'string';
379
},
380
381
_emval_in__deps: ['$Emval'],
382
_emval_in: (item, object) => {
383
item = Emval.toValue(item);
384
object = Emval.toValue(object);
385
return item in object;
386
},
387
388
_emval_delete__deps: ['$Emval'],
389
_emval_delete: (object, property) => {
390
object = Emval.toValue(object);
391
property = Emval.toValue(property);
392
return delete object[property];
393
},
394
395
_emval_throw__deps: ['$Emval'],
396
_emval_throw: (object) => {
397
object = Emval.toValue(object);
398
throw object;
399
},
400
401
#if ASYNCIFY
402
_emval_await__deps: ['$Emval', '$Asyncify'],
403
_emval_await__async: 'auto',
404
_emval_await: async (promise) => {
405
var value = await Emval.toValue(promise);
406
return Emval.toHandle(value);
407
},
408
#endif
409
410
_emval_iter_begin__deps: ['$Emval'],
411
_emval_iter_begin: (iterable) => {
412
iterable = Emval.toValue(iterable);
413
return Emval.toHandle(iterable[Symbol.iterator]());
414
},
415
416
_emval_iter_next__deps: ['$Emval'],
417
_emval_iter_next: (iterator) => {
418
iterator = Emval.toValue(iterator);
419
var result = iterator.next();
420
return result.done ? 0 : Emval.toHandle(result.value);
421
},
422
423
_emval_coro_suspend__deps: ['$Emval', '_emval_coro_resume', '_emval_coro_reject'],
424
_emval_coro_suspend: (promiseHandle, awaiterPtr) => {
425
Emval.toValue(promiseHandle)
426
.then((result) => __emval_coro_resume(awaiterPtr, Emval.toHandle(result)),
427
(error) => __emval_coro_reject(awaiterPtr, Emval.toHandle(error)));
428
},
429
430
_emval_coro_make_promise__deps: ['$Emval'],
431
_emval_coro_make_promise: (resolveHandlePtr, rejectHandlePtr) => {
432
return Emval.toHandle(new Promise((resolve, reject) => {
433
{{{ makeSetValue('resolveHandlePtr', '0', 'Emval.toHandle(resolve)', '*') }}};
434
{{{ makeSetValue('rejectHandlePtr', '0', 'Emval.toHandle(reject)', '*') }}};
435
}));
436
},
437
438
_emval_from_current_cxa_exception__deps: ['$Emval', '__cxa_rethrow'],
439
_emval_from_current_cxa_exception: () => {
440
try {
441
// Use __cxa_rethrow which already has mechanism for generating
442
// user-friendly error message and stacktrace from C++ exception
443
// if EXCEPTION_STACK_TRACES is enabled and numeric exception
444
// with metadata optimised out otherwise.
445
___cxa_rethrow();
446
} catch (e) {
447
return Emval.toHandle(e);
448
}
449
},
450
};
451
452
addToLibrary(LibraryEmVal);
453
454