From 1bedc62b5722d0fbca27af306eb1286837116961 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Fri, 11 Oct 2024 18:25:46 +0200 Subject: [PATCH] feat(core): expose `km_core_keyboard_load_from_blob` to WASM --- core/include/keyman/keyman_core_api_bits.h | 2 +- core/src/km_core_keyboard_api.cpp | 23 ++++-- core/src/meson.build | 50 +++++++------ core/src/wasm.cpp | 84 +++++++++++++++++++--- 4 files changed, 123 insertions(+), 36 deletions(-) diff --git a/core/include/keyman/keyman_core_api_bits.h b/core/include/keyman/keyman_core_api_bits.h index bd1f519bffe..2fcc832f01e 100644 --- a/core/include/keyman/keyman_core_api_bits.h +++ b/core/include/keyman/keyman_core_api_bits.h @@ -33,7 +33,7 @@ #undef _kmn_export_flag #undef _kmn_import_flag #undef _kmn_static_flag - #else // How MSVC sepcifies function level attributes adn deprecation + #else // How MSVC sepcifies function level attributes and deprecation #define _kmn_and #define _kmn_tag_fn(a) __declspec(a) #define _kmn_deprecated_flag deprecated diff --git a/core/src/km_core_keyboard_api.cpp b/core/src/km_core_keyboard_api.cpp index 70d37fa63b0..cef9f99162b 100644 --- a/core/src/km_core_keyboard_api.cpp +++ b/core/src/km_core_keyboard_api.cpp @@ -40,18 +40,16 @@ namespace } // namespace km_core_status -km_core_keyboard_load_from_blob( +keyboard_load_from_blob_internal( const km_core_path_name kb_name, - const void* blob, - const size_t blob_size, + const std::vector& buf, km_core_keyboard** keyboard ) { assert(keyboard); - if (!keyboard || !blob) { + if (!keyboard) { return KM_CORE_STATUS_INVALID_ARGUMENT; } - std::vector buf((uint8_t*)blob, (uint8_t*)blob + blob_size); *keyboard = nullptr; try { abstract_processor* kp = processor_factory(kb_name, buf); @@ -67,6 +65,21 @@ km_core_keyboard_load_from_blob( return KM_CORE_STATUS_OK; } +km_core_status +km_core_keyboard_load_from_blob( + const km_core_path_name kb_name, + const void* blob, + const size_t blob_size, + km_core_keyboard** keyboard +) { + if (!blob) { + return KM_CORE_STATUS_INVALID_ARGUMENT; + } + + std::vector buf((uint8_t*)blob, (uint8_t*)blob + blob_size); + return keyboard_load_from_blob_internal(kb_name, buf, keyboard); +} + void km_core_keyboard_dispose(km_core_keyboard *keyboard) { diff --git a/core/src/meson.build b/core/src/meson.build index 030ab0a5144..d80aae84bdd 100644 --- a/core/src/meson.build +++ b/core/src/meson.build @@ -32,8 +32,6 @@ else endif if cpp_compiler.get_id() == 'emscripten' - # TODO: why do we need this defn here? - defns += ['-DKM_CORE_LIBRARY'] icu_if_not_on_wasm = [] else # only include this if NOT on wasm. @@ -54,19 +52,21 @@ if cpp_compiler.get_id() == 'emscripten' '-g', '-Wlimited-postlink-optimizations', '-lembind' ] + util_normalize_table_generator = executable( + 'util_normalize_table_generator', + ['util_normalize_table_generator.cpp'], + cpp_args: defns + warns, + include_directories: [inc], + link_args: links, + dependencies: [icu_uc, icu_i18n], + ) - util_normalize_table_generator = executable('util_normalize_table_generator', - ['util_normalize_table_generator.cpp'], - cpp_args: defns + warns, - include_directories: [inc], - link_args: links, - dependencies: [icu_uc, icu_i18n], - ) - - util_normalize_table_h = custom_target('util_normalize_table.h', - output: 'util_normalize_table.h', - command: [util_normalize_table_generator], - capture:true) + util_normalize_table_h = custom_target( + 'util_normalize_table.h', + output: 'util_normalize_table.h', + command: [util_normalize_table_generator], + capture:true + ) generated_headers += util_normalize_table_h endif @@ -139,13 +139,21 @@ mock_files = files( ) if cpp_compiler.get_id() == 'emscripten' - host_links = ['--whole-archive', '-sALLOW_MEMORY_GROWTH=1', '-sMODULARIZE=1', - '-sEXPORT_ES6', '-sENVIRONMENT=web,webview', - '--emit-tsd', 'km-core-interface.d.ts', '-sERROR_ON_UNDEFINED_SYMBOLS=0'] - - links += ['-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\',\'stringToNewUTF8\',\'wasmExports\']', - # Forcing inclusion of debug symbols - '-g', '-Wlimited-postlink-optimizations', '--bind'] + links += [ + '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\',\'stringToNewUTF8\',\'wasmExports\']', + # Forcing inclusion of debug symbols + '-g', '-Wlimited-postlink-optimizations', + '-lembind' + ] + host_links = [ + '--whole-archive', + '-sALLOW_MEMORY_GROWTH=1', + '-sMODULARIZE=1', '-sEXPORT_NAME=createCoreProcessor', + '-sEXPORT_ES6', + '-sENVIRONMENT=web,webview', + '--emit-tsd', 'km-core-interface.d.ts', + # '-sERROR_ON_UNDEFINED_SYMBOLS=0' + ] endif lib = library('keymancore', diff --git a/core/src/wasm.cpp b/core/src/wasm.cpp index 90fc85cda88..f11158470e2 100644 --- a/core/src/wasm.cpp +++ b/core/src/wasm.cpp @@ -1,11 +1,7 @@ #ifdef __EMSCRIPTEN__ -#ifdef __EMSCRIPTEN__ #include #include - -#else -#define EMSCRIPTEN_KEEPALIVE -#endif +#include #ifdef __cplusplus #define EXTERN extern "C" EMSCRIPTEN_KEEPALIVE @@ -13,8 +9,11 @@ #define EXTERN EMSCRIPTEN_KEEPALIVE #endif +#include "processor.hpp" #include +namespace em = emscripten; + constexpr km_core_attr const engine_attrs = { 256, KM_CORE_LIB_CURRENT, @@ -24,21 +23,88 @@ constexpr km_core_attr const engine_attrs = { "SIL International" }; -EMSCRIPTEN_KEEPALIVE km_core_attr const & tmp_wasm_attributes() { +EMSCRIPTEN_KEEPALIVE km_core_attr const& tmp_wasm_attributes() { return engine_attrs; } +template class CoreReturn { +public: + CoreReturn(int status = 0, const T* obj = nullptr) : status(status), object(obj) { + } + void setStatus(int status) { + this->status = status; + } + int getStatus() const { + return status; + } + void setObject(const T* obj) { + object = obj; + } + const T* getObject() const { + return object; + } + +private: + int status; + const T* object; +}; + +km_core_status +keyboard_load_from_blob_internal(const km_core_path_name kb_name, const std::vector& buf, km_core_keyboard** keyboard); + +EMSCRIPTEN_KEEPALIVE const CoreReturn* +km_core_keyboard_load_from_blob_wasm( + std::string kb_name, + const emscripten::val& blob_val +) { + std::vector blob; + km_core_keyboard* keyboard_ptr = nullptr; + + const auto length = blob_val["length"].as(); + blob.resize(length); + + emscripten::val memoryView{emscripten::typed_memory_view(length, blob.data())}; + memoryView.call("set", blob_val); + km_core_status retVal = ::keyboard_load_from_blob_internal(kb_name.c_str(), blob, &keyboard_ptr); + return new CoreReturn(retVal, keyboard_ptr); +} + EMSCRIPTEN_BINDINGS(core_interface) { - emscripten::value_object("km_core_attr") + em::value_object("km_core_attr") .field("max_context", &km_core_attr::max_context) .field("current", &km_core_attr::current) .field("revision", &km_core_attr::revision) .field("age", &km_core_attr::age) .field("technology", &km_core_attr::technology) - //.field("vendor", &km_core_attr::vendor, emscripten::allow_raw_pointers()) + //.field("vendor", &km_core_attr::vendor, em::allow_raw_pointers()) ; - emscripten::function("tmp_wasm_attributes", &tmp_wasm_attributes); + em::function("tmp_wasm_attributes", &tmp_wasm_attributes); + + + // Unfortunately embind has an open issue with enums and typescript where it + // only generates a type for the enum, but not the values in a usable way. + // Therefore it's not much use to expose the enum here. + // See https://github.com/emscripten-core/emscripten/issues/18585 + + // em::enum_("km_core_status_codes") + // .value("OK", KM_CORE_STATUS_OK) + // .value("NO_MEM", KM_CORE_STATUS_NO_MEM) + // .value("IO_ERROR", KM_CORE_STATUS_IO_ERROR) + // .value("INVALID_ARGUMENT", KM_CORE_STATUS_INVALID_ARGUMENT) + // .value("KEY_ERROR", KM_CORE_STATUS_KEY_ERROR) + // .value("INSUFFICENT_BUFFER", KM_CORE_STATUS_INSUFFICENT_BUFFER) + // .value("INVALID_UTF", KM_CORE_STATUS_INVALID_UTF) + // .value("INVALID_KEYBOARD", KM_CORE_STATUS_INVALID_KEYBOARD) + // .value("NOT_IMPLEMENTED", KM_CORE_STATUS_NOT_IMPLEMENTED) + // .value("OS_ERROR", KM_CORE_STATUS_OS_ERROR); + + em::class_("km_core_keyboard"); + em::class_>("CoreKeyboardReturn") + .property("status", &CoreReturn::getStatus) + .property("object", &CoreReturn::getObject, em::allow_raw_pointers()); + + em::function("keyboard_load_from_blob", &km_core_keyboard_load_from_blob_wasm, em::allow_raw_pointers()); } #endif