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 /codemaker | |
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>
Diffstat (limited to 'codemaker')
-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 |
7 files changed, 295 insertions, 23 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; |