Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libccall.js
6162 views
1
/**
2
* @license
3
* Copyright 2022 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
addToLibrary({
8
// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)
9
#if MODULARIZE == 'instance' && !INCLUDE_FULL_LIBRARY
10
$getCFunc__deps: [() => error('ccall is not yet compatible with MODULARIZE=instance')],
11
#endif
12
$getCFunc__internal: true,
13
$getCFunc: (ident) => {
14
var func = Module['_' + ident]; // closure exported function
15
#if ASSERTIONS
16
assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported');
17
#endif
18
return func;
19
},
20
21
// C calling interface.
22
$ccall__deps: ['$getCFunc', '$writeArrayToMemory', '$stringToUTF8OnStack', '$stackSave', '$stackRestore', '$stackAlloc'],
23
$ccall__docs: `
24
/**
25
* @param {string|null=} returnType
26
* @param {Array=} argTypes
27
* @param {Array=} args
28
* @param {Object=} opts
29
*/`,
30
$ccall: (ident, returnType, argTypes, args, opts) => {
31
// For fast lookup of conversion functions
32
var toC = {
33
#if MEMORY64
34
'pointer': (p) => {{{ to64('p') }}},
35
#endif
36
'string': (str) => {
37
var ret = 0;
38
if (str !== null && str !== undefined && str !== 0) { // null string
39
ret = stringToUTF8OnStack(str);
40
}
41
return {{{ to64('ret') }}};
42
},
43
'array': (arr) => {
44
var ret = stackAlloc(arr.length);
45
writeArrayToMemory(arr, ret);
46
return {{{ to64('ret') }}};
47
}
48
};
49
50
function convertReturnValue(ret) {
51
if (returnType === 'string') {
52
return UTF8ToString({{{ from64Expr('ret') }}});
53
}
54
#if MEMORY64
55
if (returnType === 'pointer') return Number(ret);
56
#elif CAN_ADDRESS_2GB
57
if (returnType === 'pointer') return ret >>> 0;
58
#endif
59
if (returnType === 'boolean') return Boolean(ret);
60
return ret;
61
}
62
63
var func = getCFunc(ident);
64
var cArgs = [];
65
var stack = 0;
66
#if ASSERTIONS
67
assert(returnType !== 'array', 'Return type should not be "array".');
68
#endif
69
if (args) {
70
for (var i = 0; i < args.length; i++) {
71
var converter = toC[argTypes[i]];
72
if (converter) {
73
if (stack === 0) stack = stackSave();
74
cArgs[i] = converter(args[i]);
75
} else {
76
cArgs[i] = args[i];
77
}
78
}
79
}
80
#if ASYNCIFY == 1
81
// Data for a previous async operation that was in flight before us.
82
var previousAsync = Asyncify.currData;
83
#endif
84
var ret = func(...cArgs);
85
function onDone(ret) {
86
#if ASYNCIFY == 1
87
runtimeKeepalivePop();
88
#endif
89
if (stack !== 0) stackRestore(stack);
90
return convertReturnValue(ret);
91
}
92
#if ASYNCIFY
93
var asyncMode = opts?.async;
94
#endif
95
96
#if ASYNCIFY == 1
97
// Keep the runtime alive through all calls. Note that this call might not be
98
// async, but for simplicity we push and pop in all calls.
99
runtimeKeepalivePush();
100
if (Asyncify.currData != previousAsync) {
101
#if ASSERTIONS
102
// A change in async operation happened. If there was already an async
103
// operation in flight before us, that is an error: we should not start
104
// another async operation while one is active, and we should not stop one
105
// either. The only valid combination is to have no change in the async
106
// data (so we either had one in flight and left it alone, or we didn't have
107
// one), or to have nothing in flight and to start one.
108
assert(!(previousAsync && Asyncify.currData), 'We cannot start an async operation when one is already in flight');
109
assert(!(previousAsync && !Asyncify.currData), 'We cannot stop an async operation in flight');
110
#endif
111
// This is a new async operation. The wasm is paused and has unwound its stack.
112
// We need to return a Promise that resolves the return value
113
// once the stack is rewound and execution finishes.
114
#if ASSERTIONS
115
assert(asyncMode, 'The call to ' + ident + ' is running asynchronously. If this was intended, add the async option to the ccall/cwrap call.');
116
#endif
117
return Asyncify.whenDone().then(onDone);
118
}
119
#endif
120
121
#if ASYNCIFY == 2
122
if (asyncMode) return ret.then(onDone);
123
#endif
124
125
ret = onDone(ret);
126
#if ASYNCIFY == 1
127
// If this is an async ccall, ensure we return a promise
128
if (asyncMode) return Promise.resolve(ret);
129
#endif
130
return ret;
131
},
132
133
$cwrap__docs: `
134
/**
135
* @param {string=} returnType
136
* @param {Array=} argTypes
137
* @param {Object=} opts
138
*/`,
139
$cwrap__deps: [ '$ccall',
140
#if !ASSERTIONS
141
'$getCFunc',
142
#endif
143
],
144
$cwrap: (ident, returnType, argTypes, opts) => {
145
#if !ASSERTIONS
146
// When the function takes numbers and returns a number, we can just return
147
// the original function
148
var numericArgs = !argTypes || argTypes.every((type) => type === 'number' || type === 'boolean');
149
var numericRet = returnType !== 'string';
150
if (numericRet && numericArgs && !opts) {
151
return getCFunc(ident);
152
}
153
#endif
154
return (...args) => ccall(ident, returnType, argTypes, args, opts);
155
},
156
});
157
158