Skip to content

Commit fe7dc4c

Browse files
authored
embind: Add ahead of time JS glue code generation. (emscripten-core#20796)
Adds a new setting `-sEMBIND_AOT` that will create all the glue code invoker functions during compilation. This uses the same framework as the TypeScript generation code and creates an instrumented version of embind that is run in node to generate the glue code. This tries to reuse as much of the `craftInovkerFunction` code that the current JIT mode uses, so it will remain functionally equivalent.
1 parent 84662e1 commit fe7dc4c

6 files changed

Lines changed: 324 additions & 122 deletions

File tree

src/embind/embind.js

Lines changed: 31 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ var LibraryEmbind = {
3535
$PureVirtualError__deps: ['$extendError'],
3636
$PureVirtualError: undefined,
3737
$GenericWireTypeSize: {{{ 2 * POINTER_SIZE }}},
38+
#if EMBIND_AOT
39+
$InvokerFunctions: '<<< EMBIND_AOT_OUTPUT >>>',
40+
#endif
3841

3942
$init_embind__deps: [
4043
'$getInheritedInstanceCount', '$getLiveInheritedInstances',
@@ -759,9 +762,15 @@ var LibraryEmbind = {
759762
// (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function)
760763
// craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind.
761764
$craftInvokerFunction__deps: [
762-
'$createNamedFunction', '$runDestructors', '$throwBindingError',
765+
'$createNamedFunction', '$runDestructors', '$throwBindingError', '$usesDestructorStack',
763766
#if DYNAMIC_EXECUTION
764767
'$newFunc',
768+
#if !EMBIND_AOT
769+
'$createJsInvoker',
770+
#endif
771+
#endif
772+
#if EMBIND_AOT
773+
'$InvokerFunctions',
765774
#endif
766775
#if ASYNCIFY
767776
'$Asyncify',
@@ -798,18 +807,11 @@ var LibraryEmbind = {
798807

799808
// Determine if we need to use a dynamic stack to store the destructors for the function parameters.
800809
// TODO: Remove this completely once all function invokers are being dynamically generated.
801-
var needsDestructorStack = false;
802-
803-
for (var i = 1; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here.
804-
if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { // The type does not define a destructor function - must use dynamic stack
805-
needsDestructorStack = true;
806-
break;
807-
}
808-
}
810+
var needsDestructorStack = usesDestructorStack(argTypes);
809811

810812
var returns = (argTypes[0].name !== "void");
811813

812-
#if DYNAMIC_EXECUTION == 0
814+
#if DYNAMIC_EXECUTION == 0 && !EMBIND_AOT
813815
var expectedArgCount = argCount - 2;
814816
var argsWired = new Array(expectedArgCount);
815817
var invokerFuncArgs = [];
@@ -870,99 +872,33 @@ var LibraryEmbind = {
870872
return onDone(rv);
871873
};
872874
#else
873-
var argsList = "";
874-
var argsListWired = "";
875-
for (var i = 0; i < argCount - 2; ++i) {
876-
argsList += (i!==0?", ":"")+"arg"+i;
877-
argsListWired += (i!==0?", ":"")+"arg"+i+"Wired";
878-
}
879-
880-
var invokerFnBody = `
881-
return function (${argsList}) {
882-
if (arguments.length !== ${argCount - 2}) {
883-
throwBindingError('function ${humanName} called with ' + arguments.length + ' arguments, expected ${argCount - 2}');
884-
}`;
885-
875+
// Builld the arguments that will be passed into the closure around the invoker
876+
// function.
877+
var closureArgs = [throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]];
886878
#if EMSCRIPTEN_TRACING
887-
invokerFnBody += `Module.emscripten_trace_enter_context('embind::${humanName}');\n`;
879+
closureArgs.push(Module);
888880
#endif
889-
890-
if (needsDestructorStack) {
891-
invokerFnBody += "var destructors = [];\n";
892-
}
893-
894-
var dtorStack = needsDestructorStack ? "destructors" : "null";
895-
var args1 = ["throwBindingError", "invoker", "fn", "runDestructors", "retType", "classParam"];
896-
var args2 = [throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]];
897-
898-
#if EMSCRIPTEN_TRACING
899-
args1.push("Module");
900-
args2.push(Module);
901-
#endif
902-
903-
if (isClassMethodFunc) {
904-
invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n";
905-
}
906-
907-
for (var i = 0; i < argCount - 2; ++i) {
908-
invokerFnBody += "var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";
909-
args1.push("argType"+i);
910-
args2.push(argTypes[i+2]);
911-
}
912-
913-
if (isClassMethodFunc) {
914-
argsListWired = "thisWired" + (argsListWired.length > 0 ? ", " : "") + argsListWired;
915-
}
916-
917-
invokerFnBody +=
918-
(returns || isAsync ? "var rv = ":"") + "invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";
919-
881+
for (var i = 0; i < argCount - 2; ++i) {
882+
closureArgs.push(argTypes[i+2]);
883+
}
920884
#if ASYNCIFY == 1
921-
args1.push("Asyncify");
922-
args2.push(Asyncify);
885+
closureArgs.push(Asyncify);
923886
#endif
924-
#if ASYNCIFY
925-
invokerFnBody += "function onDone(" + (returns ? "rv" : "") + ") {\n";
926-
#endif
927-
928-
if (needsDestructorStack) {
929-
invokerFnBody += "runDestructors(destructors);\n";
930-
} else {
931-
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.
932-
var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired"));
933-
if (argTypes[i].destructorFunction !== null) {
934-
invokerFnBody += paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";
935-
args1.push(paramName+"_dtor");
936-
args2.push(argTypes[i].destructorFunction);
937-
}
887+
if (!needsDestructorStack) {
888+
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.
889+
if (argTypes[i].destructorFunction !== null) {
890+
closureArgs.push(argTypes[i].destructorFunction);
938891
}
939892
}
893+
}
940894

941-
if (returns) {
942-
invokerFnBody += "var ret = retType.fromWireType(rv);\n" +
943-
#if EMSCRIPTEN_TRACING
944-
"Module.emscripten_trace_exit_context();\n" +
945-
#endif
946-
"return ret;\n";
947-
} else {
948-
#if EMSCRIPTEN_TRACING
949-
invokerFnBody += "Module.emscripten_trace_exit_context();\n";
950-
#endif
951-
}
952-
953-
#if ASYNCIFY == 1
954-
invokerFnBody += "}\n";
955-
invokerFnBody += "return Asyncify.currData ? Asyncify.whenDone().then(onDone) : onDone(" + (returns ? "rv" : "") +");\n"
956-
#elif ASYNCIFY == 2
957-
invokerFnBody += "}\n";
958-
invokerFnBody += "return " + (isAsync ? "rv.then(onDone)" : "onDone(" + (returns ? "rv" : "") + ")") + ";";
895+
#if EMBIND_AOT
896+
var invokerFn = InvokerFunctions[cppTargetFunc].apply(null, closureArgs);
897+
#else
898+
let [args, invokerFnBody] = createJsInvoker(humanName, argTypes, isClassMethodFunc, returns, isAsync);
899+
args.push(invokerFnBody);
900+
var invokerFn = newFunc(Function, args).apply(null, closureArgs);
959901
#endif
960-
961-
invokerFnBody += "}\n";
962-
963-
args1.push(invokerFnBody);
964-
965-
var invokerFn = newFunc(Function, args1).apply(null, args2);
966902
#endif
967903
return createNamedFunction(humanName, invokerFn);
968904
},

0 commit comments

Comments
 (0)