Skip to content

Commit

Permalink
Allow move of mocks.
Browse files Browse the repository at this point in the history
  • Loading branch information
FranckRJ committed May 9, 2024
1 parent bec66cb commit 26023da
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 102 deletions.
6 changes: 6 additions & 0 deletions include/fakeit/Mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ namespace fakeit {
class Mock : public ActualInvocationsSource {
MockImpl<C, baseclasses...> impl;
public:
Mock(const Mock&) = delete;
Mock(Mock&&) noexcept = default;

Mock& operator=(const Mock&) = delete;
Mock& operator=(Mock&&) = delete;

~Mock() override = default;

static_assert(std::is_polymorphic<C>::value, "Can only mock a polymorphic type");
Expand Down
16 changes: 14 additions & 2 deletions include/fakeit/MockImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,22 @@ namespace fakeit {

MockImpl(FakeitContext &fakeit)
: MockImpl<C, baseclasses...>(fakeit, *(createFakeInstance()), false){
FakeObject<C, baseclasses...> *fake = asFakeObject(_instanceOwner.get());
fake->getVirtualTable().setCookie(1, this);
_instanceOwner.get()->getVirtualTable().setCookie(1, this);
}

MockImpl(const MockImpl&) = delete;
MockImpl(MockImpl&& other) noexcept
: _instanceOwner(std::move(other._instanceOwner))
, _proxy(std::move(other._proxy))
, _fakeit(other._fakeit) {
if (isOwner()) {
_instanceOwner.get()->getVirtualTable().setCookie(1, this);
}
}

MockImpl& operator=(const MockImpl&) = delete;
MockImpl& operator=(MockImpl&&) = delete;

~MockImpl() FAKEIT_NO_THROWS override {
_proxy.detach();
}
Expand Down
57 changes: 31 additions & 26 deletions include/mockutils/DynamicProxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,8 @@ namespace fakeit {
}

public:
InvocationHandlers(
std::vector<std::shared_ptr<Destructible>> &methodMocks,
std::vector<unsigned int> &offsets) :
_methodMocks(methodMocks), _offsets(offsets) {
for (std::vector<unsigned int>::iterator it = _offsets.begin(); it != _offsets.end(); ++it)
{
*it = std::numeric_limits<int>::max();
}
InvocationHandlers(std::vector<std::shared_ptr<Destructible>> &methodMocks, std::vector<unsigned int> &offsets)
: _methodMocks(methodMocks), _offsets(offsets) {
}

Destructible *getInvocatoinHandlerPtrById(unsigned int id) override {
Expand All @@ -66,26 +60,39 @@ namespace fakeit {
static_assert(std::is_polymorphic<C>::value, "DynamicProxy requires a polymorphic type");

DynamicProxy(C &inst) :
instance(inst),
originalVtHandle(VirtualTable<C, baseclasses...>::getVTable(instance).createHandle()),
_instancePtr(&inst),
_methodMocks(VTUtils::getVTSize<C>()),
_offsets(VTUtils::getVTSize<C>()),
_offsets(VTUtils::getVTSize<C>(), std::numeric_limits<int>::max()),
_invocationHandlers(_methodMocks, _offsets) {
_cloneVt.copyFrom(originalVtHandle.restore());
_cloneVt.setCookie(InvocationHandlerCollection::VtCookieIndex, &_invocationHandlers);
getFake().setVirtualTable(_cloneVt);
_originalVt.copyFrom(VirtualTable<C, baseclasses...>::getVTable(*_instancePtr));
_originalVt.setCookie(InvocationHandlerCollection::VtCookieIndex, &_invocationHandlers);
getFake().swapVirtualTable(_originalVt);
}

void detach() {
getFake().setVirtualTable(originalVtHandle.restore());
DynamicProxy(const DynamicProxy&) = delete;
DynamicProxy(DynamicProxy&& other) noexcept
: _originalVt(std::move(other._originalVt))
, _methodMocks(std::move(other._methodMocks))
, _members(std::move(other._members))
, _offsets(std::move(other._offsets))
, _invocationHandlers(_methodMocks, _offsets) {
std::swap(_instancePtr, other._instancePtr);
VirtualTable<C, baseclasses...>::getVTable(*_instancePtr).setCookie(InvocationHandlerCollection::VtCookieIndex, &_invocationHandlers);
}

~DynamicProxy() {
_cloneVt.dispose();
DynamicProxy& operator=(const DynamicProxy&) = delete;
DynamicProxy& operator=(DynamicProxy&&) = delete;

~DynamicProxy() = default;

void detach() {
if (_instancePtr != nullptr) {
getFake().swapVirtualTable(_originalVt);
}
}

C &get() {
return instance;
return *_instancePtr;
}

void Reset() {
Expand All @@ -94,7 +101,7 @@ namespace fakeit {
_members = {};
_offsets = {};
_offsets.resize(VTUtils::getVTSize<C>());
_cloneVt.copyFrom(originalVtHandle.restore());
VirtualTable<C, baseclasses...>::getVTable(*_instancePtr).copyFrom(_originalVt);
}

void Clear()
Expand Down Expand Up @@ -170,8 +177,7 @@ namespace fakeit {
}

VirtualTable<C, baseclasses...> &getOriginalVT() {
VirtualTable<C, baseclasses...> &vt = originalVtHandle.restore();
return vt;
return _originalVt;
}

template<typename R, typename ... arglist>
Expand Down Expand Up @@ -206,17 +212,16 @@ namespace fakeit {

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;
C* _instancePtr = nullptr;
VirtualTable<C, baseclasses...> _originalVt; // avoid delete!! this is the original!
//
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);
return reinterpret_cast<FakeObject<C, baseclasses...> &>(*_instancePtr);
}

void bind(const MethodProxy &methodProxy, Destructible *invocationHandler) {
Expand Down
9 changes: 2 additions & 7 deletions include/mockutils/FakeObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,6 @@ namespace fakeit
this->initializeDataMembersArea();
}

~FakeObject()
{
this->vtable.dispose();
}

void setMethod(unsigned int index, void* method)
{
this->vtable.setMethod(index, method);
Expand All @@ -73,9 +68,9 @@ namespace fakeit
return this->vtable;
}

void setVirtualTable(VirtualTable<C, BaseClasses...>& t)
void swapVirtualTable(VirtualTable<C, BaseClasses...>& t)
{
this->vtable = t;
std::swap(this->vtable, t);
}

void setDtor(void* dtor)
Expand Down
60 changes: 25 additions & 35 deletions include/mockutils/gcc/VirtualTable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/
#pragma once

#include <utility>

#ifndef __clang__

#include "mockutils/gcc/is_simple_inheritance_layout.hpp"
Expand All @@ -24,6 +26,26 @@ namespace fakeit {

VirtualTableBase(void **firstMethod) : _firstMethod(firstMethod) { }

VirtualTableBase(const VirtualTableBase&) = delete;
VirtualTableBase(VirtualTableBase&& other) noexcept {
std::swap(_firstMethod, other._firstMethod);
}

VirtualTableBase& operator=(const VirtualTableBase&) = delete;
VirtualTableBase& operator=(VirtualTableBase&& other) noexcept {
std::swap(_firstMethod, other._firstMethod);
return *this;
}

~VirtualTableBase() {
if (_firstMethod != nullptr) {
_firstMethod--; // type_info
_firstMethod--; // top_offset
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
}
}

void *getCookie(int index) {
return _firstMethod[-3 - index];
}
Expand All @@ -41,7 +63,8 @@ namespace fakeit {
}

protected:
void **_firstMethod;
static const unsigned int numOfCookies = 2;
void **_firstMethod = nullptr;
};

template<class C, class ... baseclasses>
Expand All @@ -51,23 +74,6 @@ namespace fakeit {
static_assert(is_simple_inheritance_layout<C>::value, "Can't mock a type with multiple inheritance");
#endif

class Handle {

friend struct VirtualTable<C, baseclasses...>;
void **firstMethod;

Handle(void **method) :
firstMethod(method) {
}

public:

VirtualTable<C, baseclasses...> &restore() {
VirtualTable<C, baseclasses...> *vt = (VirtualTable<C, baseclasses...> *) this;
return *vt;
}
};

static VirtualTable<C, baseclasses...> &getVTable(C &instance) {
fakeit::VirtualTable<C, baseclasses...> *vt = (fakeit::VirtualTable<C, baseclasses...> *) (&instance);
return *vt;
Expand All @@ -85,25 +91,17 @@ namespace fakeit {
VirtualTable(buildVTArray()) {
}

void dispose() {
_firstMethod--; // type_info
_firstMethod--; // top_offset
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
}

unsigned int dtor(int) {
C *c = (C *) this;
C &cRef = *c;
auto vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
auto& vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
unsigned int index = VTUtils::getDestructorOffset<C>();
void *dtorPtr = vt.getMethod(index);
void(*method)(C *) = union_cast<void (*)(C *)>(dtorPtr);
method(c);
return 0;
}


void setDtor(void *method) {
unsigned int index = VTUtils::getDestructorOffset<C>();
void *dtorPtr = union_cast<void *>(&VirtualTable<C, baseclasses...>::dtor);
Expand All @@ -114,7 +112,6 @@ namespace fakeit {
_firstMethod[index + 1] = dtorPtr;
}


unsigned int getSize() {
return VTUtils::getVTSize<C>();
}
Expand All @@ -130,14 +127,7 @@ namespace fakeit {
return (const std::type_info *) (_firstMethod[-1]);
}

Handle createHandle() {
Handle h(_firstMethod);
return h;
}

private:
static const unsigned int numOfCookies = 2;

static void **buildVTArray() {
int size = VTUtils::getVTSize<C>();
auto array = new void *[size + 2 + numOfCookies]{};
Expand Down
55 changes: 23 additions & 32 deletions include/mockutils/mscpp/VirtualTable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
*/
#pragma once

#include <utility>
#include <typeinfo>

namespace fakeit {

typedef unsigned long dword_;
Expand Down Expand Up @@ -161,28 +164,14 @@ namespace fakeit {
}

protected:
void **_firstMethod;
static const unsigned int numOfCookies = 3;
static const unsigned int dtorCookieIndex = numOfCookies - 1; // use the last cookie
void **_firstMethod = nullptr;
};

template<class C, class... baseclasses>
struct VirtualTable : public VirtualTableBase {

class Handle {

friend struct VirtualTable<C, baseclasses...>;

void **firstMethod;

Handle(void **method) : firstMethod(method) { }

public:

VirtualTable<C, baseclasses...> &restore() {
VirtualTable<C, baseclasses...> *vt = (VirtualTable<C, baseclasses...> *) this;
return *vt;
}
};

static VirtualTable<C, baseclasses...> &getVTable(C &instance) {
fakeit::VirtualTable<C, baseclasses...> *vt = (fakeit::VirtualTable<C, baseclasses...> *) (&instance);
return *vt;
Expand All @@ -200,23 +189,32 @@ namespace fakeit {
VirtualTable() : VirtualTable(buildVTArray()) {
}

~VirtualTable() {
VirtualTable(const VirtualTable&) = delete;
VirtualTable(VirtualTable&& other) noexcept {
std::swap(_firstMethod, other._firstMethod);
}

VirtualTable& operator=(const VirtualTable&) = delete;
VirtualTable& operator=(VirtualTable&& other) noexcept {
std::swap(_firstMethod, other._firstMethod);
return *this;
}

void dispose() {
_firstMethod--; // skip objectLocator
RTTICompleteObjectLocator<C, baseclasses...> *locator = (RTTICompleteObjectLocator<C, baseclasses...> *) _firstMethod[0];
delete locator;
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
~VirtualTable() {
if (_firstMethod != nullptr) {
_firstMethod--; // skip objectLocator
RTTICompleteObjectLocator<C, baseclasses...> *locator = (RTTICompleteObjectLocator<C, baseclasses...> *) _firstMethod[0];
delete locator;
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
}
}

// the dtor VC++ must of the format: int dtor(int)
unsigned int dtor(int) {
C *c = (C *) this;
C &cRef = *c;
auto vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
auto& vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
void *dtorPtr = vt.getCookie(dtorCookieIndex);
void(*method)(C *) = reinterpret_cast<void (*)(C *)>(dtorPtr);
method(c);
Expand Down Expand Up @@ -246,20 +244,13 @@ namespace fakeit {
}
}

Handle createHandle() {
Handle h(_firstMethod);
return h;
}

private:

class SimpleType {
};

static_assert(sizeof(unsigned int (SimpleType::*)()) == sizeof(unsigned int (C::*)()),
"Can't mock a type with multiple inheritance or with non-polymorphic base class");
static const unsigned int numOfCookies = 3;
static const unsigned int dtorCookieIndex = numOfCookies - 1; // use the last cookie

static void **buildVTArray() {
int vtSize = VTUtils::getVTSize<C>();
Expand Down
Loading

0 comments on commit 26023da

Please sign in to comment.