| // Copyright 2023 The Emscripten Authors. All rights reserved. |
| // Emscripten is available under two separate licenses, the MIT license and the |
| // University of Illinois/NCSA Open Source License. Both these licenses can be |
| // found in the LICENSE file. |
| var LibraryEmbindShared = { |
| $InternalError: "= class InternalError extends Error { constructor(message) { super(message); this.name = 'InternalError'; }}", |
| $BindingError: "= class BindingError extends Error { constructor(message) { super(message); this.name = 'BindingError'; }}", |
| |
| $throwInternalError__deps: ['$InternalError'], |
| $throwInternalError: (message) => { throw new InternalError(message); }, |
| |
| $throwBindingError__deps: ['$BindingError'], |
| $throwBindingError: (message) => { throw new BindingError(message); }, |
| |
| // typeID -> { toWireType: ..., fromWireType: ... } |
| $registeredTypes: {}, |
| |
| // typeID -> [callback] |
| $awaitingDependencies: {}, |
| |
| // typeID -> [dependentTypes] |
| $typeDependencies: {}, |
| |
| $tupleRegistrations: {}, |
| |
| $structRegistrations: {}, |
| |
| $sharedRegisterType__deps: [ |
| '$awaitingDependencies', '$registeredTypes', |
| '$typeDependencies', '$throwBindingError' ], |
| $sharedRegisterType__docs: '/** @param {Object=} options */', |
| $sharedRegisterType: function(rawType, registeredInstance, options = {}) { |
| var name = registeredInstance.name; |
| if (!rawType) { |
| throwBindingError(`type "${name}" must have a positive integer typeid pointer`); |
| } |
| if (registeredTypes.hasOwnProperty(rawType)) { |
| if (options.ignoreDuplicateRegistrations) { |
| return; |
| } else { |
| throwBindingError(`Cannot register type '${name}' twice`); |
| } |
| } |
| |
| registeredTypes[rawType] = registeredInstance; |
| delete typeDependencies[rawType]; |
| |
| if (awaitingDependencies.hasOwnProperty(rawType)) { |
| var callbacks = awaitingDependencies[rawType]; |
| delete awaitingDependencies[rawType]; |
| callbacks.forEach((cb) => cb()); |
| } |
| }, |
| |
| $whenDependentTypesAreResolved__deps: [ |
| '$awaitingDependencies', '$registeredTypes', |
| '$typeDependencies', '$throwInternalError'], |
| $whenDependentTypesAreResolved: (myTypes, dependentTypes, getTypeConverters) => { |
| myTypes.forEach((type) => typeDependencies[type] = dependentTypes); |
| |
| function onComplete(typeConverters) { |
| var myTypeConverters = getTypeConverters(typeConverters); |
| if (myTypeConverters.length !== myTypes.length) { |
| throwInternalError('Mismatched type converter count'); |
| } |
| for (var i = 0; i < myTypes.length; ++i) { |
| registerType(myTypes[i], myTypeConverters[i]); |
| } |
| } |
| |
| var typeConverters = new Array(dependentTypes.length); |
| var unregisteredTypes = []; |
| var registered = 0; |
| dependentTypes.forEach((dt, i) => { |
| if (registeredTypes.hasOwnProperty(dt)) { |
| typeConverters[i] = registeredTypes[dt]; |
| } else { |
| unregisteredTypes.push(dt); |
| if (!awaitingDependencies.hasOwnProperty(dt)) { |
| awaitingDependencies[dt] = []; |
| } |
| awaitingDependencies[dt].push(() => { |
| typeConverters[i] = registeredTypes[dt]; |
| ++registered; |
| if (registered === unregisteredTypes.length) { |
| onComplete(typeConverters); |
| } |
| }); |
| } |
| }); |
| if (0 === unregisteredTypes.length) { |
| onComplete(typeConverters); |
| } |
| }, |
| |
| $getTypeName__deps: ['$AsciiToString', '__getTypeName', 'free'], |
| $getTypeName: (type) => { |
| var ptr = ___getTypeName(type); |
| var rv = AsciiToString(ptr); |
| _free(ptr); |
| return rv; |
| }, |
| $getFunctionName__deps: [], |
| $getFunctionName: (signature) => { |
| signature = signature.trim(); |
| const argsIndex = signature.indexOf("("); |
| if (argsIndex === -1) return signature; |
| #if ASSERTIONS |
| assert(signature.endsWith(")"), "Parentheses for argument names should match."); |
| #endif |
| return signature.slice(0, argsIndex); |
| }, |
| $getFunctionArgsName__deps: [], |
| $getFunctionArgsName: (signature) => { |
| signature = signature.trim(); |
| const argsIndex = signature.indexOf("("); |
| if (argsIndex == -1) return; // Return undefined to mean we don't have any argument names |
| #if ASSERTIONS |
| assert(signature.endsWith(")"), "Parentheses for argument names should match."); |
| #endif |
| return signature.slice(argsIndex + 1, -1).replaceAll(" ", "").split(",").filter(n => n.length); |
| }, |
| $heap32VectorToArray: (count, firstElement) => { |
| var array = []; |
| for (var i = 0; i < count; i++) { |
| // TODO(https://github.com/emscripten-core/emscripten/issues/17310): |
| // Find a way to hoist the `>> 2` or `>> 3` out of this loop. |
| array.push({{{ makeGetValue('firstElement', `i * ${POINTER_SIZE}`, '*') }}}); |
| } |
| return array; |
| }, |
| |
| $requireRegisteredType__deps: [ |
| '$registeredTypes', '$getTypeName', '$throwBindingError'], |
| $requireRegisteredType: (rawType, humanName) => { |
| var impl = registeredTypes[rawType]; |
| if (undefined === impl) { |
| throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`); |
| } |
| return impl; |
| }, |
| |
| $usesDestructorStack(argTypes) { |
| // Skip return value at index 0 - it's not deleted here. |
| for (var i = 1; i < argTypes.length; ++i) { |
| // The type does not define a destructor function - must use dynamic stack |
| if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { |
| return true; |
| } |
| } |
| return false; |
| }, |
| |
| // Many of the JS invoker functions are generic and can be reused for multiple |
| // function bindings. This function needs to match createJsInvoker and create |
| // a unique signature for any inputs that will create different invoker |
| // function outputs. |
| $createJsInvokerSignature(argTypes, isClassMethodFunc, returns, isAsync) { |
| const signature = [ |
| isClassMethodFunc ? 't' : 'f', |
| returns ? 't' : 'f', |
| isAsync ? 't' : 'f' |
| ]; |
| for (let i = isClassMethodFunc ? 1 : 2; i < argTypes.length; ++i) { |
| const arg = argTypes[i]; |
| let destructorSig = ''; |
| if (arg.destructorFunction === undefined) { |
| destructorSig = 'u'; |
| } else if (arg.destructorFunction === null) { |
| destructorSig = 'n'; |
| } else { |
| destructorSig = 't'; |
| } |
| signature.push(destructorSig); |
| } |
| return signature.join(''); |
| }, |
| |
| $checkArgCount(numArgs, minArgs, maxArgs, humanName, throwBindingError) { |
| if (numArgs < minArgs || numArgs > maxArgs) { |
| var argCountMessage = minArgs == maxArgs ? minArgs : `${minArgs} to ${maxArgs}`; |
| throwBindingError(`function ${humanName} called with ${numArgs} arguments, expected ${argCountMessage}`); |
| } |
| }, |
| |
| $getRequiredArgCount(argTypes) { |
| var requiredArgCount = argTypes.length - 2; |
| for (var i = argTypes.length - 1; i >= 2; --i) { |
| if (!argTypes[i].optional) { |
| break; |
| } |
| requiredArgCount--; |
| } |
| return requiredArgCount; |
| }, |
| |
| $createJsInvoker__deps: ['$usesDestructorStack', |
| #if ASSERTIONS |
| '$checkArgCount', |
| #endif |
| ], |
| $createJsInvoker(argTypes, isClassMethodFunc, returns, isAsync) { |
| var needsDestructorStack = usesDestructorStack(argTypes); |
| var argCount = argTypes.length - 2; |
| var argsList = []; |
| var argsListWired = ['fn']; |
| if (isClassMethodFunc) { |
| argsListWired.push('thisWired'); |
| } |
| for (var i = 0; i < argCount; ++i) { |
| argsList.push(`arg${i}`) |
| argsListWired.push(`arg${i}Wired`) |
| } |
| argsList = argsList.join(',') |
| argsListWired = argsListWired.join(',') |
| |
| var invokerFnBody = `return function (${argsList}) {\n`; |
| |
| #if ASSERTIONS |
| invokerFnBody += "checkArgCount(arguments.length, minArgs, maxArgs, humanName, throwBindingError);\n"; |
| #endif |
| |
| #if EMSCRIPTEN_TRACING |
| invokerFnBody += `Module.emscripten_trace_enter_context('embind::' + humanName );\n`; |
| #endif |
| |
| if (needsDestructorStack) { |
| invokerFnBody += "var destructors = [];\n"; |
| } |
| |
| var dtorStack = needsDestructorStack ? "destructors" : "null"; |
| var args1 = ["humanName", "throwBindingError", "invoker", "fn", "runDestructors", "fromRetWire", "toClassParamWire"]; |
| |
| #if EMSCRIPTEN_TRACING |
| args1.push("Module"); |
| #endif |
| |
| if (isClassMethodFunc) { |
| invokerFnBody += `var thisWired = toClassParamWire(${dtorStack}, this);\n`; |
| } |
| |
| for (var i = 0; i < argCount; ++i) { |
| var argName = `toArg${i}Wire`; |
| invokerFnBody += `var arg${i}Wired = ${argName}(${dtorStack}, arg${i});\n`; |
| args1.push(argName); |
| } |
| |
| invokerFnBody += (returns || isAsync ? "var rv = ":"") + `invoker(${argsListWired});\n`; |
| |
| var returnVal = returns ? "rv" : ""; |
| #if ASYNCIFY == 1 |
| args1.push("Asyncify"); |
| #endif |
| #if ASYNCIFY |
| invokerFnBody += `function onDone(${returnVal}) {\n`; |
| #endif |
| |
| if (needsDestructorStack) { |
| invokerFnBody += "runDestructors(destructors);\n"; |
| } else { |
| 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. |
| var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired")); |
| if (argTypes[i].destructorFunction !== null) { |
| invokerFnBody += `${paramName}_dtor(${paramName});\n`; |
| args1.push(`${paramName}_dtor`); |
| } |
| } |
| } |
| |
| if (returns) { |
| invokerFnBody += "var ret = fromRetWire(rv);\n" + |
| #if EMSCRIPTEN_TRACING |
| "Module.emscripten_trace_exit_context();\n" + |
| #endif |
| "return ret;\n"; |
| } else { |
| #if EMSCRIPTEN_TRACING |
| invokerFnBody += "Module.emscripten_trace_exit_context();\n"; |
| #endif |
| } |
| |
| #if ASYNCIFY == 1 |
| invokerFnBody += "}\n"; |
| invokerFnBody += `return Asyncify.currData ? Asyncify.whenDone().then(onDone) : onDone(${returnVal});\n` |
| #elif ASYNCIFY == 2 |
| invokerFnBody += "}\n"; |
| invokerFnBody += "return " + (isAsync ? "rv.then(onDone)" : `onDone(${returnVal})`) + ";"; |
| #endif |
| |
| invokerFnBody += "}\n"; |
| |
| #if ASSERTIONS |
| args1.push('checkArgCount', 'minArgs', 'maxArgs'); |
| invokerFnBody = `if (arguments.length !== ${args1.length}){ throw new Error(humanName + "Expected ${args1.length} closure arguments " + arguments.length + " given."); }\n${invokerFnBody}`; |
| #endif |
| return new Function(args1, invokerFnBody); |
| } |
| }; |
| |
| addToLibrary(LibraryEmbindShared); |