Properly implement cppu::throwException for Emscripten

...by implementing (for now) just enough of the cpp2uno half of the Wasm UNO
bridge to make it work.  In general, that half suffers from the same issue as
the already-implemented uno2cpp half, namely that Wasm doesn't allow to generate
code on the fly (which, in this case, would be needed to implement the vtable
slot trampoline functions).  So, for now just hard-code the few
vtableSlotFunction_* that are needed by the UNO interfaces for
cppuhelper/source/exc_thrower.cxx.  (A proper fix would probably use the same
approach as for the uno2cpp half, and use something like wasmcallgen to generate
at least all the vtableSlotFunction_* needed for udkapi/offapi upfront.)

The RTTI for the exceptions needs to be unique across the executable for
exception catching to actually work (as it compares exception types by RTTI
address rather than by name).  We thus need to export all the relevant RTTI
symbols (which I hacked into the existing wasmcallgen for now, even if that
makes that executable's name a slight misnomer now), and access them with a new
jsGetExportedSymbol.  (This exporting would also be needed by the "classical"
approach of using dlsym on the main module, cf.
<https://gerrit.libreoffice.org/c/core/+/167187> "WIP: Emscripten: Set up
support for dlsym from main module".  And while that dlsym approach would work,
it is much simpler to just use that jsGetExportedSymbol than to use a dlsym
detour, and thereby avoid all the hassle of -sMAIN_MODULE detailed in the commit
message of that Gerrit change.)

It also turned out that including Emscripten's <cxxabi.h> needs
__USING_WASM_EXCEPTIONS__ to be defined, because it uses that in its declaration
of __cxa_throw.  (The source for setting that define internally in Emscripten is
get_cflags in the emsdk's upstream/emscripten/tools/system_libs.py, which
defines __USING_EMSCRIPTEN_EXCEPTIONS__ for the non-Wasm, Emscripten JS
exception mode, and defines __USING_WASM_EXCEPTIONS__ for
--enable-wasm-exceptions, which we use.  The commit message of
f4ec967599 "Fix redefinion of Emscripten
__cxxabiv1::__cxa_exception" documents my prior confusion of when one or the
other would be defined.)

Change-Id: Id08765ab5b4ce1553dc3a61648324526185cb64c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170246
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
This commit is contained in:
Stephan Bergmann 2024-07-09 17:28:46 +02:00
parent 4833f13124
commit 875997c896
10 changed files with 744 additions and 17 deletions

View file

@ -12,15 +12,18 @@ $(eval $(call gb_CustomTarget_CustomTarget,bridges/gcc3_wasm))
$(eval $(call gb_CustomTarget_register_targets,bridges/gcc3_wasm, \
callvirtualfunction-wrapper.cxx \
callvirtualfunction-impls.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/callvirtualfunction-wrapper.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/exports \
+$(call gb_UnoApi_get_target,udkapi) +$(call gb_UnoApi_get_target,offapi)
# vim: set noet sw=4 ts=4:

View file

@ -13,6 +13,7 @@
#include <typeinfo>
#define __USING_WASM_EXCEPTIONS__
#include <cxxabi.h>
#include <bridges/emscriptencxxabi/cxxabi.hxx>
@ -20,6 +21,81 @@
#include <uno/any2.h>
#include <uno/mapping.h>
// <https://github.com/emscripten-core/emscripten/>, system/lib/libcxxabi/src/private_typeinfo.h:
namespace __cxxabiv1
{
class _LIBCXXABI_TYPE_VIS __shim_type_info : public std::type_info
{
public:
/*MODIFIED:*/ __shim_type_info(char const* name)
: type_info(name)
{
}
_LIBCXXABI_HIDDEN virtual ~__shim_type_info();
_LIBCXXABI_HIDDEN virtual void noop1() const;
_LIBCXXABI_HIDDEN virtual void noop2() const;
_LIBCXXABI_HIDDEN virtual bool can_catch(const __shim_type_info* thrown_type,
void*& adjustedPtr) const = 0;
};
}
#if !HAVE_CXXABI_H_CLASS_TYPE_INFO
// <https://github.com/emscripten-core/emscripten/>, system/lib/libcxxabi/src/private_typeinfo.h:
namespace __cxxabiv1
{
class _LIBCXXABI_TYPE_VIS __class_type_info : public __shim_type_info
{
public:
/*MODIFIED:*/ __class_type_info(char const* name)
: __shim_type_info(name)
{
}
_LIBCXXABI_HIDDEN virtual ~__class_type_info();
_LIBCXXABI_HIDDEN void process_static_type_above_dst(void /*MODIFIED: __dynamic_cast_info*/*,
const void*, const void*, int) const;
_LIBCXXABI_HIDDEN void process_static_type_below_dst(void /*MODIFIED: __dynamic_cast_info*/*,
const void*, int) const;
_LIBCXXABI_HIDDEN void process_found_base_class(void /*MODIFIED: __dynamic_cast_info*/*, void*,
int) const;
_LIBCXXABI_HIDDEN virtual void search_above_dst(void /*MODIFIED: __dynamic_cast_info*/*,
const void*, const void*, int, bool) const;
_LIBCXXABI_HIDDEN virtual void search_below_dst(void /*MODIFIED: __dynamic_cast_info*/*,
const void*, int, bool) const;
_LIBCXXABI_HIDDEN virtual bool can_catch(const __shim_type_info*, void*&) const;
_LIBCXXABI_HIDDEN virtual void
has_unambiguous_public_base(void /*MODIFIED: __dynamic_cast_info*/*, void*, int) const;
};
}
#endif
#if !HAVE_CXXABI_H_SI_CLASS_TYPE_INFO
// <https://mentorembedded.github.io/cxx-abi/abi.html>,
// libstdc++-v3/libsupc++/cxxabi.h:
namespace __cxxabiv1
{
class _LIBCXXABI_TYPE_VIS __si_class_type_info : public __class_type_info
{
public:
const __class_type_info* __base_type;
/*MODIFIED:*/ __si_class_type_info(char const* name)
: __class_type_info(name)
{
}
_LIBCXXABI_HIDDEN virtual ~__si_class_type_info();
_LIBCXXABI_HIDDEN virtual void search_above_dst(void /*MODIFIED: __dynamic_cast_info*/*,
const void*, const void*, int, bool) const;
_LIBCXXABI_HIDDEN virtual void search_below_dst(void /*MODIFIED: __dynamic_cast_info*/*,
const void*, int, bool) const;
_LIBCXXABI_HIDDEN virtual void
has_unambiguous_public_base(void /*MODIFIED: __dynamic_cast_info*/*, void*, int) const;
};
}
#endif
#if !HAVE_CXXABI_H_CXA_EH_GLOBALS
// <https://github.com/emscripten-core/emscripten/>, system/lib/libcxxabi/src/cxa_exception.h:
namespace __cxxabiv1

View file

@ -7,13 +7,40 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <sal/config.h>
#include <typeinfo>
#include <emscripten.h>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <o3tl/string_view.hxx>
#include <o3tl/unreachable.hxx>
#include <rtl/strbuf.hxx>
#include <typelib/typeclass.h>
#include <typelib/typedescription.hxx>
#include <bridge.hxx>
#include <cppinterfaceproxy.hxx>
#include <types.hxx>
#include <vtablefactory.hxx>
#include "abi.hxx"
EM_JS(void*, jsGetExportedSymbol, (char const* name),
// clang-format off
{
const val = Module["_" + UTF8ArrayToString(HEAPU8, name)];
return typeof val === "number" || typeof val === "bigint" ? val : 0;
}
// clang-format on
);
using bridges::cpp_uno::shared::VtableFactory;
struct VtableFactory::Slot
{
void const* fn;
};
VtableFactory::Slot* VtableFactory::mapBlockToVtable(void* block)
@ -26,18 +53,537 @@ std::size_t VtableFactory::getBlockSize(sal_Int32 slotCount)
return (slotCount + 2) * sizeof(Slot);
}
namespace
{
// Some dummy type whose RTTI is used in the synthesized proxy vtables to make uses of dynamic_cast
// on such proxy objects not crash:
struct ProxyRtti
{
};
}
VtableFactory::Slot* VtableFactory::initializeBlock(void* block, sal_Int32 slotCount, sal_Int32,
typelib_InterfaceTypeDescription*)
{
Slot* slots = mapBlockToVtable(block);
slots[-2].fn = nullptr;
slots[-1].fn = &typeid(ProxyRtti);
return slots + slotCount;
}
unsigned char* VtableFactory::addLocalFunctions(Slot**, unsigned char*,
typelib_InterfaceTypeDescription const*, sal_Int32,
sal_Int32, sal_Int32)
namespace
{
std::abort();
enum class StructKind
{
Empty,
I32,
I64,
F32,
F64,
General
};
StructKind getKind(typelib_CompoundTypeDescription const* type)
{
if (type->nMembers > 1)
{
return StructKind::General;
}
auto k = StructKind::Empty;
if (type->pBaseTypeDescription != nullptr)
{
k = getKind(type->pBaseTypeDescription);
}
if (type->nMembers == 0)
{
return k;
}
if (k != StructKind::Empty)
{
return StructKind::General;
}
switch (type->ppTypeRefs[0]->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_UNSIGNED_LONG:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_ENUM:
return StructKind::I32;
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
return StructKind::I64;
case typelib_TypeClass_FLOAT:
return StructKind::F32;
case typelib_TypeClass_DOUBLE:
return StructKind::F64;
default:
return StructKind::General;
}
}
class Rtti
{
public:
std::type_info* getRtti(typelib_TypeDescription const& type);
private:
typedef std::unordered_map<OUString, std::type_info*> Map;
osl::Mutex mutex_;
Map map_;
};
std::type_info* Rtti::getRtti(typelib_TypeDescription const& type)
{
OUString unoName(type.pTypeName);
osl::MutexGuard g(mutex_);
Map::iterator i(map_.find(unoName));
if (i == map_.end())
{
OStringBuffer b("_ZTI");
auto const ns = unoName.indexOf('.') != 0;
if (ns)
{
b.append('N');
}
for (sal_Int32 j = 0; j != -1;)
{
OString s(
OUStringToOString(o3tl::getToken(unoName, 0, '.', j), RTL_TEXTENCODING_ASCII_US));
b.append(OString::number(s.getLength()) + s);
}
if (ns)
{
b.append('E');
}
OString sym(b.makeStringAndClear());
std::type_info* rtti = static_cast<std::type_info*>(jsGetExportedSymbol(sym.getStr()));
if (rtti == nullptr)
{
char const* rttiName = strdup(sym.getStr() + std::strlen("_ZTI"));
if (rttiName == nullptr)
{
throw std::bad_alloc();
}
assert(type.eTypeClass == typelib_TypeClass_EXCEPTION);
typelib_CompoundTypeDescription const& ctd
= reinterpret_cast<typelib_CompoundTypeDescription const&>(type);
if (ctd.pBaseTypeDescription == nullptr)
{
rtti = new __cxxabiv1::__class_type_info(rttiName);
}
else
{
std::type_info* base = getRtti(ctd.pBaseTypeDescription->aBase);
auto const sicti = new __cxxabiv1::__si_class_type_info(rttiName);
sicti->__base_type = static_cast<__cxxabiv1::__class_type_info*>(base);
rtti = sicti;
}
}
i = map_.insert(Map::value_type(unoName, rtti)).first;
}
return i->second;
}
struct theRttiFactory : public rtl::Static<Rtti, theRttiFactory>
{
};
std::type_info* getRtti(typelib_TypeDescription const& type)
{
return theRttiFactory::get().getRtti(type);
}
extern "C" void* /*_GLIBCXX_CDTOR_CALLABI*/ deleteException(void* exception)
{
__cxxabiv1::__cxa_exception* header = static_cast<__cxxabiv1::__cxa_exception*>(exception) - 1;
assert(header->exceptionDestructor == &deleteException);
OUString unoName(emscriptencxxabi::toUnoName(header->exceptionType->name()));
typelib_TypeDescription* td = nullptr;
typelib_typedescription_getByName(&td, unoName.pData);
assert(td != nullptr);
uno_destructData(exception, td, &css::uno::cpp_release);
typelib_typedescription_release(td);
return exception;
}
void raiseException(uno_Any* any, uno_Mapping* mapping)
{
typelib_TypeDescription* td = nullptr;
TYPELIB_DANGER_GET(&td, any->pType);
if (td == nullptr)
{
throw css::uno::RuntimeException("no typedescription for "
+ OUString::unacquired(&any->pType->pTypeName));
}
void* exc = __cxxabiv1::__cxa_allocate_exception(td->nSize);
uno_copyAndConvertData(exc, any->pData, td, mapping);
uno_any_destruct(any, nullptr);
std::type_info* rtti = getRtti(*td);
TYPELIB_DANGER_RELEASE(td);
__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*/)
{
typelib_TypeDescription* rtd = nullptr;
if (returnType != nullptr)
{
TYPELIB_DANGER_GET(&rtd, returnType);
}
void* retin = nullptr;
void** args = static_cast<void**>(alloca(count * sizeof(void*)));
void** cppArgs = static_cast<void**>(alloca(count * sizeof(void*)));
typelib_TypeDescription** argtds
= static_cast<typelib_TypeDescription**>(alloca(count * sizeof(typelib_TypeDescription*)));
std::size_t argument_index = 0;
for (sal_Int32 i = 0; i != count; ++i)
{
if (!parameters[i].bOut && bridges::cpp_uno::shared::isSimpleType(parameters[i].pTypeRef))
{
assert(false);
}
else
{
cppArgs[i] = reinterpret_cast<void*>(arguments[argument_index++]);
typelib_TypeDescription* ptd = nullptr;
TYPELIB_DANGER_GET(&ptd, parameters[i].pTypeRef);
if (!parameters[i].bIn)
{
args[i] = alloca(ptd->nSize);
argtds[i] = ptd;
}
else if (bridges::cpp_uno::shared::relatesToInterfaceType(ptd))
{
args[i] = alloca(ptd->nSize);
uno_copyAndConvertData(args[i], cppArgs[i], ptd, proxy->getBridge()->getCpp2Uno());
argtds[i] = ptd;
}
else
{
args[i] = cppArgs[i];
argtds[i] = nullptr;
TYPELIB_DANGER_RELEASE(ptd);
}
}
}
uno_Any exc;
uno_Any* pexc = &exc;
proxy->getUnoI()->pDispatcher(proxy->getUnoI(), description.get(), retin, args, &pexc);
if (pexc != nullptr)
{
for (sal_Int32 i = 0; i != count; ++i)
{
if (argtds[i] != nullptr)
{
if (parameters[i].bIn)
{
uno_destructData(args[i], argtds[i], nullptr);
}
TYPELIB_DANGER_RELEASE(argtds[i]);
}
}
if (rtd != nullptr)
{
TYPELIB_DANGER_RELEASE(rtd);
}
raiseException(&exc, proxy->getBridge()->getUno2Cpp());
}
assert(false);
}
void 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(
reinterpret_cast<char*>(thisPtr) - vtableOffset);
typelib_InterfaceTypeDescription* type = proxy->getTypeDescr();
assert(functionIndex < type->nMapFunctionIndexToMemberIndex);
sal_Int32 pos = type->pMapFunctionIndexToMemberIndex[functionIndex];
css::uno::TypeDescription desc(type->ppAllMembers[pos]);
switch (desc.get()->eTypeClass)
{
case typelib_TypeClass_INTERFACE_ATTRIBUTE:
if (type->pMapMemberIndexToFunctionIndex[pos] == functionIndex)
{
// Getter:
call(proxy, desc,
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(desc.get())
->pAttributeTypeRef,
0, nullptr, arguments, indirectRet);
}
else
{
// Setter:
typelib_MethodParameter param
= { nullptr,
reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(desc.get())
->pAttributeTypeRef,
true, false };
call(proxy, desc, nullptr, 1, &param, arguments, indirectRet);
}
break;
case typelib_TypeClass_INTERFACE_METHOD:
switch (functionIndex)
{
case 1:
proxy->acquireProxy();
break;
case 2:
proxy->releaseProxy();
break;
case 0:
{
typelib_TypeDescription* td = nullptr;
TYPELIB_DANGER_GET(
&td, (reinterpret_cast<css::uno::Type*>(arguments[0])->getTypeLibType()));
if (td != nullptr && td->eTypeClass == typelib_TypeClass_INTERFACE)
{
css::uno::XInterface* ifc = nullptr;
proxy->getBridge()->getCppEnv()->getRegisteredInterface(
proxy->getBridge()->getCppEnv(), reinterpret_cast<void**>(&ifc),
proxy->getOid().pData,
reinterpret_cast<typelib_InterfaceTypeDescription*>(td));
if (ifc != nullptr)
{
uno_any_construct(
reinterpret_cast<uno_Any*>(indirectRet), &ifc, td,
reinterpret_cast<uno_AcquireFunc>(css::uno::cpp_acquire));
ifc->release();
TYPELIB_DANGER_RELEASE(td);
break;
}
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;
}
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)
{
vtableCall(1, 0, thisPtr, {}, reinterpret_cast<unsigned>(nullptr));
}
extern "C" void vtableSlotFunction_2_0v(unsigned thisPtr)
{
vtableCall(2, 0, thisPtr, {}, reinterpret_cast<unsigned>(nullptr));
}
extern "C" void vtableSlotFunction_3_0vi(unsigned thisPtr, unsigned arg1)
{
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));
}
void const* getVtableSlotFunction(std::string_view signature)
{
if (signature == "0_0Ii")
{
return reinterpret_cast<void const*>(vtableSlotFunction_0_0Ii);
}
if (signature == "1_0v")
{
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);
}
if (signature == "4_0v")
{
return reinterpret_cast<void const*>(vtableSlotFunction_4_0v);
}
throw css::uno::RuntimeException("Wasm bridge cannot fill virtual function slot with signature "
+ OUString::fromUtf8(signature));
}
}
unsigned char* VtableFactory::addLocalFunctions(Slot** slots, unsigned char* code,
typelib_InterfaceTypeDescription const* type,
sal_Int32 functionOffset, sal_Int32 functionCount,
sal_Int32 vtableOffset)
{
*slots -= functionCount;
auto s = *slots;
for (sal_Int32 i = 0; i != type->nMembers; ++i)
{
switch (type->ppMembers[i]->eTypeClass)
{
case typelib_TypeClass_INTERFACE_ATTRIBUTE:
(s++)->fn = nullptr; //TODO
++functionOffset;
if (!reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(
css::uno::TypeDescription(type->ppMembers[i]).get())
->bReadOnly)
{
(s++)->fn = nullptr; //TODO
++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 (getKind(
reinterpret_cast<typelib_CompoundTypeDescription const*>(rtd.get())))
{
case StructKind::Empty:
break;
case StructKind::I32:
sig.append('i');
break;
case StructKind::I64:
sig.append('j');
break;
case StructKind::F32:
sig.append('f');
break;
case StructKind::F64:
sig.append('d');
break;
case StructKind::General:
sig.append('I');
break;
}
break;
}
default:
O3TL_UNREACHABLE;
}
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');
}
}
(s++)->fn = getVtableSlotFunction(sig);
++functionOffset;
break;
}
default:
O3TL_UNREACHABLE;
}
}
return code;
}
void VtableFactory::flushCode(unsigned char const*, unsigned char const*) {}

View file

@ -167,7 +167,7 @@ ExceptionThrower::ExceptionThrower()
uno_Interface::pDispatcher = ExceptionThrower_dispatch;
}
#if defined(IOS) || defined(ANDROID) || defined(EMSCRIPTEN)
#if defined(IOS) || defined(ANDROID)
#define RETHROW_FAKE_EXCEPTIONS 1
#else
#define RETHROW_FAKE_EXCEPTIONS 0
@ -255,7 +255,7 @@ void SAL_CALL throwException( Any const & exc )
Any SAL_CALL getCaughtException()
{
// why does this differ from RETHROW_FAKE_EXCEPTIONS?
#if defined(ANDROID) || defined(EMSCRIPTEN)
#if defined(ANDROID)
return Any();
#else
Mapping cpp2uno(Environment::getCurrent(), Environment(u"" UNO_LB_UNO ""_ustr));

View file

@ -0,0 +1,25 @@
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
$(eval $(call gb_CustomTarget_CustomTarget,desktop/soffice_bin-emscripten-exports))
$(eval $(call gb_CustomTarget_register_targets,desktop/soffice_bin-emscripten-exports, \
exports \
))
$(gb_CustomTarget_workdir)/desktop/soffice_bin-emscripten-exports/exports: \
$(SRCDIR)/desktop/util/Executable_soffice_bin-emscripten-exports \
$(gb_CustomTarget_workdir)/bridges/gcc3_wasm/exports
# Prior to <https://github.com/emscripten-core/emscripten/commit/
# 7baa690ed4bf801a411130c7137ce830b93120f3> "Allow comments in response files. (#21330)" towards
# emsdk 3.1.54, comment lines were not supported, so filter them out here for now:
grep -v '^#' $(SRCDIR)/desktop/util/Executable_soffice_bin-emscripten-exports >$@
cat $(gb_CustomTarget_workdir)/bridges/gcc3_wasm/exports >>$@
# vim: set noet sw=4 ts=4:

View file

@ -61,8 +61,11 @@ $(call gb_LinkTarget_get_target,$(call gb_Executable_get_linktarget,soffice_bin)
$(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)
$(call gb_Executable_get_linktarget_target,soffice_bin): \
$(gb_CustomTarget_workdir)/desktop/soffice_bin-emscripten-exports/exports
$(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"$(COMMA)"_malloc"$(COMMA)"_free"] -Wl$(COMMA)--whole-archive $(call gb_StaticLibrary_get_target,unoembind) -Wl$(COMMA)--no-whole-archive \
-s EXPORTED_FUNCTIONS=@$(gb_CustomTarget_workdir)/desktop/soffice_bin-emscripten-exports/exports -Wl$(COMMA)--whole-archive $(call gb_StaticLibrary_get_target,unoembind) -Wl$(COMMA)--no-whole-archive \
))
ifeq ($(ENABLE_QT6),TRUE)
$(eval $(call gb_Executable_add_ldflags,soffice_bin, \

View file

@ -105,6 +105,12 @@ endif
endif # $(OS)
ifeq ($(OS),EMSCRIPTEN)
$(eval $(call gb_Module_add_targets,desktop, \
CustomTarget_soffice_bin-emscripten-exports \
))
endif
ifneq (,$(filter Extension_test-active,$(MAKECMDGOALS)))
$(eval $(call gb_Module_add_targets,desktop, \
Extension_test-active \

View file

@ -0,0 +1,14 @@
#
# 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/.
#
_main
_libreofficekit_hook
_libreofficekit_hook_2
_lok_preinit
_lok_preinit_2
_malloc
_free

View file

@ -16,6 +16,7 @@
#include <exception>
#include <typeinfo>
#define __USING_WASM_EXCEPTIONS__
#include <cxxabi.h>
#include <config_cxxabi.h>
@ -67,7 +68,7 @@ struct __cxa_exception
#endif
// Manage the exception object itself.
std::type_info* exceptionType;
#if 1 //MODIFIED: #ifdef __USING_WASM_EXCEPTIONS__
#ifdef __USING_WASM_EXCEPTIONS__
// In wasm, destructors return their argument
void*(/*MODIFIED: _LIBCXXABI_DTOR_FUNC*/ *exceptionDestructor)(void*);
#else

View file

@ -19,6 +19,7 @@
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <codemaker/global.hxx>
#include <codemaker/typemanager.hxx>
@ -32,6 +33,7 @@
#include <rtl/strbuf.hxx>
#include <rtl/string.hxx>
#include <rtl/textcvt.h>
#include <rtl/textenc.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/main.h>
@ -44,11 +46,12 @@ namespace
{
std::cerr
<< "Usage:\n\n"
" wasmcallgen <cpp-output> <asm-output> <registries>\n\n"
" wasmcallgen <cpp-output> <asm-output> <exp-output> <registries>\n\n"
"where each <registry> is '+' (primary) or ':' (secondary), followed by: either a\n"
"new- or legacy-format .rdb file, a single .idl file, or a root directory of an\n"
".idl file tree. For all primary registries, Wasm UNO bridge callvirtualfunction\n"
"code is written to <cpp-output>/<asm-output>.\n";
"code is written to <cpp-output>/<asm-output>, and to-be-exported exception RTTI\n"
"symbols are written to <exp-output>.\n";
std::exit(EXIT_FAILURE);
}
@ -354,8 +357,34 @@ OString computeSignature(rtl::Reference<TypeManager> const& manager,
return buf.makeStringAndClear();
}
void appendRttiSymbolSegment(OStringBuffer& buffer, OUString const& id)
{
OString s(OUStringToOString(id, RTL_TEXTENCODING_ASCII_US));
buffer.append(OString::number(s.getLength()) + s);
}
OString computeRttiSymbol(std::vector<OUString> const& path, OUString const& id)
{
OStringBuffer buf("__ZTI");
if (!path.empty())
{
buf.append('N');
for (auto const& i : path)
{
appendRttiSymbolSegment(buf, i);
}
}
appendRttiSymbolSegment(buf, id);
if (!path.empty())
{
buf.append('E');
}
return buf.makeStringAndClear();
}
void scan(rtl::Reference<TypeManager> const& manager,
rtl::Reference<unoidl::MapCursor> const& cursor, std::set<OString>& signatures)
rtl::Reference<unoidl::MapCursor> const& cursor, std::vector<OUString>& path,
std::set<OString>& signatures, std::set<OString>& rttis)
{
assert(cursor.is());
for (;;)
@ -369,8 +398,13 @@ void scan(rtl::Reference<TypeManager> const& manager,
switch (ent->getSort())
{
case unoidl::Entity::SORT_MODULE:
scan(manager, static_cast<unoidl::ModuleEntity*>(ent.get())->createCursor(),
signatures);
path.push_back(id);
scan(manager, static_cast<unoidl::ModuleEntity*>(ent.get())->createCursor(), path,
signatures, 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 :
@ -391,14 +425,15 @@ SAL_IMPLEMENT_MAIN()
try
{
auto const args = rtl_getAppCommandArgCount();
if (args < 2)
if (args < 3)
{
badUsage();
}
auto const cppPathname = getPathnameArgument(0);
auto const asmPathname = getPathnameArgument(1);
auto const expPathname = getPathnameArgument(2);
rtl::Reference<TypeManager> mgr(new TypeManager);
for (sal_uInt32 i = 2; i != args; ++i)
for (sal_uInt32 i = 3; i != args; ++i)
{
auto const & [ uri, primary ] = parseRegistryArgument(i);
try
@ -411,10 +446,12 @@ SAL_IMPLEMENT_MAIN()
std::exit(EXIT_FAILURE);
}
}
std::vector<OUString> path;
std::set<OString> signatures;
std::set<OString> rttis;
for (auto const& prov : mgr->getPrimaryProviders())
{
scan(mgr, prov->createRootCursor(), signatures);
scan(mgr, prov->createRootCursor(), path, signatures, rttis);
}
std::ofstream cppOut(cppPathname, std::ios_base::out | std::ios_base::trunc);
if (!cppOut)
@ -588,6 +625,22 @@ SAL_IMPLEMENT_MAIN()
std::cerr << "Failed to write \"" << asmPathname << "\"\n";
std::exit(EXIT_FAILURE);
}
std::ofstream expOut(expPathname, std::ios_base::out | std::ios_base::trunc);
if (!expOut)
{
std::cerr << "Cannot open \"" << expPathname << "\" for writing\n";
std::exit(EXIT_FAILURE);
}
for (auto const& rtti : rttis)
{
expOut << rtti.getStr() << "\n";
}
expOut.close();
if (!expOut)
{
std::cerr << "Failed to write \"" << expPathname << "\"\n";
std::exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
catch (unoidl::FileFormatException const& e)