/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "abi.hxx" extern "C" void vtableSlotCall(); using namespace ::com::sun::star; namespace { void call(bridges::cpp_uno::shared::CppInterfaceProxy* proxy, uno::TypeDescription const& description, typelib_TypeDescriptionReference* returnType, sal_Int32 count, typelib_MethodParameter* parameters, sal_uInt64* gpr, sal_uInt64* fpr, sal_uInt64* stack, void* indirectRet) { typelib_TypeDescription* rtd = 0; if (returnType != 0) TYPELIB_DANGER_GET(&rtd, returnType); ReturnKind retKind = rtd == 0 ? RETURN_KIND_REG : getReturnKind(rtd); bool retConv = rtd != 0 && bridges::cpp_uno::shared::relatesToInterfaceType(rtd); void* retin = retKind == RETURN_KIND_INDIRECT && !retConv ? indirectRet : rtd == 0 ? 0 : alloca(rtd->nSize); void** args = static_cast(alloca(count * sizeof(void*))); void** cppArgs = static_cast(alloca(count * sizeof(void*))); typelib_TypeDescription** argtds = static_cast(alloca(count * sizeof(typelib_TypeDescription*))); sal_Int32 ngpr = 1; sal_Int32 nfpr = 0; sal_Int32 sp = 0; for (sal_Int32 i = 0; i != count; ++i) { if (!parameters[i].bOut && bridges::cpp_uno::shared::isSimpleType(parameters[i].pTypeRef)) { switch (parameters[i].pTypeRef->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_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: case typelib_TypeClass_CHAR: case typelib_TypeClass_ENUM: args[i] = ngpr == 8 ? stack + sp++ : gpr + ngpr++; break; case typelib_TypeClass_FLOAT: case typelib_TypeClass_DOUBLE: args[i] = nfpr == 8 ? stack + sp++ : fpr + nfpr++; break; default: assert(false); } argtds[i] = 0; } else { cppArgs[i] = reinterpret_cast(ngpr == 8 ? stack[sp++] : gpr[ngpr++]); typelib_TypeDescription* ptd = 0; 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] = 0; TYPELIB_DANGER_RELEASE(ptd); } } } uno_Any exc; uno_Any* pexc = &exc; proxy->getUnoI()->pDispatcher(proxy->getUnoI(), description.get(), retin, args, &pexc); if (pexc != 0) { for (sal_Int32 i = 0; i != count; ++i) { if (argtds[i] == 0) continue; if (parameters[i].bIn) uno_destructData(args[i], argtds[i], 0); TYPELIB_DANGER_RELEASE(argtds[i]); } if (rtd != 0) TYPELIB_DANGER_RELEASE(rtd); assert(pexc == &exc); msvc_raiseException(&exc, proxy->getBridge()->getUno2Cpp()); } for (sal_Int32 i = 0; i != count; ++i) { if (argtds[i] != 0) { if (parameters[i].bOut) { uno_destructData(cppArgs[i], argtds[i], reinterpret_cast(uno::cpp_release)); uno_copyAndConvertData(cppArgs[i], args[i], argtds[i], proxy->getBridge()->getUno2Cpp()); } uno_destructData(args[i], argtds[i], 0); TYPELIB_DANGER_RELEASE(argtds[i]); } } void* retout = 0; // avoid false -Werror=maybe-uninitialized switch (retKind) { case RETURN_KIND_REG: switch (rtd == 0 ? typelib_TypeClass_VOID : rtd->eTypeClass) { case typelib_TypeClass_VOID: 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_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: case typelib_TypeClass_CHAR: case typelib_TypeClass_ENUM: std::memcpy(gpr, retin, rtd->nSize); assert(!retConv); break; case typelib_TypeClass_FLOAT: case typelib_TypeClass_DOUBLE: std::memcpy(fpr, retin, rtd->nSize); assert(!retConv); break; case typelib_TypeClass_STRUCT: if (retConv) { retout = gpr; } else { std::memcpy(gpr, retin, rtd->nSize); } break; default: assert(false); } break; case RETURN_KIND_HFA_FLOAT: assert(rtd != 0); switch (rtd->nSize) { case 16: std::memcpy(fpr + 3, static_cast(retin) + 12, 4); [[fallthrough]]; case 12: std::memcpy(fpr + 2, static_cast(retin) + 8, 4); [[fallthrough]]; case 8: std::memcpy(fpr + 1, static_cast(retin) + 4, 4); [[fallthrough]]; case 4: std::memcpy(fpr, retin, 4); break; default: assert(false); } assert(!retConv); break; case RETURN_KIND_HFA_DOUBLE: assert(rtd != 0); std::memcpy(fpr, retin, rtd->nSize); assert(!retConv); break; case RETURN_KIND_INDIRECT: retout = indirectRet; break; } if (retConv) { uno_copyAndConvertData(retout, retin, rtd, proxy->getBridge()->getUno2Cpp()); uno_destructData(retin, rtd, 0); } if (rtd != 0) TYPELIB_DANGER_RELEASE(rtd); } extern "C" void vtableCall(sal_Int32 functionIndex, sal_Int32 vtableOffset, sal_uInt64* gpr, sal_uInt64* fpr, sal_uInt64* stack, void* indirectRet) { bridges::cpp_uno::shared::CppInterfaceProxy* proxy = bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( reinterpret_cast(gpr[0]) - vtableOffset); typelib_InterfaceTypeDescription* pInterfaceTD = proxy->getTypeDescr(); assert(functionIndex < pInterfaceTD->nMapFunctionIndexToMemberIndex); sal_Int32 nMemberPos = pInterfaceTD->pMapFunctionIndexToMemberIndex[functionIndex]; assert(nMemberPos < pInterfaceTD->nAllMembers); uno::TypeDescription aMemberDescr(pInterfaceTD->ppAllMembers[nMemberPos]); switch (aMemberDescr.get()->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: { typelib_TypeDescriptionReference* pAttrTypeRef = reinterpret_cast(aMemberDescr.get()) ->pAttributeTypeRef; if (pInterfaceTD->pMapMemberIndexToFunctionIndex[nMemberPos] == functionIndex) { // Getter: call(proxy, aMemberDescr, pAttrTypeRef, 0, 0, gpr, fpr, stack, indirectRet); } else { // Setter: typelib_MethodParameter param = { 0, pAttrTypeRef, true, false }; call(proxy, aMemberDescr, 0, 1, ¶m, gpr, fpr, stack, 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(gpr[1])->getTypeLibType())); if (td != 0 && td->eTypeClass == typelib_TypeClass_INTERFACE) { uno::XInterface* ifc = nullptr; proxy->getBridge()->getCppEnv()->getRegisteredInterface( proxy->getBridge()->getCppEnv(), reinterpret_cast(&ifc), proxy->getOid().pData, reinterpret_cast(td)); if (ifc != 0) { uno_any_construct(reinterpret_cast(indirectRet), &ifc, td, reinterpret_cast(uno::cpp_acquire)); ifc->release(); TYPELIB_DANGER_RELEASE(td); break; } TYPELIB_DANGER_RELEASE(td); } } [[fallthrough]]; default: typelib_InterfaceMethodTypeDescription* pMethodTD = reinterpret_cast( aMemberDescr.get()); call(proxy, aMemberDescr, pMethodTD->pReturnTypeRef, pMethodTD->nParams, pMethodTD->pParams, gpr, fpr, stack, indirectRet); } break; default: assert(false); } } std::size_t const codeSnippetSize = 8 * 4; unsigned char* GenerateVTableSlotTrampoline(unsigned char* code, sal_Int32 functionIndex, sal_Int32 vtableOffset) { // movz x9, reinterpret_cast(code)[0] = 0xD2800009 | ((functionIndex & 0xFFFF) << 5); // movk x9, , LSL #16 reinterpret_cast(code)[1] = 0xF2A00009 | ((functionIndex >> 16) << 5); // movz x10, reinterpret_cast(code)[2] = 0xD280000A | ((vtableOffset & 0xFFFF) << 5); // movk x10, , LSL #16 reinterpret_cast(code)[3] = 0xF2A0000A | ((vtableOffset >> 16) << 5); // ldr x11, +2*4 reinterpret_cast(code)[4] = 0x5800004B; // br x11 reinterpret_cast(code)[5] = 0xD61F0160; reinterpret_cast(code)[3] = reinterpret_cast(&vtableSlotCall); return code + codeSnippetSize; } } namespace bridges::cpp_uno::shared { struct bridges::cpp_uno::shared::VtableFactory::Slot { void* fn; }; bridges::cpp_uno::shared::VtableFactory::Slot* bridges::cpp_uno::shared::VtableFactory::mapBlockToVtable(void* block) { return static_cast(block) + 1; } std::size_t bridges::cpp_uno::shared::VtableFactory::getBlockSize(sal_Int32 slotCount) { return (slotCount + 1) * sizeof(Slot) + slotCount * codeSnippetSize; } bridges::cpp_uno::shared::VtableFactory::Slot* bridges::cpp_uno::shared::VtableFactory::initializeBlock(void* block, sal_Int32 slotCount, sal_Int32, typelib_InterfaceTypeDescription*) { struct Rtti { sal_Int32 n0, n1, n2; type_info* rtti; Rtti() : n0(0) , n1(0) , n2(0) , rtti(RTTInfos::get("com.sun.star.uno.XInterface")) { } }; static Rtti rtti; Slot* slots = mapBlockToVtable(block); slots[-1].fn = &rtti; return slots + slotCount; } unsigned char* VtableFactory::addLocalFunctions(VtableFactory::Slot** slots, unsigned char* code, typelib_InterfaceTypeDescription const* type, sal_Int32 functionOffset, sal_Int32 functionCount, sal_Int32 vtableOffset) { (*slots) -= functionCount; VtableFactory::Slot* s = *slots; for (sal_Int32 i = 0; i != type->nMembers; ++i) { typelib_TypeDescription* td = nullptr; TYPELIB_DANGER_GET(&td, type->ppMembers[i]); assert(td != 0); switch (td->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: { typelib_InterfaceAttributeTypeDescription* atd = reinterpret_cast(td); // Getter: (s++)->fn = code; code = GenerateVTableSlotTrampoline(code, functionOffset++, vtableOffset); // Setter: if (!atd->bReadOnly) { (s++)->fn = code; code = GenerateVTableSlotTrampoline(code, functionOffset++, vtableOffset); } break; } case typelib_TypeClass_INTERFACE_METHOD: (s++)->fn = code; code = GenerateVTableSlotTrampoline(code, functionOffset++, vtableOffset); break; default: assert(false); } TYPELIB_DANGER_RELEASE(td); } return code; } void VtableFactory::flushCode(unsigned char const* begin, unsigned char const* end) { FlushInstructionCache(GetCurrentProcess(), begin, end - begin); } } // namespace bridges::cpp_uno::shared /* vim:set shiftwidth=4 softtabstop=4 expandtab: */