Files
UnrealEngine/Engine/Source/ThirdParty/FakeIt/2.0.2/include/mockutils/DynamicProxy.hpp
2025-05-18 13:04:45 +08:00

228 lines
7.8 KiB
C++

/*
* Copyright (c) 2014 Eran Pe'er.
*
* This program is made available under the terms of the MIT License.
*
* Created on Mar 10, 2014
*/
#pragma once
#include <functional>
#include <type_traits>
#include <vector>
#include <array>
#include <new>
#ifdef _MSC_VER
#include "mockutils/mscpp/VirtualTable.hpp"
#else
#include "mockutils/gcc/VirtualTable.hpp"
#endif
#include "mockutils/union_cast.hpp"
#include "mockutils/MethodInvocationHandler.hpp"
#include "mockutils/VTUtils.hpp"
#include "mockutils/FakeObject.hpp"
#include "mockutils/MethodProxy.hpp"
#include "MethodProxyCreator.hpp"
namespace fakeit {
class InvocationHandlers : public InvocationHandlerCollection {
std::vector<std::shared_ptr<Destructible>> &_methodMocks;
std::vector<unsigned int> &_offsets;
unsigned int getOffset(unsigned int id) {
unsigned int offset = 0;
for (; offset < _offsets.size(); offset++) {
if (_offsets[offset] == id) {
break;
}
}
return offset;
}
public:
InvocationHandlers(
std::vector<std::shared_ptr<Destructible>> &methodMocks,
std::vector<unsigned int> &offsets) :
_methodMocks(methodMocks), _offsets(offsets) {
}
Destructible *getInvocatoinHandlerPtrById(unsigned int id) override {
unsigned int offset = getOffset(id);
std::shared_ptr<Destructible> ptr = _methodMocks[offset];
return ptr.get();
}
};
template<typename C, typename ... baseclasses>
struct DynamicProxy {
static_assert(std::is_polymorphic<C>::value, "DynamicProxy requires a polymorphic type");
DynamicProxy(C &instance) :
instance(instance),
originalVtHandle(VirtualTable<C, baseclasses...>::getVTable(instance).createHandle()),
_methodMocks(VTUtils::getVTSize<C>()),
_offsets(VTUtils::getVTSize<C>()),
_invocationHandlers(_methodMocks, _offsets) {
_cloneVt.copyFrom(originalVtHandle.restore());
_cloneVt.setCookie(InvocationHandlerCollection::VT_COOKIE_INDEX, &_invocationHandlers);
getFake().setVirtualTable(_cloneVt);
}
void detach() {
getFake().setVirtualTable(originalVtHandle.restore());
}
~DynamicProxy() {
_cloneVt.dispose();
}
C &get() {
return instance;
}
void Reset() {
_methodMocks = {{}};
_members = {};
_cloneVt.copyFrom(originalVtHandle.restore());
}
template<int id, typename R, typename ... arglist>
void stubMethod(R(C::*vMethod)(arglist...), MethodInvocationHandler<R, arglist...> *methodInvocationHandler) {
auto offset = VTUtils::getOffset(vMethod);
MethodProxyCreator<R, arglist...> creator;
bind(creator.template createMethodProxy<id + 1>(offset), methodInvocationHandler);
}
void stubDtor(MethodInvocationHandler<void> *methodInvocationHandler) {
auto offset = VTUtils::getDestructorOffset<C>();
MethodProxyCreator<void> creator;
bindDtor(creator.createMethodProxy<0>(offset), methodInvocationHandler);
}
template<typename R, typename ... arglist>
bool isMethodStubbed(R(C::*vMethod)(arglist...)) {
unsigned int offset = VTUtils::getOffset(vMethod);
return isBinded(offset);
}
bool isDtorStubbed() {
unsigned int offset = VTUtils::getDestructorOffset<C>();
return isBinded(offset);
}
template<typename R, typename ... arglist>
Destructible *getMethodMock(R(C::*vMethod)(arglist...)) {
auto offset = VTUtils::getOffset(vMethod);
std::shared_ptr<Destructible> ptr = _methodMocks[offset];
return ptr.get();
}
Destructible *getDtorMock() {
auto offset = VTUtils::getDestructorOffset<C>();
std::shared_ptr<Destructible> ptr = _methodMocks[offset];
return ptr.get();
}
template<typename DATA_TYPE, typename ... arglist>
void stubDataMember(DATA_TYPE C::*member, const arglist &... initargs) {
DATA_TYPE C::*theMember = (DATA_TYPE C::*) member;
C &mock = get();
DATA_TYPE *memberPtr = &(mock.*theMember);
_members.push_back(
std::shared_ptr<DataMemeberWrapper < DATA_TYPE, arglist...> >
{new DataMemeberWrapper < DATA_TYPE, arglist...>(memberPtr,
initargs...)});
}
template<typename DATA_TYPE>
void getMethodMocks(std::vector<DATA_TYPE> &into) const {
for (std::shared_ptr<Destructible> ptr : _methodMocks) {
DATA_TYPE p = dynamic_cast<DATA_TYPE>(ptr.get());
if (p) {
into.push_back(p);
}
}
}
VirtualTable<C, baseclasses...> &getOriginalVT() {
VirtualTable<C, baseclasses...> &vt = originalVtHandle.restore();
return vt;
}
private:
template<typename DATA_TYPE, typename ... arglist>
class DataMemeberWrapper : public Destructible {
private:
DATA_TYPE *dataMember;
public:
DataMemeberWrapper(DATA_TYPE *dataMember, const arglist &... initargs) :
dataMember(dataMember) {
new(dataMember) DATA_TYPE{initargs ...};
}
~DataMemeberWrapper() {
dataMember->~DATA_TYPE();
}
};
static_assert(sizeof(C) == sizeof(FakeObject<C, baseclasses...>), "This is a problem");
C &instance;
typename VirtualTable<C, baseclasses...>::Handle originalVtHandle; // avoid delete!! this is the original!
VirtualTable<C, baseclasses...> _cloneVt;
//
std::vector<std::shared_ptr<Destructible>> _methodMocks;
std::vector<std::shared_ptr<Destructible>> _members;
std::vector<unsigned int> _offsets;
InvocationHandlers _invocationHandlers;
FakeObject<C, baseclasses...> &getFake() {
return reinterpret_cast<FakeObject<C, baseclasses...> &>(instance);
}
void bind(const MethodProxy &methodProxy, Destructible *invocationHandler) {
getFake().setMethod(methodProxy.getOffset(), methodProxy.getProxy());
_methodMocks[methodProxy.getOffset()].reset(invocationHandler);
_offsets[methodProxy.getOffset()] = methodProxy.getId();
}
void bindDtor(const MethodProxy &methodProxy, Destructible *invocationHandler) {
getFake().setDtor(methodProxy.getProxy());
_methodMocks[methodProxy.getOffset()].reset(invocationHandler);
_offsets[methodProxy.getOffset()] = methodProxy.getId();
}
template<typename DATA_TYPE>
DATA_TYPE getMethodMock(unsigned int offset) {
std::shared_ptr<Destructible> ptr = _methodMocks[offset];
return dynamic_cast<DATA_TYPE>(ptr.get());
}
template<typename BaseClass>
void checkMultipleInheritance() {
C *ptr = (C *) (unsigned int) 1;
BaseClass *basePtr = ptr;
int delta = (unsigned long) basePtr - (unsigned long) ptr;
if (delta > 0) {
// base class does not start on same position as derived class.
// this is multiple inheritance.
throw std::invalid_argument(std::string("multiple inheritance is not supported"));
}
}
bool isBinded(unsigned int offset) {
std::shared_ptr<Destructible> ptr = _methodMocks[offset];
return ptr.get() != nullptr;
}
};
}