Skip to content

Commit cf7f6b3

Browse files
authored
[embind] Export embind exports as ESM exports for MODULARIZE=instance. (#23404)
Using the embind AOT mode we can generate a list of all the embind bindings and export them as ES module exports.
1 parent 5fa5862 commit cf7f6b3

File tree

5 files changed

+76
-5
lines changed

5 files changed

+76
-5
lines changed

src/lib/libembind.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ var LibraryEmbind = {
3232
$PureVirtualError: class extends Error {},
3333
$GenericWireTypeSize: {{{ 2 * POINTER_SIZE }}},
3434
#if EMBIND_AOT
35-
$InvokerFunctions: '<<< EMBIND_AOT_OUTPUT >>>',
35+
$InvokerFunctions: '<<< EMBIND_AOT_INVOKERS >>>',
3636
#endif
3737
// If register_type is used, emval will be registered multiple times for
3838
// different type id's, but only a single type object is needed on the JS side

src/lib/libembind_gen.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ var LibraryEmbind = {
4444
},
4545
$FunctionDefinition__deps: ['$createJsInvoker', '$createJsInvokerSignature', '$emittedFunctions'],
4646
$FunctionDefinition: class {
47+
hasPublicSymbol = true;
4748
constructor(name, returnType, argumentTypes, functionIndex, thisType = null, isNonnullReturn = false, isAsync = false) {
4849
this.name = name;
4950
this.returnType = returnType;
@@ -154,6 +155,7 @@ var LibraryEmbind = {
154155
}
155156
},
156157
$ClassDefinition: class {
158+
hasPublicSymbol = true;
157159
constructor(typeId, name, base = null) {
158160
this.typeId = typeId;
159161
this.name = name;
@@ -265,6 +267,7 @@ var LibraryEmbind = {
265267
}
266268
},
267269
$ConstantDefinition: class {
270+
hasPublicSymbol = true;
268271
constructor(type, name) {
269272
this.type = type;
270273
this.name = name;
@@ -275,6 +278,7 @@ var LibraryEmbind = {
275278
}
276279
},
277280
$EnumDefinition: class {
281+
hasPublicSymbol = true;
278282
constructor(typeId, name) {
279283
this.typeId = typeId;
280284
this.name = name;
@@ -453,14 +457,21 @@ var LibraryEmbind = {
453457

454458
print() {
455459
const out = ['{\n'];
460+
const publicSymbols = [];
456461
for (const def of this.definitions) {
462+
if (def.hasPublicSymbol) {
463+
publicSymbols.push(def.name);
464+
}
457465
if (!def.printJs) {
458466
continue;
459467
}
460468
def.printJs(out);
461469
}
462-
out.push('}')
463-
console.log(out.join(''));
470+
out.push('}\n');
471+
console.log(JSON.stringify({
472+
'invokers': out.join(''),
473+
publicSymbols,
474+
}));
464475
}
465476
},
466477

test/modularize_instance_embind.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <stdio.h>
2+
#include <emscripten.h>
3+
#include <emscripten/bind.h>
4+
5+
using namespace emscripten;
6+
7+
void foo() {
8+
printf("foo\n");
9+
}
10+
11+
struct Bar {
12+
void print() {
13+
printf("bar\n");
14+
}
15+
};
16+
17+
int main() {
18+
printf("main\n");
19+
}
20+
21+
EMSCRIPTEN_BINDINGS(xxx) {
22+
function("foo", &foo);
23+
class_<Bar>("Bar")
24+
.constructor<>()
25+
.function("print", &Bar::print);
26+
}

test/test_core.py

+18
Original file line numberDiff line numberDiff line change
@@ -9624,6 +9624,24 @@ def test_modularize_instance(self, args):
96249624

96259625
self.assertContained('main1\nmain2\nfoo\nbar\nbaz\n', self.run_js('runner.mjs'))
96269626

9627+
def test_modularize_instance_embind(self):
9628+
self.run_process([EMCC, test_file('modularize_instance_embind.cpp'),
9629+
'-sMODULARIZE=instance',
9630+
'-lembind',
9631+
'-sEMBIND_AOT',
9632+
'-o', 'modularize_instance_embind.mjs'])
9633+
9634+
create_file('runner.mjs', '''
9635+
import init, { foo, Bar } from "./modularize_instance_embind.mjs";
9636+
await init();
9637+
foo();
9638+
const bar = new Bar();
9639+
bar.print();
9640+
bar.delete();
9641+
''')
9642+
9643+
self.assertContained('main\nfoo\nbar\n', self.run_js('runner.mjs'))
9644+
96279645

96289646
# Generate tests for everything
96299647
def make_run(name, emcc_args, settings=None, env=None,

tools/link.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,9 @@ def limit_incoming_module_api():
13801380
settings.REQUIRED_EXPORTS.append('__getTypeName')
13811381
if settings.PTHREADS or settings.WASM_WORKERS:
13821382
settings.REQUIRED_EXPORTS.append('_embind_initialize_bindings')
1383+
# Needed to assign the embind exports to the ES exports.
1384+
if settings.MODULARIZE == 'instance':
1385+
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$addOnPostCtor']
13831386

13841387
if options.emit_tsd:
13851388
settings.EMIT_TSD = True
@@ -2053,9 +2056,22 @@ def phase_emit_tsd(options, wasm_target, js_target, js_syms, metadata):
20532056
def phase_embind_aot(options, wasm_target, js_syms):
20542057
out = run_embind_gen(options, wasm_target, js_syms, {})
20552058
if DEBUG:
2056-
write_file(in_temp('embind_aot.js'), out)
2059+
write_file(in_temp('embind_aot.json'), out)
2060+
out = json.loads(out)
20572061
src = read_file(final_js)
2058-
src = do_replace(src, '<<< EMBIND_AOT_OUTPUT >>>', out)
2062+
src = do_replace(src, '<<< EMBIND_AOT_INVOKERS >>>', out['invokers'])
2063+
if settings.MODULARIZE == 'instance':
2064+
# Add ES module exports for the embind exports.
2065+
decls = '\n'.join([f'export var {name};' for name in out['publicSymbols']])
2066+
# Assign the runtime exports from Module to the ES export.
2067+
assigns = '\n'.join([f'{name} = Module[\'{name}\'];' for name in out['publicSymbols']])
2068+
exports = f'''
2069+
// start embind exports
2070+
function assignEmbindExports() {{ {assigns} }};
2071+
addOnPostCtor(assignEmbindExports);
2072+
{decls}
2073+
// end embind exports'''
2074+
src += exports
20592075
write_file(final_js, src)
20602076

20612077

0 commit comments

Comments
 (0)