offapi/org/libreoffice/embindtest/XTest.idl |    3 
 static/source/embindmaker/embindmaker.cxx   |  160 ++++++++++++++++++++++++++--
 unotest/source/embindtest/embindtest.cxx    |   22 +++
 unotest/source/embindtest/embindtest.js     |   36 ++++++
 4 files changed, 212 insertions(+), 9 deletions(-)

New commits:
commit 3454a73a11722c45016c9b5aa0d95402fc1196f0
Author:     Stephan Bergmann <[email protected]>
AuthorDate: Fri Apr 12 08:57:44 2024 +0200
Commit:     Stephan Bergmann <[email protected]>
CommitDate: Fri Apr 12 20:52:54 2024 +0200

    Embind: support .implement()-based JS UNO objects
    
    Change-Id: I3a8bf5986b91b886547cfe57e49275f7c79ddc11
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166020
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <[email protected]>

diff --git a/offapi/org/libreoffice/embindtest/XTest.idl 
b/offapi/org/libreoffice/embindtest/XTest.idl
index b3b5237ce2b4..8817e94669fc 100644
--- a/offapi/org/libreoffice/embindtest/XTest.idl
+++ b/offapi/org/libreoffice/embindtest/XTest.idl
@@ -113,6 +113,9 @@ interface XTest {
     sequence<Struct> getSequenceStruct();
     boolean isSequenceStruct([in] sequence<Struct> value);
     void throwRuntimeException();
+    void passJob([in] com::sun::star::task::XJob object);
+    void passJobExecutor([in] com::sun::star::task::XJobExecutor object);
+    void passInterface([in] com::sun::star::uno::XInterface object);
 };
 
 }; }; };
diff --git a/static/source/embindmaker/embindmaker.cxx 
b/static/source/embindmaker/embindmaker.cxx
index 4241d10b1575..07214c289681 100644
--- a/static/source/embindmaker/embindmaker.cxx
+++ b/static/source/embindmaker/embindmaker.cxx
@@ -501,7 +501,7 @@ void dumpAttributes(std::ostream& out, 
rtl::Reference<TypeManager> const& manage
             }
             out << "->get" << attr.name << "(); }";
         }
-        out << ")
";
+        out << ", ::emscripten::pure_virtual())
";
         if (!attr.readOnly)
         {
             out << "        .function(\"set" << attr.name << "\", ";
@@ -530,7 +530,7 @@ void dumpAttributes(std::ostream& out, 
rtl::Reference<TypeManager> const& manage
                 }
                 out << "->set" << attr.name << "(the_value); }";
             }
-            out << ")
";
+            out << ", ::emscripten::pure_virtual())
";
         }
     }
 }
@@ -660,7 +660,7 @@ void dumpWrapper(std::ostream& out, 
rtl::Reference<TypeManager> const& manager,
     {
         out << ", ::emscripten::allow_raw_pointers()";
     }
-    out << ")
";
+    out << ", ::emscripten::pure_virtual())
";
 }
 
 void dumpMethods(std::ostream& out, rtl::Reference<TypeManager> const& manager,
@@ -676,18 +676,18 @@ void dumpMethods(std::ostream& out, 
rtl::Reference<TypeManager> const& manager,
         else
         {
             out << "        .function(\"" << meth.name << "\", &" << 
cppName(name)
-                << "::" << meth.name << ")
";
+                << "::" << meth.name << ", ::emscripten::pure_virtual())
";
         }
     }
 }
 
-rtl::Reference<unoidl::InterfaceTypeEntity> 
resolveBase(rtl::Reference<TypeManager> const& manager,
-                                                        OUString const& name)
+rtl::Reference<unoidl::InterfaceTypeEntity>
+resolveInterface(rtl::Reference<TypeManager> const& manager, OUString const& 
name)
 {
     auto const ent = manager->getManager()->findEntity(name);
     if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
     {
-        throw CannotDumpException("bad interface base \"" + name + "\"");
+        throw CannotDumpException("bad interface \"" + name + "\"");
     }
     return static_cast<unoidl::InterfaceTypeEntity*>(ent.get());
 }
@@ -695,7 +695,7 @@ rtl::Reference<unoidl::InterfaceTypeEntity> 
resolveBase(rtl::Reference<TypeManag
 void recordVisitedBases(rtl::Reference<TypeManager> const& manager, OUString 
const& name,
                         std::set<OUString>& visitedBases)
 {
-    auto const ent = resolveBase(manager, name);
+    auto const ent = resolveInterface(manager, name);
     for (auto const& base : ent->getDirectMandatoryBases())
     {
         if (visitedBases.insert(base.name).second)
@@ -709,7 +709,7 @@ void dumpBase(std::ostream& out, 
rtl::Reference<TypeManager> const& manager,
               OUString const& interface, OUString const& name, 
std::set<OUString>& visitedBases,
               std::list<OUString> const& baseTrail)
 {
-    auto const ent = resolveBase(manager, name);
+    auto const ent = resolveInterface(manager, name);
     for (auto const& base : ent->getDirectMandatoryBases())
     {
         if (visitedBases.insert(base.name).second)
@@ -723,6 +723,126 @@ void dumpBase(std::ostream& out, 
rtl::Reference<TypeManager> const& manager,
     dumpMethods(out, manager, interface, ent, baseTrail);
 }
 
+void dumpWrapperClassMembers(std::ostream& out, rtl::Reference<TypeManager> 
const& manager,
+                             OUString const& interface, OUString const& name,
+                             std::set<OUString>& visitedBases)
+{
+    auto const ent = resolveInterface(manager, name);
+    for (auto const& base : ent->getDirectMandatoryBases())
+    {
+        if (visitedBases.insert(base.name).second)
+        {
+            dumpWrapperClassMembers(out, manager, interface, base.name, 
visitedBases);
+        }
+    }
+    for (auto const& attr : ent->getDirectAttributes())
+    {
+        out << "    ";
+        dumpType(out, manager, attr.type);
+        out << " get" << attr.name << "() override { return call<";
+        dumpType(out, manager, attr.type);
+        out << ">(\"get" << attr.name << "\"); }
";
+        if (!attr.readOnly)
+        {
+            out << "    void set" << attr.name << "(";
+            dumpType(out, manager, attr.type);
+            switch (manager->getSort(resolveOuterTypedefs(manager, attr.type)))
+            {
+                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:
+                    break;
+                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:
+                    out << " const &";
+                    break;
+                default:
+                    throw CannotDumpException("unexpected entity \"" + 
attr.type
+                                              + "\" as attribute type");
+            }
+            out << " the_value) override { return call<void>(\"set" << 
attr.name
+                << "\", the_value); }
";
+        }
+    }
+    for (auto const& meth : ent->getDirectMethods())
+    {
+        out << "    ";
+        dumpType(out, manager, meth.returnType);
+        out << " " << meth.name << "(";
+        bool first = true;
+        for (auto const& param : meth.parameters)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                out << ", ";
+            }
+            dumpType(out, manager, param.type);
+            if (param.direction == 
unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
+            {
+                switch (manager->getSort(resolveOuterTypedefs(manager, 
param.type)))
+                {
+                    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:
+                        break;
+                    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:
+                        out << " const &";
+                        break;
+                    default:
+                        throw CannotDumpException("unexpected entity \"" + 
param.type
+                                                  + "\" as parameter type");
+                }
+            }
+            else
+            {
+                out << " &";
+            }
+            out << " " << param.name;
+        }
+        out << ") override { return call<";
+        dumpType(out, manager, meth.returnType);
+        out << ">(\"" << meth.name << "\"";
+        for (auto const& param : meth.parameters)
+        {
+            out << ", " << param.name;
+        }
+        out << "); }
";
+    }
+}
+
 void dumpRegisterFunctionProlog(std::ostream& out, unsigned long long& counter)
 {
     out << "static void __attribute__((noinline)) register" << counter << "() {
";
@@ -926,6 +1046,21 @@ SAL_IMPLEMENT_MAIN()
             assert(ent.is());
             assert(ent->getSort() == unoidl::Entity::SORT_INTERFACE_TYPE);
             rtl::Reference const 
ifcEnt(static_cast<unoidl::InterfaceTypeEntity*>(ent.get()));
+            {
+                auto i = ifc.lastIndexOf('.');
+                auto j = i + 1;
+                if (i == -1)
+                {
+                    i = 0;
+                }
+                cppOut << "namespace the_wrappers" << cppName(ifc.copy(0, i)) 
<< " {
"
+                       << "struct " << ifc.copy(j) << " final: public 
::emscripten::wrapper<"
+                       << cppName(ifc) << "> {
"
+                       << "    EMSCRIPTEN_WRAPPER(" << ifc.copy(j) << ");
";
+                std::set<OUString> visitedBases;
+                dumpWrapperClassMembers(cppOut, mgr, ifc, ifc, visitedBases);
+                cppOut << "};
}
";
+            }
             dumpRegisterFunctionProlog(cppOut, n);
             cppOut << "    ::emscripten::class_<" << cppName(ifc);
             //TODO: Embind only supports single inheritance, so use that 
support at least for a UNO
@@ -937,10 +1072,17 @@ SAL_IMPLEMENT_MAIN()
                 cppOut << ", ::emscripten::base<" << cppName(bases[0].name) << 
">";
             }
             cppOut << ">(\"uno_Type_" << jsName(ifc)
+                   << "\")
"
+                      "        .allow_subclass<the_wrappers"
+                   << cppName(ifc) << ">(\"uno_Wrapper_" << jsName(ifc)
                    << "\")
"
                       "        .smart_ptr<::com::sun::star::uno::Reference<"
                    << cppName(ifc) << ">>(\"uno_Reference_" << jsName(ifc)
                    << "\")
"
+                      "        .class_function(\"reference\", +[]("
+                   << cppName(ifc)
+                   << " * the_interface) { return 
::com::sun::star::uno::Reference(the_interface); "
+                      "}, ::emscripten::allow_raw_pointers())
"
                       "        "
                       
".constructor(+[](::com::sun::star::uno::Reference<::com::sun::star::uno::"
                       "XInterface> const & the_object) { return 
::com::sun::star::uno::Reference<"
diff --git a/unotest/source/embindtest/embindtest.cxx 
b/unotest/source/embindtest/embindtest.cxx
index 39ba4c839dae..bcbb6d0ec870 100644
--- a/unotest/source/embindtest/embindtest.cxx
+++ b/unotest/source/embindtest/embindtest.cxx
@@ -9,11 +9,14 @@
 
 #include <sal/config.h>
 
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
 #include <com/sun/star/uno/Any.hxx>
 #include <com/sun/star/uno/Reference.hxx>
 #include <com/sun/star/uno/RuntimeException.hpp>
 #include <com/sun/star/uno/Sequence.hxx>
 #include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
 #include <cppu/unotype.hxx>
 #include <cppuhelper/implbase.hxx>
 #include <cppuhelper/weak.hxx>
@@ -502,6 +505,25 @@ class Test : public 
cppu::WeakImplHelper<org::libreoffice::embindtest::XTest>
     {
         throw css::uno::RuntimeException(u"test"_ustr);
     }
+
+    void SAL_CALL passJob(css::uno::Reference<css::task::XJob> const& object) 
override
+    {
+        object->execute({ { u"name"_ustr, css::uno::Any(u"job"_ustr) } });
+    }
+
+    void SAL_CALL
+    passJobExecutor(css::uno::Reference<css::task::XJobExecutor> const& 
object) override
+    {
+        object->trigger(u"executor"_ustr);
+    }
+
+    void SAL_CALL passInterface(css::uno::Reference<css::uno::XInterface> 
const& object) override
+    {
+        css::uno::Reference<css::task::XJob>(object, css::uno::UNO_QUERY_THROW)
+            ->execute({ { u"name"_ustr, css::uno::Any(u"queried job"_ustr) } 
});
+        css::uno::Reference<css::task::XJobExecutor>(object, 
css::uno::UNO_QUERY_THROW)
+            ->trigger(u"queried executor"_ustr);
+    }
 };
 }
 
diff --git a/unotest/source/embindtest/embindtest.js 
b/unotest/source/embindtest/embindtest.js
index 8c0ee0138828..749488cfa567 100644
--- a/unotest/source/embindtest/embindtest.js
+++ b/unotest/source/embindtest/embindtest.js
@@ -544,6 +544,42 @@ Module.addOnPostRun(function() {
         console.assert(e.message === undefined); //TODO
         //TODO: console.assert(e.Message.startsWith('test'));
     }
+    const obj = {
+        refcount: 0,
+        queryInterface(type) {
+            if (type == 'com.sun.star.uno.XInterface') {
+                return new Module.uno_Any(type, 
css.uno.XInterface.reference(this.implXInterface));
+            } else if (type == 'com.sun.star.task.XJob') {
+                return new Module.uno_Any(type, 
css.task.XJob.reference(this.implXJob));
+            } else if (type == 'com.sun.star.task.XJobExecutor') {
+                return new Module.uno_Any(
+                    type, 
css.task.XJobExecutor.reference(this.implXJobExecutor));
+            } else {
+                return new Module.uno_Any(Module.uno_Type.Void(), undefined);
+            }
+        },
+        acquire() { ++this.refcount; },
+        release() {
+            if (--this.refcount === 0) {
+                this.implXInterface.delete();
+                this.implXJob.delete();
+                this.implXJobExecutor.delete();
+            }
+        },
+        execute(args) {
+            console.log('Hello ' + args.get(0).Value.get());
+            return new Module.uno_Any(Module.uno_Type.Void(), undefined);
+        },
+        trigger(event) { console.log('Ola ' + event); }
+    };
+    obj.implXInterface = css.uno.XInterface.implement(obj);
+    obj.implXJob = css.task.XJob.implement(obj);
+    obj.implXJobExecutor = css.task.XJobExecutor.implement(obj);
+    obj.acquire();
+    test.passJob(css.task.XJob.reference(obj.implXJob));
+    
test.passJobExecutor(css.task.XJobExecutor.reference(obj.implXJobExecutor));
+    test.passInterface(css.uno.XInterface.reference(obj.implXInterface));
+    obj.release();
 });
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to