File size: 5,018 Bytes
00df61d | 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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /**
* @license
* Copyright 2022 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
addToLibrary({
// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)
#if MODULARIZE == 'instance' && !INCLUDE_FULL_LIBRARY
$getCFunc__deps: [() => error('ccall is not yet compatible with MODULARIZE=instance')],
#endif
$getCFunc__internal: true,
$getCFunc: (ident) => {
var func = Module['_' + ident]; // closure exported function
#if ASSERTIONS
assert(func, `Cannot call unknown function ${ident}, make sure it is exported`);
#endif
return func;
},
// C calling interface.
$ccall__deps: ['$getCFunc', '$writeArrayToMemory', '$stringToUTF8OnStack', '$stackSave', '$stackRestore', '$stackAlloc'],
$ccall__docs: `
/**
* @param {string|null=} returnType
* @param {Array=} argTypes
* @param {Array=} args
* @param {Object=} opts
*/`,
$ccall: (ident, returnType, argTypes, args, opts) => {
// For fast lookup of conversion functions
var toC = {
#if MEMORY64
'pointer': (p) => {{{ to64('p') }}},
#endif
'string': (str) => {
var ret = 0;
if (str !== null && str !== undefined && str !== 0) { // null string
ret = stringToUTF8OnStack(str);
}
return {{{ to64('ret') }}};
},
'array': (arr) => {
var ret = stackAlloc(arr.length);
writeArrayToMemory(arr, ret);
return {{{ to64('ret') }}};
}
};
function convertReturnValue(ret) {
if (returnType === 'string') {
return UTF8ToString({{{ from64Expr('ret') }}});
}
#if MEMORY64
if (returnType === 'pointer') return Number(ret);
#elif CAN_ADDRESS_2GB
if (returnType === 'pointer') return ret >>> 0;
#endif
if (returnType === 'boolean') return Boolean(ret);
return ret;
}
var func = getCFunc(ident);
var cArgs = [];
var stack = 0;
#if ASSERTIONS
assert(returnType !== 'array', 'return type should not be "array"');
#endif
if (args) {
for (var i = 0; i < args.length; i++) {
var converter = toC[argTypes[i]];
if (converter) {
if (stack === 0) stack = stackSave();
cArgs[i] = converter(args[i]);
} else {
cArgs[i] = args[i];
}
}
}
#if ASYNCIFY == 1
// Data for a previous async operation that was in flight before us.
var previousAsync = Asyncify.currData;
#endif
var ret = func(...cArgs);
function onDone(ret) {
#if ASYNCIFY == 1
runtimeKeepalivePop();
#endif
if (stack !== 0) stackRestore(stack);
return convertReturnValue(ret);
}
#if ASYNCIFY
var asyncMode = opts?.async;
#endif
#if ASYNCIFY == 1
// Keep the runtime alive through all calls. Note that this call might not be
// async, but for simplicity we push and pop in all calls.
runtimeKeepalivePush();
if (Asyncify.currData != previousAsync) {
#if ASSERTIONS
// A change in async operation happened. If there was already an async
// operation in flight before us, that is an error: we should not start
// another async operation while one is active, and we should not stop one
// either. The only valid combination is to have no change in the async
// data (so we either had one in flight and left it alone, or we didn't have
// one), or to have nothing in flight and to start one.
assert(!(previousAsync && Asyncify.currData), 'We cannot start an async operation when one is already in flight');
assert(!(previousAsync && !Asyncify.currData), 'We cannot stop an async operation in flight');
#endif
// This is a new async operation. The wasm is paused and has unwound its stack.
// We need to return a Promise that resolves the return value
// once the stack is rewound and execution finishes.
#if ASSERTIONS
assert(asyncMode, `The call to ${ident} is running asynchronously. If this was intended, add the async option to the ccall/cwrap call.`);
#endif
return Asyncify.whenDone().then(onDone);
}
#endif
#if ASYNCIFY == 2
if (asyncMode) return ret.then(onDone);
#endif
ret = onDone(ret);
#if ASYNCIFY == 1
// If this is an async ccall, ensure we return a promise
if (asyncMode) return Promise.resolve(ret);
#endif
return ret;
},
$cwrap__docs: `
/**
* @param {string=} returnType
* @param {Array=} argTypes
* @param {Object=} opts
*/`,
$cwrap__deps: [ '$ccall',
#if !ASSERTIONS
'$getCFunc',
#endif
],
$cwrap: (ident, returnType, argTypes, opts) => {
#if !ASSERTIONS
// When the function takes numbers and returns a number, we can just return
// the original function
var numericArgs = !argTypes || argTypes.every((type) => type === 'number' || type === 'boolean');
var numericRet = returnType !== 'string';
if (numericRet && numericArgs && !opts) {
return getCFunc(ident);
}
#endif
return (...args) => ccall(ident, returnType, argTypes, args, opts);
},
});
|