Skip to content

Commit 54a57e2

Browse files
committed
Add option to emit TypeScript definitions for Wasm module exports.
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 4aefde3 commit 54a57e2

13 files changed

+328
-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;
@@ -69,7 +74,7 @@ export interface DerivedClass extends BaseClass {
6974

7075
export type ValArr = [ number, number, number ];
7176

72-
export interface MainModule {
77+
interface EmbindModule {
7378
Test: {staticFunction(_0: number): number; staticFunctionWithParam(x: number): number; staticProperty: number};
7479
class_returning_fn(): Test;
7580
class_unique_ptr_returning_fn(): Test;
@@ -95,3 +100,4 @@ export interface MainModule {
95100
string_test(_0: ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string): string;
96101
wstring_test(_0: string): string;
97102
}
103+
export type MainModule = WasmModule & EmbindModule;

test/other/embind_tsgen_bigint.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1-
export interface MainModule {
1+
interface WasmModule {
2+
}
3+
4+
interface EmbindModule {
25
bigintFn(_0: bigint): bigint;
36
}
7+
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
@@ -3083,14 +3083,14 @@ def test_embind_tsgen_ignore(self):
30833083
'-lembind', # Test duplicated link option.
30843084
]
30853085
self.emcc(test_file('other/embind_tsgen.cpp'), extra_args)
3086-
self.assertFileContents(test_file('other/embind_tsgen.d.ts'), read_file('embind_tsgen.d.ts'))
3086+
self.assertFileContents(test_file('other/embind_tsgen_ignore_1.d.ts'), read_file('embind_tsgen.d.ts'))
30873087
# Test these args separately since they conflict with arguments in the first test.
30883088
extra_args = ['-sMODULARIZE',
30893089
'--embed-file', 'fail.js',
30903090
'-sMINIMAL_RUNTIME=2',
30913091
'-sEXPORT_ES6=1']
30923092
self.emcc(test_file('other/embind_tsgen.cpp'), extra_args)
3093-
self.assertFileContents(test_file('other/embind_tsgen.d.ts'), read_file('embind_tsgen.d.ts'))
3093+
self.assertFileContents(test_file('other/embind_tsgen_ignore_2.d.ts'), read_file('embind_tsgen.d.ts'))
30943094

30953095
def test_embind_tsgen_test_embind(self):
30963096
self.run_process([EMCC, test_file('embind/embind_test.cpp'),
@@ -3136,6 +3136,12 @@ def test_embind_jsgen_method_pointer_stability(self):
31363136
# AOT JS generation still works correctly.
31373137
self.do_runf('other/embind_jsgen_method_pointer_stability.cpp', 'done')
31383138

3139+
def test_emit_tsd(self):
3140+
self.run_process([EMCC, test_file('other/test_emit_tsd.c'),
3141+
'--emit-tsd', 'test_emit_tsd.d.ts', '-Wno-experimental'] +
3142+
self.get_emcc_args())
3143+
self.assertFileContents(test_file('other/test_emit_tsd.d.ts'), read_file('test_emit_tsd.d.ts'))
3144+
31393145
def test_emconfig(self):
31403146
output = self.run_process([emconfig, 'LLVM_ROOT'], stdout=PIPE).stdout.strip()
31413147
self.assertEqual(output, config.LLVM_ROOT)

0 commit comments

Comments
 (0)