/* -*- 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/.
*/
#include <sal/config.h>
#include <cstring>
#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 <wasm/generated.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)
{
return static_cast <Slot*>(block) + 2;
}
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;
}
namespace
{
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);
}
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);
}
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
= 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))
{
args[i] = arguments.data() + i;
argtds[i] = nullptr;
}
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());
}
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;
}
}
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(
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:
return 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 };
return call(proxy, desc, nullptr, 1, ¶m, arguments, indirectRet);
}
break ;
case typelib_TypeClass_INTERFACE_METHOD:
switch (functionIndex)
{
case 1:
proxy->acquireProxy();
return {};
case 2:
proxy->releaseProxy();
return {};
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);
return {};
}
TYPELIB_DANGER_RELEASE(td);
}
}
[[fallthrough]];
default :
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);
}
default :
O3TL_UNREACHABLE;
}
}
namespace
{
void appendSignatureOffsets(OStringBuffer& buffer, sal_Int32 functionOffset, sal_Int32 vtableOffset)
{
buffer.append(OString::number(functionOffset) + "_" + OString::number(vtableOffset));
}
void appendSignatureReturnType(OStringBuffer& buffer, typelib_TypeDescriptionReference* type)
{
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 appendSignatureParameter(OStringBuffer& buffer, bool out,
typelib_TypeDescriptionReference const * type)
{
if (!out && bridges::cpp_uno::shared::isSimpleType(type))
{
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;
}
}
else
{
buffer.append('i' );
}
}
}
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:
{
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)
{
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:
{
auto const mtd = reinterpret_cast <typelib_InterfaceMethodTypeDescription*>(
css::uno::TypeDescription(type->ppMembers[i]).get());
OStringBuffer sig;
appendSignatureOffsets(sig, functionOffset, vtableOffset);
appendSignatureReturnType(sig, mtd->pReturnTypeRef);
for (sal_Int32 j = 0; j != mtd->nParams; ++j)
{
appendSignatureParameter(sig, mtd->pParams[j].bOut, mtd->pParams[j].pTypeRef);
}
(s++)->fn = getVtableSlotFunction(sig);
++functionOffset;
break ;
}
default :
O3TL_UNREACHABLE;
}
}
return code;
}
void VtableFactory::flushCode(unsigned char const *, unsigned char const *) {}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=96 H=98 G=96
¤ Dauer der Verarbeitung: 0.8 Sekunden
¤
*© Formatika GbR, Deutschland