From e52e77275f816d1e8f97a7db59154ad56c0573d9 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 18 Sep 2024 17:46:36 +0000 Subject: [PATCH 1/4] [tsgen] Expand TS generation test to compile and run a TS file. In the new TS file we can add tests for things where just checking if the definition file compiles is not enough. This will help catch issues like --- test/other/embind_tsgen.cpp | 13 +- test/other/embind_tsgen.d.ts | 8 ++ test/other/embind_tsgen_ignore_1.d.ts | 14 ++- test/other/embind_tsgen_ignore_2.d.ts | 14 ++- test/other/embind_tsgen_ignore_3.d.ts | 14 ++- test/other/embind_tsgen_main.ts | 30 +++++ test/other/embind_tsgen_module.d.ts | 168 ++++++++++++++++++++++++++ test/other/embind_tsgen_package.json | 3 + test/test_other.py | 13 +- 9 files changed, 253 insertions(+), 24 deletions(-) create mode 100644 test/other/embind_tsgen_main.ts create mode 100644 test/other/embind_tsgen_module.d.ts create mode 100644 test/other/embind_tsgen_package.json diff --git a/test/other/embind_tsgen.cpp b/test/other/embind_tsgen.cpp index 8687f8e5dc49a..66bfe440d86ac 100644 --- a/test/other/embind_tsgen.cpp +++ b/test/other/embind_tsgen.cpp @@ -58,12 +58,19 @@ struct ValArr { EMSCRIPTEN_DECLARE_VAL_TYPE(CallbackType); struct ValObj { - Foo foo; Bar bar; + std::string string; CallbackType callback; ValObj() : callback(val::undefined()) {} }; +ValObj getValObj() { + ValObj o; + return o; +} + +void setValObj(ValObj v) {} + class ClassWithConstructor { public: ClassWithConstructor(int, const ValArr&) {} @@ -190,9 +197,11 @@ EMSCRIPTEN_BINDINGS(Test) { .element(emscripten::index<3>()); value_object("ValObj") - .field("foo", &ValObj::foo) + .field("string", &ValObj::string) .field("bar", &ValObj::bar) .field("callback", &ValObj::callback); + function("getValObj", &getValObj); + function("setValObj", &setValObj); register_vector("IntVec"); diff --git a/test/other/embind_tsgen.d.ts b/test/other/embind_tsgen.d.ts index 367232ae5f178..e6a0a5d471dd2 100644 --- a/test/other/embind_tsgen.d.ts +++ b/test/other/embind_tsgen.d.ts @@ -108,6 +108,12 @@ export interface InterfaceWrapper extends Interface { export type ValArr = [ number, number, number ]; +export type ValObj = { + string: EmbindString, + bar: Bar, + callback: (message: string) => void +}; + interface EmbindModule { Test: { staticFunction(_0: number): number; @@ -158,6 +164,8 @@ interface EmbindModule { smart_ptr_function(_0: ClassWithSmartPtrConstructor | null): number; smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor | null): number; function_with_callback_param(_0: (message: string) => void): number; + getValObj(): ValObj; + setValObj(_0: ValObj): void; string_test(_0: EmbindString): string; wstring_test(_0: string): string; } diff --git a/test/other/embind_tsgen_ignore_1.d.ts b/test/other/embind_tsgen_ignore_1.d.ts index d7a513215c143..a3526282d6aa2 100644 --- a/test/other/embind_tsgen_ignore_1.d.ts +++ b/test/other/embind_tsgen_ignore_1.d.ts @@ -93,12 +93,6 @@ export interface ClassWithSmartPtrConstructor extends ClassHandle { fn(_0: number): number; } -export type ValObj = { - foo: Foo, - bar: Bar, - callback: (message: string) => void -}; - export interface BaseClass extends ClassHandle { fn(_0: number): number; } @@ -117,6 +111,12 @@ export interface InterfaceWrapper extends Interface { export type ValArr = [ number, number, number ]; +export type ValObj = { + string: EmbindString, + bar: Bar, + callback: (message: string) => void +}; + interface EmbindModule { Test: { staticFunction(_0: number): number; @@ -167,6 +167,8 @@ interface EmbindModule { smart_ptr_function(_0: ClassWithSmartPtrConstructor | null): number; smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor | null): number; function_with_callback_param(_0: (message: string) => void): number; + getValObj(): ValObj; + setValObj(_0: ValObj): void; string_test(_0: EmbindString): string; wstring_test(_0: string): string; } diff --git a/test/other/embind_tsgen_ignore_2.d.ts b/test/other/embind_tsgen_ignore_2.d.ts index 7e2923be3c762..f56a1d323f526 100644 --- a/test/other/embind_tsgen_ignore_2.d.ts +++ b/test/other/embind_tsgen_ignore_2.d.ts @@ -70,12 +70,6 @@ export interface ClassWithSmartPtrConstructor extends ClassHandle { fn(_0: number): number; } -export type ValObj = { - foo: Foo, - bar: Bar, - callback: (message: string) => void -}; - export interface BaseClass extends ClassHandle { fn(_0: number): number; } @@ -94,6 +88,12 @@ export interface InterfaceWrapper extends Interface { export type ValArr = [ number, number, number ]; +export type ValObj = { + string: EmbindString, + bar: Bar, + callback: (message: string) => void +}; + interface EmbindModule { Test: { staticFunction(_0: number): number; @@ -144,6 +144,8 @@ interface EmbindModule { smart_ptr_function(_0: ClassWithSmartPtrConstructor | null): number; smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor | null): number; function_with_callback_param(_0: (message: string) => void): number; + getValObj(): ValObj; + setValObj(_0: ValObj): void; string_test(_0: EmbindString): string; wstring_test(_0: string): string; } diff --git a/test/other/embind_tsgen_ignore_3.d.ts b/test/other/embind_tsgen_ignore_3.d.ts index 367232ae5f178..df88531793f07 100644 --- a/test/other/embind_tsgen_ignore_3.d.ts +++ b/test/other/embind_tsgen_ignore_3.d.ts @@ -84,12 +84,6 @@ export interface ClassWithSmartPtrConstructor extends ClassHandle { fn(_0: number): number; } -export type ValObj = { - foo: Foo, - bar: Bar, - callback: (message: string) => void -}; - export interface BaseClass extends ClassHandle { fn(_0: number): number; } @@ -108,6 +102,12 @@ export interface InterfaceWrapper extends Interface { export type ValArr = [ number, number, number ]; +export type ValObj = { + string: EmbindString, + bar: Bar, + callback: (message: string) => void +}; + interface EmbindModule { Test: { staticFunction(_0: number): number; @@ -158,6 +158,8 @@ interface EmbindModule { smart_ptr_function(_0: ClassWithSmartPtrConstructor | null): number; smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor | null): number; function_with_callback_param(_0: (message: string) => void): number; + getValObj(): ValObj; + setValObj(_0: ValObj): void; string_test(_0: EmbindString): string; wstring_test(_0: string): string; } diff --git a/test/other/embind_tsgen_main.ts b/test/other/embind_tsgen_main.ts new file mode 100644 index 0000000000000..a3a39683245af --- /dev/null +++ b/test/other/embind_tsgen_main.ts @@ -0,0 +1,30 @@ +// Example TS program that consumes the emscripten-generated module to to +// illustrate how the type definitions are used and test they are workings as +// expected. +import moduleFactory from './embind_tsgen.mjs'; + +const module = await moduleFactory(); + +// Test a few variations of passing value_objects with strings. +module.setValObj({ + bar: module.Bar.valueOne, + string: "ABCD", + callback: () => {} +}); + +module.setValObj({ + bar: module.Bar.valueOne, + string: new Int8Array([65, 66, 67, 68]), + callback: () => {} +}); + +const valObj = module.getValObj(); +// TODO: remove the cast below when better definitions are generated for value +// objects. +const valString : string = valObj.string as string; + +// Ensure nonnull pointers do no need a cast or nullptr check to use. +const obj = module.getNonnullPointer(); +obj.delete(); + +console.log('ts ran'); diff --git a/test/other/embind_tsgen_module.d.ts b/test/other/embind_tsgen_module.d.ts new file mode 100644 index 0000000000000..1631d67bcde96 --- /dev/null +++ b/test/other/embind_tsgen_module.d.ts @@ -0,0 +1,168 @@ +// TypeScript bindings for emscripten-generated code. Automatically generated at compile time. +declare namespace RuntimeExports { + let HEAPF32: any; + let HEAPF64: any; + let HEAP_DATA_VIEW: any; + let HEAP8: any; + let HEAPU8: any; + let HEAP16: any; + let HEAPU16: any; + let HEAP32: any; + let HEAPU32: any; + let HEAP64: any; + let HEAPU64: any; +} +interface WasmModule { + _main(_0: number, _1: number): number; +} + +type EmbindString = ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string; +export interface ClassHandle { + isAliasOf(other: ClassHandle): boolean; + delete(): void; + deleteLater(): this; + isDeleted(): boolean; + clone(): this; +} +export interface Test extends ClassHandle { + x: number; + readonly y: number; + get stringProperty(): string; + set stringProperty(value: EmbindString); + functionOne(_0: number, _1: number): number; + functionTwo(_0: number, _1: number): number; + functionFour(_0: boolean): number; + functionFive(x: number, y: number): number; + constFn(): number; + longFn(_0: number): number; + functionThree(_0: EmbindString): number; + functionSix(str: EmbindString): number; +} + +export interface Obj extends ClassHandle { +} + +export interface BarValue { + value: T; +} +export type Bar = BarValue<0>|BarValue<1>|BarValue<2>; + +export interface EmptyEnumValue { + value: T; +} +export type EmptyEnum = never/* Empty Enumerator */; + +export type ValArrIx = [ Bar, Bar, Bar, Bar ]; + +export interface IntVec extends ClassHandle { + push_back(_0: number): void; + resize(_0: number, _1: number): void; + size(): number; + get(_0: number): number | undefined; + set(_0: number, _1: number): boolean; +} + +export interface MapIntInt extends ClassHandle { + keys(): IntVec; + get(_0: number): number | undefined; + set(_0: number, _1: number): void; + size(): number; +} + +export interface Foo extends ClassHandle { + process(_0: Test): void; +} + +export interface ClassWithConstructor extends ClassHandle { + fn(_0: number): number; +} + +export interface ClassWithTwoConstructors extends ClassHandle { +} + +export interface ClassWithSmartPtrConstructor extends ClassHandle { + fn(_0: number): number; +} + +export interface BaseClass extends ClassHandle { + fn(_0: number): number; +} + +export interface DerivedClass extends BaseClass { + fn2(_0: number): number; +} + +export interface Interface extends ClassHandle { + invoke(_0: EmbindString): void; +} + +export interface InterfaceWrapper extends Interface { + notifyOnDestruction(): void; +} + +export type ValArr = [ number, number, number ]; + +export type ValObj = { + string: EmbindString, + bar: Bar, + callback: (message: string) => void +}; + +interface EmbindModule { + Test: { + staticFunction(_0: number): number; + staticFunctionWithParam(x: number): number; + staticProperty: number; + get staticStringProperty(): string; + set staticStringProperty(value: EmbindString); + }; + class_returning_fn(): Test; + class_unique_ptr_returning_fn(): Test; + Obj: {}; + getPointer(_0: Obj | null): Obj | null; + getNonnullPointer(): Obj; + a_class_instance: Test; + an_enum: Bar; + Bar: {valueOne: BarValue<0>, valueTwo: BarValue<1>, valueThree: BarValue<2>}; + EmptyEnum: {}; + enum_returning_fn(): Bar; + IntVec: { + new(): IntVec; + }; + MapIntInt: { + new(): MapIntInt; + }; + Foo: {}; + ClassWithConstructor: { + new(_0: number, _1: ValArr): ClassWithConstructor; + }; + ClassWithTwoConstructors: { + new(): ClassWithTwoConstructors; + new(_0: number): ClassWithTwoConstructors; + }; + ClassWithSmartPtrConstructor: { + new(_0: number, _1: ValArr): ClassWithSmartPtrConstructor; + }; + BaseClass: {}; + DerivedClass: {}; + Interface: { + implement(_0: any): InterfaceWrapper; + extend(_0: EmbindString, _1: any): any; + }; + InterfaceWrapper: {}; + a_bool: boolean; + an_int: number; + optional_test(_0?: Foo): number | undefined; + global_fn(_0: number, _1: number): number; + optional_and_nonoptional_test(_0: Foo | undefined, _1: number): number | undefined; + smart_ptr_function(_0: ClassWithSmartPtrConstructor | null): number; + smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor | null): number; + function_with_callback_param(_0: (message: string) => void): number; + getValObj(): ValObj; + setValObj(_0: ValObj): void; + string_test(_0: EmbindString): string; + wstring_test(_0: string): string; +} + +export type MainModule = WasmModule & typeof RuntimeExports & EmbindModule; +export default function MainModuleFactory (options?: unknown): Promise; diff --git a/test/other/embind_tsgen_package.json b/test/other/embind_tsgen_package.json new file mode 100644 index 0000000000000..3dbc1ca591c05 --- /dev/null +++ b/test/other/embind_tsgen_package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/test/test_other.py b/test/test_other.py index db997372b2d0a..194e47b20b16c 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -3386,15 +3386,20 @@ def test_jspi_add_function(self): def test_embind_tsgen(self, opts): # Check that TypeScript generation works and that the program is runs as # expected. - self.do_runf('other/embind_tsgen.cpp', 'main ran', - emcc_args=['-lembind', '--emit-tsd', 'embind_tsgen.d.ts'] + opts) + self.emcc(test_file('other/embind_tsgen.cpp'), + ['-o', 'embind_tsgen.mjs', '-lembind', '--emit-tsd', 'embind_tsgen.d.ts'] + opts) # Test that the output compiles with a TS file that uses the defintions. - cmd = shared.get_npm_cmd('tsc') + ['embind_tsgen.d.ts', '--noEmit'] + shutil.copyfile(test_file('other/embind_tsgen_main.ts'), 'main.ts') + # A package file with type=module is needed to tell the TSC that we're + # using modules. + shutil.copyfile(test_file('other/embind_tsgen_package.json'), 'package.json') + cmd = shared.get_npm_cmd('tsc') + ['embind_tsgen.d.ts', 'main.ts', '--module', 'NodeNext', '--moduleResolution', 'nodenext'] shared.check_call(cmd) + self.assertContained('main ran\nts ran', self.run_js('main.js')) actual = read_file('embind_tsgen.d.ts') - self.assertFileContents(test_file('other/embind_tsgen.d.ts'), actual) + self.assertFileContents(test_file('other/embind_tsgen_module.d.ts'), actual) def test_embind_tsgen_ignore(self): create_file('fail.js', 'assert(false);') From 2a31238c2ee5d41d31be4fde256dd7e95aebbc1b Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 20 Nov 2024 19:12:30 +0000 Subject: [PATCH 2/4] Update comment. --- test/test_other.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_other.py b/test/test_other.py index 194e47b20b16c..867990d1980ce 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -3391,8 +3391,8 @@ def test_embind_tsgen(self, opts): # Test that the output compiles with a TS file that uses the defintions. shutil.copyfile(test_file('other/embind_tsgen_main.ts'), 'main.ts') - # A package file with type=module is needed to tell the TSC that we're - # using modules. + # A package file with type=module is needed to enabled ES modules in TSC and + # also run the output JS file as a module in node. shutil.copyfile(test_file('other/embind_tsgen_package.json'), 'package.json') cmd = shared.get_npm_cmd('tsc') + ['embind_tsgen.d.ts', 'main.ts', '--module', 'NodeNext', '--moduleResolution', 'nodenext'] shared.check_call(cmd) From 9e6365a7ef836fde465b90cc45b7d883d6a088c0 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 20 Nov 2024 19:59:33 +0000 Subject: [PATCH 3/4] move test --- test/other/embind_tsgen.d.ts | 6 ------ test/test_other.py | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/test/other/embind_tsgen.d.ts b/test/other/embind_tsgen.d.ts index e6a0a5d471dd2..df88531793f07 100644 --- a/test/other/embind_tsgen.d.ts +++ b/test/other/embind_tsgen.d.ts @@ -84,12 +84,6 @@ export interface ClassWithSmartPtrConstructor extends ClassHandle { fn(_0: number): number; } -export type ValObj = { - foo: Foo, - bar: Bar, - callback: (message: string) => void -}; - export interface BaseClass extends ClassHandle { fn(_0: number): number; } diff --git a/test/test_other.py b/test/test_other.py index 867990d1980ce..472e655e464e4 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -3396,10 +3396,10 @@ def test_embind_tsgen(self, opts): shutil.copyfile(test_file('other/embind_tsgen_package.json'), 'package.json') cmd = shared.get_npm_cmd('tsc') + ['embind_tsgen.d.ts', 'main.ts', '--module', 'NodeNext', '--moduleResolution', 'nodenext'] shared.check_call(cmd) - self.assertContained('main ran\nts ran', self.run_js('main.js')) - actual = read_file('embind_tsgen.d.ts') self.assertFileContents(test_file('other/embind_tsgen_module.d.ts'), actual) + self.assertContained('main ran\nts ran', self.run_js('main.js')) + def test_embind_tsgen_ignore(self): create_file('fail.js', 'assert(false);') From 5a27a38eb62fdd955624f02d03136bc50c882713 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 20 Nov 2024 20:11:38 +0000 Subject: [PATCH 4/4] flake --- test/test_other.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_other.py b/test/test_other.py index 472e655e464e4..ece2fc6ab9dbe 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -3400,7 +3400,6 @@ def test_embind_tsgen(self, opts): self.assertFileContents(test_file('other/embind_tsgen_module.d.ts'), actual) self.assertContained('main ran\nts ran', self.run_js('main.js')) - def test_embind_tsgen_ignore(self): create_file('fail.js', 'assert(false);') self.emcc_args += ['-lembind', '--emit-tsd', 'embind_tsgen.d.ts']