Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import Web Assembly support from snapchat's djinni fork #157

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Djinni generator parses an interface definition file and generates:
- Objective-C++ code to convert between C++ and Objective-C
- Python and C code to convert between C++ and Python over CFFI
- C++/CLI code to convert between C++ and C#
- C++ code to convert between WebAssembly and TS/JS


## Installation
Expand Down Expand Up @@ -86,3 +87,5 @@ The code in this repository is in large portions copied from [dropbox/djinni](ht
- Jacob Potter
- Iulia Tamas
- Andrew Twyman

WebAssembly support is borrowed in large part from [snapchat/djinni](https://github.com/snapchat/djinni).
25 changes: 25 additions & 0 deletions docs/cli-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ djinni \
| `--cppcli-namespace ...` | The namespace name to use for generated C++/CLI classes. |
| `--cppcli-include-cpp-prefix <prefix>` | The prefix for `#include` of the main C++ header files from C++/CLI files. |

### WebAssembly/Typescript/Javascript

| Argument | Description |
|----------------------------------------|----------------------------------------------------------------------------|
| `--wasm-out <out-folder>` | WebAssembly bridge code output folder. |
| `--wasm-include-prefix` | The prefix for #includes of WASM bridge C++ header files. |
| `--wasm-include-cpp-prefix` | The prefix for #includes of C++ header files. |
| `--wasm-base-lib-include-prefix` | The path prefix to be added to djinni support library inlcude lines in generated files |
| `--ts-out <out-folder>` | Path to the Typescript type definitions output folder |
| `--ts-module <module>` | Name of the module for the Typescript types. `module.ts` by default. |
| `--ts-support-files-out <out-folder>` | Path for where the support files `DjinniModule.[js\|ts]` should be generated. No support files are generated if the path is not specified. |


### Yaml Generation

Expand Down Expand Up @@ -230,6 +242,19 @@ Possible values: `FooBar`, `fooBar`, `foo_bar`, `FOO_BAR`, `m_fooBar`.
| `--ident-cppcli-const` | `FooBar` |
| `--ident-cppcli-file` | `FooBar` |

#### Javascript / Typescript

| Argument | Default |
|-------------------------|-----------|
| `--ident-js-type` | `FooBar` |
| `--ident-js-type-param` | `FooBar` |
| `--ident-js-method` | `fooBar` |
| `--ident-js-local` | `fooBar` |
| `--ident-js-enum` | `FOO_BAR` |
| `--ident-js-const` | `FOO_BAR` |
| `--ident-js-field` | `fooBar` |


Example:

The djinni idl for an enum
Expand Down
38 changes: 37 additions & 1 deletion docs/generated-code-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,42 @@ Add all generated files to your build target, and link against the [djinni-suppo

C++/CLI sources have to be compiled with MSVC and the [`/clr` (Common Language Runtime Compilation)](https://docs.microsoft.com/en-us/cpp/build/reference/clr-common-language-runtime-compilation?view=msvc-160) option.

## TS/JS C++/WASM Project

Djinni can generate code that bridges C++ (that compiles to Web Assembly) and Javascript/TypeScript in web browsers.

For WASM, Djinni generates:
- C++ code, which should be compiled into the WASM bindary
- TypeScript code, provides optional TypeScript interface definitions

The generated code can be used with both plain Javascript and TypeScript.

Almost all Djinni features are supported, including importing external types via
yaml.

Notable differences when comparing to the Java/ObjC support:

- deriving(ord, eq) is not applicable to Javascript because JS doesn't support
overloading standard comparison methods.

### Includes & Build target

The following code will be generated for each defined type:

| Type | C++ header | C++ source | WASM header/sources | TS definitions |
|------------|--------------------------|----------------------------|-------------------------------------|---------------------------|
| Enum/Flags | my\_enum.hpp | | my_enum.hpp, my_enum.cpp | module.ts :three: |
| | my\_enum+json.hpp :two: | | | DjinniModule.ts/js :four: |
| Record | my\_record.hpp | my\_record.cpp | my_record.hpp, my_enum.cpp | |
| | my\_record+json.hpp :two:| | | |
| Interface | my\_interface.hpp | my\_interface.cpp :one: | my_interface.hpp, my_interface.cpp | |

- :one: Generated only for types that contain constants.
- :two: Generated only if cpp json serialization is enabled.
- :three: Name of file configurable via command-line options.
- :four: Generated if `ts-support-files-out` is specified at command line.


## C++ JSON Serialization support

Serialization from C++ types to/from JSON is supported. This feature is currently only enabled for `nlohmann/json`, and if enabled creates `to_json`/`from_json` methods for all djinni records and enums.
Expand Down Expand Up @@ -219,4 +255,4 @@ namespace nlohmann {
}
};
}
```
```
7 changes: 7 additions & 0 deletions src/it/resources/cpp_interface_dependency.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,10 @@ cs:
header: '"InterfaceDependency.hpp"'
typename: InterfaceDependency^
reference: true
wasm:
translator: '::InterfaceDependency'
header: '"InterfaceDependency.hpp"'
typename: InterfaceDependency
ts:
typename: InterfaceDependency
module: "InterfaceDependency.ts"
8 changes: 8 additions & 0 deletions src/it/resources/expected/all_datatypes/generated-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ src/it/resources/result/all_datatypes/cwrapper/dh__map_int8_t_bool.cpp
src/it/resources/result/all_datatypes/cwrapper-headers/dh__all_datatypes.h
src/it/resources/result/all_datatypes/cwrapper/dh__all_datatypes.hpp
src/it/resources/result/all_datatypes/cwrapper/dh__all_datatypes.cpp
src/it/resources/result/all_datatypes/wasm/enum_data.hpp
src/it/resources/result/all_datatypes/wasm/enum_data.cpp
src/it/resources/result/all_datatypes/wasm/all_datatypes.hpp
src/it/resources/result/all_datatypes/wasm/all_datatypes.cpp
src/it/resources/result/all_datatypes/ts/support-lib/DjinniModule.ts
src/it/resources/result/all_datatypes/ts/support-lib/DjinniModule.js
src/it/resources/result/all_datatypes/ts/module.ts

33 changes: 33 additions & 0 deletions src/it/resources/expected/all_datatypes/ts/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from all_datatypes.djinni


export enum EnumData {
A,
B,
}

export interface /*record*/ AllDatatypes {
booleanData: boolean;
integer8Data: number;
integer16Data: number;
integer32Data: number;
integer64Data: bigint;
float32Data: number;
float64Data: number;
stringData: string;
binaryData: Uint8Array;
dateData: Date;
listData: Array<boolean>;
setData: Set<boolean>;
mapData: Map<number, boolean>;
optionalData?: boolean;
enumData: EnumData;
}

export interface ns_testsuite {
}
export interface Module_statics {

testsuite: ns_testsuite;
}
46 changes: 46 additions & 0 deletions src/it/resources/expected/all_datatypes/wasm/all_datatypes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from all_datatypes.djinni

#include "all_datatypes.hpp" // my header
#include "enum_data.hpp"

namespace djinni_generated {

auto AllDatatypes::toCpp(const JsType& j) -> CppType {
return {::djinni::Bool::Boxed::toCpp(j["booleanData"]),
::djinni::I8::Boxed::toCpp(j["integer8Data"]),
::djinni::I16::Boxed::toCpp(j["integer16Data"]),
::djinni::I32::Boxed::toCpp(j["integer32Data"]),
::djinni::I64::Boxed::toCpp(j["integer64Data"]),
::djinni::F32::Boxed::toCpp(j["float32Data"]),
::djinni::F64::Boxed::toCpp(j["float64Data"]),
::djinni::String::Boxed::toCpp(j["stringData"]),
::djinni::Binary::Boxed::toCpp(j["binaryData"]),
::djinni::Date::Boxed::toCpp(j["dateData"]),
::djinni::List<::djinni::Bool>::Boxed::toCpp(j["listData"]),
::djinni::Set<::djinni::Bool>::Boxed::toCpp(j["setData"]),
::djinni::Map<::djinni::I8, ::djinni::Bool>::Boxed::toCpp(j["mapData"]),
::djinni::Optional<std::optional, ::djinni::Bool>::Boxed::toCpp(j["optionalData"]),
::djinni_generated::EnumData::Boxed::toCpp(j["enumData"])};
}
auto AllDatatypes::fromCpp(const CppType& c) -> JsType {
em::val js = em::val::object();
js.set("booleanData", ::djinni::Bool::Boxed::fromCpp(c.booleanData));
js.set("integer8Data", ::djinni::I8::Boxed::fromCpp(c.integer8Data));
js.set("integer16Data", ::djinni::I16::Boxed::fromCpp(c.integer16Data));
js.set("integer32Data", ::djinni::I32::Boxed::fromCpp(c.integer32Data));
js.set("integer64Data", ::djinni::I64::Boxed::fromCpp(c.integer64Data));
js.set("float32Data", ::djinni::F32::Boxed::fromCpp(c.float32Data));
js.set("float64Data", ::djinni::F64::Boxed::fromCpp(c.float64Data));
js.set("stringData", ::djinni::String::Boxed::fromCpp(c.stringData));
js.set("binaryData", ::djinni::Binary::Boxed::fromCpp(c.binaryData));
js.set("dateData", ::djinni::Date::Boxed::fromCpp(c.dateData));
js.set("listData", ::djinni::List<::djinni::Bool>::Boxed::fromCpp(c.listData));
js.set("setData", ::djinni::Set<::djinni::Bool>::Boxed::fromCpp(c.setData));
js.set("mapData", ::djinni::Map<::djinni::I8, ::djinni::Bool>::Boxed::fromCpp(c.mapData));
js.set("optionalData", ::djinni::Optional<std::optional, ::djinni::Bool>::Boxed::fromCpp(c.optionalData));
js.set("enumData", ::djinni_generated::EnumData::Boxed::fromCpp(c.enum_data));
return js;
}

} // namespace djinni_generated
21 changes: 21 additions & 0 deletions src/it/resources/expected/all_datatypes/wasm/all_datatypes.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from all_datatypes.djinni

#pragma once

#include "all_datatypes.hpp"
#include "djinni_wasm.hpp"

namespace djinni_generated {

struct AllDatatypes
{
using CppType = ::testsuite::AllDatatypes;
using JsType = em::val;
using Boxed = AllDatatypes;

static CppType toCpp(const JsType& j);
static JsType fromCpp(const CppType& c);
};

} // namespace djinni_generated
30 changes: 30 additions & 0 deletions src/it/resources/expected/all_datatypes/wasm/enum_data.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from all_datatypes.djinni

#include "enum_data.hpp" // my header
#include <mutex>

namespace djinni_generated {

namespace {
EM_JS(void, djinni_init_testsuite_enum_data_consts, (), {
Module.testsuite_EnumData = {
A : 0,
B : 1,
}
})
}

void EnumData::staticInitializeConstants() {
static std::once_flag initOnce;
std::call_once(initOnce, [] {
djinni_init_testsuite_enum_data_consts();
::djinni::djinni_register_name_in_ns("testsuite_EnumData", "testsuite.EnumData");
});
}

EMSCRIPTEN_BINDINGS(testsuite_enum_data) {
EnumData::staticInitializeConstants();
}

} // namespace djinni_generated
15 changes: 15 additions & 0 deletions src/it/resources/expected/all_datatypes/wasm/enum_data.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from all_datatypes.djinni

#pragma once

#include "djinni_wasm.hpp"
#include "enum_data.hpp"

namespace djinni_generated {

struct EnumData: ::djinni::WasmEnum<::testsuite::EnumData> {
static void staticInitializeConstants();
};

} // namespace djinni_generated
4 changes: 4 additions & 0 deletions src/it/resources/expected/deprecation/generated-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ src/it/resources/result/deprecation/cpp-headers/my_flags.hpp
src/it/resources/result/deprecation/cpp-headers/my_record.hpp
src/it/resources/result/deprecation/cpp-headers/my_interface.hpp
src/it/resources/result/deprecation/cpp/my_interface.cpp
src/it/resources/result/deprecation/ts/support-lib/DjinniModule.ts
src/it/resources/result/deprecation/ts/support-lib/DjinniModule.js
src/it/resources/result/deprecation/ts/module.ts

78 changes: 78 additions & 0 deletions src/it/resources/expected/deprecation/ts/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from deprecation.djinni


/**
* enum comment
*
* @deprecated Use something else
*/
export enum MyEnum {
/** @deprecated Use something else */
OPTION1,
/** not deprecated */
OPTION2,
}

/**
* flags comment
*
* @deprecated Use someother flags
*/
export enum MyFlags {
/** @deprecated Use someother flag */
FLAG1 = 1 << 0,
/** not deprecated */
FLAG2 = 1 << 1,
}

/**
* record comment
*
* @deprecated Use someother record
*/
export interface /*record*/ MyRecord {
/** @deprecated Use someother attribute */
attribute: string;
/** not deprecated */
another: string;
/** @deprecated Use someother attribute */
again: string;
}
export namespace MyRecord {
/** @deprecated Use someother constant */
export const VERSION = 1;
}

/**
* interface comment
*
* @deprecated Use someother interface
*/
export interface MyInterface {
/** @deprecated Use someother method */
methodA(value: number): void;
/** @deprecated Use someother method */
methodB(value: number): void;
/** not deprecated */
methodD(): void;
/** really im not */
methodE(): void;
}
export namespace MyInterface {
/** @deprecated Use someother constant */
export const VERSION = 1;
}
export interface MyInterface_statics {
/** @deprecated Use someother method */
methodC(value: number): void;
}

export interface ns_testsuite {
MyInterface: MyInterface_statics;
}
export interface Module_statics {
testsuite_MyInterface: MyInterface_statics;

testsuite: ns_testsuite;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ src/it/resources/result/my_client_interface/cwrapper-headers/cw__my_client_inter
src/it/resources/result/my_client_interface/cwrapper/cw__my_client_interface.hpp
src/it/resources/result/my_client_interface/cwrapper/cw__my_client_interface.cpp
src/it/resources/result/my_client_interface/cffi/pycffi_lib_build.py
src/it/resources/result/my_client_interface/wasm/my_client_interface.hpp
src/it/resources/result/my_client_interface/wasm/my_client_interface.cpp
src/it/resources/result/my_client_interface/ts/support-lib/DjinniModule.ts
src/it/resources/result/my_client_interface/ts/support-lib/DjinniModule.js
src/it/resources/result/my_client_interface/ts/module.ts
10 changes: 10 additions & 0 deletions src/it/resources/expected/my_client_interface/ts/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from my_client_interface.djinni


export interface MyClientInterface {
logString(str: string): boolean;
}

export interface Module_statics {
}
Loading
Loading