Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libembind_shared.js
4150 views
1
// Copyright 2023 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
var LibraryEmbindShared = {
6
$InternalError: "= class InternalError extends Error { constructor(message) { super(message); this.name = 'InternalError'; }}",
7
$BindingError: "= class BindingError extends Error { constructor(message) { super(message); this.name = 'BindingError'; }}",
8
9
$throwInternalError__deps: ['$InternalError'],
10
$throwInternalError: (message) => { throw new InternalError(message); },
11
12
$throwBindingError__deps: ['$BindingError'],
13
$throwBindingError: (message) => { throw new BindingError(message); },
14
15
// typeID -> { toWireType: ..., fromWireType: ... }
16
$registeredTypes: {},
17
18
// typeID -> [callback]
19
$awaitingDependencies: {},
20
21
// typeID -> [dependentTypes]
22
$typeDependencies: {},
23
24
$tupleRegistrations: {},
25
26
$structRegistrations: {},
27
28
$sharedRegisterType__deps: [
29
'$awaitingDependencies', '$registeredTypes',
30
'$typeDependencies', '$throwBindingError' ],
31
$sharedRegisterType__docs: '/** @param {Object=} options */',
32
$sharedRegisterType: function(rawType, registeredInstance, options = {}) {
33
var name = registeredInstance.name;
34
if (!rawType) {
35
throwBindingError(`type "${name}" must have a positive integer typeid pointer`);
36
}
37
if (registeredTypes.hasOwnProperty(rawType)) {
38
if (options.ignoreDuplicateRegistrations) {
39
return;
40
} else {
41
throwBindingError(`Cannot register type '${name}' twice`);
42
}
43
}
44
45
registeredTypes[rawType] = registeredInstance;
46
delete typeDependencies[rawType];
47
48
if (awaitingDependencies.hasOwnProperty(rawType)) {
49
var callbacks = awaitingDependencies[rawType];
50
delete awaitingDependencies[rawType];
51
callbacks.forEach((cb) => cb());
52
}
53
},
54
55
$whenDependentTypesAreResolved__deps: [
56
'$awaitingDependencies', '$registeredTypes',
57
'$typeDependencies', '$throwInternalError'],
58
$whenDependentTypesAreResolved: (myTypes, dependentTypes, getTypeConverters) => {
59
myTypes.forEach((type) => typeDependencies[type] = dependentTypes);
60
61
function onComplete(typeConverters) {
62
var myTypeConverters = getTypeConverters(typeConverters);
63
if (myTypeConverters.length !== myTypes.length) {
64
throwInternalError('Mismatched type converter count');
65
}
66
for (var i = 0; i < myTypes.length; ++i) {
67
registerType(myTypes[i], myTypeConverters[i]);
68
}
69
}
70
71
var typeConverters = new Array(dependentTypes.length);
72
var unregisteredTypes = [];
73
var registered = 0;
74
dependentTypes.forEach((dt, i) => {
75
if (registeredTypes.hasOwnProperty(dt)) {
76
typeConverters[i] = registeredTypes[dt];
77
} else {
78
unregisteredTypes.push(dt);
79
if (!awaitingDependencies.hasOwnProperty(dt)) {
80
awaitingDependencies[dt] = [];
81
}
82
awaitingDependencies[dt].push(() => {
83
typeConverters[i] = registeredTypes[dt];
84
++registered;
85
if (registered === unregisteredTypes.length) {
86
onComplete(typeConverters);
87
}
88
});
89
}
90
});
91
if (0 === unregisteredTypes.length) {
92
onComplete(typeConverters);
93
}
94
},
95
96
$getTypeName__deps: ['$AsciiToString', '__getTypeName', 'free'],
97
$getTypeName: (type) => {
98
var ptr = ___getTypeName(type);
99
var rv = AsciiToString(ptr);
100
_free(ptr);
101
return rv;
102
},
103
$getFunctionName__deps: [],
104
$getFunctionName: (signature) => {
105
signature = signature.trim();
106
const argsIndex = signature.indexOf("(");
107
if (argsIndex === -1) return signature;
108
#if ASSERTIONS
109
assert(signature.endsWith(")"), "Parentheses for argument names should match.");
110
#endif
111
return signature.slice(0, argsIndex);
112
},
113
$getFunctionArgsName__deps: [],
114
$getFunctionArgsName: (signature) => {
115
signature = signature.trim();
116
const argsIndex = signature.indexOf("(");
117
if (argsIndex == -1) return; // Return undefined to mean we don't have any argument names
118
#if ASSERTIONS
119
assert(signature.endsWith(")"), "Parentheses for argument names should match.");
120
#endif
121
return signature.slice(argsIndex + 1, -1).replaceAll(" ", "").split(",").filter(n => n.length);
122
},
123
$heap32VectorToArray: (count, firstElement) => {
124
var array = [];
125
for (var i = 0; i < count; i++) {
126
// TODO(https://github.com/emscripten-core/emscripten/issues/17310):
127
// Find a way to hoist the `>> 2` or `>> 3` out of this loop.
128
array.push({{{ makeGetValue('firstElement', `i * ${POINTER_SIZE}`, '*') }}});
129
}
130
return array;
131
},
132
133
$requireRegisteredType__deps: [
134
'$registeredTypes', '$getTypeName', '$throwBindingError'],
135
$requireRegisteredType: (rawType, humanName) => {
136
var impl = registeredTypes[rawType];
137
if (undefined === impl) {
138
throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`);
139
}
140
return impl;
141
},
142
143
$usesDestructorStack(argTypes) {
144
// Skip return value at index 0 - it's not deleted here.
145
for (var i = 1; i < argTypes.length; ++i) {
146
// The type does not define a destructor function - must use dynamic stack
147
if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) {
148
return true;
149
}
150
}
151
return false;
152
},
153
154
// Many of the JS invoker functions are generic and can be reused for multiple
155
// function bindings. This function needs to match createJsInvoker and create
156
// a unique signature for any inputs that will create different invoker
157
// function outputs.
158
$createJsInvokerSignature(argTypes, isClassMethodFunc, returns, isAsync) {
159
const signature = [
160
isClassMethodFunc ? 't' : 'f',
161
returns ? 't' : 'f',
162
isAsync ? 't' : 'f'
163
];
164
for (let i = isClassMethodFunc ? 1 : 2; i < argTypes.length; ++i) {
165
const arg = argTypes[i];
166
let destructorSig = '';
167
if (arg.destructorFunction === undefined) {
168
destructorSig = 'u';
169
} else if (arg.destructorFunction === null) {
170
destructorSig = 'n';
171
} else {
172
destructorSig = 't';
173
}
174
signature.push(destructorSig);
175
}
176
return signature.join('');
177
},
178
179
$checkArgCount(numArgs, minArgs, maxArgs, humanName, throwBindingError) {
180
if (numArgs < minArgs || numArgs > maxArgs) {
181
var argCountMessage = minArgs == maxArgs ? minArgs : `${minArgs} to ${maxArgs}`;
182
throwBindingError(`function ${humanName} called with ${numArgs} arguments, expected ${argCountMessage}`);
183
}
184
},
185
186
$getRequiredArgCount(argTypes) {
187
var requiredArgCount = argTypes.length - 2;
188
for (var i = argTypes.length - 1; i >= 2; --i) {
189
if (!argTypes[i].optional) {
190
break;
191
}
192
requiredArgCount--;
193
}
194
return requiredArgCount;
195
},
196
197
$createJsInvoker__deps: ['$usesDestructorStack',
198
#if ASSERTIONS
199
'$checkArgCount',
200
#endif
201
],
202
$createJsInvoker(argTypes, isClassMethodFunc, returns, isAsync) {
203
var needsDestructorStack = usesDestructorStack(argTypes);
204
var argCount = argTypes.length - 2;
205
var argsList = [];
206
var argsListWired = ['fn'];
207
if (isClassMethodFunc) {
208
argsListWired.push('thisWired');
209
}
210
for (var i = 0; i < argCount; ++i) {
211
argsList.push(`arg${i}`)
212
argsListWired.push(`arg${i}Wired`)
213
}
214
argsList = argsList.join(',')
215
argsListWired = argsListWired.join(',')
216
217
var invokerFnBody = `return function (${argsList}) {\n`;
218
219
#if ASSERTIONS
220
invokerFnBody += "checkArgCount(arguments.length, minArgs, maxArgs, humanName, throwBindingError);\n";
221
#endif
222
223
#if EMSCRIPTEN_TRACING
224
invokerFnBody += `Module.emscripten_trace_enter_context('embind::' + humanName );\n`;
225
#endif
226
227
if (needsDestructorStack) {
228
invokerFnBody += "var destructors = [];\n";
229
}
230
231
var dtorStack = needsDestructorStack ? "destructors" : "null";
232
var args1 = ["humanName", "throwBindingError", "invoker", "fn", "runDestructors", "fromRetWire", "toClassParamWire"];
233
234
#if EMSCRIPTEN_TRACING
235
args1.push("Module");
236
#endif
237
238
if (isClassMethodFunc) {
239
invokerFnBody += `var thisWired = toClassParamWire(${dtorStack}, this);\n`;
240
}
241
242
for (var i = 0; i < argCount; ++i) {
243
var argName = `toArg${i}Wire`;
244
invokerFnBody += `var arg${i}Wired = ${argName}(${dtorStack}, arg${i});\n`;
245
args1.push(argName);
246
}
247
248
invokerFnBody += (returns || isAsync ? "var rv = ":"") + `invoker(${argsListWired});\n`;
249
250
var returnVal = returns ? "rv" : "";
251
#if ASYNCIFY == 1
252
args1.push("Asyncify");
253
#endif
254
#if ASYNCIFY
255
invokerFnBody += `function onDone(${returnVal}) {\n`;
256
#endif
257
258
if (needsDestructorStack) {
259
invokerFnBody += "runDestructors(destructors);\n";
260
} else {
261
for (var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method.
262
var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired"));
263
if (argTypes[i].destructorFunction !== null) {
264
invokerFnBody += `${paramName}_dtor(${paramName});\n`;
265
args1.push(`${paramName}_dtor`);
266
}
267
}
268
}
269
270
if (returns) {
271
invokerFnBody += "var ret = fromRetWire(rv);\n" +
272
#if EMSCRIPTEN_TRACING
273
"Module.emscripten_trace_exit_context();\n" +
274
#endif
275
"return ret;\n";
276
} else {
277
#if EMSCRIPTEN_TRACING
278
invokerFnBody += "Module.emscripten_trace_exit_context();\n";
279
#endif
280
}
281
282
#if ASYNCIFY == 1
283
invokerFnBody += "}\n";
284
invokerFnBody += `return Asyncify.currData ? Asyncify.whenDone().then(onDone) : onDone(${returnVal});\n`
285
#elif ASYNCIFY == 2
286
invokerFnBody += "}\n";
287
invokerFnBody += "return " + (isAsync ? "rv.then(onDone)" : `onDone(${returnVal})`) + ";";
288
#endif
289
290
invokerFnBody += "}\n";
291
292
#if ASSERTIONS
293
args1.push('checkArgCount', 'minArgs', 'maxArgs');
294
invokerFnBody = `if (arguments.length !== ${args1.length}){ throw new Error(humanName + "Expected ${args1.length} closure arguments " + arguments.length + " given."); }\n${invokerFnBody}`;
295
#endif
296
return new Function(args1, invokerFnBody);
297
}
298
};
299
300
addToLibrary(LibraryEmbindShared);
301
302