diff options
author | Stephan Bergmann <stephan.bergmann@allotropia.de> | 2024-01-21 14:50:15 +0100 |
---|---|---|
committer | Stephan Bergmann <stephan.bergmann@allotropia.de> | 2024-01-21 15:51:39 +0100 |
commit | b2ade3e63e34556cad8157e25f8c75d29e79537d (patch) | |
tree | 3d268145cc15bedf0d1860c2285ba37c5d62969f | |
parent | writerfilter : use constexpr frozen unordered_set instead of std::unordered_set (diff) | |
download | core-b2ade3e63e34556cad8157e25f8c75d29e79537d.tar.gz core-b2ade3e63e34556cad8157e25f8c75d29e79537d.zip |
Extract embindmaker from cppumaker into its own tool
...that directly generates one large .cxx
Change-Id: I046539b83f8fde5eda7168c93a8b819137399665
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162343
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
-rw-r--r-- | Repository.mk | 1 | ||||
-rw-r--r-- | RepositoryModule_build.mk | 1 | ||||
-rw-r--r-- | codemaker/source/cppumaker/cppuoptions.cxx | 18 | ||||
-rw-r--r-- | codemaker/source/cppumaker/cpputype.cxx | 204 | ||||
-rw-r--r-- | codemaker/source/cppumaker/cpputype.hxx | 3 | ||||
-rw-r--r-- | codemaker/source/cppumaker/dumputils.cxx | 14 | ||||
-rw-r--r-- | codemaker/source/cppumaker/dumputils.hxx | 3 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | include/codemaker/typemanager.hxx | 5 | ||||
-rw-r--r-- | solenv/gbuild/UnoApi.mk | 17 | ||||
-rw-r--r-- | solenv/gbuild/UnoApiTarget.mk | 15 | ||||
-rw-r--r-- | solenv/gbuild/extensions/pre_BuildTools.mk | 1 | ||||
-rw-r--r-- | static/CustomTarget_unoembind.mk | 22 | ||||
-rw-r--r-- | static/Executable_embindmaker.mk | 26 | ||||
-rw-r--r-- | static/Module_static.mk | 8 | ||||
-rw-r--r-- | static/StaticLibrary_unoembind.mk | 27 | ||||
-rw-r--r-- | static/source/embindmaker/embindmaker.cxx | 510 |
17 files changed, 606 insertions, 272 deletions
diff --git a/Repository.mk b/Repository.mk index 3e2e29098e66..2eb8ae6936e8 100644 --- a/Repository.mk +++ b/Repository.mk @@ -33,6 +33,7 @@ $(eval $(call gb_Helper_register_executables,NONE, \ concat-deps \ cpp \ cppunittester \ + $(if $(or $(filter EMSCRIPTEN,$(BUILD_TYPE_FOR_HOST)),$(filter EMSCRIPTEN,$(OS))),embindmaker) \ gbuildtojson \ $(if $(filter MSC,$(COM)), \ gcc-wrapper \ diff --git a/RepositoryModule_build.mk b/RepositoryModule_build.mk index 2059c1f2a5a9..3efcb3cad705 100644 --- a/RepositoryModule_build.mk +++ b/RepositoryModule_build.mk @@ -45,6 +45,7 @@ $(eval $(call gb_Module_add_moduledirs,cross_toolset,\ shell \ solenv \ soltools \ + $(if $(filter EMSCRIPTEN,$(BUILD_TYPE_FOR_HOST)),static) \ stoc \ store \ tools \ diff --git a/codemaker/source/cppumaker/cppuoptions.cxx b/codemaker/source/cppumaker/cppuoptions.cxx index 548b4d369a8e..d16c7dacecba 100644 --- a/codemaker/source/cppumaker/cppuoptions.cxx +++ b/codemaker/source/cppumaker/cppuoptions.cxx @@ -216,24 +216,6 @@ bool CppuOptions::initOptions(int ac, char* av[], bool bCmdFile) m_options["-G"_ostr] = OString(); break; - case 'W': // generate embind javascript bindings for LOWA - if (av[i][2] != '\0') - { - OString tmp("'-W', please check"_ostr); - if (i <= ac - 1) - { - tmp += OString::Concat(" your input '") + av[i] + "'"; - } - - throw IllegalArgument(tmp); - } - - if (!isValid("-C"_ostr) && !isValid("-CS"_ostr) && !isValid("-L"_ostr)) - { - throw IllegalArgument("'-W' requires '-C' or '-CS' or '-L' option"_ostr); - } - m_options["-W"_ostr] = 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 2b995e1d31ae..9ae5d689072e 100644 --- a/codemaker/source/cppumaker/cpputype.cxx +++ b/codemaker/source/cppumaker/cpputype.cxx @@ -159,7 +159,6 @@ OString getFileExtension(FileType eFileType) default: case FileType::HDL: return ".hdl"_ostr; case FileType::HPP: return ".hpp"_ostr; - case FileType::EMBIND_CXX: return "_embind.cxx"_ostr; } } @@ -189,8 +188,6 @@ public: virtual void dumpHppFile(FileStream& o, codemaker::cppumaker::Includes & includes) = 0; - void dumpEmbindCppFile(FileStream& o); - OUString dumpHeaderDefine(FileStream& o, std::u16string_view extension) const; void dumpGetCppuType(FileStream & out); @@ -241,8 +238,6 @@ protected: assert(false); // this cannot happen } - virtual void dumpEmbindDeclaration(FileStream &) {}; - virtual void dumpFiles(OUString const & uri, CppuOptions const & options); virtual void addLightGetCppuTypeIncludes( @@ -324,8 +319,6 @@ void CppuType::dumpFiles(OUString const & uri, CppuOptions const & options) { dumpFile(uri, name_, FileType::HDL, options); dumpFile(uri, name_, FileType::HPP, options); - if(options.isValid("-W"_ostr)) - dumpFile(uri, name_, FileType::EMBIND_CXX, options); } void CppuType::addLightGetCppuTypeIncludes( @@ -460,9 +453,6 @@ void CppuType::dumpFile( case FileType::HDL: dumpHdlFile(out, includes); break; - case FileType::EMBIND_CXX: - dumpEmbindCppFile(out); - break; } } catch (...) { out.close(); @@ -608,13 +598,6 @@ void CppuType::dumpHFileContent( out << " *);\n\n#endif\n"; } -void CppuType::dumpEmbindCppFile(FileStream &out) -{ - out << "#include <emscripten/bind.h>\n" - "#include <" << name_.replace('.', '/') << ".hpp>\n"; - dumpEmbindDeclaration(out); -} - void CppuType::dumpGetCppuType(FileStream & out) { if (name_ == "com.sun.star.uno.XInterface") { @@ -1136,14 +1119,10 @@ 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); @@ -1214,53 +1193,6 @@ void InterfaceType::dumpDeclaration(FileStream & out) out << "};\n\n"; } -void InterfaceType::dumpEmbindDeclaration(FileStream & out) -{ - // TODO: This is a temporary workaround that likely causes the Embind UNO - // bindings to leak memory. Reference counting and cloning mechanisms of - // Embind should be investigated to figure out what exactly we need here. - 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 << "\n::emscripten::class_<" << 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 << "\n::emscripten::class_<::css::uno::Reference<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">, ::emscripten::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, ::emscripten::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) { @@ -1312,31 +1244,6 @@ 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()) { @@ -1377,115 +1284,6 @@ 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 << "); }, ::emscripten::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); @@ -3748,8 +3546,6 @@ private: virtual void dumpFiles(OUString const & uri, CppuOptions const & options) override { dumpFile(uri, name_, FileType::HPP, options); - if(options.isValid("-W"_ostr)) - dumpFile(uri, name_, FileType::EMBIND_CXX, options); } }; diff --git a/codemaker/source/cppumaker/cpputype.hxx b/codemaker/source/cppumaker/cpputype.hxx index a6f8f9bfe8a3..c41cfa287646 100644 --- a/codemaker/source/cppumaker/cpputype.hxx +++ b/codemaker/source/cppumaker/cpputype.hxx @@ -32,8 +32,7 @@ namespace codemaker::cppumaker { enum class FileType { HDL, - HPP, - EMBIND_CXX + HPP }; } diff --git a/codemaker/source/cppumaker/dumputils.cxx b/codemaker/source/cppumaker/dumputils.cxx index 54867523b0d4..2a3e809e70f3 100644 --- a/codemaker/source/cppumaker/dumputils.cxx +++ b/codemaker/source/cppumaker/dumputils.cxx @@ -74,20 +74,6 @@ 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 c7021cba7408..24e5bae3bede 100644 --- a/codemaker/source/cppumaker/dumputils.hxx +++ b/codemaker/source/cppumaker/dumputils.hxx @@ -35,9 +35,6 @@ 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/configure.ac b/configure.ac index 78d9ec669c39..5e836e8c0ab4 100644 --- a/configure.ac +++ b/configure.ac @@ -1422,6 +1422,9 @@ if test "$_os" = "Emscripten"; then if test $EMSCRIPTEN_ERROR -ne 0; then AC_MSG_ERROR(["Please fix your EMSDK setup to build with Emscripten!"]) fi + + dnl Some build-side things are conditional on "EMSCRIPTEN in BUILD_TYPE_FOR_HOST": + BUILD_TYPE="$BUILD_TYPE EMSCRIPTEN" fi AC_SUBST(EMSDK_FILE_PACKAGER) diff --git a/include/codemaker/typemanager.hxx b/include/codemaker/typemanager.hxx index 3e6f67fe4b3f..7e312f3f8094 100644 --- a/include/codemaker/typemanager.hxx +++ b/include/codemaker/typemanager.hxx @@ -43,8 +43,13 @@ class TypeManager final : public salhelper::SimpleReferenceObject { public: TypeManager(); + rtl::Reference<unoidl::Manager> const & getManager() const { return manager_; } + void loadProvider(OUString const & uri, bool primary); + std::vector<rtl::Reference<unoidl::Provider>> const & getPrimaryProviders() const + { return primaryProviders_; } + bool foundAtPrimaryProvider(OUString const & name) const; codemaker::UnoType::Sort getSort( diff --git a/solenv/gbuild/UnoApi.mk b/solenv/gbuild/UnoApi.mk index 9ed314703f33..f396642901fe 100644 --- a/solenv/gbuild/UnoApi.mk +++ b/solenv/gbuild/UnoApi.mk @@ -36,8 +36,6 @@ 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 @@ -71,19 +69,4 @@ $(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 e4f92eb2d985..6eacbc66a0ab 100644 --- a/solenv/gbuild/UnoApiTarget.mk +++ b/solenv/gbuild/UnoApiTarget.mk @@ -152,7 +152,7 @@ $(call gb_UnoApiHeadersTarget_get_real_comprehensive_target,%) : \ $(call gb_Output_announce,$*,$(true),HPC,3) $(call gb_Trace_StartRange,$*,HPC) $(call gb_UnoApiHeadersTarget__command,$@,$*,$(call gb_UnoApiHeadersTarget_get_comprehensive_dir,$*), \ - -C $(if $(filter EMSCRIPTEN, $(OS)), -W)) + -C) $(call gb_Trace_EndRange,$*,HPC) $(call gb_UnoApiHeadersTarget_get_real_target,%) : \ @@ -237,19 +237,6 @@ 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 - -define gb_UnoApiEmbindTarget__add_embind -$(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/extensions/pre_BuildTools.mk b/solenv/gbuild/extensions/pre_BuildTools.mk index c31c809b9993..2522a4e7680c 100644 --- a/solenv/gbuild/extensions/pre_BuildTools.mk +++ b/solenv/gbuild/extensions/pre_BuildTools.mk @@ -16,6 +16,7 @@ gb_BUILD_TOOLS_executables = \ climaker \ cpp \ cppumaker \ + $(if $(filter EMSCRIPTEN,$(BUILD_TYPE_FOR_HOST)),embindmaker) \ gencoll_rule \ genconv_dict \ gendict \ diff --git a/static/CustomTarget_unoembind.mk b/static/CustomTarget_unoembind.mk new file mode 100644 index 000000000000..4bed6f585fa7 --- /dev/null +++ b/static/CustomTarget_unoembind.mk @@ -0,0 +1,22 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; 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/. +# + +$(eval $(call gb_CustomTarget_CustomTarget,static/unoembind)) + +$(eval $(call gb_CustomTarget_register_targets,static/unoembind, \ + bindings_uno.cxx \ +)) + +$(call gb_CustomTarget_get_workdir,static/unoembind)/bindings_uno.cxx: \ + $(call gb_Executable_get_target_for_build,embindmaker) $(call gb_UnoApi_get_target,udkapi) \ + $(call gb_UnoApi_get_target,offapi) + $(call gb_Executable_get_command,embindmaker) uno +$(call gb_UnoApi_get_target,udkapi) \ + +$(call gb_UnoApi_get_target,offapi) > $@ + +# vim: set noet sw=4 ts=4: diff --git a/static/Executable_embindmaker.mk b/static/Executable_embindmaker.mk new file mode 100644 index 000000000000..de160a15f3cb --- /dev/null +++ b/static/Executable_embindmaker.mk @@ -0,0 +1,26 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; 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/. +# + +$(eval $(call gb_Executable_Executable,embindmaker)) + +$(eval $(call gb_Executable_add_exception_objects,embindmaker, \ + static/source/embindmaker/embindmaker \ +)) + +$(eval $(call gb_Executable_use_libraries,embindmaker, \ + sal \ + salhelper \ + unoidl \ +)) + +$(eval $(call gb_Executable_use_static_libraries,embindmaker, \ + codemaker \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/static/Module_static.mk b/static/Module_static.mk index cd37748e1396..3b785c7ba4cb 100644 --- a/static/Module_static.mk +++ b/static/Module_static.mk @@ -18,6 +18,8 @@ $(eval $(call gb_Module_add_targets,static,\ ifeq (EMSCRIPTEN,$(OS)) $(eval $(call gb_Module_add_targets,static,\ CustomTarget_emscripten_fs_image \ + CustomTarget_unoembind \ + StaticLibrary_unoembind \ $(if $(ENABLE_QT5), \ CustomTarget_wasm-qt5-mandelbrot_moc \ Executable_wasm-qt5-mandelbrot \ @@ -27,4 +29,10 @@ endif endif +ifneq ($(filter EMSCRIPTEN,$(BUILD_TYPE_FOR_HOST)),) +$(eval $(call gb_Module_add_targets,static, \ + Executable_embindmaker \ +)) +endif + # vim: set noet sw=4 ts=4: diff --git a/static/StaticLibrary_unoembind.mk b/static/StaticLibrary_unoembind.mk new file mode 100644 index 000000000000..e2a2bfacd892 --- /dev/null +++ b/static/StaticLibrary_unoembind.mk @@ -0,0 +1,27 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; 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/. +# + +$(eval $(call gb_StaticLibrary_StaticLibrary,unoembind)) + +$(eval $(call gb_StaticLibrary_add_exception_objects,unoembind, \ + static/source/unoembindhelpers/PrimaryBindings \ +)) + +$(eval $(call gb_StaticLibrary_add_generated_exception_objects,unoembind, \ + CustomTarget/static/unoembind/bindings_uno \ +)) + +$(eval $(call gb_StaticLibrary_use_api,unoembind,\ + offapi \ + udkapi \ +)) + +$(call gb_StaticLibrary_get_target,unoembind): $(call gb_CustomTarget_get_target,static/unoembind) + +# vim: set noet sw=4 ts=4: diff --git a/static/source/embindmaker/embindmaker.cxx b/static/source/embindmaker/embindmaker.cxx new file mode 100644 index 000000000000..184d635da5c0 --- /dev/null +++ b/static/source/embindmaker/embindmaker.cxx @@ -0,0 +1,510 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstdlib> +#include <iostream> +#include <string_view> +#include <utility> +#include <vector> + +#include <codemaker/global.hxx> +#include <codemaker/typemanager.hxx> +#include <osl/file.hxx> +#include <osl/process.h> +#include <rtl/process.h> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/main.h> +#include <sal/types.h> +#include <unoidl/unoidl.hxx> + +namespace +{ +void badUsage() +{ + std::cerr + << "Usage:\n\n" + " embindmaker <prefix> <registries>\n\n" + "where each <registry> is '+' (primary) or ':' (secondary), followed by: either a\n" + "new- or legacy-format .rdb file, a single .idl file, or a root directory of an\n" + ".idl file tree. Embind code for all primary registries is written to stdout," + "with <prefix> used as part of the name passed to EMSCRIPTEN_BINDINGS.\n"; + std::exit(EXIT_FAILURE); +} + +std::pair<OUString, bool> parseRegistryArgument(sal_uInt32 argument) +{ + OUString arg; + rtl_getAppCommandArg(argument, &arg.pData); + bool primary; + if (arg.startsWith(u"+", &arg)) + { + primary = true; + } + else if (arg.startsWith(u":", &arg)) + { + primary = false; + } + else + { + std::cerr << "Bad registry argument \"" << arg << "\"\n"; + std::exit(EXIT_FAILURE); + } + OUString url; + auto const e1 = osl::FileBase::getFileURLFromSystemPath(arg, url); + if (e1 != osl::FileBase::E_None) + { + std::cerr << "Cannot convert \"" << arg << "\" to file URL, error code " << +e1 << "\n"; + std::exit(EXIT_FAILURE); + } + OUString cwd; + auto const e2 = osl_getProcessWorkingDir(&cwd.pData); + if (e2 != osl_Process_E_None) + { + std::cerr << "Cannot obtain working directory, error code " << +e2 << "\n"; + std::exit(EXIT_FAILURE); + } + OUString abs; + auto const e3 = osl::FileBase::getAbsoluteFileURL(cwd, url, abs); + if (e3 != osl::FileBase::E_None) + { + std::cerr << "Cannot make \"" << url << "\" into an absolute file URL, error code " << +e3 + << "\n"; + std::exit(EXIT_FAILURE); + } + return { abs, primary }; +} + +void scan(rtl::Reference<unoidl::MapCursor> const& cursor, std::u16string_view prefix, + std::vector<OUString>& interfaces) +{ + assert(cursor.is()); + for (;;) + { + OUString id; + auto const ent = cursor->getNext(&id); + if (!ent.is()) + { + break; + } + OUString name(prefix + id); + switch (ent->getSort()) + { + case unoidl::Entity::SORT_MODULE: + scan(static_cast<unoidl::ModuleEntity*>(ent.get())->createCursor(), + Concat2View(name + "."), interfaces); + break; + case unoidl::Entity::SORT_INTERFACE_TYPE: + interfaces.emplace_back(name); + break; + default: + break; + } + } +} + +OUString cppName(OUString const& name) { return "::" + name.replaceAll(u".", u"::"); } + +OUString jsName(OUString const& name) { return name.replace('.', '$'); } + +void dumpAttributes(OUString const& name, rtl::Reference<unoidl::InterfaceTypeEntity> const& entity) +{ + for (auto const& attr : entity->getDirectAttributes()) + { + std::cout << " .function(\"get" << attr.name << "\", &" << cppName(name) << "::get" + << attr.name << ")\n"; + if (!attr.readOnly) + { + std::cout << " .function(\"set" << attr.name << "\", &" << cppName(name) + << "::set" << attr.name << ")\n"; + } + } +} + +OUString resolveOuterTypedefs(rtl::Reference<TypeManager> const& manager, OUString const& name) +{ + for (OUString n(name);;) + { + rtl::Reference<unoidl::Entity> ent; + if (manager->getSort(n, &ent) != codemaker::UnoType::Sort::Typedef) + { + return n; + } + n = dynamic_cast<unoidl::TypedefEntity&>(*ent).getType(); + } +} + +OUString resolveAllTypedefs(rtl::Reference<TypeManager> const& manager, std::u16string_view name) +{ + sal_Int32 k1; + OUString n(b2u(codemaker::UnoType::decompose(u2b(name), &k1))); + for (;;) + { + rtl::Reference<unoidl::Entity> ent; + if (manager->getSort(n, &ent) != codemaker::UnoType::Sort::Typedef) + { + break; + } + sal_Int32 k2; + n = b2u(codemaker::UnoType::decompose( + u2b(static_cast<unoidl::TypedefEntity*>(ent.get())->getType()), &k2)); + k1 += k2; //TODO: overflow + } + OUStringBuffer b; + for (sal_Int32 i = 0; i != k1; ++i) + { + b.append("[]"); + } + b.append(n); + return b.makeStringAndClear(); +} + +bool passByReference(rtl::Reference<TypeManager> const& manager, OUString const& name) +{ + switch (manager->getSort(resolveOuterTypedefs(manager, name))) + { + case codemaker::UnoType::Sort::Boolean: + case codemaker::UnoType::Sort::Byte: + case codemaker::UnoType::Sort::Short: + case codemaker::UnoType::Sort::UnsignedShort: + case codemaker::UnoType::Sort::Long: + case codemaker::UnoType::Sort::UnsignedLong: + case codemaker::UnoType::Sort::Hyper: + case codemaker::UnoType::Sort::UnsignedHyper: + case codemaker::UnoType::Sort::Float: + case codemaker::UnoType::Sort::Double: + case codemaker::UnoType::Sort::Char: + case codemaker::UnoType::Sort::Enum: + return false; + case codemaker::UnoType::Sort::String: + case codemaker::UnoType::Sort::Type: + case codemaker::UnoType::Sort::Any: + case codemaker::UnoType::Sort::Sequence: + case codemaker::UnoType::Sort::PlainStruct: + case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct: + case codemaker::UnoType::Sort::Interface: + return true; + default: + throw CannotDumpException("unexpected entity \"" + name + + "\" in call to passByReference"); + } +} + +void dumpType(rtl::Reference<TypeManager> const& manager, std::u16string_view name, bool isConst) +{ + sal_Int32 k; + std::vector<OString> args; + OUString n( + b2u(codemaker::UnoType::decompose(u2b(resolveAllTypedefs(manager, name)), &k, &args))); + if (isConst) + { + std::cout << "const "; + } + for (sal_Int32 i = 0; i != k; ++i) + { + std::cout << "::css::uno::Sequence<"; + } + switch (manager->getSort(n)) + { + case codemaker::UnoType::Sort::Void: + std::cout << "void"; + break; + case codemaker::UnoType::Sort::Boolean: + std::cout << "::sal_Bool"; + break; + case codemaker::UnoType::Sort::Byte: + std::cout << "::sal_Int8"; + break; + case codemaker::UnoType::Sort::Short: + std::cout << "::sal_Int16"; + break; + case codemaker::UnoType::Sort::UnsignedShort: + std::cout << "::sal_uInt16"; + break; + case codemaker::UnoType::Sort::Long: + std::cout << "::sal_Int32"; + break; + case codemaker::UnoType::Sort::UnsignedLong: + std::cout << "::sal_uInt32"; + break; + case codemaker::UnoType::Sort::Hyper: + std::cout << "::sal_Int64"; + break; + case codemaker::UnoType::Sort::UnsignedHyper: + std::cout << "::sal_uInt64"; + break; + case codemaker::UnoType::Sort::Float: + std::cout << "float"; + break; + case codemaker::UnoType::Sort::Double: + std::cout << "double"; + break; + case codemaker::UnoType::Sort::Char: + std::cout << "::sal_Unicode"; + break; + case codemaker::UnoType::Sort::String: + std::cout << "::rtl::OUString"; + break; + case codemaker::UnoType::Sort::Type: + std::cout << "::css::uno::Type"; + break; + case codemaker::UnoType::Sort::Any: + std::cout << "::css::uno::Any"; + break; + case codemaker::UnoType::Sort::Enum: + case codemaker::UnoType::Sort::PlainStruct: + case codemaker::UnoType::Sort::Exception: + std::cout << cppName(n); + break; + case codemaker::UnoType::Sort::PolymorphicStructTemplate: + std::cout << cppName(n); + if (!args.empty()) + { + std::cout << "<"; + bool first = true; + for (auto const& arg : args) + { + if (first) + { + first = false; + } + else + { + std::cout << ", "; + } + dumpType(manager, b2u(arg), false); + } + std::cout << ">"; + } + break; + case codemaker::UnoType::Sort::Interface: + std::cout << "::css::uno::Reference<"; + std::cout << cppName(n); + std::cout << ">"; + break; + default: + throw CannotDumpException(OUString::Concat("unexpected entity \"") + name + + "\" in call to dumpType"); + } + for (sal_Int32 i = 0; i != k; ++i) + { + std::cout << ">"; + } +} + +void dumpParameters(rtl::Reference<TypeManager> const& manager, + unoidl::InterfaceTypeEntity::Method const& method, bool declarations) +{ + bool first = true; + for (auto const& param : method.parameters) + { + if (first) + { + first = false; + } + else + { + std::cout << ", "; + } + bool isConst; + bool isRef; + if (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN) + { + isConst = passByReference(manager, param.type); + isRef = isConst; + } + else + { + isConst = false; + isRef = true; + } + // For the embind wrapper, we define a pointer instead of a reference: + if (declarations) + { + dumpType(manager, param.type, isConst); + std::cout << " "; + } + if (isRef) + { + std::cout << "*"; + } + if (declarations) + { + std::cout << " "; + } + std::cout << param.name; + } +} + +void dumpWrapper(rtl::Reference<TypeManager> const& manager, OUString const& interfaceName, + unoidl::InterfaceTypeEntity::Method const& method, bool forReference) +{ + std::cout << " .function(\"" << method.name << "\", +[]("; + if (forReference) + { + std::cout << "::com::sun::star::uno::Reference<"; + } + std::cout << cppName(interfaceName); + if (forReference) + { + std::cout << ">"; + } + std::cout << " * the_self"; + if (!method.parameters.empty()) + { + std::cout << ", "; + } + dumpParameters(manager, method, true); + std::cout << ") { return the_self->"; + if (forReference) + { + std::cout << "get()->"; + } + std::cout << method.name << "("; + dumpParameters(manager, method, false); + std::cout << "); }, ::emscripten::allow_raw_pointers())\n"; +} + +void dumpMethods(rtl::Reference<TypeManager> const& manager, OUString const& name, + rtl::Reference<unoidl::InterfaceTypeEntity> const& entity, bool forReference) +{ + for (auto const& meth : entity->getDirectMethods()) + { + if (forReference) + { + dumpWrapper(manager, name, meth, true); + } + else if (std::any_of( + meth.parameters.begin(), meth.parameters.end(), [](auto const& parameter) { + return parameter.direction + != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN; + })) + { + dumpWrapper(manager, name, meth, false); + } + else + { + std::cout << " .function(\"" << meth.name << "\", &" << cppName(name) + << "::" << meth.name << ")\n"; + } + } +} +} + +SAL_IMPLEMENT_MAIN() +{ + try + { + auto const args = rtl_getAppCommandArgCount(); + if (args == 0) + { + badUsage(); + } + OUString prefix; + rtl_getAppCommandArg(0, &prefix.pData); + rtl::Reference<TypeManager> mgr(new TypeManager); + for (sal_uInt32 i = 1; i != args; ++i) + { + auto const & [ uri, primary ] = parseRegistryArgument(i); + try + { + mgr->loadProvider(uri, primary); + } + catch (unoidl::NoSuchFileException&) + { + std::cerr << "Input <" << uri << "> does not exist\n"; + std::exit(EXIT_FAILURE); + } + } + std::vector<OUString> interfaces; + for (auto const& prov : mgr->getPrimaryProviders()) + { + scan(prov->createRootCursor(), u"", interfaces); + } + std::cout << "#include <emscripten/bind.h>\n" + "#include <com/sun/star/uno/Any.hxx>\n" + "#include <com/sun/star/uno/Reference.hxx>\n"; + for (auto const& ifc : interfaces) + { + std::cout << "#include <" << ifc.replace('.', '/') << ".hpp>\n"; + } + std::cout << "\n" + "// TODO: This is a temporary workaround that likely causes the Embind UNO\n" + "// bindings to leak memory. Reference counting and cloning mechanisms of\n" + "// Embind should be investigated to figure out what exactly we need here:\n" + "namespace emscripten::internal {\n"; + for (auto const& ifc : interfaces) + { + std::cout << " template<> void raw_destructor<" << cppName(ifc) << ">(" + << cppName(ifc) << " *) {}\n"; + } + std::cout << "}\n\n" + "EMSCRIPTEN_BINDINGS(uno_bindings_" + << prefix << ") {\n"; + for (auto const& ifc : interfaces) + { + auto const ent = mgr->getManager()->findEntity(ifc); + assert(ent.is()); + assert(ent->getSort() == unoidl::Entity::SORT_INTERFACE_TYPE); + rtl::Reference const ifcEnt(static_cast<unoidl::InterfaceTypeEntity*>(ent.get())); + std::cout << " ::emscripten::class_<" << cppName(ifc) << ">(\"" << jsName(ifc) + << "\")\n"; + dumpAttributes(ifc, ifcEnt); + dumpMethods(mgr, ifc, ifcEnt, false); + std::cout << " ;\n" + " ::emscripten::class_<::com::sun::star::uno::Reference<" + << cppName(ifc) + << ">, ::emscripten::base<::com::sun::star::uno::BaseReference>>(\"" + << jsName(ifc) + << "Ref\")\n" + " .constructor<>()\n" + " .constructor<::com::sun::star::uno::BaseReference, " + "::com::sun::star::uno::UnoReference_Query>()\n" + " .function(\"is\", &::com::sun::star::uno::Reference<" + << cppName(ifc) + << ">::is)\n" + " .function(\"get\", &::com::sun::star::uno::Reference<" + << cppName(ifc) + << ">::get, ::emscripten::allow_raw_pointers())\n" + " .function(\"set\", " + "::emscripten::select_overload<bool(::com::sun::star::uno::Any const " + "&, " + "com::sun::star::uno::UnoReference_Query)>(&::com::sun::star::uno::" + "Reference<" + << cppName(ifc) << ">::set))\n"; + dumpAttributes(ifc, ifcEnt); + dumpMethods(mgr, ifc, ifcEnt, true); + std::cout << " ;\n"; + } + std::cout << "}\n"; + return EXIT_SUCCESS; + } + catch (unoidl::FileFormatException const& e) + { + std::cerr << "Bad input <" << e.getUri() << ">: " << e.getDetail() << "\n"; + std::exit(EXIT_FAILURE); + } + catch (CannotDumpException const& e) + { + std::cerr << "Failure: " << e.getMessage() << "\n"; + std::exit(EXIT_FAILURE); + } + catch (std::exception const& e) + { + std::cerr << "Failure: " << e.what() << "\n"; + std::exit(EXIT_FAILURE); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |