Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libccall.js
4150 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
#endif
57
if (returnType === 'boolean') return Boolean(ret);
58
return ret;
59
}
60
61
var func = getCFunc(ident);
62
var cArgs = [];
63
var stack = 0;
64
#if ASSERTIONS
65
assert(returnType !== 'array', 'Return type should not be "array".');
66
#endif
67
if (args) {
68
for (var i = 0; i < args.length; i++) {
69
var converter = toC[argTypes[i]];
70
if (converter) {
71
if (stack === 0) stack = stackSave();
72
cArgs[i] = converter(args[i]);
73
} else {
74
cArgs[i] = args[i];
75
}
76
}
77
}
78
#if ASYNCIFY == 1
79
// Data for a previous async operation that was in flight before us.
80
var previousAsync = Asyncify.currData;
81
#endif
82
var ret = func(...cArgs);
83
function onDone(ret) {
84
#if ASYNCIFY == 1
85
runtimeKeepalivePop();
86
#endif
87
if (stack !== 0) stackRestore(stack);
88
return convertReturnValue(ret);
89
}
90
#if ASYNCIFY
91
var asyncMode = opts?.async;
92
#endif
93
94
#if ASYNCIFY == 1
95
// Keep the runtime alive through all calls. Note that this call might not be
96
// async, but for simplicity we push and pop in all calls.
97
runtimeKeepalivePush();
98
if (Asyncify.currData != previousAsync) {
99
#if ASSERTIONS
100
// A change in async operation happened. If there was already an async
101
// operation in flight before us, that is an error: we should not start
102
// another async operation while one is active, and we should not stop one
103
// either. The only valid combination is to have no change in the async
104
// data (so we either had one in flight and left it alone, or we didn't have
105
// one), or to have nothing in flight and to start one.
106
assert(!(previousAsync && Asyncify.currData), 'We cannot start an async operation when one is already flight');
107
assert(!(previousAsync && !Asyncify.currData), 'We cannot stop an async operation in flight');
108
#endif
109
// This is a new async operation. The wasm is paused and has unwound its stack.
110
// We need to return a Promise that resolves the return value
111
// once the stack is rewound and execution finishes.
112
#if ASSERTIONS
113
assert(asyncMode, 'The call to ' + ident + ' is running asynchronously. If this was intended, add the async option to the ccall/cwrap call.');
114
#endif
115
return Asyncify.whenDone().then(onDone);
116
}
117
#endif
118
119
#if ASYNCIFY == 2
120
if (asyncMode) return ret.then(onDone);
121
#endif
122
123
ret = onDone(ret);
124
#if ASYNCIFY == 1
125
// If this is an async ccall, ensure we return a promise
126
if (asyncMode) return Promise.resolve(ret);
127
#endif
128
return ret;
129
},
130
131
$cwrap__docs: `
132
/**
133
* @param {string=} returnType
134
* @param {Array=} argTypes
135
* @param {Object=} opts
136
*/`,
137
$cwrap__deps: [ '$ccall',
138
#if !ASSERTIONS
139
'$getCFunc',
140
#endif
141
],
142
$cwrap: (ident, returnType, argTypes, opts) => {
143
#if !ASSERTIONS
144
// When the function takes numbers and returns a number, we can just return
145
// the original function
146
var numericArgs = !argTypes || argTypes.every((type) => type === 'number' || type === 'boolean');
147
var numericRet = returnType !== 'string';
148
if (numericRet && numericArgs && !opts) {
149
return getCFunc(ident);
150
}
151
#endif
152
return (...args) => ccall(ident, returnType, argTypes, args, opts);
153
},
154
});
155
156