bridges/CustomTarget_gcc3_wasm.mk                |   12 
 bridges/Library_cpp_uno.mk                       |    4 
 bridges/inc/wasm/generated.hxx                   |    6 
 bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx     |  366 ++++++++--------
 bridges/source/cpp_uno/gcc3_wasm/uno2cpp.cxx     |    2 
 offapi/UnoApi_offapi.mk                          |    1 
 offapi/org/libreoffice/embindtest/BridgeTest.idl |   16 
 static/source/wasmcallgen/wasmcallgen.cxx        |  510 ++++++++++++++++++++---
 unotest/source/embindtest/embindtest.component   |    5 
 unotest/source/embindtest/embindtest.cxx         |  247 +++++++++++
 unotest/source/embindtest/embindtest.js          |   10 
 11 files changed, 929 insertions(+), 250 deletions(-)

New commits:
commit 74829f2a64b6989ddf1d126a6fd2f2d2e9000d3b
Author:     Stephan Bergmann <stephan.bergm...@allotropia.de>
AuthorDate: Thu Jul 11 12:53:01 2024 +0200
Commit:     Stephan Bergmann <stephan.bergm...@allotropia.de>
CommitDate: Thu Jul 11 17:16:17 2024 +0200

    Fully implement the Wasm UNO bridge cpp2uno direction
    
    ...after 875997c8962da7f6b72950b201186a19b3c20d3a "Properly implement
    cppu::throwException for Emscripten" had implemented only those parts that 
were
    absolutely necessary for that exception throwing.  As detailed in the commit
    message there, wasmcallgen has been extended to additionally generate all 
the
    required vtable slot call trampoline code (which cannot be generated on the 
fly
    for Wasm, as would be done for other platforms).  Consequently, some of the
    "callvirtualfunction"-centric file names have been changed to "generated" 
as the
    output of wasmcallgen is now more general.  (And wasmcallgen itself should 
also
    be renamed, in a follow-up commit.  And when adding to the wasmcallgen code
    here, some existing parts of its implementation have been cleaned up, too.)
    
    There is no direct way to test this half of the Wasm UNO bridge directly 
from
    unotest/source/embindtest/embindtest.js, so a new
    org.libreoffice.embindtest.BridgeTest singleton has been added, which 
triggers
    new test code in unotest/source/embindtest/embindtest.cxx that tests the 
bridge
    in a way similar to testtools' bridgetest machinery.
    
    Change-Id: I521a1d6c2160aedc814f7603b0b99861e5fbd1eb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170374
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <stephan.bergm...@allotropia.de>

diff --git a/bridges/CustomTarget_gcc3_wasm.mk 
b/bridges/CustomTarget_gcc3_wasm.mk
index 7452fc477e4f..0393b1b8193b 100644
--- a/bridges/CustomTarget_gcc3_wasm.mk
+++ b/bridges/CustomTarget_gcc3_wasm.mk
@@ -10,19 +10,19 @@
 $(eval $(call gb_CustomTarget_CustomTarget,bridges/gcc3_wasm))
 
 $(eval $(call gb_CustomTarget_register_targets,bridges/gcc3_wasm, \
-    callvirtualfunction-wrapper.cxx \
-    callvirtualfunction-impls.s \
+    generated-cxx.cxx \
+    generated-asm.s \
     exports \
 ))
 
-$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/callvirtualfunction-impls.s \
-$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/callvirtualfunction-wrapper.cxx \
+$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/generated-asm.s \
+$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/generated-cxx.cxx \
 $(gb_CustomTarget_workdir)/bridges/gcc3_wasm/exports: \
         $(call gb_Executable_get_target_for_build,wasmcallgen) $(call 
gb_UnoApi_get_target,udkapi) \
         $(call gb_UnoApi_get_target,offapi)
        $(call gb_Executable_get_command,wasmcallgen) \
-        
$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/callvirtualfunction-wrapper.cxx \
-        
$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/callvirtualfunction-impls.s \
+        $(gb_CustomTarget_workdir)/bridges/gcc3_wasm/generated-cxx.cxx \
+        $(gb_CustomTarget_workdir)/bridges/gcc3_wasm/generated-asm.s \
         $(gb_CustomTarget_workdir)/bridges/gcc3_wasm/exports \
         +$(call gb_UnoApi_get_target,udkapi) +$(call 
gb_UnoApi_get_target,offapi)
 
diff --git a/bridges/Library_cpp_uno.mk b/bridges/Library_cpp_uno.mk
index 5ee781e7aa8c..94f17f0149b0 100644
--- a/bridges/Library_cpp_uno.mk
+++ b/bridges/Library_cpp_uno.mk
@@ -85,10 +85,10 @@ else ifeq ($(OS),EMSCRIPTEN)
 bridges_SELECTED_BRIDGE := gcc3_wasm
 bridge_exception_objects := abi cpp2uno uno2cpp
 $(eval $(call gb_Library_add_generated_asmobjects,$(CPPU_ENV)_uno, \
-    CustomTarget/bridges/gcc3_wasm/callvirtualfunction-impls \
+    CustomTarget/bridges/gcc3_wasm/generated-asm \
 ))
 $(eval $(call gb_Library_add_generated_exception_objects,$(CPPU_ENV)_uno, \
-    CustomTarget/bridges/gcc3_wasm/callvirtualfunction-wrapper \
+    CustomTarget/bridges/gcc3_wasm/generated-cxx \
 ))
 $(eval $(call gb_Library_use_static_libraries,$(CPPU_ENV)_uno, \
     emscriptencxxabi \
diff --git a/bridges/inc/wasm/callvirtualfunction.hxx 
b/bridges/inc/wasm/generated.hxx
similarity index 72%
rename from bridges/inc/wasm/callvirtualfunction.hxx
rename to bridges/inc/wasm/generated.hxx
index 23b446cd45b3..b37fc589c1bf 100644
--- a/bridges/inc/wasm/callvirtualfunction.hxx
+++ b/bridges/inc/wasm/generated.hxx
@@ -12,10 +12,16 @@
 #include <sal/config.h>
 
 #include <string_view>
+#include <vector>
 
 #include <sal/types.h>
 
 void callVirtualFunction(std::string_view signature, sal_uInt32 target, 
sal_uInt64 const* arguments,
                          void* returnValue);
 
+sal_uInt64 vtableCall(sal_Int32 functionIndex, sal_Int32 vtableOffset, 
unsigned thisPtr,
+                      std::vector<sal_uInt64> const& arguments, unsigned 
indirectRet);
+
+void const* getVtableSlotFunction(std::string_view signature);
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx 
b/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx
index 921e2f23b551..000a364e0c40 100644
--- a/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx
+++ b/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx
@@ -9,6 +9,7 @@
 
 #include <sal/config.h>
 
+#include <cstring>
 #include <typeinfo>
 
 #include <emscripten.h>
@@ -24,6 +25,7 @@
 #include <cppinterfaceproxy.hxx>
 #include <types.hxx>
 #include <vtablefactory.hxx>
+#include <wasm/generated.hxx>
 
 #include "abi.hxx"
 
@@ -176,19 +178,21 @@ void raiseException(uno_Any* any, uno_Mapping* mapping)
     __cxxabiv1::__cxa_throw(exc, rtti, deleteException);
 }
 
-void call(bridges::cpp_uno::shared::CppInterfaceProxy* proxy,
-          css::uno::TypeDescription const& description,
-          typelib_TypeDescriptionReference* returnType, sal_Int32 count,
-          typelib_MethodParameter* parameters, std::vector<sal_uInt64> 
arguments,
-          unsigned /*indirectRect*/)
+sal_uInt64 call(bridges::cpp_uno::shared::CppInterfaceProxy* proxy,
+                css::uno::TypeDescription const& description,
+                typelib_TypeDescriptionReference* returnType, sal_Int32 count,
+                typelib_MethodParameter* parameters, std::vector<sal_uInt64> 
arguments,
+                unsigned indirectRet)
 {
     typelib_TypeDescription* rtd = nullptr;
     if (returnType != nullptr)
     {
         TYPELIB_DANGER_GET(&rtd, returnType);
     }
-    void* retin = nullptr;
-
+    auto const retConv = rtd != nullptr && 
bridges::cpp_uno::shared::relatesToInterfaceType(rtd);
+    void* retin = reinterpret_cast<void*>(indirectRet) != nullptr && !retConv
+                      ? reinterpret_cast<void*>(indirectRet)
+                      : rtd == nullptr ? nullptr : alloca(rtd->nSize);
     void** args = static_cast<void**>(alloca(count * sizeof(void*)));
     void** cppArgs = static_cast<void**>(alloca(count * sizeof(void*)));
     typelib_TypeDescription** argtds
@@ -198,7 +202,8 @@ void call(bridges::cpp_uno::shared::CppInterfaceProxy* 
proxy,
     {
         if (!parameters[i].bOut && 
bridges::cpp_uno::shared::isSimpleType(parameters[i].pTypeRef))
         {
-            assert(false);
+            args[i] = arguments.data() + i;
+            argtds[i] = nullptr;
         }
         else
         {
@@ -246,11 +251,54 @@ void call(bridges::cpp_uno::shared::CppInterfaceProxy* 
proxy,
         }
         raiseException(&exc, proxy->getBridge()->getUno2Cpp());
     }
-    assert(false);
+    for (sal_Int32 i = 0; i != count; ++i)
+    {
+        if (argtds[i] != nullptr)
+        {
+            if (parameters[i].bOut)
+            {
+                uno_destructData(cppArgs[i], argtds[i],
+                                 
reinterpret_cast<uno_ReleaseFunc>(css::uno::cpp_release));
+                uno_copyAndConvertData(cppArgs[i], args[i], argtds[i],
+                                       proxy->getBridge()->getUno2Cpp());
+            }
+            uno_destructData(args[i], argtds[i], nullptr);
+            TYPELIB_DANGER_RELEASE(argtds[i]);
+        }
+    }
+    sal_uInt64 retVal = {};
+    if (retConv)
+    {
+        uno_copyAndConvertData(reinterpret_cast<void*>(indirectRet), retin, 
rtd,
+                               proxy->getBridge()->getUno2Cpp());
+        uno_destructData(retin, rtd, nullptr);
+    }
+    else if (rtd != nullptr)
+    {
+        // Make sure to sign-extend the return value for small signed integer 
types:
+        switch (rtd->eTypeClass)
+        {
+            case typelib_TypeClass_BYTE:
+                retVal = static_cast<int>(*static_cast<sal_Int8 
const*>(retin));
+                break;
+            case typelib_TypeClass_SHORT:
+                retVal = static_cast<int>(*static_cast<sal_Int16 
const*>(retin));
+                break;
+            default:
+                std::memcpy(&retVal, retin, rtd->nSize);
+                break;
+        }
+    }
+    if (rtd != nullptr)
+    {
+        TYPELIB_DANGER_RELEASE(rtd);
+    }
+    return retVal;
+}
 }
 
-void vtableCall(sal_Int32 functionIndex, sal_Int32 vtableOffset, unsigned 
thisPtr,
-                std::vector<sal_uInt64> const& arguments, unsigned indirectRet)
+sal_uInt64 vtableCall(sal_Int32 functionIndex, sal_Int32 vtableOffset, 
unsigned thisPtr,
+                      std::vector<sal_uInt64> const& arguments, unsigned 
indirectRet)
 {
     bridges::cpp_uno::shared::CppInterfaceProxy* proxy
         = bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy(
@@ -265,10 +313,10 @@ void vtableCall(sal_Int32 functionIndex, sal_Int32 
vtableOffset, unsigned thisPt
             if (type->pMapMemberIndexToFunctionIndex[pos] == functionIndex)
             {
                 // Getter:
-                call(proxy, desc,
-                     
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(desc.get())
-                         ->pAttributeTypeRef,
-                     0, nullptr, arguments, indirectRet);
+                return call(proxy, desc,
+                            
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(desc.get())
+                                ->pAttributeTypeRef,
+                            0, nullptr, arguments, indirectRet);
             }
             else
             {
@@ -278,7 +326,7 @@ void vtableCall(sal_Int32 functionIndex, sal_Int32 
vtableOffset, unsigned thisPt
                         
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(desc.get())
                             ->pAttributeTypeRef,
                         true, false };
-                call(proxy, desc, nullptr, 1, &param, arguments, indirectRet);
+                return call(proxy, desc, nullptr, 1, &param, arguments, 
indirectRet);
             }
             break;
         case typelib_TypeClass_INTERFACE_METHOD:
@@ -286,10 +334,10 @@ void vtableCall(sal_Int32 functionIndex, sal_Int32 
vtableOffset, unsigned thisPt
             {
                 case 1:
                     proxy->acquireProxy();
-                    break;
+                    return {};
                 case 2:
                     proxy->releaseProxy();
-                    break;
+                    return {};
                 case 0:
                 {
                     typelib_TypeDescription* td = nullptr;
@@ -309,78 +357,135 @@ void vtableCall(sal_Int32 functionIndex, sal_Int32 
vtableOffset, unsigned thisPt
                                 
reinterpret_cast<uno_AcquireFunc>(css::uno::cpp_acquire));
                             ifc->release();
                             TYPELIB_DANGER_RELEASE(td);
-                            break;
+                            return {};
                         }
                         TYPELIB_DANGER_RELEASE(td);
                     }
                 }
                     [[fallthrough]];
                 default:
-                    call(proxy, desc,
-                         
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(desc.get())
-                             ->pReturnTypeRef,
-                         
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(desc.get())
-                             ->nParams,
-                         
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(desc.get())
-                             ->pParams,
-                         arguments, indirectRet);
-                    break;
+                    return call(
+                        proxy, desc,
+                        
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(desc.get())
+                            ->pReturnTypeRef,
+                        
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(desc.get())
+                            ->nParams,
+                        
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(desc.get())
+                            ->pParams,
+                        arguments, indirectRet);
             }
-            break;
         default:
             O3TL_UNREACHABLE;
     }
 }
 
-extern "C" void vtableSlotFunction_0_0Ii(unsigned indirectRet, unsigned 
thisPtr, unsigned arg1)
-{
-    vtableCall(0, 0, thisPtr, { sal_uInt64(arg1) }, indirectRet);
-}
-
-extern "C" void vtableSlotFunction_1_0v(unsigned thisPtr)
+namespace
 {
-    vtableCall(1, 0, thisPtr, {}, reinterpret_cast<unsigned>(nullptr));
-}
-
-extern "C" void vtableSlotFunction_2_0v(unsigned thisPtr)
+void appendSignatureOffsets(OStringBuffer& buffer, sal_Int32 functionOffset, 
sal_Int32 vtableOffset)
 {
-    vtableCall(2, 0, thisPtr, {}, reinterpret_cast<unsigned>(nullptr));
+    buffer.append(OString::number(functionOffset) + "_" + 
OString::number(vtableOffset));
 }
 
-extern "C" void vtableSlotFunction_3_0vi(unsigned thisPtr, unsigned arg1)
+void appendSignatureReturnType(OStringBuffer& buffer, 
typelib_TypeDescriptionReference* type)
 {
-    vtableCall(3, 0, thisPtr, { arg1 }, reinterpret_cast<unsigned>(nullptr));
-}
-
-extern "C" void vtableSlotFunction_4_0v(unsigned thisPtr)
-{
-    vtableCall(4, 0, thisPtr, {}, reinterpret_cast<unsigned>(nullptr));
+    switch (type->eTypeClass)
+    {
+        case typelib_TypeClass_VOID:
+            buffer.append('v');
+            break;
+        case typelib_TypeClass_BOOLEAN:
+        case typelib_TypeClass_BYTE:
+        case typelib_TypeClass_SHORT:
+        case typelib_TypeClass_UNSIGNED_SHORT:
+        case typelib_TypeClass_LONG:
+        case typelib_TypeClass_UNSIGNED_LONG:
+        case typelib_TypeClass_CHAR:
+        case typelib_TypeClass_ENUM:
+            buffer.append('i');
+            break;
+        case typelib_TypeClass_HYPER:
+        case typelib_TypeClass_UNSIGNED_HYPER:
+            buffer.append('j');
+            break;
+        case typelib_TypeClass_FLOAT:
+            buffer.append('f');
+            break;
+        case typelib_TypeClass_DOUBLE:
+            buffer.append('d');
+            break;
+        case typelib_TypeClass_STRING:
+        case typelib_TypeClass_TYPE:
+        case typelib_TypeClass_ANY:
+        case typelib_TypeClass_SEQUENCE:
+        case typelib_TypeClass_INTERFACE:
+            buffer.append('I');
+            break;
+        case typelib_TypeClass_STRUCT:
+        {
+            css::uno::TypeDescription td(type);
+            switch (abi_wasm::getKind(
+                reinterpret_cast<typelib_CompoundTypeDescription 
const*>(td.get())))
+            {
+                case abi_wasm::StructKind::Empty:
+                    break;
+                case abi_wasm::StructKind::I32:
+                    buffer.append('i');
+                    break;
+                case abi_wasm::StructKind::I64:
+                    buffer.append('j');
+                    break;
+                case abi_wasm::StructKind::F32:
+                    buffer.append('f');
+                    break;
+                case abi_wasm::StructKind::F64:
+                    buffer.append('d');
+                    break;
+                case abi_wasm::StructKind::General:
+                    buffer.append('I');
+                    break;
+            }
+            break;
+        }
+        default:
+            O3TL_UNREACHABLE;
+    }
 }
 
-void const* getVtableSlotFunction(std::string_view signature)
+void appendSignatureParameter(OStringBuffer& buffer, bool out,
+                              typelib_TypeDescriptionReference const* type)
 {
-    if (signature == "0_0Ii")
-    {
-        return reinterpret_cast<void const*>(vtableSlotFunction_0_0Ii);
-    }
-    if (signature == "1_0v")
+    if (!out && bridges::cpp_uno::shared::isSimpleType(type))
     {
-        return reinterpret_cast<void const*>(vtableSlotFunction_1_0v);
-    }
-    if (signature == "2_0v")
-    {
-        return reinterpret_cast<void const*>(vtableSlotFunction_2_0v);
-    }
-    if (signature == "3_0vi")
-    {
-        return reinterpret_cast<void const*>(vtableSlotFunction_3_0vi);
+        switch (type->eTypeClass)
+        {
+            case typelib_TypeClass_BOOLEAN:
+            case typelib_TypeClass_BYTE:
+            case typelib_TypeClass_SHORT:
+            case typelib_TypeClass_UNSIGNED_SHORT:
+            case typelib_TypeClass_LONG:
+            case typelib_TypeClass_ENUM:
+            case typelib_TypeClass_UNSIGNED_LONG:
+            case typelib_TypeClass_CHAR:
+                buffer.append('i');
+                break;
+            case typelib_TypeClass_HYPER:
+            case typelib_TypeClass_UNSIGNED_HYPER:
+                buffer.append('j');
+                break;
+            case typelib_TypeClass_FLOAT:
+                buffer.append('f');
+                break;
+            case typelib_TypeClass_DOUBLE:
+                buffer.append('d');
+                break;
+            default:
+                O3TL_UNREACHABLE;
+        }
     }
-    if (signature == "4_0v")
+    else
     {
-        return reinterpret_cast<void const*>(vtableSlotFunction_4_0v);
+        buffer.append('i');
     }
-    throw css::uno::RuntimeException("Wasm bridge cannot fill virtual function 
slot with signature "
-                                     + OUString::fromUtf8(signature));
 }
 }
 
@@ -396,132 +501,37 @@ unsigned char* VtableFactory::addLocalFunctions(Slot** 
slots, unsigned char* cod
         switch (type->ppMembers[i]->eTypeClass)
         {
             case typelib_TypeClass_INTERFACE_ATTRIBUTE:
-                (s++)->fn = nullptr; //TODO
+            {
+                auto const atd = 
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(
+                    css::uno::TypeDescription(type->ppMembers[i]).get());
+                OStringBuffer sigGetter;
+                appendSignatureOffsets(sigGetter, functionOffset, 
vtableOffset);
+                appendSignatureReturnType(sigGetter, atd->pAttributeTypeRef);
+                (s++)->fn = getVtableSlotFunction(sigGetter);
                 ++functionOffset;
                 if 
(!reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(
                          css::uno::TypeDescription(type->ppMembers[i]).get())
                          ->bReadOnly)
                 {
-                    (s++)->fn = nullptr; //TODO
+                    OStringBuffer sigSetter;
+                    appendSignatureOffsets(sigSetter, functionOffset, 
vtableOffset);
+                    sigSetter.append('v');
+                    appendSignatureParameter(sigSetter, false, 
atd->pAttributeTypeRef);
+                    (s++)->fn = getVtableSlotFunction(sigSetter);
                     ++functionOffset;
                 }
                 break;
+            }
             case typelib_TypeClass_INTERFACE_METHOD:
             {
-                OStringBuffer sig;
-                sig.append(OString::number(functionOffset) + "_" + 
OString::number(vtableOffset));
                 auto const mtd = 
reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(
                     css::uno::TypeDescription(type->ppMembers[i]).get());
-                switch (mtd->pReturnTypeRef->eTypeClass)
-                {
-                    case typelib_TypeClass_VOID:
-                        sig.append('v');
-                        break;
-                    case typelib_TypeClass_BOOLEAN:
-                    case typelib_TypeClass_BYTE:
-                    case typelib_TypeClass_SHORT:
-                    case typelib_TypeClass_UNSIGNED_SHORT:
-                    case typelib_TypeClass_LONG:
-                    case typelib_TypeClass_UNSIGNED_LONG:
-                    case typelib_TypeClass_CHAR:
-                    case typelib_TypeClass_ENUM:
-                        sig.append('i');
-                        break;
-                    case typelib_TypeClass_HYPER:
-                    case typelib_TypeClass_UNSIGNED_HYPER:
-                        sig.append('j');
-                        break;
-                    case typelib_TypeClass_FLOAT:
-                        sig.append('f');
-                        break;
-                    case typelib_TypeClass_DOUBLE:
-                        sig.append('d');
-                        break;
-                    case typelib_TypeClass_STRING:
-                    case typelib_TypeClass_TYPE:
-                    case typelib_TypeClass_ANY:
-                    case typelib_TypeClass_SEQUENCE:
-                    case typelib_TypeClass_INTERFACE:
-                        sig.append('I');
-                        break;
-                    case typelib_TypeClass_STRUCT:
-                    {
-                        css::uno::TypeDescription rtd(mtd->pReturnTypeRef);
-                        switch (abi_wasm::getKind(
-                            reinterpret_cast<typelib_CompoundTypeDescription 
const*>(rtd.get())))
-                        {
-                            case abi_wasm::StructKind::Empty:
-                                break;
-                            case abi_wasm::StructKind::I32:
-                                sig.append('i');
-                                break;
-                            case abi_wasm::StructKind::I64:
-                                sig.append('j');
-                                break;
-                            case abi_wasm::StructKind::F32:
-                                sig.append('f');
-                                break;
-                            case abi_wasm::StructKind::F64:
-                                sig.append('d');
-                                break;
-                            case abi_wasm::StructKind::General:
-                                sig.append('I');
-                                break;
-                        }
-                        break;
-                    }
-                    default:
-                        O3TL_UNREACHABLE;
-                }
+                OStringBuffer sig;
+                appendSignatureOffsets(sig, functionOffset, vtableOffset);
+                appendSignatureReturnType(sig, mtd->pReturnTypeRef);
                 for (sal_Int32 j = 0; j != mtd->nParams; ++j)
                 {
-                    if (!mtd->pParams[j].bOut
-                        && 
bridges::cpp_uno::shared::isSimpleType(mtd->pParams[j].pTypeRef))
-                    {
-                        switch (mtd->pParams[j].pTypeRef->eTypeClass)
-                        {
-                            case typelib_TypeClass_BOOLEAN:
-                                sig.append('i');
-                                break;
-                            case typelib_TypeClass_BYTE:
-                                sig.append('i');
-                                break;
-                            case typelib_TypeClass_SHORT:
-                                sig.append('i');
-                                break;
-                            case typelib_TypeClass_UNSIGNED_SHORT:
-                                sig.append('i');
-                                break;
-                            case typelib_TypeClass_LONG:
-                            case typelib_TypeClass_ENUM:
-                                sig.append('i');
-                                break;
-                            case typelib_TypeClass_UNSIGNED_LONG:
-                                sig.append('i');
-                                break;
-                            case typelib_TypeClass_HYPER:
-                                sig.append('j');
-                                break;
-                            case typelib_TypeClass_UNSIGNED_HYPER:
-                                sig.append('j');
-                                break;
-                            case typelib_TypeClass_FLOAT:
-                                sig.append('f');
-                                break;
-                            case typelib_TypeClass_DOUBLE:
-                                sig.append('d');
-                                break;
-                            case typelib_TypeClass_CHAR:
-                                sig.append('i');
-                                break;
-                            default:
-                                O3TL_UNREACHABLE;
-                        }
-                    }
-                    else
-                    {
-                        sig.append('i');
-                    }
+                    appendSignatureParameter(sig, mtd->pParams[j].bOut, 
mtd->pParams[j].pTypeRef);
                 }
                 (s++)->fn = getVtableSlotFunction(sig);
                 ++functionOffset;
diff --git a/bridges/source/cpp_uno/gcc3_wasm/uno2cpp.cxx 
b/bridges/source/cpp_uno/gcc3_wasm/uno2cpp.cxx
index f3467c497460..f3b257dc1d9b 100644
--- a/bridges/source/cpp_uno/gcc3_wasm/uno2cpp.cxx
+++ b/bridges/source/cpp_uno/gcc3_wasm/uno2cpp.cxx
@@ -33,7 +33,7 @@
 #include <types.hxx>
 #include <unointerfaceproxy.hxx>
 #include <vtables.hxx>
-#include <wasm/callvirtualfunction.hxx>
+#include <wasm/generated.hxx>
 
 #include "abi.hxx"
 
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index fe01fc172776..096a55e69d38 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -4449,6 +4449,7 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,offapi,org/libreoffice/embindtest, \
     XTest \
 ))
 $(eval $(call gb_UnoApi_add_idlfiles_nohdl,offapi,org/libreoffice/embindtest, \
+    BridgeTest \
     Test \
 ))
 endif
diff --git a/offapi/org/libreoffice/embindtest/BridgeTest.idl 
b/offapi/org/libreoffice/embindtest/BridgeTest.idl
new file mode 100644
index 000000000000..ed34bd56ea4c
--- /dev/null
+++ b/offapi/org/libreoffice/embindtest/BridgeTest.idl
@@ -0,0 +1,16 @@
+/* -*- 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/.
+ */
+
+module org { module libreoffice { module embindtest {
+
+singleton BridgeTest: com::sun::star::task::XJob;
+
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/static/source/wasmcallgen/wasmcallgen.cxx 
b/static/source/wasmcallgen/wasmcallgen.cxx
index 2c61a48c6b25..0c7b93c76f39 100644
--- a/static/source/wasmcallgen/wasmcallgen.cxx
+++ b/static/source/wasmcallgen/wasmcallgen.cxx
@@ -25,9 +25,12 @@
 #include <codemaker/typemanager.hxx>
 #include <codemaker/unotype.hxx>
 #include <o3tl/safeint.hxx>
+#include <o3tl/temporary.hxx>
+#include <o3tl/unreachable.hxx>
 #include <osl/file.hxx>
 #include <osl/process.h>
 #include <osl/thread.h>
+#include <rtl/character.hxx>
 #include <rtl/process.h>
 #include <rtl/ref.hxx>
 #include <rtl/strbuf.hxx>
@@ -49,9 +52,9 @@ namespace
            "  wasmcallgen <cpp-output> <asm-output> <exp-output> <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.  For all primary registries, Wasm UNO bridge 
callvirtualfunction
"
-           "code is written to <cpp-output>/<asm-output>, and to-be-exported 
exception RTTI
"
-           "symbols are written to <exp-output>.
";
+           ".idl file tree.  For all primary registries, Wasm UNO bridge code 
is written to
"
+           "<cpp-output>/<asm-output>, and to-be-exported exception RTTI 
symbols are written
"
+           "to <exp-output>.
";
     std::exit(EXIT_FAILURE);
 }
 
@@ -245,14 +248,13 @@ StructKind getKind(rtl::Reference<TypeManager> const& 
manager, std::u16string_vi
     }
 }
 
-OString computeSignature(rtl::Reference<TypeManager> const& manager,
-                         unoidl::InterfaceTypeEntity::Method const& method)
+void appendCallSignatureReturnType(OStringBuffer& buffer,
+                                   rtl::Reference<TypeManager> const& manager, 
OUString const& type)
 {
-    OStringBuffer buf;
-    switch (manager->getSort(resolveAllTypedefs(manager, method.returnType)))
+    switch (manager->getSort(resolveAllTypedefs(manager, type)))
     {
         case codemaker::UnoType::Sort::Void:
-            buf.append('v');
+            buffer.append('v');
             break;
         case codemaker::UnoType::Sort::Boolean:
         case codemaker::UnoType::Sort::Byte:
@@ -262,101 +264,328 @@ OString computeSignature(rtl::Reference<TypeManager> 
const& manager,
         case codemaker::UnoType::Sort::UnsignedLong:
         case codemaker::UnoType::Sort::Char:
         case codemaker::UnoType::Sort::Enum:
-            buf.append('i');
+            buffer.append('i');
             break;
         case codemaker::UnoType::Sort::Hyper:
         case codemaker::UnoType::Sort::UnsignedHyper:
-            buf.append('j');
+            buffer.append('j');
             break;
         case codemaker::UnoType::Sort::Float:
-            buf.append('f');
+            buffer.append('f');
             break;
         case codemaker::UnoType::Sort::Double:
-            buf.append('d');
+            buffer.append('d');
             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::Interface:
-            buf.append("vi");
+            buffer.append("vi");
             break;
         case codemaker::UnoType::Sort::PlainStruct:
         case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
         {
-            switch (getKind(manager, method.returnType))
+            switch (getKind(manager, type))
             {
                 case StructKind::Empty:
                     break;
                 case StructKind::I32:
-                    buf.append('i');
+                    buffer.append('i');
                     break;
                 case StructKind::I64:
-                    buf.append('j');
+                    buffer.append('j');
                     break;
                 case StructKind::F32:
-                    buf.append('f');
+                    buffer.append('f');
                     break;
                 case StructKind::F64:
-                    buf.append('d');
+                    buffer.append('d');
                     break;
                 case StructKind::General:
-                    buf.append("vi");
+                    buffer.append("vi");
                     break;
             }
             break;
         }
         default:
-            throw CannotDumpException("unexpected entity \"" + 
method.returnType
-                                      + "\" in call to computeSignature");
+            throw CannotDumpException("unexpected entity \"" + type
+                                      + "\" in call to 
appendCallSignatureReturnType");
+    }
+}
+
+void appendCallSignatureParameter(
+    OStringBuffer& buffer, rtl::Reference<TypeManager> const& manager,
+    unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction, 
OUString const& type)
+{
+    if (direction == 
unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
+    {
+        switch (manager->getSort(resolveAllTypedefs(manager, 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::Char:
+            case codemaker::UnoType::Sort::Enum:
+            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:
+                buffer.append('i');
+                break;
+            case codemaker::UnoType::Sort::Hyper:
+            case codemaker::UnoType::Sort::UnsignedHyper:
+                buffer.append('j');
+                break;
+            case codemaker::UnoType::Sort::Float:
+                buffer.append('f');
+                break;
+            case codemaker::UnoType::Sort::Double:
+                buffer.append('d');
+                break;
+            default:
+                throw CannotDumpException("unexpected entity \"" + type
+                                          + "\" in call to 
appendCallSignatureParameter");
+        }
     }
+    else
+    {
+        buffer.append('i');
+    }
+}
+
+OString computeGetterCallSignature(rtl::Reference<TypeManager> const& manager,
+                                   unoidl::InterfaceTypeEntity::Attribute 
const& attribute)
+{
+    OStringBuffer buf;
+    appendCallSignatureReturnType(buf, manager, attribute.type);
+    buf.append('i');
+    return buf.makeStringAndClear();
+}
+
+OString computeSetterCallSignature(rtl::Reference<TypeManager> const& manager,
+                                   unoidl::InterfaceTypeEntity::Attribute 
const& attribute)
+{
+    OStringBuffer buf("vi");
+    appendCallSignatureParameter(
+        buf, manager, 
unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN, attribute.type);
+    return buf.makeStringAndClear();
+}
+
+OString computeMethodCallSignature(rtl::Reference<TypeManager> const& manager,
+                                   unoidl::InterfaceTypeEntity::Method const& 
method)
+{
+    OStringBuffer buf;
+    appendCallSignatureReturnType(buf, manager, method.returnType);
     buf.append('i');
     for (auto const& param : method.parameters)
     {
-        if (param.direction == 
unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
+        appendCallSignatureParameter(buf, manager, param.direction, 
param.type);
+    }
+    return buf.makeStringAndClear();
+}
+
+void appendSlotSignatureOffsets(OStringBuffer& buffer, sal_Int32 
functionOffset,
+                                sal_Int32 vtableOffset)
+{
+    buffer.append(OString::number(functionOffset) + "_" + 
OString::number(vtableOffset));
+}
+
+void appendSlotSignatureReturnType(OStringBuffer& buffer,
+                                   rtl::Reference<TypeManager> const& manager, 
OUString const& type)
+{
+    switch (manager->getSort(resolveAllTypedefs(manager, type)))
+    {
+        case codemaker::UnoType::Sort::Void:
+            buffer.append('v');
+            break;
+        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::Char:
+        case codemaker::UnoType::Sort::Enum:
+            buffer.append('i');
+            break;
+        case codemaker::UnoType::Sort::Hyper:
+        case codemaker::UnoType::Sort::UnsignedHyper:
+            buffer.append('j');
+            break;
+        case codemaker::UnoType::Sort::Float:
+            buffer.append('f');
+            break;
+        case codemaker::UnoType::Sort::Double:
+            buffer.append('d');
+            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::Interface:
+            buffer.append('I');
+            break;
+        case codemaker::UnoType::Sort::PlainStruct:
+        case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
         {
-            switch (manager->getSort(resolveAllTypedefs(manager, param.type)))
+            switch (getKind(manager, 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::Char:
-                case codemaker::UnoType::Sort::Enum:
-                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:
-                    buf.append('i');
+                case StructKind::Empty:
                     break;
-                case codemaker::UnoType::Sort::Hyper:
-                case codemaker::UnoType::Sort::UnsignedHyper:
-                    buf.append('j');
+                case StructKind::I32:
+                    buffer.append('i');
                     break;
-                case codemaker::UnoType::Sort::Float:
-                    buf.append('f');
+                case StructKind::I64:
+                    buffer.append('j');
                     break;
-                case codemaker::UnoType::Sort::Double:
-                    buf.append('d');
+                case StructKind::F32:
+                    buffer.append('f');
+                    break;
+                case StructKind::F64:
+                    buffer.append('d');
+                    break;
+                case StructKind::General:
+                    buffer.append('I');
                     break;
-                default:
-                    throw CannotDumpException("unexpected entity \"" + 
param.type
-                                              + "\" in call to 
computeSignature");
             }
+            break;
         }
-        else
+        default:
+            throw CannotDumpException("unexpected entity \"" + type
+                                      + "\" in call to 
appendSlotSignatureReturnType");
+    }
+}
+
+void appendSlotSignatureParameter(
+    OStringBuffer& buffer, rtl::Reference<TypeManager> const& manager,
+    unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction, 
OUString const& type)
+{
+    if (direction == 
unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
+    {
+        switch (manager->getSort(resolveAllTypedefs(manager, type)))
         {
-            buf.append('i');
+            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::Char:
+            case codemaker::UnoType::Sort::Enum:
+            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:
+                buffer.append('i');
+                break;
+            case codemaker::UnoType::Sort::Hyper:
+            case codemaker::UnoType::Sort::UnsignedHyper:
+                buffer.append('j');
+                break;
+            case codemaker::UnoType::Sort::Float:
+                buffer.append('f');
+                break;
+            case codemaker::UnoType::Sort::Double:
+                buffer.append('d');
+                break;
+            default:
+                throw CannotDumpException("unexpected entity \"" + type
+                                          + "\" in call to 
appendSlotSignatureParameter");
         }
     }
+    else
+    {
+        buffer.append('i');
+    }
+}
+
+OString computeGetterSlotSignature(rtl::Reference<TypeManager> const& manager,
+                                   unoidl::InterfaceTypeEntity::Attribute 
const& attribute,
+                                   sal_Int32 functionOffset, sal_Int32 
vtableOffset)
+{
+    OStringBuffer buf;
+    appendSlotSignatureOffsets(buf, functionOffset, vtableOffset);
+    appendSlotSignatureReturnType(buf, manager, attribute.type);
+    return buf.makeStringAndClear();
+}
+
+OString computeSetterSlotSignature(rtl::Reference<TypeManager> const& manager,
+                                   unoidl::InterfaceTypeEntity::Attribute 
const& attribute,
+                                   sal_Int32 functionOffset, sal_Int32 
vtableOffset)
+{
+    OStringBuffer buf;
+    appendSlotSignatureOffsets(buf, functionOffset, vtableOffset);
+    buf.append('v');
+    appendSlotSignatureParameter(
+        buf, manager, 
unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN, attribute.type);
     return buf.makeStringAndClear();
 }
 
+OString computeMethodSlotSignature(rtl::Reference<TypeManager> const& manager,
+                                   unoidl::InterfaceTypeEntity::Method const& 
method,
+                                   sal_Int32 functionOffset, sal_Int32 
vtableOffset)
+{
+    OStringBuffer buf;
+    appendSlotSignatureOffsets(buf, functionOffset, vtableOffset);
+    appendSlotSignatureReturnType(buf, manager, method.returnType);
+    for (auto const& param : method.parameters)
+    {
+        appendSlotSignatureParameter(buf, manager, param.direction, 
param.type);
+    }
+    return buf.makeStringAndClear();
+}
+
+void computeSlotSignatures(rtl::Reference<TypeManager> const& manager,
+                           unoidl::InterfaceTypeEntity const& interface, 
sal_Int32& functionOffset,
+                           sal_Int32& vtableOffset, std::set<OString>& 
slotSignatures)
+{
+    auto const orgVtableOffset = vtableOffset;
+    auto firstBase = true;
+    for (auto const& base : interface.getDirectMandatoryBases())
+    {
+        auto const ent = manager->getManager()->findEntity(base.name);
+        if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+        {
+            throw CannotDumpException("unexpected base type \"" + base.name
+                                      + "\" in call to computeSlotSignatures");
+        }
+        sal_Int32 freshFunctionOffset = 0;
+        computeSlotSignatures(manager, 
*static_cast<unoidl::InterfaceTypeEntity const*>(ent.get()),
+                              firstBase ? functionOffset : 
freshFunctionOffset, vtableOffset,
+                              slotSignatures);
+        vtableOffset += 4;
+        firstBase = false;
+    }
+    for (auto const& attr : interface.getDirectAttributes())
+    {
+        slotSignatures.insert(
+            computeGetterSlotSignature(manager, attr, functionOffset, 
orgVtableOffset));
+        ++functionOffset;
+        if (!attr.readOnly)
+        {
+            slotSignatures.insert(
+                computeSetterSlotSignature(manager, attr, functionOffset, 
orgVtableOffset));
+            ++functionOffset;
+        }
+    }
+    for (auto const& meth : interface.getDirectMethods())
+    {
+        slotSignatures.insert(
+            computeMethodSlotSignature(manager, meth, functionOffset, 
orgVtableOffset));
+        ++functionOffset;
+    }
+}
+
 void appendRttiSymbolSegment(OStringBuffer& buffer, OUString const& id)
 {
     OString s(OUStringToOString(id, RTL_TEXTENCODING_ASCII_US));
@@ -384,7 +613,8 @@ OString computeRttiSymbol(std::vector<OUString> const& 
path, OUString const& id)
 
 void scan(rtl::Reference<TypeManager> const& manager,
           rtl::Reference<unoidl::MapCursor> const& cursor, 
std::vector<OUString>& path,
-          std::set<OString>& signatures, std::set<OString>& rttis)
+          std::set<OString>& callSignatures, std::set<OString>& slotSignatures,
+          std::set<OString>& rttis)
 {
     assert(cursor.is());
     for (;;)
@@ -400,19 +630,31 @@ void scan(rtl::Reference<TypeManager> const& manager,
             case unoidl::Entity::SORT_MODULE:
                 path.push_back(id);
                 scan(manager, 
static_cast<unoidl::ModuleEntity*>(ent.get())->createCursor(), path,
-                     signatures, rttis);
+                     callSignatures, slotSignatures, rttis);
                 path.pop_back();
                 break;
             case unoidl::Entity::SORT_EXCEPTION_TYPE:
                 rttis.insert(computeRttiSymbol(path, id));
                 break;
             case unoidl::Entity::SORT_INTERFACE_TYPE:
-                for (auto const& meth :
-                     static_cast<unoidl::InterfaceTypeEntity 
const*>(ent.get())->getDirectMethods())
+            {
+                auto const ite = static_cast<unoidl::InterfaceTypeEntity 
const*>(ent.get());
+                for (auto const& attr : ite->getDirectAttributes())
                 {
-                    signatures.insert(computeSignature(manager, meth));
+                    callSignatures.insert(computeGetterCallSignature(manager, 
attr));
+                    if (!attr.readOnly)
+                    {
+                        
callSignatures.insert(computeSetterCallSignature(manager, attr));
+                    }
+                }
+                for (auto const& meth : ite->getDirectMethods())
+                {
+                    callSignatures.insert(computeMethodCallSignature(manager, 
meth));
                 }
+                computeSlotSignatures(manager, *ite, 
o3tl::temporary<sal_Int32>(0),
+                                      o3tl::temporary<sal_Int32>(0), 
slotSignatures);
                 break;
+            }
             default:
                 break;
         }
@@ -447,11 +689,12 @@ SAL_IMPLEMENT_MAIN()
             }
         }
         std::vector<OUString> path;
-        std::set<OString> signatures;
+        std::set<OString> callSignatures;
+        std::set<OString> slotSignatures;
         std::set<OString> rttis;
         for (auto const& prov : mgr->getPrimaryProviders())
         {
-            scan(mgr, prov->createRootCursor(), path, signatures, rttis);
+            scan(mgr, prov->createRootCursor(), path, callSignatures, 
slotSignatures, rttis);
         }
         std::ofstream cppOut(cppPathname, std::ios_base::out | 
std::ios_base::trunc);
         if (!cppOut)
@@ -460,18 +703,20 @@ SAL_IMPLEMENT_MAIN()
             std::exit(EXIT_FAILURE);
         }
         cppOut << "#include <sal/config.h>
"
+                  "#include <bit>
"
                   "#include <string_view>
"
                   "#include <com/sun/star/uno/RuntimeException.hpp>
"
                   "#include <rtl/ustring.hxx>
"
-                  "#include <wasm/callvirtualfunction.hxx>
";
-        for (auto const& sig : signatures)
+                  "#include <sal/types.h>
"
+                  "#include <wasm/generated.hxx>
";
+        for (auto const& sig : callSignatures)
         {
             cppOut << "extern \"C\" void callVirtualFunction_" << sig
                    << "(sal_uInt32 target, sal_uInt64 const * arguments, void 
* returnValue);
";
         }
         cppOut << "void callVirtualFunction(std::string_view signature, 
sal_uInt32 target, "
                   "sal_uInt64 const * arguments, void * returnValue) {
";
-        for (auto const& sig : signatures)
+        for (auto const& sig : callSignatures)
         {
             cppOut << "    if (signature == \"" << sig << "\") {
"
                    << "        callVirtualFunction_" << sig << "(target, 
arguments, returnValue);
"
@@ -481,6 +726,145 @@ SAL_IMPLEMENT_MAIN()
         cppOut << "    throw css::uno::RuntimeException(\"Wasm bridge cannot 
call virtual function "
                   "with signature \" + OUString::fromUtf8(signature));
"
                   "}
";
+        if (!slotSignatures.empty())
+        {
+            cppOut << "namespace {
";
+        }
+        for (auto const& sig : slotSignatures)
+        {
+            auto const i1 = sig.indexOf('_');
+            assert(i1 != -1);
+            sal_Int32 i2 = i1 + 1;
+            for (;; ++i2)
+            {
+                assert(i2 < sig.getLength());
+                if (!rtl::isAsciiDigit(static_cast<unsigned char>(sig[i2])))
+                {
+                    break;
+                }
+            }
+            assert(i2 != i1);
+            cppOut << "extern \"C\" ";
+            switch (sig[i2])
+            {
+                case 'd':
+                    cppOut << "double";
+                    break;
+                case 'f':
+                    cppOut << "float";
+                    break;
+                case 'i':
+                    cppOut << "unsigned";
+                    break;
+                case 'j':
+                    cppOut << "unsigned long long";
+                    break;
+                default:
+                    cppOut << "void";
+                    break;
+            }
+            cppOut << " vtableSlotFunction_" << sig << "(";
+            if (sig[i2] == 'I')
+            {
+                cppOut << "unsigned indirectRet, ";
+            }
+            cppOut << "unsigned thisPtr";
+            for (sal_Int32 i = i2 + 1; i != sig.getLength(); ++i)
+            {
+                cppOut << ", ";
+                switch (sig[i])
+                {
+                    case 'd':
+                        cppOut << "double";
+                        break;
+                    case 'f':
+                        cppOut << "float";
+                        break;
+                    case 'i':
+                        cppOut << "unsigned";
+                        break;
+                    case 'j':
+                        cppOut << "unsigned long long";
+                        break;
+                    default:
+                        O3TL_UNREACHABLE;
+                }
+                cppOut << " arg" << (i - i2);
+            }
+            cppOut << ") { ";
+            switch (sig[i2])
+            {
+                case 'd':
+                    cppOut << "return 
std::bit_cast<double>(static_cast<unsigned long long>(";
+                    break;
+                case 'f':
+                    cppOut << "return 
std::bit_cast<float>(static_cast<unsigned>(";
+                    break;
+                case 'i':
+                    cppOut << "return static_cast<unsigned>(";
+                    break;
+                case 'j':
+                    cppOut << "return static_cast<unsigned long long>(";
+                    break;
+            }
+            cppOut << "vtableCall(" << sig.subView(0, i1) << ", "
+                   << sig.subView(i1 + 1, i2 - (i1 + 1)) << ", thisPtr, {";
+            for (sal_Int32 i = i2 + 1; i != sig.getLength(); ++i)
+            {
+                if (i != i2 + 1)
+                {
+                    cppOut << ", ";
+                }
+                cppOut << "sal_uInt64(";
+                switch (sig[i])
+                {
+                    case 'd':
+                        cppOut << "std::bit_cast<unsigned long long>(";
+                        break;
+                    case 'f':
+                        cppOut << "std::bit_cast<unsigned>(";
+                        break;
+                }
+                cppOut << "arg" << (i - i2) << ")";
+                switch (sig[i])
+                {
+                    case 'd':
+                    case 'f':
+                        cppOut << ")";
+                        break;
+                }
+            }
+            cppOut << "}, "
+                   << (sig[i2] == 'I' ? "indirectRet" : 
"reinterpret_cast<unsigned>(nullptr)")
+                   << ")";
+            switch (sig[i2])
+            {
+                case 'd':
+                case 'f':
+                    cppOut << "))";
+                    break;
+                case 'i':
+                case 'j':
+                    cppOut << ")";
+                    break;
+            }
+            cppOut << "; }
";
+        }
+        if (!slotSignatures.empty())
+        {
+            cppOut << "}
";
+        }
+        cppOut << "void const * getVtableSlotFunction(std::string_view 
signature) {
";
+        for (auto const& sig : slotSignatures)
+        {
+            cppOut << "    if (signature == \"" << sig << "\") {
"
+                   << "        return reinterpret_cast<void 
const*>(vtableSlotFunction_" << sig
+                   << ");
"
+                   << "    }
";
+        }
+        cppOut << "    throw css::uno::RuntimeException(\"Wasm bridge cannot 
fill virtual function "
+                  "slot with signature \" + OUString::fromUtf8(signature));
"
+                  "}
";
         cppOut.close();
         if (!cppOut)
         {
@@ -495,11 +879,11 @@ SAL_IMPLEMENT_MAIN()
         }
         asmOut << "    .text
"
                   "    .tabletype __indirect_function_table, funcref
";
-        for (auto const& sig : signatures)
+        for (auto const& sig : callSignatures)
         {
             asmOut << "        .functype callVirtualFunction_" << sig << " 
(i32, i32, i32) -> ()
";
         }
-        for (auto const& sig : signatures)
+        for (auto const& sig : callSignatures)
         {
             asmOut << "        .section .text.callVirtualFunction_" << sig
                    << ",\"\",@
"
diff --git a/unotest/source/embindtest/embindtest.component 
b/unotest/source/embindtest/embindtest.component
index 57a89249e8c0..f1106d3bc96a 100644
--- a/unotest/source/embindtest/embindtest.component
+++ b/unotest/source/embindtest/embindtest.component
@@ -12,6 +12,11 @@
         environment="@CPPU_ENV@"
         loader="com.sun.star.loader.SharedLibrary"
         xmlns="http://openoffice.org/2010/uno-components";>
+    <implementation
+            
constructor="org_libreoffice_comp_embindtest_BridgeTest_get_implementation"
+            name="org.libreoffice.comp.embindtest.BridgeTest">
+        <singleton name="org.libreoffice.embindtest.BridgeTest"/>
+    </implementation>
     <implementation
             
constructor="org_libreoffice_comp_embindtest_Test_get_implementation"
             name="org.libreoffice.comp.embindtest.Test">
diff --git a/unotest/source/embindtest/embindtest.cxx 
b/unotest/source/embindtest/embindtest.cxx
index 929db82e8900..81ede0e9cf93 100644
--- a/unotest/source/embindtest/embindtest.cxx
+++ b/unotest/source/embindtest/embindtest.cxx
@@ -9,6 +9,10 @@
 
 #include <sal/config.h>
 
+#include <cassert>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
 #include <com/sun/star/task/XJob.hpp>
 #include <com/sun/star/task/XJobExecutor.hpp>
 #include <com/sun/star/uno/Any.hxx>
@@ -26,9 +30,14 @@
 #include <org/libreoffice/embindtest/Struct.hpp>
 #include <org/libreoffice/embindtest/StructLong.hpp>
 #include <org/libreoffice/embindtest/StructString.hpp>
+#include <org/libreoffice/embindtest/Test.hpp>
 #include <org/libreoffice/embindtest/XTest.hpp>
 #include <rtl/ustring.hxx>
 #include <sal/types.h>
+#include <uno/dispatcher.hxx>
+#include <uno/environment.h>
+#include <uno/environment.hxx>
+#include <uno/mapping.hxx>
 
 namespace com::sun::star::uno
 {
@@ -601,6 +610,244 @@ class Test : public 
cppu::WeakImplHelper<org::libreoffice::embindtest::XTest>
 
     OUString stringAttribute_;
 };
+
+class BridgeTest : public cppu::WeakImplHelper<css::task::XJob>
+{
+public:
+    explicit BridgeTest(css::uno::Reference<css::uno::XComponentContext> 
const& context)
+        : context_(context)
+    {
+    }
+
+private:
+    css::uno::Any SAL_CALL
+    execute(css::uno::Sequence<css::beans::NamedValue> const& Arguments) 
override
+    {
+        if (Arguments.hasElements())
+        {
+            throw css::lang::IllegalArgumentException(u"BridgeTest execute 
args not empty"_ustr, {},
+                                                      0);
+        }
+        auto const envCppOrig = css::uno::Environment::getCurrent();
+        css::uno::Environment envUno;
+        uno_createEnvironment(reinterpret_cast<uno_Environment**>(&envUno),
+                              u"" UNO_LB_UNO ""_ustr.pData, nullptr);
+        css::uno::Mapping cpp2uno(envCppOrig.get(), envUno.get());
+        css::uno::Environment envCpp;
+        if (!cpp2uno.is())
+        {
+            throw css::uno::RuntimeException(u"cannot get C++ to UNO 
mapping"_ustr);
+        }
+        uno_createEnvironment(reinterpret_cast<uno_Environment**>(&envCpp),
+                              envCppOrig.getTypeName().pData, nullptr);
+        css::uno::Mapping uno2cpp(envUno.get(), envCpp.get());
+        if (!uno2cpp.is())
+        {
+            throw css::uno::RuntimeException(u"cannot get UNO to C++ 
mapping"_ustr);
+        }
+        css::uno::UnoInterfaceReference ifcUno;
+        cpp2uno.mapInterface(reinterpret_cast<void**>(&ifcUno.m_pUnoI),
+                             
org::libreoffice::embindtest::Test::get(context_).get(),
+                             
cppu::UnoType<org::libreoffice::embindtest::XTest>::get());
+        if (!ifcUno.is())
+        {
+            throw css::uno::RuntimeException(u"cannot map from C++ to 
UNO"_ustr);
+        }
+        css::uno::Reference<org::libreoffice::embindtest::XTest> ifcCpp;
+        uno2cpp.mapInterface(reinterpret_cast<void**>(&ifcCpp), ifcUno.get(),
+                             
cppu::UnoType<org::libreoffice::embindtest::XTest>::get());
+        if (!ifcCpp.is())
+        {
+            throw css::uno::RuntimeException(u"cannot map from UNO to 
C++"_ustr);
+        }
+        {
+            auto const val = ifcCpp->getBoolean();
+            assert(val);
+            auto const ok = ifcCpp->isBoolean(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getByte();
+            assert(val == -12);
+            auto const ok = ifcCpp->isByte(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getShort();
+            assert(val == -1234);
+            auto const ok = ifcCpp->isShort(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getUnsignedShort();
+            assert(val == 54321);
+            auto const ok = ifcCpp->isUnsignedShort(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getLong();
+            assert(val == -123456);
+            auto const ok = ifcCpp->isLong(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getUnsignedLong();
+            assert(val == 3456789012);
+            auto const ok = ifcCpp->isUnsignedLong(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getHyper();
+            assert(val == -123456789);
+            auto const ok = ifcCpp->isHyper(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getUnsignedHyper();
+            assert(val == 9876543210);
+            auto const ok = ifcCpp->isUnsignedHyper(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getFloat();
+            assert(val == -10.25);
+            auto const ok = ifcCpp->isFloat(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getDouble();
+            assert(val == 100.5);
+            auto const ok = ifcCpp->isDouble(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getChar();
+            assert(val == u'Ö');
+            auto const ok = ifcCpp->isChar(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getString();
+            assert(val == u"hä"_ustr);
+            auto const ok = ifcCpp->isString(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getType();
+            assert(val == cppu::UnoType<sal_Int32>::get());
+            auto const ok = ifcCpp->isType(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getEnum();
+            assert(val == org::libreoffice::embindtest::Enum_E_2);
+            auto const ok = ifcCpp->isEnum(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getStruct();
+            assert((val == org::libreoffice::embindtest::Struct{ -123456, 
100.5, u"hä"_ustr }));
+            auto const ok = ifcCpp->isStruct(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getStructLong();
+            assert(val == org::libreoffice::embindtest::StructLong{ -123456 });
+            auto const ok = ifcCpp->isStructLong(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getStructString();
+            assert(val == org::libreoffice::embindtest::StructString{ 
u"hä"_ustr });
+            auto const ok = ifcCpp->isStructString(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getAnyVoid();
+            assert(val == css::uno::Any());
+            auto const ok = ifcCpp->isAnyVoid(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getSequenceBoolean();
+            assert((val == css::uno::Sequence<sal_Bool>{ true, true, false }));
+            auto const ok = ifcCpp->isSequenceBoolean(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            auto const val = ifcCpp->getNull();
+            assert(val == 
css::uno::Reference<org::libreoffice::embindtest::XTest>());
+            auto const ok = ifcCpp->isNull(val);
+            assert(ok == css::uno::Any(true));
+        }
+        {
+            sal_Bool value1;
+            sal_Int8 value2;
+            sal_Int16 value3;
+            sal_uInt16 value4;
+            sal_Int32 value5;
+            sal_uInt32 value6;
+            sal_Int64 value7;
+            sal_uInt64 value8;
+            float value9;
+            double value10;
+            sal_Unicode value11;
+            OUString value12;
+            css::uno::Type value13;
+            css::uno::Any value14;
+            css::uno::Sequence<OUString> value15;
+            org::libreoffice::embindtest::Enum value16;
+            org::libreoffice::embindtest::Struct value17;
+            css::uno::Reference<org::libreoffice::embindtest::XTest> value18;
+            ifcCpp->getOut(value1, value2, value3, value4, value5, value6, 
value7, value8, value9,
+                           value10, value11, value12, value13, value14, 
value15, value16, value17,
+                           value18);
+            assert(value1);
+            assert(value2 == -12);
+            assert(value3 == -1234);
+            assert(value4 == 54321);
+            assert(value5 == -123456);
+            assert(value6 == 3456789012);
+            assert(value7 == -123456789);
+            assert(value8 == 9876543210);
+            assert(value9 == -10.25);
+            assert(value10 == 100.5);
+            assert(value11 == u'Ö');
+            assert(value12 == u"hä"_ustr);
+            assert(value13 == cppu::UnoType<sal_Int32>::get());
+            assert(value14 == css::uno::Any(sal_Int32(-123456)));
+            assert((value15
+                    == css::uno::Sequence<OUString>{ u"foo"_ustr, 
u"barr"_ustr, u"bazzz"_ustr }));
+            assert(value16 == org::libreoffice::embindtest::Enum_E_2);
+            assert((value17 == org::libreoffice::embindtest::Struct{ -123456, 
100.5, u"hä"_ustr }));
+            assert(value18 == ifcCpp);
+        }
+        try
+        {
+            ifcCpp->throwRuntimeException();
+            assert(false);
+        }
+        catch (css::uno::RuntimeException& e)
+        {
+            assert(e.Message.startsWith("test"));
+        }
+        {
+            ifcCpp->setStringAttribute(u"hä"_ustr);
+            auto const val = ifcCpp->getStringAttribute();
+            assert(val == u"hä"_ustr);
+        }
+        return css::uno::Any(true);
+    }
+
+    css::uno::Reference<css::uno::XComponentContext> context_;
+};
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_libreoffice_comp_embindtest_BridgeTest_get_implementation(
+    css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> 
const&)
+{
+    return cppu::acquire(new BridgeTest(context));
 }
 
 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
diff --git a/unotest/source/embindtest/embindtest.js 
b/unotest/source/embindtest/embindtest.js
index d47fed4703ab..5c2451377550 100644
--- a/unotest/source/embindtest/embindtest.js
+++ b/unotest/source/embindtest/embindtest.js
@@ -1107,6 +1107,16 @@ Module.addOnPostRun(function() {
         console.assert(ret.get() === 'hä');
         ret.delete();
     }
+    {
+        const args = new Module.uno_Sequence_com$sun$star$beans$NamedValue(
+            0, Module.uno_Sequence.FromSize);
+        const ret =
+              
Module.uno.org.libreoffice.embindtest.BridgeTest(Module.getUnoComponentContext()).
+              execute(args);
+        args.delete();
+        console.assert(ret.get() === true);
+        ret.delete();
+    }
 });
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to