Repository.mk                              |    1 
 RepositoryModule_build.mk                  |    1 
 codemaker/source/cppumaker/cppuoptions.cxx |   18 -
 codemaker/source/cppumaker/cpputype.cxx    |  204 -----------
 codemaker/source/cppumaker/cpputype.hxx    |    3 
 codemaker/source/cppumaker/dumputils.cxx   |   14 
 codemaker/source/cppumaker/dumputils.hxx   |    3 
 configure.ac                               |    3 
 include/codemaker/typemanager.hxx          |    5 
 solenv/gbuild/UnoApi.mk                    |   17 
 solenv/gbuild/UnoApiTarget.mk              |   15 
 solenv/gbuild/extensions/pre_BuildTools.mk |    1 
 static/CustomTarget_unoembind.mk           |   22 +
 static/Executable_embindmaker.mk           |   26 +
 static/Module_static.mk                    |    8 
 static/StaticLibrary_unoembind.mk          |   27 +
 static/source/embindmaker/embindmaker.cxx  |  510 +++++++++++++++++++++++++++++
 17 files changed, 606 insertions(+), 272 deletions(-)

New commits:
commit b2ade3e63e34556cad8157e25f8c75d29e79537d
Author:     Stephan Bergmann <stephan.bergm...@allotropia.de>
AuthorDate: Sun Jan 21 14:50:15 2024 +0100
Commit:     Stephan Bergmann <stephan.bergm...@allotropia.de>
CommitDate: Sun Jan 21 15:51:39 2024 +0100

    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.bergm...@allotropia.de>

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] != '
-                    {
-                        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] == '
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 << " *);

#endif
";
 }
 
-void CppuType::dumpEmbindCppFile(FileStream &out)
-{
-    out << "#include <emscripten/bind.h>
"
-           "#include <" << name_.replace('.', '/') << ".hpp>
";
-    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 << "};

";
 }
 
-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 { 
"
-           "template<> void raw_destructor<" << 
codemaker::cpp::scopedCppName(u2b(name_))
-        << ">(" << codemaker::cpp::scopedCppName(u2b(name_)) << "*){}
"
-           "}}
";
-
-    out << "EMSCRIPTEN_BINDINGS(uno_bindings_";
-    codemaker::cppumaker::dumpTypeFullWithDecorator(out, name_, u"_");
-    codemaker::cppumaker::dumpTypeIdentifier(out, name_);
-    out << ") {
";
-
-    out << "
::emscripten::class_<" << codemaker::cpp::scopedCppName(u2b(name_)) << ">(\"";
-    codemaker::cppumaker::dumpTypeFullWithDecorator(out, name_, u"$");
-    codemaker::cppumaker::dumpTypeIdentifier(out, name_);
-    out << "\")
";
-
-    inc();
-    // dump bindings for attributes and methods.
-    dumpEmbindAttributeBindings(out);
-    dumpEmbindMethodBindings(out);
-    out << indent() << ";
";
-    dec();
-
-    // dump reference bindings.
-    out << "
::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\")
";
-    inc();
-    out << indent() << ".constructor<>()
"
-        << indent() << ".constructor<::css::uno::BaseReference, 
::css::uno::UnoReference_Query>()
"
-        << indent() << ".function(\"is\", &::css::uno::Reference<" << 
codemaker::cpp::scopedCppName(u2b(name_)) << ">::is)
"
-        << indent() << ".function(\"get\", &::css::uno::Reference<" << 
codemaker::cpp::scopedCppName(u2b(name_)) << ">::get, 
::emscripten::allow_raw_pointers())
"
-        << 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))
";
-    dumpEmbindAttributeBindings(out);
-    dumpEmbindMethodBindings(out, true);
-    out << indent() << ";
";
-    dec();
-
-    out << "}
";
-}
-
-
 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
";
-    }
-    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 << ")
";
-        if (!attr.readOnly)
-        {
-            out << indent();
-            out << ".function(\"";
-            out << "set" << attr.name << "\", &" << 
codemaker::cpp::scopedCppName(u2b(name_))
-                << "::set" << attr.name << ")
";
-        }
-    }
-}
-
 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() )
";
-}
-
-void InterfaceType::dumpEmbindMethodBindings(FileStream & out, bool 
bDumpForReference) const
-{
-    if (!entity_->getDirectMethods().empty()) {
-        out << indent() << "// Bindings for methods
";
-    }
-    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 << ")
";
-    }
-}
-
-
-
 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:

"
+           "  embindmaker <prefix> <registries>

"
+           "where each <registry> is '+' (primary) or ':' (secondary), 
followed by: either a
"
+           "new- or legacy-format .rdb file, a single .idl file, or a root 
directory of an
"
+           ".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.
";
+    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 << "\"
";
+        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 << "
";
+        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 << "
";
+        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
+                  << "
";
+        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 << ")
";
+        if (!attr.readOnly)
+        {
+            std::cout << "        .function(\"set" << attr.name << "\", &" << 
cppName(name)
+                      << "::set" << attr.name << ")
";
+        }
+    }
+}
+
+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())
";
+}
+
+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 << ")
";
+        }
+    }
+}
+}
+
+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
";
+                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>
"
+                     "#include <com/sun/star/uno/Any.hxx>
"
+                     "#include <com/sun/star/uno/Reference.hxx>
";
+        for (auto const& ifc : interfaces)
+        {
+            std::cout << "#include <" << ifc.replace('.', '/') << ".hpp>
";
+        }
+        std::cout << "
"
+                     "// 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:
"
+                     "namespace emscripten::internal {
";
+        for (auto const& ifc : interfaces)
+        {
+            std::cout << "    template<> void raw_destructor<" << cppName(ifc) 
<< ">("
+                      << cppName(ifc) << " *) {}
";
+        }
+        std::cout << "}

"
+                     "EMSCRIPTEN_BINDINGS(uno_bindings_"
+                  << prefix << ") {
";
+        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)
+                      << "\")
";
+            dumpAttributes(ifc, ifcEnt);
+            dumpMethods(mgr, ifc, ifcEnt, false);
+            std::cout << "        ;
"
+                         "    
::emscripten::class_<::com::sun::star::uno::Reference<"
+                      << cppName(ifc)
+                      << ">, 
::emscripten::base<::com::sun::star::uno::BaseReference>>(\""
+                      << jsName(ifc)
+                      << "Ref\")
"
+                         "        .constructor<>()
"
+                         "        
.constructor<::com::sun::star::uno::BaseReference, "
+                         "::com::sun::star::uno::UnoReference_Query>()
"
+                         "        .function(\"is\", 
&::com::sun::star::uno::Reference<"
+                      << cppName(ifc)
+                      << ">::is)
"
+                         "        .function(\"get\", 
&::com::sun::star::uno::Reference<"
+                      << cppName(ifc)
+                      << ">::get, ::emscripten::allow_raw_pointers())
"
+                         "        .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))
";
+            dumpAttributes(ifc, ifcEnt);
+            dumpMethods(mgr, ifc, ifcEnt, true);
+            std::cout << "        ;
";
+        }
+        std::cout << "}
";
+        return EXIT_SUCCESS;
+    }
+    catch (unoidl::FileFormatException const& e)
+    {
+        std::cerr << "Bad input <" << e.getUri() << ">: " << e.getDetail() << "
";
+        std::exit(EXIT_FAILURE);
+    }
+    catch (CannotDumpException const& e)
+    {
+        std::cerr << "Failure: " << e.getMessage() << "
";
+        std::exit(EXIT_FAILURE);
+    }
+    catch (std::exception const& e)
+    {
+        std::cerr << "Failure: " << e.what() << "
";
+        std::exit(EXIT_FAILURE);
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to