/* * 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 #ifndef __clang__ #include "mockutils/gcc/is_simple_inheritance_layout.hpp" #endif #include "mockutils/VTUtils.hpp" #include "mockutils/union_cast.hpp" namespace fakeit { struct VirtualTableBase { static VirtualTableBase &getVTable(void *instance) { fakeit::VirtualTableBase *vt = (fakeit::VirtualTableBase *) (instance); return *vt; } VirtualTableBase(void **firstMethod) : _firstMethod(firstMethod) { } void *getCookie(int index) { return _firstMethod[-3 - index]; } void setCookie(int index, void *value) { _firstMethod[-3 - index] = value; } void *getMethod(unsigned int index) const { return _firstMethod[index]; } void setMethod(unsigned int index, void *method) { _firstMethod[index] = method; } protected: void **_firstMethod; }; template struct VirtualTable : public VirtualTableBase { #ifndef __clang__ static_assert(is_simple_inheritance_layout::value, "Can't mock a type with multiple inheritance"); #endif class Handle { friend struct VirtualTable; void **firstMethod; Handle(void **firstMethod) : firstMethod(firstMethod) { } public: VirtualTable &restore() { VirtualTable *vt = (VirtualTable *) this; return *vt; } }; static VirtualTable &getVTable(C &instance) { fakeit::VirtualTable *vt = (fakeit::VirtualTable *) (&instance); return *vt; } void copyFrom(VirtualTable &from) { unsigned int size = VTUtils::getVTSize(); //firstMethod[-1] = from.firstMethod[-1]; // copy type_info for (size_t i = 0; i < size; ++i) { _firstMethod[i] = from.getMethod(i); } } VirtualTable() : 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::getVTable(cRef); unsigned int index = VTUtils::getDestructorOffset(); void *dtorPtr = vt.getMethod(index); void(*method)(C *) = union_cast(dtorPtr); method(c); return 0; } void setDtor(void *method) { unsigned int index = VTUtils::getDestructorOffset(); void *dtorPtr = union_cast(&VirtualTable::dtor); // replace the non deleting destructor. // for example (c1->~C()) calls the non deleting dtor only _firstMethod[index] = method; // replace the deleting destructor with a method that calls the non deleting one _firstMethod[index + 1] = dtorPtr; } unsigned int getSize() { return VTUtils::getVTSize(); } void initAll(void *value) { unsigned int size = getSize(); for (unsigned int i = 0; i < size; i++) { setMethod(i, value); } } const std::type_info *getTypeId() { 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(); auto array = new void *[size + 2 + numOfCookies]{}; array += numOfCookies; // skip cookies array++; // skip top_offset array[0] = (void *) &typeid(C); // type_info array++; // skip type_info ptr return array; } VirtualTable(void **firstMethod) : VirtualTableBase(firstMethod) { } }; }