office-gobmx/cppuhelper/source/exc_thrower.cxx
Stephan Bergmann 875997c896 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>
2024-07-10 12:32:15 +02:00

313 lines
9.2 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <rtl/instance.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <uno/dispatcher.hxx>
#include <uno/lbnames.h>
#include <uno/mapping.hxx>
#include <cppuhelper/detail/XExceptionThrower.hpp>
#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
#include <com/sun/star/ucb/NameClashException.hpp>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <cppuhelper/exc_hlp.hxx>
using namespace ::cppu;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
namespace
{
using cppuhelper::detail::XExceptionThrower;
struct ExceptionThrower : public uno_Interface, XExceptionThrower
{
ExceptionThrower();
virtual ~ExceptionThrower() {}
static Type const & getCppuType()
{
return cppu::UnoType<XExceptionThrower>::get();
}
// XInterface
virtual Any SAL_CALL queryInterface( Type const & type ) override;
virtual void SAL_CALL acquire() noexcept override;
virtual void SAL_CALL release() noexcept override;
// XExceptionThrower
virtual void SAL_CALL throwException( Any const & exc ) override;
virtual void SAL_CALL rethrowException() override;
};
extern "C"
{
void ExceptionThrower_acquire_release_nop(
SAL_UNUSED_PARAMETER uno_Interface * )
{}
void ExceptionThrower_dispatch(
uno_Interface * pUnoI, typelib_TypeDescription const * pMemberType,
void * pReturn, void * pArgs [], uno_Any ** ppException )
{
OSL_ASSERT( pMemberType->eTypeClass == typelib_TypeClass_INTERFACE_METHOD );
switch (reinterpret_cast< typelib_InterfaceMemberTypeDescription * >(
const_cast< typelib_TypeDescription * >( pMemberType ) )->
nPosition)
{
case 0: // queryInterface()
{
Type const & rType_demanded =
*static_cast< Type const * >( pArgs[ 0 ] );
if (rType_demanded.equals( cppu::UnoType<XInterface>::get() ) ||
rType_demanded.equals( ExceptionThrower::getCppuType() ))
{
typelib_TypeDescription * pTD = nullptr;
TYPELIB_DANGER_GET( &pTD, rType_demanded.getTypeLibType() );
uno_any_construct(
static_cast< uno_Any * >( pReturn ), &pUnoI, pTD, nullptr );
TYPELIB_DANGER_RELEASE( pTD );
}
else
{
uno_any_construct(
static_cast< uno_Any * >( pReturn ), nullptr, nullptr, nullptr );
}
*ppException = nullptr;
break;
}
case 1: // acquire()
case 2: // release()
*ppException = nullptr;
break;
case 3: // throwException()
{
uno_Any * pAny = static_cast< uno_Any * >( pArgs[ 0 ] );
OSL_ASSERT( pAny->pType->eTypeClass == typelib_TypeClass_EXCEPTION );
uno_type_any_construct( *ppException, pAny->pData, pAny->pType, nullptr );
break;
}
default:
{
OSL_ASSERT( false );
RuntimeException exc( u"not implemented!"_ustr );
uno_type_any_construct(
*ppException, &exc, cppu::UnoType<decltype(exc)>::get().getTypeLibType(), nullptr );
break;
}
}
}
} // extern "C"
Any ExceptionThrower::queryInterface( Type const & type )
{
if (type.equals( cppu::UnoType<XInterface>::get() ) ||
type.equals( ExceptionThrower::getCppuType() ))
{
XExceptionThrower * that = this;
return Any( &that, type );
}
return Any();
}
void ExceptionThrower::acquire() noexcept
{
}
void ExceptionThrower::release() noexcept
{
}
void ExceptionThrower::throwException( Any const & exc )
{
OSL_FAIL( "unexpected!" );
cppu::throwException( exc );
}
void ExceptionThrower::rethrowException()
{
throw;
}
ExceptionThrower::ExceptionThrower()
{
uno_Interface::acquire = ExceptionThrower_acquire_release_nop;
uno_Interface::release = ExceptionThrower_acquire_release_nop;
uno_Interface::pDispatcher = ExceptionThrower_dispatch;
}
#if defined(IOS) || defined(ANDROID)
#define RETHROW_FAKE_EXCEPTIONS 1
#else
#define RETHROW_FAKE_EXCEPTIONS 0
#endif
class theExceptionThrower : public rtl::Static<ExceptionThrower, theExceptionThrower> {};
#if RETHROW_FAKE_EXCEPTIONS
// In the native iOS / Android app, where we don't have any Java, Python,
// BASIC, or other scripting, the only thing that would use the C++/UNO bridge
// functionality that invokes codeSnippet() was cppu::throwException().
//
// codeSnippet() is part of what corresponds to the code that uses
// run-time-generated machine code on other platforms. We can't generate code
// at run-time on iOS, that has been known forever.
//
// Instead of digging in and trying to understand what is wrong, another
// solution was chosen. It turns out that the number of types of exception
// objects thrown by cppu::throwException() is fairly small. During startup of
// the LibreOffice code, and loading of an .odt document, only one kind of
// exception is thrown this way... (The lovely
// css::ucb:InteractiveAugmentedIOException.)
//
// So we can simply have code that checks what the type of object being thrown
// is, and explicitly throws such an object then with a normal C++ throw
// statement. Seems to work.
template <class E> void tryThrow(css::uno::Any const& aException)
{
E aSpecificException;
if (aException >>= aSpecificException)
throw aSpecificException;
}
void lo_mobile_throwException(css::uno::Any const& aException)
{
assert(aException.getValueTypeClass() == css::uno::TypeClass_EXCEPTION);
tryThrow<css::ucb::InteractiveAugmentedIOException>(aException);
tryThrow<css::ucb::NameClashException>(aException);
tryThrow<css::uno::RuntimeException>(aException);
SAL_WARN("cppuhelper", "lo_mobile_throwException: Unhandled exception type: " << aException.getValueTypeName());
assert(false);
}
#endif // RETHROW_FAKE_EXCEPTIONS
} // anonymous namespace
namespace cppu
{
void SAL_CALL throwException( Any const & exc )
{
if (exc.getValueTypeClass() != TypeClass_EXCEPTION)
{
throw RuntimeException(
u"no UNO exception given "
"(must be derived from com::sun::star::uno::Exception)!"_ustr );
}
#if RETHROW_FAKE_EXCEPTIONS
lo_mobile_throwException(exc);
#else
Mapping uno2cpp(Environment(u"" UNO_LB_UNO ""_ustr), Environment::getCurrent());
if (! uno2cpp.is())
{
throw RuntimeException(
u"cannot get binary UNO to C++ mapping!"_ustr );
}
Reference< XExceptionThrower > xThrower;
uno2cpp.mapInterface(
reinterpret_cast< void ** >( &xThrower ),
static_cast< uno_Interface * >( &theExceptionThrower::get() ),
ExceptionThrower::getCppuType() );
OSL_ASSERT( xThrower.is() );
xThrower->throwException( exc );
#endif // !RETHROW_FAKE_EXCEPTIONS
}
Any SAL_CALL getCaughtException()
{
// why does this differ from RETHROW_FAKE_EXCEPTIONS?
#if defined(ANDROID)
return Any();
#else
Mapping cpp2uno(Environment::getCurrent(), Environment(u"" UNO_LB_UNO ""_ustr));
if (! cpp2uno.is())
{
throw RuntimeException(
u"cannot get C++ to binary UNO mapping!"_ustr );
}
Mapping uno2cpp(Environment(u"" UNO_LB_UNO ""_ustr), Environment::getCurrent());
if (! uno2cpp.is())
{
throw RuntimeException(
u"cannot get binary UNO to C++ mapping!"_ustr );
}
typelib_TypeDescription * pTD = nullptr;
TYPELIB_DANGER_GET(
&pTD, ExceptionThrower::getCppuType().getTypeLibType() );
UnoInterfaceReference unoI;
cpp2uno.mapInterface(
reinterpret_cast< void ** >( &unoI.m_pUnoI ),
static_cast< XExceptionThrower * >( &theExceptionThrower::get() ), pTD );
OSL_ASSERT( unoI.is() );
typelib_TypeDescription * pMemberTD = nullptr;
TYPELIB_DANGER_GET(
&pMemberTD,
reinterpret_cast< typelib_InterfaceTypeDescription * >( pTD )->
ppMembers[ 1 ] /* rethrowException() */ );
uno_Any exc_mem;
uno_Any * exc = &exc_mem;
unoI.dispatch( pMemberTD, nullptr, nullptr, &exc );
TYPELIB_DANGER_RELEASE( pMemberTD );
TYPELIB_DANGER_RELEASE( pTD );
if (exc == nullptr)
{
throw RuntimeException( u"rethrowing C++ exception failed!"_ustr );
}
Any ret;
uno_any_destruct( &ret, reinterpret_cast< uno_ReleaseFunc >(cpp_release) );
uno_type_any_constructAndConvert(
&ret, exc->pData, exc->pType, uno2cpp.get() );
uno_any_destruct( exc, nullptr );
return ret;
#endif
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */