bridges/CustomTarget_gcc3_wasm.mk | 5 bridges/source/cpp_uno/gcc3_wasm/abi.hxx | 76 ++ bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx | 554 ++++++++++++++++- cppuhelper/source/exc_thrower.cxx | 4 desktop/CustomTarget_soffice_bin-emscripten-exports.mk | 25 desktop/Executable_soffice_bin.mk | 5 desktop/Module_desktop.mk | 6 desktop/util/Executable_soffice_bin-emscripten-exports | 14 include/bridges/emscriptencxxabi/cxxabi.hxx | 3 static/source/wasmcallgen/wasmcallgen.cxx | 69 +- 10 files changed, 744 insertions(+), 17 deletions(-)
New commits: commit 875997c8962da7f6b72950b201186a19b3c20d3a Author: Stephan Bergmann <stephan.bergm...@allotropia.de> AuthorDate: Tue Jul 9 17:28:46 2024 +0200 Commit: Stephan Bergmann <stephan.bergm...@allotropia.de> CommitDate: Wed Jul 10 12:32:15 2024 +0200 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 f4ec967599f5dafa1ce477631d7c2e8de005e28f "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.bergm...@allotropia.de> diff --git a/bridges/CustomTarget_gcc3_wasm.mk b/bridges/CustomTarget_gcc3_wasm.mk index a88da8577282..7452fc477e4f 100644 --- a/bridges/CustomTarget_gcc3_wasm.mk +++ b/bridges/CustomTarget_gcc3_wasm.mk @@ -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: diff --git a/bridges/source/cpp_uno/gcc3_wasm/abi.hxx b/bridges/source/cpp_uno/gcc3_wasm/abi.hxx index e09b68ebd76f..b60f6383fd1a 100644 --- a/bridges/source/cpp_uno/gcc3_wasm/abi.hxx +++ b/bridges/source/cpp_uno/gcc3_wasm/abi.hxx @@ -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 diff --git a/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx b/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx index dc02fda3946e..2877a706a67a 100644 --- a/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx +++ b/bridges/source/cpp_uno/gcc3_wasm/cpp2uno.cxx @@ -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 +{ +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, ¶m, 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) { - std::abort(); + *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*) {} diff --git a/cppuhelper/source/exc_thrower.cxx b/cppuhelper/source/exc_thrower.cxx index eec416775038..db2c9b0d5a46 100644 --- a/cppuhelper/source/exc_thrower.cxx +++ b/cppuhelper/source/exc_thrower.cxx @@ -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)); diff --git a/desktop/CustomTarget_soffice_bin-emscripten-exports.mk b/desktop/CustomTarget_soffice_bin-emscripten-exports.mk new file mode 100644 index 000000000000..eec384150801 --- /dev/null +++ b/desktop/CustomTarget_soffice_bin-emscripten-exports.mk @@ -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: diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk index 163bfb6409f2..f159c877f447 100644 --- a/desktop/Executable_soffice_bin.mk +++ b/desktop/Executable_soffice_bin.mk @@ -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, \ diff --git a/desktop/Module_desktop.mk b/desktop/Module_desktop.mk index de56f3bd139f..862662e2acf2 100644 --- a/desktop/Module_desktop.mk +++ b/desktop/Module_desktop.mk @@ -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 \ diff --git a/desktop/util/Executable_soffice_bin-emscripten-exports b/desktop/util/Executable_soffice_bin-emscripten-exports new file mode 100644 index 000000000000..2206ccccb2d5 --- /dev/null +++ b/desktop/util/Executable_soffice_bin-emscripten-exports @@ -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 diff --git a/include/bridges/emscriptencxxabi/cxxabi.hxx b/include/bridges/emscriptencxxabi/cxxabi.hxx index 8c380961cb60..eda61f1229e4 100644 --- a/include/bridges/emscriptencxxabi/cxxabi.hxx +++ b/include/bridges/emscriptencxxabi/cxxabi.hxx @@ -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 diff --git a/static/source/wasmcallgen/wasmcallgen.cxx b/static/source/wasmcallgen/wasmcallgen.cxx index 0453bdb8aff8..2c61a48c6b25 100644 --- a/static/source/wasmcallgen/wasmcallgen.cxx +++ b/static/source/wasmcallgen/wasmcallgen.cxx @@ -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: " - " wasmcallgen <cpp-output> <asm-output> <registries> " + " 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>. "; + "code is written to <cpp-output>/<asm-output>, and to-be-exported exception RTTI " + "symbols are written to <exp-output>. "; 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 << "\" "; 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 "; + std::exit(EXIT_FAILURE); + } + for (auto const& rtti : rttis) + { + expOut << rtti.getStr() << " "; + } + expOut.close(); + if (!expOut) + { + std::cerr << "Failed to write \"" << expPathname << "\" "; + std::exit(EXIT_FAILURE); + } return EXIT_SUCCESS; } catch (unoidl::FileFormatException const& e)