Skip to content

Commit 57ba793

Browse files
authored
Add option to emit TypeScript definitions for Wasm module exports. (#21279)
The new flag `--emit-tsd <filename>` will generate a TypeScript defintion file for any Wasm module exports. If embind is also used the definitions for those types will also be included in the same file. This still doesn't give the full picture of Wasm module e.g. missing HEAP<N> and the various helper functions defined in JS.
1 parent 7459cab commit 57ba793

13 files changed

+329
-21
lines changed

emcc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def __init__(self):
140140
self.ignore_dynamic_linking = False
141141
self.shell_path = None
142142
self.source_map_base = ''
143+
self.emit_tsd = ''
143144
self.embind_emit_tsd = ''
144145
self.emrun = False
145146
self.cpu_profiler = False
@@ -1276,6 +1277,9 @@ def consume_arg_file():
12761277
options.source_map_base = consume_arg()
12771278
elif check_arg('--embind-emit-tsd'):
12781279
options.embind_emit_tsd = consume_arg()
1280+
elif check_arg('--emit-tsd'):
1281+
diagnostics.warning('experimental', '--emit-tsd is still experimental. Not all definitions are generated.')
1282+
options.emit_tsd = consume_arg()
12791283
elif check_flag('--no-entry'):
12801284
options.no_entry = True
12811285
elif check_arg('--js-library'):

src/embind/embind_gen.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ var LibraryEmbind = {
365365
def.print(this.typeToJsName.bind(this), out);
366366
}
367367
// Print module definitions
368-
out.push('export interface MainModule {\n');
368+
out.push('interface EmbindModule {\n');
369369
for (const def of this.definitions) {
370370
if (!def.printModuleEntry) {
371371
continue;

test/other/embind_tsgen.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
2+
interface WasmModule {
3+
_main(_0: number, _1: number): number;
4+
}
5+
16
export interface Test {
27
x: number;
38
readonly y: number;
@@ -77,7 +82,7 @@ export interface DerivedClass extends BaseClass {
7782

7883
export type ValArr = [ number, number, number ];
7984

80-
export interface MainModule {
85+
interface EmbindModule {
8186
Test: {staticFunction(_0: number): number; staticFunctionWithParam(x: number): number; staticProperty: number};
8287
class_returning_fn(): Test;
8388
class_unique_ptr_returning_fn(): Test;
@@ -104,3 +109,4 @@ export interface MainModule {
104109
string_test(_0: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): string;
105110
wstring_test(_0: string): string;
106111
}
112+
export type MainModule = WasmModule & EmbindModule;

test/other/embind_tsgen_bigint.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
export interface MainModule {
1+
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
2+
interface WasmModule {
3+
}
4+
5+
interface EmbindModule {
26
bigintFn(_0: bigint): bigint;
37
}
8+
export type MainModule = WasmModule & EmbindModule;

test/other/embind_tsgen_ignore_1.d.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
2+
interface WasmModule {
3+
_pthread_self(): number;
4+
_main(_0: number, _1: number): number;
5+
__emscripten_tls_init(): number;
6+
__emscripten_proxy_main(_0: number, _1: number): number;
7+
__embind_initialize_bindings(): void;
8+
__emscripten_thread_init(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number): void;
9+
__emscripten_thread_crashed(): void;
10+
__emscripten_thread_exit(_0: number): void;
11+
}
12+
13+
export interface Test {
14+
x: number;
15+
readonly y: number;
16+
functionOne(_0: number, _1: number): number;
17+
functionTwo(_0: number, _1: number): number;
18+
functionFour(_0: boolean): number;
19+
functionFive(x: number, y: number): number;
20+
constFn(): number;
21+
longFn(_0: number): number;
22+
functionThree(_0: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): number;
23+
functionSix(str: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): number;
24+
delete(): void;
25+
}
26+
27+
export interface BarValue<T extends number> {
28+
value: T;
29+
}
30+
export type Bar = BarValue<0>|BarValue<1>|BarValue<2>;
31+
32+
export interface EmptyEnumValue<T extends number> {
33+
value: T;
34+
}
35+
export type EmptyEnum = never/* Empty Enumerator */;
36+
37+
export type ValArrIx = [ Bar, Bar, Bar, Bar ];
38+
39+
export interface IntVec {
40+
push_back(_0: number): void;
41+
resize(_0: number, _1: number): void;
42+
size(): number;
43+
set(_0: number, _1: number): boolean;
44+
get(_0: number): any;
45+
delete(): void;
46+
}
47+
48+
export interface Foo {
49+
process(_0: Test): void;
50+
delete(): void;
51+
}
52+
53+
export type ValObj = {
54+
foo: Foo,
55+
bar: Bar
56+
};
57+
58+
export interface ClassWithConstructor {
59+
fn(_0: number): number;
60+
delete(): void;
61+
}
62+
63+
export interface ClassWithTwoConstructors {
64+
delete(): void;
65+
}
66+
67+
export interface ClassWithSmartPtrConstructor {
68+
fn(_0: number): number;
69+
delete(): void;
70+
}
71+
72+
export interface BaseClass {
73+
fn(_0: number): number;
74+
delete(): void;
75+
}
76+
77+
export interface DerivedClass extends BaseClass {
78+
fn2(_0: number): number;
79+
delete(): void;
80+
}
81+
82+
export type ValArr = [ number, number, number ];
83+
84+
interface EmbindModule {
85+
Test: {staticFunction(_0: number): number; staticFunctionWithParam(x: number): number; staticProperty: number};
86+
class_returning_fn(): Test;
87+
class_unique_ptr_returning_fn(): Test;
88+
a_class_instance: Test;
89+
an_enum: Bar;
90+
Bar: {valueOne: BarValue<0>, valueTwo: BarValue<1>, valueThree: BarValue<2>};
91+
EmptyEnum: {};
92+
enum_returning_fn(): Bar;
93+
IntVec: {new(): IntVec};
94+
Foo: {};
95+
ClassWithConstructor: {new(_0: number, _1: ValArr): ClassWithConstructor};
96+
ClassWithTwoConstructors: {new(): ClassWithTwoConstructors; new(_0: number): ClassWithTwoConstructors};
97+
ClassWithSmartPtrConstructor: {new(_0: number, _1: ValArr): ClassWithSmartPtrConstructor};
98+
BaseClass: {};
99+
DerivedClass: {};
100+
a_bool: boolean;
101+
an_int: number;
102+
global_fn(_0: number, _1: number): number;
103+
optional_test(_0: Foo | undefined): number | undefined;
104+
smart_ptr_function(_0: ClassWithSmartPtrConstructor): number;
105+
smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor): number;
106+
function_with_callback_param(_0: (message: string) => void): number;
107+
string_test(_0: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): string;
108+
wstring_test(_0: string): string;
109+
}
110+
export type MainModule = WasmModule & EmbindModule;

test/other/embind_tsgen_ignore_2.d.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
2+
interface WasmModule {
3+
}
4+
5+
export interface Test {
6+
x: number;
7+
readonly y: number;
8+
functionOne(_0: number, _1: number): number;
9+
functionTwo(_0: number, _1: number): number;
10+
functionFour(_0: boolean): number;
11+
functionFive(x: number, y: number): number;
12+
constFn(): number;
13+
longFn(_0: number): number;
14+
functionThree(_0: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): number;
15+
functionSix(str: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): number;
16+
delete(): void;
17+
}
18+
19+
export interface BarValue<T extends number> {
20+
value: T;
21+
}
22+
export type Bar = BarValue<0>|BarValue<1>|BarValue<2>;
23+
24+
export interface EmptyEnumValue<T extends number> {
25+
value: T;
26+
}
27+
export type EmptyEnum = never/* Empty Enumerator */;
28+
29+
export type ValArrIx = [ Bar, Bar, Bar, Bar ];
30+
31+
export interface IntVec {
32+
push_back(_0: number): void;
33+
resize(_0: number, _1: number): void;
34+
size(): number;
35+
set(_0: number, _1: number): boolean;
36+
get(_0: number): any;
37+
delete(): void;
38+
}
39+
40+
export interface Foo {
41+
process(_0: Test): void;
42+
delete(): void;
43+
}
44+
45+
export type ValObj = {
46+
foo: Foo,
47+
bar: Bar
48+
};
49+
50+
export interface ClassWithConstructor {
51+
fn(_0: number): number;
52+
delete(): void;
53+
}
54+
55+
export interface ClassWithTwoConstructors {
56+
delete(): void;
57+
}
58+
59+
export interface ClassWithSmartPtrConstructor {
60+
fn(_0: number): number;
61+
delete(): void;
62+
}
63+
64+
export interface BaseClass {
65+
fn(_0: number): number;
66+
delete(): void;
67+
}
68+
69+
export interface DerivedClass extends BaseClass {
70+
fn2(_0: number): number;
71+
delete(): void;
72+
}
73+
74+
export type ValArr = [ number, number, number ];
75+
76+
interface EmbindModule {
77+
Test: {staticFunction(_0: number): number; staticFunctionWithParam(x: number): number; staticProperty: number};
78+
class_returning_fn(): Test;
79+
class_unique_ptr_returning_fn(): Test;
80+
a_class_instance: Test;
81+
an_enum: Bar;
82+
Bar: {valueOne: BarValue<0>, valueTwo: BarValue<1>, valueThree: BarValue<2>};
83+
EmptyEnum: {};
84+
enum_returning_fn(): Bar;
85+
IntVec: {new(): IntVec};
86+
Foo: {};
87+
ClassWithConstructor: {new(_0: number, _1: ValArr): ClassWithConstructor};
88+
ClassWithTwoConstructors: {new(): ClassWithTwoConstructors; new(_0: number): ClassWithTwoConstructors};
89+
ClassWithSmartPtrConstructor: {new(_0: number, _1: ValArr): ClassWithSmartPtrConstructor};
90+
BaseClass: {};
91+
DerivedClass: {};
92+
a_bool: boolean;
93+
an_int: number;
94+
global_fn(_0: number, _1: number): number;
95+
optional_test(_0: Foo | undefined): number | undefined;
96+
smart_ptr_function(_0: ClassWithSmartPtrConstructor): number;
97+
smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor): number;
98+
function_with_callback_param(_0: (message: string) => void): number;
99+
string_test(_0: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): string;
100+
wstring_test(_0: string): string;
101+
}
102+
export type MainModule = WasmModule & EmbindModule;

test/other/embind_tsgen_memory64.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
export interface MainModule {
1+
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
2+
interface WasmModule {
3+
}
4+
5+
interface EmbindModule {
26
longFn(_0: bigint): bigint;
37
}
8+
export type MainModule = WasmModule & EmbindModule;

test/other/test_emit_tsd.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <emscripten.h>
2+
3+
EMSCRIPTEN_KEEPALIVE void fooVoid() {}
4+
EMSCRIPTEN_KEEPALIVE int fooInt(int a, int b) {
5+
return 42;
6+
}
7+
8+
int main() {
9+
10+
}

test/other/test_emit_tsd.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
2+
interface WasmModule {
3+
_fooVoid(): void;
4+
_fooInt(_0: number, _1: number): number;
5+
_main(_0: number, _1: number): number;
6+
}
7+
8+
export type MainModule = WasmModule;

test/test_other.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,14 +3136,14 @@ def test_embind_tsgen_ignore(self):
31363136
'-lembind', # Test duplicated link option.
31373137
]
31383138
self.emcc(test_file('other/embind_tsgen.cpp'), extra_args)
3139-
self.assertFileContents(test_file('other/embind_tsgen.d.ts'), read_file('embind_tsgen.d.ts'))
3139+
self.assertFileContents(test_file('other/embind_tsgen_ignore_1.d.ts'), read_file('embind_tsgen.d.ts'))
31403140
# Test these args separately since they conflict with arguments in the first test.
31413141
extra_args = ['-sMODULARIZE',
31423142
'--embed-file', 'fail.js',
31433143
'-sMINIMAL_RUNTIME=2',
31443144
'-sEXPORT_ES6=1']
31453145
self.emcc(test_file('other/embind_tsgen.cpp'), extra_args)
3146-
self.assertFileContents(test_file('other/embind_tsgen.d.ts'), read_file('embind_tsgen.d.ts'))
3146+
self.assertFileContents(test_file('other/embind_tsgen_ignore_2.d.ts'), read_file('embind_tsgen.d.ts'))
31473147

31483148
def test_embind_tsgen_test_embind(self):
31493149
self.run_process([EMXX, test_file('embind/embind_test.cpp'),
@@ -3189,6 +3189,12 @@ def test_embind_jsgen_method_pointer_stability(self):
31893189
# AOT JS generation still works correctly.
31903190
self.do_runf('other/embind_jsgen_method_pointer_stability.cpp', 'done')
31913191

3192+
def test_emit_tsd(self):
3193+
self.run_process([EMCC, test_file('other/test_emit_tsd.c'),
3194+
'--emit-tsd', 'test_emit_tsd.d.ts', '-Wno-experimental'] +
3195+
self.get_emcc_args())
3196+
self.assertFileContents(test_file('other/test_emit_tsd.d.ts'), read_file('test_emit_tsd.d.ts'))
3197+
31923198
def test_emconfig(self):
31933199
output = self.run_process([emconfig, 'LLVM_ROOT'], stdout=PIPE).stdout.strip()
31943200
self.assertEqual(output, config.LLVM_ROOT)

0 commit comments

Comments
 (0)