| |
| |
| |
| |
| |
|
|
| addToLibrary({ |
| |
| #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]; |
| #if ASSERTIONS |
| assert(func, `Cannot call unknown function ${ident}, make sure it is exported`); |
| #endif |
| return func; |
| }, |
|
|
| |
| $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) => { |
| |
| var toC = { |
| #if MEMORY64 |
| 'pointer': (p) => {{{ to64('p') }}}, |
| #endif |
| 'string': (str) => { |
| var ret = 0; |
| if (str !== null && str !== undefined && str !== 0) { |
| 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 |
| |
| 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 |
| |
| |
| runtimeKeepalivePush(); |
| if (Asyncify.currData != previousAsync) { |
| #if ASSERTIONS |
| |
| |
| |
| |
| |
| |
| 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 |
| |
| |
| |
| #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 (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 |
| |
| |
| 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); |
| }, |
| }); |
|
|