diff options
author | Sarper Akdemir <sarper.akdemir.extern@allotropia.de> | 2023-08-07 14:41:32 +0300 |
---|---|---|
committer | Sarper Akdemir <sarper.akdemir.extern@allotropia.de> | 2023-09-18 19:59:57 +0200 |
commit | 841f898574affb526a516224d7c3db9b137ea62b (patch) | |
tree | b26718c1a063f47b2a75eb939b4fd0d55f90f79f | |
parent | 971425f9b7613183a565e9b4ec5792b3f67bb133 (diff) |
JavaScript uno bindings for WASM with Embind - first cut
A rough implementation of uno bindings for LOWA using embind.
Adds new parameter '-W' to cppumaker to generate _embind.cxx
files alongside .hdl & .hpp.
For usage examples see static/README.wasm.md
Change-Id: Iee5d05e37bfba8e101c08212b15c05f7f2fa6c33
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/156273
Tested-by: Jenkins
Reviewed-by: Sarper Akdemir <sarper.akdemir.extern@allotropia.de>
-rw-r--r-- | codemaker/source/cppumaker/cppuoptions.cxx | 18 | ||||
-rw-r--r-- | codemaker/source/cppumaker/cpputype.cxx | 250 | ||||
-rw-r--r-- | codemaker/source/cppumaker/cpputype.hxx | 9 | ||||
-rw-r--r-- | codemaker/source/cppumaker/dumputils.cxx | 14 | ||||
-rw-r--r-- | codemaker/source/cppumaker/dumputils.hxx | 3 | ||||
-rw-r--r-- | codemaker/source/cppumaker/includes.cxx | 19 | ||||
-rw-r--r-- | codemaker/source/cppumaker/includes.hxx | 5 | ||||
-rw-r--r-- | desktop/Executable_soffice_bin.mk | 5 | ||||
-rw-r--r-- | solenv/gbuild/UnoApi.mk | 17 | ||||
-rw-r--r-- | solenv/gbuild/UnoApiTarget.mk | 18 | ||||
-rw-r--r-- | solenv/gbuild/platform/unxgcc.mk | 14 | ||||
-rw-r--r-- | static/README.wasm.md | 39 | ||||
-rw-r--r-- | static/source/unoembindhelpers/PrimaryBindings.cxx | 121 |
13 files changed, 505 insertions, 27 deletions
diff --git a/codemaker/source/cppumaker/cppuoptions.cxx b/codemaker/source/cppumaker/cppuoptions.cxx index 0a2ad962381a..f4ccf69b1ae7 100644 --- a/codemaker/source/cppumaker/cppuoptions.cxx +++ b/codemaker/source/cppumaker/cppuoptions.cxx @@ -216,6 +216,24 @@ bool CppuOptions::initOptions(int ac, char* av[], bool bCmdFile) m_options["-G"] = OString(); break; + case 'W': // generate embind javascript bindings for LOWA + if (av[i][2] != '\0') + { + OString tmp("'-W', please check"); + if (i <= ac - 1) + { + tmp += OString::Concat(" your input '") + av[i] + "'"; + } + + throw IllegalArgument(tmp); + } + + if (!isValid("-C") && !isValid("-CS") && !isValid("-L")) + { + throw IllegalArgument("'-W' requires '-C' or '-CS' or '-L' option"); + } + m_options["-W"] = OString(); + break; case 'X': // support for eXtra type rdbs { if (av[i][2] == '\0') diff --git a/codemaker/source/cppumaker/cpputype.cxx b/codemaker/source/cppumaker/cpputype.cxx index bb8e3ed7d33c..cb3422de16d0 100644 --- a/codemaker/source/cppumaker/cpputype.cxx +++ b/codemaker/source/cppumaker/cpputype.cxx @@ -53,6 +53,8 @@ namespace { +using FileType = codemaker::cppumaker::FileType; + bool isBootstrapType(OUString const & name) { static char const * const names[] = { @@ -150,6 +152,17 @@ bool isBootstrapType(OUString const & name) return std::any_of(std::begin(names), std::end(names), pred); } +OString getFileExtension(FileType eFileType) +{ + switch(eFileType) + { + default: + case FileType::HDL: return ".hdl"; + case FileType::HPP: return ".hpp"; + case FileType::EMBIND_CXX: return "_embind.cxx"; + } +} + class CppuType { public: @@ -163,7 +176,7 @@ public: void dump(CppuOptions const & options); void dumpFile( - std::u16string_view uri, std::u16string_view name, bool hpp, + std::u16string_view uri, std::u16string_view name, FileType eFileType, CppuOptions const & options); void dumpDependedTypes( @@ -176,6 +189,8 @@ public: virtual void dumpHppFile(FileStream& o, codemaker::cppumaker::Includes & includes) = 0; + virtual void dumpEmbindCppFile(FileStream& o); + OUString dumpHeaderDefine(FileStream& o, std::u16string_view extension) const; void dumpGetCppuType(FileStream & out); @@ -226,6 +241,8 @@ protected: assert(false); // this cannot happen } + virtual void dumpEmbindDeclaration(FileStream &) {}; + virtual void dumpFiles(OUString const & uri, CppuOptions const & options); virtual void addLightGetCppuTypeIncludes( @@ -305,8 +322,10 @@ const void CppuType::dumpFiles(OUString const & uri, CppuOptions const & options) { - dumpFile(uri, name_, false, options); - dumpFile(uri, name_, true, options); + dumpFile(uri, name_, FileType::HDL, options); + dumpFile(uri, name_, FileType::HPP, options); + if(options.isValid("-W")) + dumpFile(uri, name_, FileType::EMBIND_CXX, options); } void CppuType::addLightGetCppuTypeIncludes( @@ -411,12 +430,12 @@ void CppuType::dump(CppuOptions const & options) } void CppuType::dumpFile( - std::u16string_view uri, std::u16string_view name, bool hpp, + std::u16string_view uri, std::u16string_view name, FileType eFileType, CppuOptions const & options) { OUString fileUri( b2u(createFileNameFromType( - u2b(uri), u2b(name), hpp ? ".hpp" : ".hdl"))); + u2b(uri), u2b(name), getFileExtension(eFileType)))); if (fileUri.isEmpty()) { throw CannotDumpException(OUString::Concat("empty target URI for entity ") + name); } @@ -430,13 +449,20 @@ void CppuType::dumpFile( if(!out.isValid()) { throw CannotDumpException("cannot open " + tmpUri + " for writing"); } - codemaker::cppumaker::Includes includes(m_typeMgr, m_dependencies, hpp); + codemaker::cppumaker::Includes includes(m_typeMgr, m_dependencies, eFileType); try { - if (hpp) { - addGetCppuTypeIncludes(includes); - dumpHppFile(out, includes); - } else { - dumpHdlFile(out, includes); + switch(eFileType) + { + case FileType::HPP: + addGetCppuTypeIncludes(includes); + dumpHppFile(out, includes); + break; + case FileType::HDL: + dumpHdlFile(out, includes); + break; + case FileType::EMBIND_CXX: + dumpEmbindCppFile(out); + break; } } catch (...) { out.close(); @@ -582,6 +608,16 @@ void CppuType::dumpHFileContent( out << " *);\n\n#endif\n"; } +void CppuType::dumpEmbindCppFile(FileStream &out) +{ + out << "#ifdef EMSCRIPTEN\n"; + out << "#include <emscripten/bind.h>\n" + "#include <" << name_.replace('.', '/') << ".hpp>\n"; + out << "using namespace emscripten;\n\n"; + dumpEmbindDeclaration(out); + out << "#endif\n"; +} + void CppuType::dumpGetCppuType(FileStream & out) { if (name_ == "com.sun.star.uno.XInterface") { @@ -1103,10 +1139,14 @@ public: OUString const & name, rtl::Reference< TypeManager > const & typeMgr); virtual void dumpDeclaration(FileStream& o) override; + virtual void dumpEmbindDeclaration(FileStream& o) override; void dumpHppFile(FileStream& o, codemaker::cppumaker::Includes & includes) override; void dumpAttributes(FileStream& o) const; + void dumpEmbindAttributeBindings(FileStream& o) const; void dumpMethods(FileStream& o) const; + void dumpEmbindMethodBindings(FileStream& o, bool bDumpForReference=false) const; + void dumpEmbindWrapperFunc(FileStream& o, const unoidl::InterfaceTypeEntity::Method& method, bool bDumpForReference=false) const; void dumpNormalGetCppuType(FileStream& o) override; void dumpComprehensiveGetCppuType(FileStream& o) override; void dumpCppuAttributeRefs(FileStream& o, sal_uInt32& index); @@ -1168,15 +1208,65 @@ void InterfaceType::dumpDeclaration(FileStream & out) << ("static inline ::css::uno::Type const & SAL_CALL" " static_type(void * = 0);\n\n"); dec(); +#ifdef EMSCRIPTEN + out << "#ifndef EMSCRIPTEN\n"; +#endif out << "protected:\n"; inc(); out << indent() << "~" << id_ << ("() SAL_NOEXCEPT {} // avoid warnings about virtual members and" " non-virtual dtor\n"); +#ifdef EMSCRIPTEN + out << "#endif\n"; +#endif dec(); out << "};\n\n"; } +void InterfaceType::dumpEmbindDeclaration(FileStream & out) +{ + out << "namespace emscripten { namespace internal { \n" + "template<> void raw_destructor<" << codemaker::cpp::scopedCppName(u2b(name_)) + << ">(" << codemaker::cpp::scopedCppName(u2b(name_)) << "*){}\n" + "}}\n"; + + out << "EMSCRIPTEN_BINDINGS(uno_bindings_"; + codemaker::cppumaker::dumpTypeFullWithDecorator(out, name_, u"_"); + codemaker::cppumaker::dumpTypeIdentifier(out, name_); + out << ") {\n"; + + out << "\nclass_<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">(\""; + codemaker::cppumaker::dumpTypeFullWithDecorator(out, name_, u"$"); + codemaker::cppumaker::dumpTypeIdentifier(out, name_); + out << "\")\n"; + + inc(); + // dump bindings for attributes and methods. + dumpEmbindAttributeBindings(out); + dumpEmbindMethodBindings(out); + out << indent() << ";\n"; + dec(); + + // dump reference bindings. + out << "\nclass_<::css::uno::Reference<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">, base<::css::uno::BaseReference>>(\""; + codemaker::cppumaker::dumpTypeFullWithDecorator(out, name_, u"$"); + codemaker::cppumaker::dumpTypeIdentifier(out, name_); + out << "Ref\")\n"; + inc(); + out << indent() << ".constructor<>()\n" + << indent() << ".constructor<::css::uno::BaseReference, ::css::uno::UnoReference_Query>()\n" + << indent() << ".function(\"is\", &::css::uno::Reference<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">::is)\n" + << indent() << ".function(\"get\", &::css::uno::Reference<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">::get, allow_raw_pointers())\n" + << indent() << ".function(\"set\", emscripten::select_overload<bool(const ::css::uno::Any&, com::sun::star::uno::UnoReference_Query)>(&::css::uno::Reference<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">::set))\n"; + dumpEmbindAttributeBindings(out); + dumpEmbindMethodBindings(out, true); + out << indent() << ";\n"; + dec(); + + out << "}\n"; +} + + void InterfaceType::dumpHppFile( FileStream & out, codemaker::cppumaker::Includes & includes) { @@ -1228,6 +1318,31 @@ void InterfaceType::dumpAttributes(FileStream & out) const } } +void InterfaceType::dumpEmbindAttributeBindings(FileStream& out) const +{ + if (!entity_->getDirectAttributes().empty()) + { + out << indent() << "// Bindings for attributes\n"; + } + for (const unoidl::InterfaceTypeEntity::Attribute& attr : entity_->getDirectAttributes()) + { + if (m_isDeprecated || isDeprecated(attr.annotations)) + continue; + + out << indent(); + out << ".function(\""; + out << "get" << attr.name << "\", &" << codemaker::cpp::scopedCppName(u2b(name_)) << "::get" + << attr.name << ")\n"; + if (!attr.readOnly) + { + out << indent(); + out << ".function(\""; + out << "set" << attr.name << "\", &" << codemaker::cpp::scopedCppName(u2b(name_)) + << "::set" << attr.name << ")\n"; + } + } +} + void InterfaceType::dumpMethods(FileStream & out) const { if (!entity_->getDirectMethods().empty()) { @@ -1268,6 +1383,115 @@ void InterfaceType::dumpMethods(FileStream & out) const } } +void InterfaceType::dumpEmbindWrapperFunc(FileStream& out, + const unoidl::InterfaceTypeEntity::Method& method, + bool bDumpForReference) const +{ + out << indent(); + out << ".function(\"" << method.name << "\", "; + out << indent() << "+[]("; + if (bDumpForReference) + out << "::css::uno::Reference<"; + out << codemaker::cpp::scopedCppName(u2b(name_)); + if (bDumpForReference) + out << ">"; + out << "* self"; + if(!method.parameters.empty()) + out << ","; + + auto dumpParameters = [&](bool bDumpType) + { + // dumpParams with references as pointers + if (!method.parameters.empty()) + { + out << " "; + for (std::vector<unoidl::InterfaceTypeEntity::Method::Parameter>::const_iterator + parameter(method.parameters.begin()); + parameter != method.parameters.end();) + { + bool isConst; + bool isRef; + if (parameter->direction + == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN) + { + isConst = passByReference(parameter->type); + isRef = isConst; + } + else + { + isConst = false; + isRef = true; + } + // for the embind wrapper, we define a pointer instead of a reference. + if (bDumpType) + dumpType(out, parameter->type, isConst, /*isRef=*/false); + if (isRef) + out << "*"; + + out << " " << parameter->name; + ++parameter; + if (parameter != method.parameters.end()) + { + out << ", "; + } + } + out << " "; + } + }; + dumpParameters(/*bDumpType=*/true); + + if (bDumpForReference) + { + out << ") { return self->get()->" << method.name << "("; + } + else + { + out << ") { return self->" << method.name << "("; + } + + dumpParameters(/*bDumpType=*/false); + out << "); }, allow_raw_pointers() )\n"; +} + +void InterfaceType::dumpEmbindMethodBindings(FileStream & out, bool bDumpForReference) const +{ + if (!entity_->getDirectMethods().empty()) { + out << indent() << "// Bindings for methods\n"; + } + for (const unoidl::InterfaceTypeEntity::Method& method : entity_->getDirectMethods()) { + if( m_isDeprecated || isDeprecated(method.annotations) ) + continue; + + // if dumping the method binding for a reference implementation + // dump wrapper. + if(bDumpForReference) + { + dumpEmbindWrapperFunc(out, method, true); + continue; + } + + bool bHasOutParams = std::any_of( + method.parameters.begin(), method.parameters.end(), + [](const auto& parameter) { + return parameter.direction + != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN; + }); + + if (bHasOutParams) + { + dumpEmbindWrapperFunc(out, method, false); + continue; + } + + out << indent(); + out << ".function(\"" << method.name << "\", &" + << codemaker::cpp::scopedCppName(u2b(name_)) + << "::" << method.name << ")\n"; + } +} + + + void InterfaceType::dumpNormalGetCppuType(FileStream & out) { dumpGetCppuTypePreamble(out); @@ -3515,7 +3739,9 @@ private: } virtual void dumpFiles(OUString const & uri, CppuOptions const & options) override { - dumpFile(uri, name_, true, options); + dumpFile(uri, name_, FileType::HPP, options); + if(options.isValid("-W")) + dumpFile(uri, name_, FileType::EMBIND_CXX, options); } }; diff --git a/codemaker/source/cppumaker/cpputype.hxx b/codemaker/source/cppumaker/cpputype.hxx index 40fc94f26c29..a6f8f9bfe8a3 100644 --- a/codemaker/source/cppumaker/cpputype.hxx +++ b/codemaker/source/cppumaker/cpputype.hxx @@ -28,6 +28,15 @@ namespace rtl { class OUString; } class CppuOptions; class TypeManager; +namespace codemaker::cppumaker { +enum class FileType +{ + HDL, + HPP, + EMBIND_CXX +}; +} + void produce( OUString const & name, rtl::Reference< TypeManager > const & manager, codemaker::GeneratedTypeSet & generated, CppuOptions const & options); diff --git a/codemaker/source/cppumaker/dumputils.cxx b/codemaker/source/cppumaker/dumputils.cxx index 2a3e809e70f3..54867523b0d4 100644 --- a/codemaker/source/cppumaker/dumputils.cxx +++ b/codemaker/source/cppumaker/dumputils.cxx @@ -74,6 +74,20 @@ void dumpTypeIdentifier(FileStream & out, std::u16string_view entityName) { out << entityName.substr(entityName.rfind('.') + 1); } +bool dumpTypeFullWithDecorator(FileStream& out, std::u16string_view entityName, std::u16string_view decorator) +{ + bool bOutput = false; + for (sal_Int32 i = 0; i >= 0;) + { + std::u16string_view id(o3tl::getToken(entityName, 0, '.', i)); + if (i >= 0) + { + out << id << decorator; + bOutput = true; + } + } + return bOutput; +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/cppumaker/dumputils.hxx b/codemaker/source/cppumaker/dumputils.hxx index 24e5bae3bede..c7021cba7408 100644 --- a/codemaker/source/cppumaker/dumputils.hxx +++ b/codemaker/source/cppumaker/dumputils.hxx @@ -35,6 +35,9 @@ bool dumpNamespaceOpen(FileStream& out, std::u16string_view entityName, bool ful bool dumpNamespaceClose(FileStream& out, std::u16string_view entityName, bool fullModuleType); void dumpTypeIdentifier(FileStream& out, std::u16string_view entityName); + +bool dumpTypeFullWithDecorator(FileStream& out, std::u16string_view entityName, + std::u16string_view decorator); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/cppumaker/includes.cxx b/codemaker/source/cppumaker/includes.cxx index 9dacdf268265..05f768bbc505 100644 --- a/codemaker/source/cppumaker/includes.cxx +++ b/codemaker/source/cppumaker/includes.cxx @@ -20,6 +20,7 @@ #include "includes.hxx" +#include "cpputype.hxx" #include "dependencies.hxx" #include "dumputils.hxx" @@ -40,8 +41,8 @@ using codemaker::cppumaker::Includes; Includes::Includes( rtl::Reference< TypeManager > manager, - codemaker::cppumaker::Dependencies const & dependencies, bool hpp): - m_manager(std::move(manager)), m_map(dependencies.getMap()), m_hpp(hpp), + codemaker::cppumaker::Dependencies const & dependencies, FileType eFileType): + m_manager(std::move(manager)), m_map(dependencies.getMap()), m_filetype(eFileType), m_includeCassert(false), m_includeAny(dependencies.hasAnyDependency()), m_includeReference(false), m_includeSequence(dependencies.hasSequenceDependency()), @@ -136,7 +137,7 @@ void dumpEmptyLineBeforeFirst(FileStream & out, bool * first) { void Includes::dump( FileStream & out, OUString const * companionHdl, bool exceptions) { - OSL_ASSERT(companionHdl == nullptr || m_hpp); + OSL_ASSERT(companionHdl == nullptr || m_filetype == FileType::HPP); if (!m_includeReference) { for (const auto& pair : m_map) { @@ -159,12 +160,12 @@ void Includes::dump( { if (exceptions || pair.second != Dependencies::KIND_EXCEPTION) { dumpEmptyLineBeforeFirst(out, &first); - if (m_hpp || pair.second == Dependencies::KIND_BASE + if ((m_filetype == FileType::HPP) || pair.second == Dependencies::KIND_BASE || !isInterfaceType(u2b(pair.first))) { // If we know our name, then avoid including ourselves. if (!companionHdl || *companionHdl != pair.first) { - dumpInclude(out, u2b(pair.first), m_hpp); + dumpInclude(out, u2b(pair.first), (m_filetype == FileType::HPP)); } } else { bool ns = dumpNamespaceOpen(out, pair.first, false); @@ -185,22 +186,22 @@ void Includes::dump( static char const * hxxExtension[2] = { "h", "hxx" }; if (m_includeAny) { dumpEmptyLineBeforeFirst(out, &first); - out << "#include \"com/sun/star/uno/Any." << hxxExtension[m_hpp] + out << "#include \"com/sun/star/uno/Any." << hxxExtension[(m_filetype == FileType::HPP)] << "\"\n"; } if (m_includeReference) { dumpEmptyLineBeforeFirst(out, &first); - out << "#include \"com/sun/star/uno/Reference." << hxxExtension[m_hpp] + out << "#include \"com/sun/star/uno/Reference." << hxxExtension[(m_filetype == FileType::HPP)] << "\"\n"; } if (m_includeSequence) { dumpEmptyLineBeforeFirst(out, &first); - out << "#include \"com/sun/star/uno/Sequence." << hxxExtension[m_hpp] + out << "#include \"com/sun/star/uno/Sequence." << hxxExtension[(m_filetype == FileType::HPP)] << "\"\n"; } if (m_includeType) { dumpEmptyLineBeforeFirst(out, &first); - out << "#include \"com/sun/star/uno/Type." << hxxExtension[m_hpp] + out << "#include \"com/sun/star/uno/Type." << hxxExtension[(m_filetype == FileType::HPP)] << "\"\n"; } if (m_includeCppuMacrosHxx) { diff --git a/codemaker/source/cppumaker/includes.hxx b/codemaker/source/cppumaker/includes.hxx index 8cd830b4a731..dcdcb5836f7e 100644 --- a/codemaker/source/cppumaker/includes.hxx +++ b/codemaker/source/cppumaker/includes.hxx @@ -29,12 +29,13 @@ class FileStream; class TypeManager; namespace codemaker::cppumaker { +enum class FileType; class Includes { public: Includes( rtl::Reference< TypeManager > manager, - Dependencies const & dependencies, bool hpp); + Dependencies const & dependencies, FileType eFileType); ~Includes(); @@ -73,7 +74,7 @@ private: rtl::Reference< TypeManager > m_manager; Dependencies::Map m_map; - bool m_hpp; + FileType m_filetype; bool m_includeCassert; bool m_includeAny; bool m_includeReference; diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk index ce908c1be318..c2d811c2e86c 100644 --- a/desktop/Executable_soffice_bin.mk +++ b/desktop/Executable_soffice_bin.mk @@ -48,9 +48,12 @@ endif endif ifeq ($(OS),EMSCRIPTEN) +$(call gb_LinkTarget_get_target,$(call gb_Executable_get_linktarget,soffice_bin)) : $(call gb_StaticLibrary_get_linktarget_target,unoembind) +$(call gb_LinkTarget_get_headers_target,$(call gb_Executable_get_linktarget,soffice_bin)) : $(call gb_StaticLibrary_get_headers_target,unoembind) +$(call gb_LinkTarget__static_lib_dummy_depend,unoembind) $(eval $(call gb_Executable_add_ldflags,soffice_bin,\ - -s EXPORTED_FUNCTIONS=["_main"$(COMMA)"_libreofficekit_hook"$(COMMA)"_libreofficekit_hook_2"$(COMMA)"_lok_preinit"$(COMMA)"_lok_preinit_2"] \ + -s EXPORTED_FUNCTIONS=["_main"$(COMMA)"_libreofficekit_hook"$(COMMA)"_libreofficekit_hook_2"$(COMMA)"_lok_preinit"$(COMMA)"_lok_preinit_2"] -Wl$(COMMA)--whole-archive $(call gb_StaticLibrary_get_target,unoembind) -Wl$(COMMA)--no-whole-archive \ )) endif diff --git a/solenv/gbuild/UnoApi.mk b/solenv/gbuild/UnoApi.mk index f396642901fe..9ed314703f33 100644 --- a/solenv/gbuild/UnoApi.mk +++ b/solenv/gbuild/UnoApi.mk @@ -36,6 +36,8 @@ define gb_UnoApi_add_idlfiles $(call gb_UnoApiTarget_add_idlfiles,$(1),$(2),$(3)) $(call gb_UnoApiHeadersTarget_add_headerfiles,$(1),$(2),$(addsuffix .hpp,$(3))) $(call gb_UnoApiHeadersTarget_add_headerfiles,$(1),$(2),$(addsuffix .hdl,$(3))) +$(if $(filter EMSCRIPTEN, $(OS)),\ + $(call gb_UnoApiHeadersTarget_add_embind,$(1),$(2),$(addsuffix _embind,$(3)))) endef @@ -69,4 +71,19 @@ $(call gb_UnoApiTarget_set_reference_rdbfile,$(1),$(2)) endef +ifeq ($(OS),EMSCRIPTEN) +$(eval $(call gb_StaticLibrary_StaticLibrary,unoembind)) +$(eval $(call gb_StaticLibrary_set_include,unoembind,\ + $$(INCLUDE) \ +)) +$(eval $(call gb_StaticLibrary_use_api,unoembind,\ + offapi \ + udkapi \ +)) +$(eval $(call gb_StaticLibrary_add_exception_objects,unoembind,\ + static/source/unoembindhelpers/PrimaryBindings\ +)) + +endif + # vim: set noet sw=4 ts=4: diff --git a/solenv/gbuild/UnoApiTarget.mk b/solenv/gbuild/UnoApiTarget.mk index 67e8bccca906..7c52ecc81f1e 100644 --- a/solenv/gbuild/UnoApiTarget.mk +++ b/solenv/gbuild/UnoApiTarget.mk @@ -151,7 +151,8 @@ $(call gb_UnoApiHeadersTarget_get_real_comprehensive_target,%) : \ $(gb_UnoApiHeadersTarget_CPPUMAKERDEPS) $(call gb_Output_announce,$*,$(true),HPC,3) $(call gb_Trace_StartRange,$*,HPC) - $(call gb_UnoApiHeadersTarget__command,$@,$*,$(call gb_UnoApiHeadersTarget_get_comprehensive_dir,$*),-C) + $(call gb_UnoApiHeadersTarget__command,$@,$*,$(call gb_UnoApiHeadersTarget_get_comprehensive_dir,$*), \ + -C $(if $(filter EMSCRIPTEN, $(OS)), -W)) $(call gb_Trace_EndRange,$*,HPC) $(call gb_UnoApiHeadersTarget_get_real_target,%) : \ @@ -231,6 +232,21 @@ define gb_UnoApiHeadersTarget_add_headerfiles $(foreach hdr,$(3),$(call gb_UnoApiHeadersTarget_add_headerfile,$(1),$(2)/$(hdr))) endef +# call gb_UnoApiEmbindTarget_add_embind,unoapi,directory,headerfilenames +define gb_UnoApiHeadersTarget_add_embind +$(if $(filter offapi udkapi, $(1)),\ + $(foreach hdr,$(3),$(eval $(call gb_UnoApiEmbindTarget__add_embind,$(1),$(2),$(hdr))))) +endef + +# CaptionEscapeDirection contains "auto" as a variable name.. so exclude that +define gb_UnoApiEmbindTarget__add_embind +$(if $(filter-out CaptionEscapeDirection_embind, $(3)),\ +$(eval $(call gb_StaticLibrary_add_generated_exception_objects,unoembind,\ + UnoApiHeadersTarget/$(1)/comprehensive/$(2)/$(3) \ +))) + +endef + define gb_UnoApiHeadersTarget__use_api_for_target $(call gb_UnoApiHeadersTarget_get_$(3),$(1)) : $(call gb_UnoApiTarget_get_target,$(2)) $(call gb_UnoApiHeadersTarget_get_$(3),$(1)) : UNOAPI_DEPS += -X$(call gb_UnoApiTarget_get_target,$(2)) diff --git a/solenv/gbuild/platform/unxgcc.mk b/solenv/gbuild/platform/unxgcc.mk index bfb860a41fc2..29adbf9dc42f 100644 --- a/solenv/gbuild/platform/unxgcc.mk +++ b/solenv/gbuild/platform/unxgcc.mk @@ -191,8 +191,18 @@ endef define gb_LinkTarget__command_staticlink $(call gb_Helper_abbreviate_dirs,\ rm -f $(1) && \ - $(if $(filter EMSCRIPTEN,$(OS)),unset PYTHONWARNINGS ;) \ + $(if $(filter EMSCRIPTEN,$(OS)),unset PYTHONWARNINGS ; \ + RESPONSEFILE=$(call gb_var2file,$(shell $(gb_MKTEMP)), \ + $(foreach object,$(COBJECTS),$(call gb_CObject_get_target,$(object))) \ + $(foreach object,$(CXXOBJECTS),$(call gb_CxxObject_get_target,$(object))) \ + $(foreach object,$(ASMOBJECTS),$(call gb_AsmObject_get_target,$(object))) \ + $(foreach object,$(GENCOBJECTS),$(call gb_GenCObject_get_target,$(object))) \ + $(foreach object,$(GENCXXOBJECTS),$(call gb_GenCxxObject_get_target,$(object))) \ + $(foreach object,$(GENNASMOBJECTS),$(call gb_GenNasmObject_get_target,$(object))) \ + $(foreach extraobjectlist,$(EXTRAOBJECTLISTS),$(shell cat $(extraobjectlist)))) && ) \ $(gb_AR) $(gb_LTOPLUGINFLAGS) -rsu $(1) \ + $(if $(filter EMSCRIPTEN, $(OS)), \ + @$${RESPONSEFILE} $(if $(findstring s,$(MAKEFLAGS)),2> /dev/null); rm $${RESPONSEFILE},\ $(foreach object,$(COBJECTS),$(call gb_CObject_get_target,$(object))) \ $(foreach object,$(CXXOBJECTS),$(call gb_CxxObject_get_target,$(object))) \ $(foreach object,$(ASMOBJECTS),$(call gb_AsmObject_get_target,$(object))) \ @@ -200,7 +210,7 @@ $(call gb_Helper_abbreviate_dirs,\ $(foreach object,$(GENCXXOBJECTS),$(call gb_GenCxxObject_get_target,$(object))) \ $(foreach object,$(GENNASMOBJECTS),$(call gb_GenNasmObject_get_target,$(object))) \ $(foreach extraobjectlist,$(EXTRAOBJECTLISTS),@$(extraobjectlist)) \ - $(if $(findstring s,$(MAKEFLAGS)),2> /dev/null)) + $(if $(findstring s,$(MAKEFLAGS)),2> /dev/null))) endef define gb_LinkTarget__command diff --git a/static/README.wasm.md b/static/README.wasm.md index 6e74599eb3ae..f39a79247d00 100644 --- a/static/README.wasm.md +++ b/static/README.wasm.md @@ -214,6 +214,45 @@ WASM dynamic dispatch: - <https://fitzgeraldnick.com/2018/04/26/how-does-dynamic-dispatch-work-in-wasm.html> +### UNO bindings with Embind + +Right now there's a very rough implementation in place. With lots of different +bits unimplemented. And it _might_ be leaking memory. i.e. Lots of room for +improvement! ;) + +Some usage examples through javascript of the current implementation: +```js +// inserts a string at the start of the Writer document. +xModel = Module.getCurrentModelFromViewSh(); +xTextDocument = new Module.com$sun$star$text$XTextDocumentRef(xModel, Module.UnoReference_Query.UNO_QUERY); +xText = xTextDocument.getText(); +xSimpleText = new Module.com$sun$star$text$XSimpleTextRef(xText, Module.UnoReference_Query.UNO_QUERY); +xTextCursor = xSimpleText.createTextCursor(); +xTextRange = new Module.com$sun$star$text$XTextRangeRef(xTextCursor, Module.UnoReference_Query.UNO_QUERY); +xTextRange.setString(new Module.OUString("string here!")); +xModel.delete(); xTextDocument.delete(); xText.delete(); xSimpleText.delete(); xTextCursor.delete(); xTextRange.delete(); +``` + +```js +// changes each paragraph of the Writer document to a random color. +xModel = Module.getCurrentModelFromViewSh(); +xTextDocument = new Module.com$sun$star$text$XTextDocumentRef(xModel, Module.UnoReference_Query.UNO_QUERY); +xText = xTextDocument.getText(); +xEnumAccess = new Module.com$sun$star$container$XEnumerationAccessRef(xText, Module.UnoReference_Query.UNO_QUERY); +xParaEnumeration = xEnumAccess.createEnumeration(); + +while (xParaEnumeration.hasMoreElements()) { + xParagraph = new Module.com$sun$star$text$XTextRangeRef(); + xParagraph.set(xParaEnumeration.nextElement(), Module.UnoReference_Query.UNO_QUERY); + if (xParagraph.is()) { + xParaProps = new Module.com$sun$star$beans$XPropertySetRef(xParagraph, Module.UnoReference_Query.UNO_QUERY); + xParaProps.setPropertyValue(new Module.OUString("CharColor"), new Module.Any(Math.floor(Math.random() * 0xFFFFFF), Module.UnoType.long)); + } +} +``` + + + ## Tools for problem diagnosis * `nm -s` should list the symbols in the archive, based on the index generated by ranlib. diff --git a/static/source/unoembindhelpers/PrimaryBindings.cxx b/static/source/unoembindhelpers/PrimaryBindings.cxx new file mode 100644 index 000000000000..e9a0c496310b --- /dev/null +++ b/static/source/unoembindhelpers/PrimaryBindings.cxx @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#ifdef EMSCRIPTEN +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/uno/XReference.hpp> + +#include <emscripten/bind.h> + +#include <sal/log.hxx> +#include <sfx2/viewsh.hxx> + +using namespace emscripten; +using namespace css::uno; + +namespace +{ +Reference<css::frame::XModel> getCurrentModelFromViewSh() +{ + SfxViewShell* pSh = nullptr; + pSh = SfxViewShell::Current(); + if (!pSh) + { + return {}; + } + return pSh->GetCurrentDocument(); +} +} + +EMSCRIPTEN_BINDINGS(PrimaryBindings) +{ + // Reference bits + class_<BaseReference>("BaseReference"); + enum_<UnoReference_Query>("UnoReference_Query").value("UNO_QUERY", UNO_QUERY); + + class_<OUString>("OUString") + .constructor( + +[](const std::string& rString) -> OUString { + return OUString::fromUtf8(std::string_view{ rString.c_str() }); + }, + allow_raw_pointers()) + .function("toString", +[](const OUString& rSelf) -> std::string { + return std::string{ rSelf.toUtf8() }; + }); + + // Types used for Any construction + enum_<TypeClass>("UnoType") + .value("void", TypeClass::TypeClass_VOID) + .value("char", TypeClass::TypeClass_CHAR) + .value("bool", TypeClass::TypeClass_BOOLEAN) + .value("byte", TypeClass::TypeClass_BYTE) + .value("short", TypeClass::TypeClass_SHORT) + .value("unsigned_short", TypeClass::TypeClass_UNSIGNED_SHORT) + .value("long", TypeClass::TypeClass_LONG) + .value("unsigned_long", TypeClass::TypeClass_UNSIGNED_LONG) + .value("hyper", TypeClass::TypeClass_HYPER) + .value("unsigned_hyper", TypeClass::TypeClass_UNSIGNED_HYPER) + .value("float", TypeClass::TypeClass_FLOAT) + .value("double", TypeClass::TypeClass_DOUBLE) + .value("string", TypeClass::TypeClass_STRING); + + // Any + class_<Any>("Any").constructor( + +[](const val& rObject, const TypeClass& rUnoType) -> Any { + switch (rUnoType) + { + case TypeClass_VOID: + break; + case TypeClass_CHAR: + return Any{ rObject.as<sal_Int8>() }; + case TypeClass_BOOLEAN: + return Any{ rObject.as<bool>() }; + case TypeClass_BYTE: + return Any{ rObject.as<sal_Int8>() }; + case TypeClass_SHORT: + return Any{ rObject.as<sal_Int16>() }; + case TypeClass_UNSIGNED_SHORT: + return Any{ rObject.as<sal_uInt16>() }; + case TypeClass_LONG: + return Any{ rObject.as<sal_Int32>() }; + case TypeClass_UNSIGNED_LONG: + return Any{ rObject.as<sal_uInt32>() }; + case TypeClass_HYPER: + return Any{ rObject.as<sal_Int64>() }; + case TypeClass_UNSIGNED_HYPER: + return Any{ rObject.as<sal_uInt64>() }; + case TypeClass_FLOAT: + return Any{ rObject.as<float>() }; + case TypeClass_DOUBLE: + return Any{ rObject.as<double>() }; + case TypeClass_STRING: + return Any{ OUString::fromUtf8(std::string_view{ rObject.as<std::string>() }) }; + case TypeClass_TYPE: + case TypeClass_ANY: + case TypeClass_ENUM: + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + case TypeClass_SEQUENCE: + case TypeClass_INTERFACE: + case TypeClass_TYPEDEF: + case TypeClass_SERVICE: + case TypeClass_MODULE: + case TypeClass_INTERFACE_METHOD: + case TypeClass_INTERFACE_ATTRIBUTE: + default: + break; + } + return {}; + }, + allow_raw_pointers()); + + function("getCurrentModelFromViewSh", &getCurrentModelFromViewSh); +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |