codemaker/source/cppumaker/cppuoptions.cxx         |   18 +
 codemaker/source/cppumaker/cpputype.cxx            |  250 +++++++++++++++++++--
 codemaker/source/cppumaker/cpputype.hxx            |    9 
 codemaker/source/cppumaker/dumputils.cxx           |   14 +
 codemaker/source/cppumaker/dumputils.hxx           |    3 
 codemaker/source/cppumaker/includes.cxx            |   19 -
 codemaker/source/cppumaker/includes.hxx            |    5 
 desktop/Executable_soffice_bin.mk                  |    5 
 solenv/gbuild/UnoApi.mk                            |   17 +
 solenv/gbuild/UnoApiTarget.mk                      |   18 +
 solenv/gbuild/platform/unxgcc.mk                   |   14 +
 static/README.wasm.md                              |   39 +++
 static/source/unoembindhelpers/PrimaryBindings.cxx |  121 ++++++++++
 13 files changed, 505 insertions(+), 27 deletions(-)

New commits:
commit 841f898574affb526a516224d7c3db9b137ea62b
Author:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
AuthorDate: Mon Aug 7 14:41:32 2023 +0300
Commit:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
CommitDate: Mon Sep 18 19:59:57 2023 +0200

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

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

Reply via email to