mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-12-07 10:28:30 +00:00
First version of automatic hookman/hookfunc generation functionality + test
Currently supports (msvc): all params, ret types: integeral, floating-point Todo: other rettypes (pod/objects, ctors/dtors), gcc support, integration with ISourceHook::AddHook --HG-- branch : hookman_autogen extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/branches/hookman_autogen%40532
This commit is contained in:
parent
f440bbd1b2
commit
7fa9f150fb
1292
sourcehook/sourcehook_hookmangen.cpp
Normal file
1292
sourcehook/sourcehook_hookmangen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
232
sourcehook/sourcehook_hookmangen.h
Normal file
232
sourcehook/sourcehook_hookmangen.h
Normal file
@ -0,0 +1,232 @@
|
||||
/* ======== SourceHook ========
|
||||
* Copyright (C) 2004-2007 Metamod:Source Development Team
|
||||
* No warranties of any kind
|
||||
*
|
||||
* License: zlib/libpng
|
||||
*
|
||||
* Author(s): Pavol "PM OnoTo" Marko
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#ifndef __SOURCEHOOK_HOOKMANGEN_H__
|
||||
#define __SOURCEHOOK_HOOKMANGEN_H__
|
||||
|
||||
#include "sourcehook_impl.h"
|
||||
#include "windows.h"
|
||||
|
||||
namespace SourceHook
|
||||
{
|
||||
namespace Impl
|
||||
{
|
||||
|
||||
// Code gen stuff
|
||||
#if defined HAVE_STDINT_H && !defined WIN32
|
||||
#include <stdint.h>
|
||||
typedef int8_t jit_int8_t;
|
||||
typedef uint8_t jit_uint8_t;
|
||||
typedef int32_t jit_int32_t;
|
||||
typedef uint32_t jit_uint32_t;
|
||||
typedef int64_t jit_int64_t;
|
||||
typedef uint64_t jit_uint64_t;
|
||||
#elif defined WIN32
|
||||
typedef __int8 jit_int8_t;
|
||||
typedef unsigned __int8 jit_uint8_t;
|
||||
typedef __int32 jit_int32_t;
|
||||
typedef unsigned __int32 jit_uint32_t;
|
||||
typedef __int64 jit_int64_t;
|
||||
typedef unsigned __int64 jit_uint64_t;
|
||||
#endif
|
||||
typedef unsigned int jitoffs_t;
|
||||
typedef signed int jitrel_t;
|
||||
|
||||
|
||||
class GenBuffer
|
||||
{
|
||||
unsigned char *m_pData;
|
||||
jitoffs_t m_Size;
|
||||
jitoffs_t m_AllocatedSize;
|
||||
|
||||
public:
|
||||
GenBuffer() : m_pData(NULL), m_Size(0), m_AllocatedSize(0)
|
||||
{
|
||||
}
|
||||
~GenBuffer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
jitoffs_t GetSize()
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
unsigned char *GetData()
|
||||
{
|
||||
return m_pData;
|
||||
}
|
||||
|
||||
template <class PT> void push(PT what)
|
||||
{
|
||||
push((const unsigned char *)&what, sizeof(PT));
|
||||
}
|
||||
|
||||
void push(const unsigned char *data, jitoffs_t size)
|
||||
{
|
||||
jitoffs_t newSize = m_Size + size;
|
||||
if (newSize > m_AllocatedSize)
|
||||
{
|
||||
m_AllocatedSize = newSize > m_AllocatedSize*2 ? newSize : m_AllocatedSize*2;
|
||||
if (m_AllocatedSize < 64)
|
||||
m_AllocatedSize = 64;
|
||||
|
||||
unsigned char *newBuf;
|
||||
try
|
||||
{
|
||||
//!!!! Better use of pages! or something!
|
||||
newBuf = reinterpret_cast<unsigned char*>(VirtualAlloc(NULL, m_AllocatedSize, MEM_COMMIT, PAGE_READWRITE));
|
||||
}
|
||||
catch (std::bad_alloc)
|
||||
{
|
||||
newBuf = NULL;
|
||||
}
|
||||
if (!newBuf)
|
||||
{
|
||||
SH_ASSERT(0, ("bad_alloc: couldn't allocate 0x%08X bytes of memory\n", m_AllocatedSize));
|
||||
return;
|
||||
}
|
||||
memcpy((void*)newBuf, (const void*)m_pData, m_Size);
|
||||
if (m_pData)
|
||||
VirtualFree(m_pData, 0, MEM_RELEASE);
|
||||
m_pData = newBuf;
|
||||
}
|
||||
memcpy((void*)(m_pData + m_Size), (const void*)data, size);
|
||||
m_Size = newSize;
|
||||
}
|
||||
|
||||
template <class PT> void rewrite(jitoffs_t offset, PT what)
|
||||
{
|
||||
rewrite(offset, (const unsigned char *)&what, sizeof(PT));
|
||||
}
|
||||
|
||||
void rewrite(jitoffs_t offset, const unsigned char *data, jitoffs_t size)
|
||||
{
|
||||
SH_ASSERT(offset + size <= m_AllocatedSize, ("rewrite too far"));
|
||||
|
||||
memcpy((void*)(m_pData + offset), (const void*)data, size);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_pData)
|
||||
VirtualFree(m_pData, 0, MEM_RELEASE);
|
||||
m_pData = NULL;
|
||||
m_Size = 0;
|
||||
m_AllocatedSize = 0;
|
||||
}
|
||||
|
||||
operator unsigned char *()
|
||||
{
|
||||
return GetData();
|
||||
}
|
||||
|
||||
void write_ubyte(jit_uint8_t x) { push(x); }
|
||||
void write_byte(jit_uint8_t x) { push(x); }
|
||||
|
||||
void write_ushort(unsigned short x) { push(x); }
|
||||
void write_short(signed short x) { push(x); }
|
||||
|
||||
void write_uint32(jit_uint32_t x) { push(x); }
|
||||
void write_int32(jit_uint32_t x) { push(x); }
|
||||
|
||||
jitoffs_t get_outputpos()
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
void start_count(jitoffs_t &offs)
|
||||
{
|
||||
offs = get_outputpos();
|
||||
}
|
||||
void end_count(jitoffs_t &offs)
|
||||
{
|
||||
offs = get_outputpos() - offs;
|
||||
}
|
||||
// :TODO: real buffer which uses virtualalloc correctly
|
||||
};
|
||||
|
||||
class GenContext
|
||||
{
|
||||
const static int SIZE_MWORD = 4;
|
||||
const static int SIZE_PTR = sizeof(void*);
|
||||
|
||||
CProto m_Proto;
|
||||
int m_VtblOffs;
|
||||
int m_VtblIdx;
|
||||
ISourceHook *m_SHPtr;
|
||||
|
||||
GenBuffer m_HookFunc;
|
||||
GenBuffer m_PubFunc;
|
||||
|
||||
ProtoInfo *m_BuiltPI;
|
||||
PassInfo *m_BuiltPI_Params;
|
||||
PassInfo::V2Info *m_BuiltPI_Params2;
|
||||
|
||||
// For hookfunc
|
||||
void **m_pHI;
|
||||
void **m_HookfuncVfnptr;
|
||||
|
||||
// Level 3 - Helpers
|
||||
int m_RegCounter;
|
||||
jit_int8_t NextRegEBX_ECX_EDX();
|
||||
|
||||
// size info
|
||||
jit_int32_t GetRealSize(const IntPassInfo &info);
|
||||
jit_int32_t GetStackSize(const IntPassInfo &info);
|
||||
short GetParamsStackSize(); // sum(GetStackSize(i), 0 <= i < numOfParams)
|
||||
|
||||
|
||||
// Param push
|
||||
jit_int32_t PushParams(jit_int32_t param_base_offset);
|
||||
jit_int32_t PushRef(jit_int32_t param_offset, const IntPassInfo &pi);
|
||||
jit_int32_t PushBasic(jit_int32_t param_offset, const IntPassInfo &pi);
|
||||
jit_int32_t PushFloat(jit_int32_t param_offset, const IntPassInfo &pi);
|
||||
jit_int32_t PushObject(jit_int32_t param_offset, const IntPassInfo &pi);
|
||||
|
||||
// Ret val processing
|
||||
void SaveRetVal(int v_where);
|
||||
void ProcessPluginRetVal(int v_cur_res, int v_pContext, int v_plugin_ret);
|
||||
|
||||
void PrepareReturn(int v_status, int v_pContext, int v_retptr);
|
||||
void DoReturn(int v_retptr);
|
||||
|
||||
// Call hooks
|
||||
void GenerateCallHooks(int v_status, int v_prev_res, int v_cur_res, int v_iter,
|
||||
int v_pContext, int base_param_offset, int v_plugin_ret);
|
||||
|
||||
// Call orig
|
||||
void GenerateCallOrig(int v_status, int v_pContext,
|
||||
int param_base_offs, int v_this, int v_vfnptr_origentry, int v_orig_ret, int v_override_ret);
|
||||
|
||||
// Hook loop
|
||||
void CallSetupHookLoop(int v_orig_ret, int v_override_ret,
|
||||
int v_cur_res, int v_prev_res, int v_status, int v_vnfptr_origentry,
|
||||
int v_this, int v_pContext);
|
||||
|
||||
void CallEndContext(int v_pContext);
|
||||
|
||||
// Level 2 -> called from Generate()
|
||||
bool PassInfoSupported(const IntPassInfo &pi, bool is_ret);
|
||||
void Clear();
|
||||
void BuildProtoInfo();
|
||||
unsigned char *GenerateHookFunc();
|
||||
unsigned char *GeneratePubFunc();
|
||||
public:
|
||||
// Level 1 -> Public interface
|
||||
GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr);
|
||||
~GenContext();
|
||||
|
||||
HookManagerPubFunc Generate();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
1585
sourcehook/sourcehook_hookmangen_x86.h
Normal file
1585
sourcehook/sourcehook_hookmangen_x86.h
Normal file
File diff suppressed because it is too large
Load Diff
6
sourcehook/test/generate.bat
Normal file
6
sourcehook/test/generate.bat
Normal file
@ -0,0 +1,6 @@
|
||||
:: Generates everything
|
||||
:: Usage:
|
||||
:: generate.bat <num-of-arguments>
|
||||
|
||||
|
||||
..\generate\shworker iter testhookmangen.hxx testhookmangen.h %1
|
||||
@ -46,6 +46,7 @@ DECL_TEST(Multi);
|
||||
DECL_TEST(Ref);
|
||||
DECL_TEST(RefRet);
|
||||
DECL_TEST(VPHooks);
|
||||
DECL_TEST(HookManGen);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -68,6 +69,7 @@ int main(int argc, char *argv[])
|
||||
DO_TEST(Ref);
|
||||
DO_TEST(RefRet);
|
||||
DO_TEST(VPHooks);
|
||||
DO_TEST(HookManGen);
|
||||
|
||||
cout << endl << "----" << endl << "Passed: " << passed << endl << "Failed: " << failed << endl;
|
||||
cout << "Total: " << passed + failed << endl;
|
||||
|
||||
@ -17,6 +17,7 @@ struct CAutoPtrDestruction
|
||||
CAutoPtrDestruction(T *p) : m_Ptr(p) { }
|
||||
~CAutoPtrDestruction() { if (m_Ptr) delete m_Ptr; }
|
||||
void clear() { m_Ptr = NULL; }
|
||||
void set(T *ptr) { m_Ptr = ptr; }
|
||||
};
|
||||
|
||||
struct CSHPtrAutoDestruction
|
||||
|
||||
@ -31,9 +31,25 @@ struct State
|
||||
return (typeid(other) == typeid(this)) ? true : false;
|
||||
}
|
||||
|
||||
virtual bool Ignore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void Dump() = 0;
|
||||
};
|
||||
|
||||
struct IgnoreState : public State
|
||||
{
|
||||
virtual bool Ignore()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual void Dump()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::list<State*> StateList;
|
||||
namespace
|
||||
{
|
||||
@ -53,6 +69,8 @@ namespace
|
||||
State *cs = va_arg(argptr, State*);
|
||||
if (!cs)
|
||||
break;
|
||||
if (cs->Ignore())
|
||||
continue;
|
||||
requiredstates.push_back(cs);
|
||||
}
|
||||
va_end(argptr);
|
||||
@ -86,6 +104,14 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok && g_Verbose)
|
||||
{
|
||||
std::cout << std::endl << "FAIL: Should be:" << std::endl;
|
||||
DumpStates(&requiredstates);
|
||||
std::cout << std::endl << "FAIL: Is:" << std::endl;
|
||||
DumpStates(sl);
|
||||
}
|
||||
|
||||
for (StateList::iterator iter = requiredstates.begin(); iter != requiredstates.end(); ++iter)
|
||||
delete *iter;
|
||||
for (StateList::iterator iter = sl->begin(); iter != sl->end(); ++iter)
|
||||
|
||||
470
sourcehook/test/testhookmangen.cpp
Normal file
470
sourcehook/test/testhookmangen.cpp
Normal file
@ -0,0 +1,470 @@
|
||||
#include <string>
|
||||
#include "sourcehook.h"
|
||||
#include "sourcehook_test.h"
|
||||
#include "testevents.h"
|
||||
#include "sourcehook_hookmangen.h"
|
||||
|
||||
// TESTHOOKMANGEN
|
||||
// Test automatic hookman generation
|
||||
// Tests void and non-void functions
|
||||
// 0 to 6 params:
|
||||
// integer-type, float-type, plain-old-data struct and objects with ctors/dtors
|
||||
// both byval and byref
|
||||
// also tests ignore/supercede
|
||||
// also tests recalls
|
||||
|
||||
// :TODO: test override as well
|
||||
|
||||
namespace
|
||||
{
|
||||
#include "testhookmangen.h"
|
||||
|
||||
StateList g_States;
|
||||
SourceHook::ISourceHook *g_SHPtr;
|
||||
SourceHook::Plugin g_PLID;
|
||||
|
||||
// PtrBuf(ptr) gives ptrs unique numbers
|
||||
// in the order they appear
|
||||
SourceHook::List<const void*> g_PtrHash;
|
||||
|
||||
bool g_Inside_LeafFunc = false; // inside a hook or a func
|
||||
|
||||
template <class T>
|
||||
int PtrBuf(T ptr)
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
const void *vptr = reinterpret_cast<const void*>(ptr);
|
||||
for (SourceHook::List<const void*>::iterator iter = g_PtrHash.begin(); iter != g_PtrHash.end(); ++iter)
|
||||
{
|
||||
if (*iter == vptr)
|
||||
return a;
|
||||
else
|
||||
++a;
|
||||
}
|
||||
g_PtrHash.push_back(vptr);
|
||||
return static_cast<int>(g_PtrHash.size()) - 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T PtrBufPtr(T ptr)
|
||||
{
|
||||
PtrBuf(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void PtrBuf_Clear(int leave_in = 0)
|
||||
{
|
||||
for (SourceHook::List<const void*>::iterator iter = g_PtrHash.begin(); iter != g_PtrHash.end();)
|
||||
{
|
||||
if (--leave_in < 0)
|
||||
iter = g_PtrHash.erase(iter);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// POD / Object types
|
||||
template <int MYSIZE>
|
||||
struct POD
|
||||
{
|
||||
char x[MYSIZE];
|
||||
|
||||
bool operator==(const POD<MYSIZE> &other)
|
||||
{
|
||||
return memcmp(reinterpret_cast<void*>(x), reinterpret_cast<const void*>(other.x), MYSIZE) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <int MYSIZE>
|
||||
std::ostream& operator <<(std::ostream &os, const POD<MYSIZE> &obj)
|
||||
{
|
||||
os << "Some POD!";
|
||||
return os;
|
||||
}
|
||||
|
||||
MAKE_STATE_1(State_ObjOCtor_Called, int /*MYSIZE*/);
|
||||
MAKE_STATE_1(State_ObjCCtor_Called, int /*MYSIZE*/);
|
||||
MAKE_STATE_1(State_ObjODtor_Called, int /*MYSIZE*/);
|
||||
|
||||
template <int MYSIZE>
|
||||
struct Object
|
||||
{
|
||||
char x[MYSIZE];
|
||||
|
||||
Object(char initch)
|
||||
{
|
||||
memset(reinterpret_cast<void*>(x), initch, MYSIZE);
|
||||
if (!g_Inside_LeafFunc)
|
||||
ADD_STATE(State_ObjOCtor_Called(MYSIZE));
|
||||
}
|
||||
|
||||
Object()
|
||||
{
|
||||
if (!g_Inside_LeafFunc)
|
||||
ADD_STATE(State_ObjOCtor_Called(MYSIZE));
|
||||
}
|
||||
|
||||
Object(const Object<MYSIZE> & other)
|
||||
{
|
||||
memcpy(reinterpret_cast<void*>(x), reinterpret_cast<const void*>(other.x), MYSIZE);
|
||||
if (!g_Inside_LeafFunc)
|
||||
ADD_STATE(State_ObjCCtor_Called(MYSIZE));
|
||||
}
|
||||
|
||||
~Object()
|
||||
{
|
||||
if (!g_Inside_LeafFunc)
|
||||
ADD_STATE(State_ObjODtor_Called(MYSIZE));
|
||||
}
|
||||
|
||||
bool operator==(const Object<MYSIZE> &other)
|
||||
{
|
||||
return memcmp(reinterpret_cast<void*>(x), reinterpret_cast<const void*>(other.x), MYSIZE) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <int MYSIZE>
|
||||
std::ostream& operator <<(std::ostream &os, const Object<MYSIZE> &obj)
|
||||
{
|
||||
os << "Some Obj!";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
// "Increment" something: (reproducible change for recall tests)
|
||||
// integer: ++
|
||||
// float: += 1.3
|
||||
// pod/object: x[i]++, 0 <= i < MYSIZE
|
||||
template <class T>
|
||||
struct Increment
|
||||
{
|
||||
static void Incr(T &what)
|
||||
{
|
||||
++what;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Increment<float>
|
||||
{
|
||||
static void Incr(float &what)
|
||||
{
|
||||
what += 1.3f;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Increment<double>
|
||||
{
|
||||
static void Incr(double &what)
|
||||
{
|
||||
what += 1.3;
|
||||
}
|
||||
};
|
||||
|
||||
template<int MYSIZE>
|
||||
struct Increment< POD<MYSIZE> >
|
||||
{
|
||||
static void Incr(POD<MYSIZE> &what)
|
||||
{
|
||||
for (int i = 0; i < MYSIZE; ++i)
|
||||
++ what.x[i];
|
||||
}
|
||||
};
|
||||
|
||||
template<int MYSIZE>
|
||||
struct Increment< Object<MYSIZE> >
|
||||
{
|
||||
static void Incr(Object<MYSIZE> &what)
|
||||
{
|
||||
for (int i = 0; i < MYSIZE; ++i)
|
||||
++ what.x[i];
|
||||
}
|
||||
};
|
||||
|
||||
// MyDelegate base class for other delegates
|
||||
class MyDelegate : public SourceHook::ISHDelegate
|
||||
{
|
||||
// Unneeded
|
||||
// because we don't use old SH_REMOVE_HOOK syntax
|
||||
virtual bool IsEqual(SourceHook::ISHDelegate *pOtherDeleg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void DeleteThis()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
THGM_MAKE_TEST0_void(0);
|
||||
THGM_SETUP_PI0(0);
|
||||
|
||||
THGM_MAKE_TEST1_void(1, char);
|
||||
THGM_SETUP_PI1(1, char, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST1_void(2, short);
|
||||
THGM_SETUP_PI1(2, short, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST1_void(3, int);
|
||||
THGM_SETUP_PI1(3, int, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST1_void(4, float);
|
||||
THGM_SETUP_PI1(4, float, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST1_void(5, double);
|
||||
THGM_SETUP_PI1(5, double, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST5_void(6, char, short, int, float, double);
|
||||
THGM_SETUP_PI5(6,
|
||||
char, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal,
|
||||
short, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal,
|
||||
int, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal,
|
||||
float, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByVal,
|
||||
double, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByVal
|
||||
);
|
||||
|
||||
THGM_MAKE_TEST2_void(7, char&, double&);
|
||||
THGM_SETUP_PI2(7,
|
||||
char&, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByRef,
|
||||
double&, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByRef
|
||||
);
|
||||
|
||||
THGM_MAKE_TEST1_void(8, POD<7>);
|
||||
THGM_SETUP_PI1(8, POD<7>, SourceHook::PassInfo::PassType_Object, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST1_void(9, POD<600>);
|
||||
THGM_SETUP_PI1(9, POD<600>, SourceHook::PassInfo::PassType_Object, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST1_void(10, POD<600> &);
|
||||
THGM_SETUP_PI1(10, POD<600> &, SourceHook::PassInfo::PassType_Object, SourceHook::PassInfo::PassFlag_ByRef);
|
||||
|
||||
THGM_MAKE_TEST2_void(11, Object<3>, Object<600>&);
|
||||
THGM_SETUP_PI2(11,
|
||||
Object<3>, SourceHook::PassInfo::PassType_Object, SourceHook::PassInfo::PassFlag_ByVal | SourceHook::PassInfo::PassFlag_OCtor | SourceHook::PassInfo::PassFlag_ODtor | SourceHook::PassInfo::PassFlag_CCtor,
|
||||
Object<600> &, SourceHook::PassInfo::PassType_Object, SourceHook::PassInfo::PassFlag_ByRef | SourceHook::PassInfo::PassFlag_OCtor | SourceHook::PassInfo::PassFlag_ODtor | SourceHook::PassInfo::PassFlag_CCtor
|
||||
);
|
||||
|
||||
THGM_MAKE_TEST0(101, char);
|
||||
THGM_SETUP_PI0(101);
|
||||
THGM_SETUP_RI(101, char, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST0(102, short);
|
||||
THGM_SETUP_PI0(102);
|
||||
THGM_SETUP_RI(102, short, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST0(103, int);
|
||||
THGM_SETUP_PI0(103);
|
||||
THGM_SETUP_RI(103, int, SourceHook::PassInfo::PassType_Basic, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST0(104, float);
|
||||
THGM_SETUP_PI0(104);
|
||||
THGM_SETUP_RI(104, float, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
|
||||
THGM_MAKE_TEST0(105, double);
|
||||
THGM_SETUP_PI0(105);
|
||||
THGM_SETUP_RI(105, double, SourceHook::PassInfo::PassType_Float, SourceHook::PassInfo::PassFlag_ByVal);
|
||||
}
|
||||
|
||||
|
||||
bool TestHookManGen(std::string &error)
|
||||
{
|
||||
GET_SHPTR(g_SHPtr);
|
||||
g_PLID = 1337;
|
||||
|
||||
THGM_DO_TEST_void(0, ());
|
||||
|
||||
THGM_DO_TEST_void(1, (100));
|
||||
|
||||
THGM_DO_TEST_void(2, (0x1F00));
|
||||
|
||||
THGM_DO_TEST_void(3, (0x1F000000));
|
||||
|
||||
THGM_DO_TEST_void(4, (0.5f));
|
||||
|
||||
THGM_DO_TEST_void(5, (5.5));
|
||||
|
||||
THGM_DO_TEST_void(6, (100, 0x1f00, 0x1f000000, 0.5f, 5.5));
|
||||
|
||||
char a = 5;
|
||||
double b = 233.33;
|
||||
THGM_DO_TEST_void(7, (a, b));
|
||||
|
||||
POD<7> pod7 = {78};
|
||||
THGM_DO_TEST_void(8, (pod7));
|
||||
|
||||
POD<600> pod600 = {34};
|
||||
THGM_DO_TEST_void(9, (pod600));
|
||||
|
||||
THGM_DO_TEST_void(10, (pod600));
|
||||
|
||||
// Test11: Special: constructors/destructors
|
||||
PtrBuf_Clear();
|
||||
Object<3> obj3(33);
|
||||
Object<600> obj600(21);
|
||||
|
||||
CHECK_STATES((&g_States,
|
||||
new State_ObjOCtor_Called(3),
|
||||
new State_ObjOCtor_Called(600),
|
||||
NULL), "Test11 Part0");
|
||||
|
||||
setuppi_11();
|
||||
g_Genc11 = new SourceHook::Impl::GenContext(&protoinfo_11, 0, 0, g_SHPtr);
|
||||
g_Genc_ad11.set(g_Genc11);
|
||||
SourceHook::HookManagerPubFunc myhookman11 = g_Genc11->Generate();
|
||||
int hook1_11, hook2_11, hook3_11, hook4_11;
|
||||
|
||||
TestClass11 *pTest11 = new TestClass11;
|
||||
CAutoPtrDestruction<TestClass11> apd11(pTest11);
|
||||
|
||||
/* no hooks - no hooks */
|
||||
PtrBuf_Clear();
|
||||
pTest11->Func(obj3, obj600);
|
||||
|
||||
g_Inside_LeafFunc = true;
|
||||
CHECK_STATES((&g_States,
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Func11(pTest11, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
NULL), "Test" "11" " Part1");
|
||||
g_Inside_LeafFunc = false;
|
||||
|
||||
/* hook1 - no hooks */
|
||||
THGM_ADD_HOOK(11, 1);
|
||||
|
||||
PtrBuf_Clear();
|
||||
pTest11->Func(obj3, obj600);
|
||||
g_Inside_LeafFunc = true;
|
||||
CHECK_STATES((&g_States,
|
||||
new State_ObjCCtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg1_11(pTest11, 0, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Func11(pTest11, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
new State_ObjODtor_Called(3),
|
||||
NULL), "Test" "11" " Part2");
|
||||
g_Inside_LeafFunc = false;
|
||||
|
||||
THGM_REMOVE_HOOK(11, 1);
|
||||
|
||||
/* hook1, hook2 - hook3, hook4 */
|
||||
THGM_ADD_HOOK(11, 1);
|
||||
THGM_ADD_HOOK(11, 2);
|
||||
THGM_ADD_HOOK(11, 3);
|
||||
THGM_ADD_HOOK(11, 4);
|
||||
PtrBuf_Clear();
|
||||
pTest11->Func(obj3, obj600);
|
||||
g_Inside_LeafFunc = true;
|
||||
CHECK_STATES((&g_States,
|
||||
new State_ObjCCtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg1_11(pTest11, 0, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg2_11(pTest11, 1, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg3_11(pTest11, 2, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg4_11(pTest11, 3, ParamState_m11 (obj3, obj600)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
new State_ObjODtor_Called(3),
|
||||
NULL), "Test" "11" " Part3");
|
||||
g_Inside_LeafFunc = false;
|
||||
|
||||
/* hook1 - hook3, hook4 WITH RECALLS */
|
||||
THGM_REMOVE_HOOK(11, 2);
|
||||
PtrBuf_Clear();
|
||||
TestClass11::ms_DoRecall = true;
|
||||
pTest11->Func(obj3, obj600);
|
||||
g_Inside_LeafFunc = true;
|
||||
CHECK_STATES((&g_States,
|
||||
new State_ObjCCtor_Called(3),
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg1_11(pTest11, 0 /* first deleg ptr */, ParamState_m11 (obj3, obj600)),
|
||||
|
||||
// recall !
|
||||
// second hookfunc -> new copy
|
||||
new State_ObjCCtor_Called(3),
|
||||
|
||||
// in second hookfunc now
|
||||
// -> calls orig func
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Func11(pTest11, ParamState_m11 (obj3, obj600)(1)),
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
// calls first posthook
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg3_11(pTest11, 1 /* second deleg ptr */, ParamState_m11 (obj3, obj600)(1)),
|
||||
|
||||
// recall!
|
||||
// third hookfunc -> new copy
|
||||
new State_ObjCCtor_Called(3),
|
||||
|
||||
// calls second posthook
|
||||
|
||||
new State_ObjCCtor_Called(3),
|
||||
new State_Deleg4_11(pTest11, 2 /* third deleg ptr */, ParamState_m11 (obj3, obj600)(2)),
|
||||
|
||||
// recall!
|
||||
// fourth hookfunc -> new copy
|
||||
new State_ObjCCtor_Called(3),
|
||||
|
||||
// has nothing to do though!
|
||||
|
||||
// fourth hookfunc done -> ret
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
// third hookfunc done -> ret
|
||||
new State_ObjODtor_Called(3),
|
||||
// ret from hookhandler which did the recall
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
// second hookfunc done -> ret
|
||||
new State_ObjODtor_Called(3),
|
||||
// ret from hookhandler which did the recall
|
||||
new State_ObjODtor_Called(3),
|
||||
|
||||
// deleg1's instance
|
||||
new State_ObjODtor_Called(3),
|
||||
// first hookfunc done -> ret
|
||||
new State_ObjODtor_Called(3),
|
||||
NULL), "Test" "11" " Part4");
|
||||
g_Inside_LeafFunc = false;
|
||||
|
||||
THGM_REMOVE_HOOK(11, 1);
|
||||
THGM_REMOVE_HOOK(11, 3);
|
||||
THGM_REMOVE_HOOK(11, 4);
|
||||
|
||||
THGM_DO_TEST(101, ());
|
||||
|
||||
THGM_DO_TEST(102, ());
|
||||
|
||||
THGM_DO_TEST(103, ());
|
||||
|
||||
THGM_DO_TEST(104, ());
|
||||
|
||||
THGM_DO_TEST(105, ());
|
||||
|
||||
// Shutdown now!
|
||||
// If we don't SH will auto-shutdown _after_ genc's destructor is called
|
||||
// -> crash
|
||||
|
||||
Test_CompleteShutdown(g_SHPtr);
|
||||
return true;
|
||||
}
|
||||
2090
sourcehook/test/testhookmangen.h
Normal file
2090
sourcehook/test/testhookmangen.h
Normal file
File diff suppressed because it is too large
Load Diff
578
sourcehook/test/testhookmangen.hxx
Normal file
578
sourcehook/test/testhookmangen.hxx
Normal file
@ -0,0 +1,578 @@
|
||||
// Strip &
|
||||
template <class T> struct StripRef
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T> struct StripRef<T&>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
// Address of constructor/destructor
|
||||
// (using wrappers)
|
||||
template <class T>
|
||||
class Ctor_Thunk
|
||||
{
|
||||
public:
|
||||
void NormalConstructor()
|
||||
{
|
||||
new(this) T;
|
||||
}
|
||||
|
||||
void CopyConstructor(const T &other)
|
||||
{
|
||||
new(this) T(other);
|
||||
}
|
||||
|
||||
void Destructor()
|
||||
{
|
||||
reinterpret_cast<T*>(this)->~T();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
void *FindFuncAddr(T mfp)
|
||||
{
|
||||
if (sizeof(mfp) != sizeof(void*))
|
||||
return NULL;
|
||||
else
|
||||
return fastdelegate::detail::horrible_cast<void*>(mfp);
|
||||
}
|
||||
|
||||
// Reference carrier
|
||||
template <class T> struct MyRefCarrier
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T> struct MyRefCarrier<T&>
|
||||
{
|
||||
class type
|
||||
{
|
||||
T *m_StoredRef;
|
||||
public:
|
||||
type() : m_StoredRef(NULL)
|
||||
{
|
||||
}
|
||||
type(T& ref) : m_StoredRef(&ref)
|
||||
{
|
||||
}
|
||||
|
||||
T& operator= (T& ref)
|
||||
{
|
||||
m_StoredRef = &ref;
|
||||
return ref;
|
||||
}
|
||||
|
||||
operator T& () const
|
||||
{
|
||||
return *m_StoredRef;
|
||||
}
|
||||
|
||||
bool operator== (const typename MyRefCarrier<T&>::type &other)
|
||||
{
|
||||
return m_StoredRef == other.m_StoredRef;
|
||||
}
|
||||
|
||||
friend std::ostream& operator <<(std::ostream &os,const typename MyRefCarrier<T&>::type &obj)
|
||||
{
|
||||
os << *obj.m_StoredRef;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Stores parameter status
|
||||
@[$1,0,$a:
|
||||
|
||||
template<int dummy@[$2,1,$1:, class p$2@]>
|
||||
struct ParamState$1
|
||||
{
|
||||
@[$2,1,$1:typename MyRefCarrier<p$2>::type m_$2; @]
|
||||
|
||||
bool operator==(const ParamState$1<dummy@[$2,1,$1:, p$2@]> &other)
|
||||
{
|
||||
return true
|
||||
@[$2,1,$1: && m_$1 == other.m_$1@]
|
||||
;
|
||||
}
|
||||
ParamState$1(@[$2,1,$1|, :p$2 a$2@]) @[$1!=0::@] @[$2,1,$1|, :m_$2(a$2)@]
|
||||
{
|
||||
}
|
||||
|
||||
ParamState$1<dummy@[$2,1,$1:, p$2@]> & operator() (int incrsteps)
|
||||
{
|
||||
@[$1!=0:int i;@]
|
||||
@[$2,1,$1:
|
||||
for (i = 0; i < incrsteps; ++i)
|
||||
Increment<StripRef< p$2 >::type>::Incr(m_$2);
|
||||
@]
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
@[$1!=0:template<@[$2,1,$1|, :class p$2@]>@]
|
||||
std::ostream& operator <<(std::ostream &os,const ParamState$1<0@[$2,1,$1:, p$2@]> &obj)
|
||||
{
|
||||
@[$1!=0:os@] @[$2,1,$1:<< obj.m_$2@];
|
||||
return os;
|
||||
}
|
||||
|
||||
@]
|
||||
|
||||
#define CAT2(a, b) a##b
|
||||
#define CAT3(a, b, c) a##b##c
|
||||
#define CAT4(a, b, c, d) a##b##c##d
|
||||
|
||||
// hook1: pre ignore
|
||||
// hook2: pre supercede
|
||||
// hook3: post ignore
|
||||
// hook4: post supercede
|
||||
|
||||
|
||||
@[$1,0,$a:
|
||||
|
||||
#define THGM_MAKE_TEST$1_void(id@[$2,1,$1:, param$2@]) \
|
||||
struct TestClass##id; \
|
||||
typedef ParamState$1<0@[$2,1,$1:, param$2@] > ParamState_m##id; \
|
||||
MAKE_STATE_2(State_Func##id, TestClass##id* /*thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg1_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg2_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg3_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg4_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
\
|
||||
struct TestClass##id \
|
||||
{ \
|
||||
static bool ms_DoRecall; \
|
||||
\
|
||||
virtual void Func(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Func##id(this, ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
} \
|
||||
\
|
||||
struct Delegate1 : public MyDelegate \
|
||||
{ \
|
||||
virtual void Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg1_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_NEWPARAMS(MRES_IGNORED, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META(MRES_IGNORED); \
|
||||
} \
|
||||
}; \
|
||||
struct Delegate2 : public MyDelegate \
|
||||
{ \
|
||||
virtual void Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg2_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_NEWPARAMS(MRES_SUPERCEDE, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META(MRES_SUPERCEDE); \
|
||||
} \
|
||||
}; \
|
||||
struct Delegate3 : public MyDelegate \
|
||||
{ \
|
||||
virtual void Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg3_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_NEWPARAMS(MRES_IGNORED, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META(MRES_IGNORED); \
|
||||
} \
|
||||
}; \
|
||||
struct Delegate4 : public MyDelegate \
|
||||
{ \
|
||||
virtual void Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg4_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_NEWPARAMS(MRES_SUPERCEDE, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META(MRES_SUPERCEDE); \
|
||||
}; \
|
||||
}; \
|
||||
}; \
|
||||
\
|
||||
bool TestClass##id::ms_DoRecall = false; \
|
||||
\
|
||||
SourceHook::PassInfo paraminfos_##id[$1+1]; \
|
||||
SourceHook::PassInfo::V2Info paraminfos2_##id[$1+1]; \
|
||||
SourceHook::ProtoInfo protoinfo_##id = { $1, {0, 0, 0}, paraminfos_##id, \
|
||||
SourceHook::ProtoInfo::CallConv_ThisCall, __SH_EPI, paraminfos2_##id }; \
|
||||
\
|
||||
SourceHook::Impl::GenContext *g_Genc##id = NULL; \
|
||||
CAutoPtrDestruction<SourceHook::Impl::GenContext> g_Genc_ad##id(NULL);
|
||||
|
||||
|
||||
#define THGM_MAKE_TEST$1(id, ret_type@[$2,1,$1:, param$2@]) \
|
||||
struct TestClass##id; \
|
||||
typedef ParamState$1<0@[$2,1,$1:, param$2@] > ParamState_m##id; \
|
||||
MAKE_STATE_2(State_Func##id, TestClass##id* /*thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg1_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg2_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg3_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
MAKE_STATE_3(State_Deleg4_##id, TestClass##id* /*ifptr*/, int /*deleg thisptr*/, ParamState_m##id ); \
|
||||
\
|
||||
struct TestClass##id \
|
||||
{ \
|
||||
static bool ms_DoRecall; \
|
||||
\
|
||||
virtual ret_type Func(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Func##id(this, ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
\
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
struct Delegate1 : public MyDelegate \
|
||||
{ \
|
||||
virtual ret_type Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg1_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, 1, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META_VALUE(MRES_IGNORED, 1); \
|
||||
} \
|
||||
}; \
|
||||
struct Delegate2 : public MyDelegate \
|
||||
{ \
|
||||
virtual ret_type Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg2_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_VALUE_NEWPARAMS(MRES_SUPERCEDE, 2, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, 2); \
|
||||
} \
|
||||
}; \
|
||||
struct Delegate3 : public MyDelegate \
|
||||
{ \
|
||||
virtual ret_type Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg3_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, 3, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META_VALUE(MRES_IGNORED, 3); \
|
||||
} \
|
||||
}; \
|
||||
struct Delegate4 : public MyDelegate \
|
||||
{ \
|
||||
virtual ret_type Call(@[$2,1,$1|, :param$2 p$2@]) \
|
||||
{ \
|
||||
g_Inside_LeafFunc = true; \
|
||||
ADD_STATE(State_Deleg4_##id(META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1|, :p$2@]))); \
|
||||
g_Inside_LeafFunc = false; \
|
||||
if (ms_DoRecall) \
|
||||
{ \
|
||||
@[$2,1,$1:Increment<StripRef< param$2 >::type>::Incr(p$2);@] \
|
||||
RETURN_META_VALUE_NEWPARAMS(MRES_SUPERCEDE, 4, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \
|
||||
} \
|
||||
else \
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, 4); \
|
||||
}; \
|
||||
}; \
|
||||
}; \
|
||||
\
|
||||
bool TestClass##id::ms_DoRecall = false; \
|
||||
\
|
||||
SourceHook::PassInfo paraminfos_##id[$1+1]; \
|
||||
SourceHook::PassInfo::V2Info paraminfos2_##id[$1+1]; \
|
||||
SourceHook::ProtoInfo protoinfo_##id = { $1, {0, 0, 0}, paraminfos_##id, \
|
||||
SourceHook::ProtoInfo::CallConv_ThisCall, __SH_EPI, paraminfos2_##id }; \
|
||||
\
|
||||
SourceHook::Impl::GenContext *g_Genc##id = NULL; \
|
||||
CAutoPtrDestruction<SourceHook::Impl::GenContext> g_Genc_ad##id(NULL);
|
||||
|
||||
#define THGM_SETUP_PI$1(id@[$2,1,$1:, p$2_type, p$2_passtype, p$2_flags@]) \
|
||||
void setuppi_##id() \
|
||||
{ \
|
||||
paraminfos_##id[0].size = 1; paraminfos_##id[0].type = 0; paraminfos_##id[0].flags = 0; \
|
||||
\
|
||||
@[$2,1,$1: paraminfos_##id[$2].size = sizeof(p$2_type); paraminfos_##id[$2].type = p$2_passtype; paraminfos_##id[$2].flags = p$2_flags; @] \
|
||||
\
|
||||
@[$2,1,$1: paraminfos2_##id[$2].pNormalCtor = (paraminfos_##id[$2].flags & SourceHook::PassInfo::PassFlag_OCtor) ? FindFuncAddr(&Ctor_Thunk<StripRef< p$2_type >::type>::NormalConstructor) : NULL; @] \
|
||||
@[$2,1,$1: paraminfos2_##id[$2].pCopyCtor = (paraminfos_##id[$2].flags & SourceHook::PassInfo::PassFlag_CCtor) ? FindFuncAddr(&Ctor_Thunk<StripRef< p$2_type >::type>::CopyConstructor) : NULL; @] \
|
||||
@[$2,1,$1: paraminfos2_##id[$2].pDtor = (paraminfos_##id[$2].flags & SourceHook::PassInfo::PassFlag_ODtor) ? FindFuncAddr(&Ctor_Thunk<StripRef< p$2_type >::type>::Destructor) : NULL; @] \
|
||||
}
|
||||
|
||||
@]
|
||||
|
||||
#define THGM_SETUP_RI(id, ret_type, ret_passtype, ret_flags) \
|
||||
void setupri_##id() \
|
||||
{ \
|
||||
protoinfo_##id.retPassInfo.size = sizeof(ret_type); \
|
||||
protoinfo_##id.retPassInfo.type = ret_passtype; \
|
||||
protoinfo_##id.retPassInfo.flags = ret_flags; \
|
||||
\
|
||||
protoinfo_##id.retPassInfo2.pNormalCtor = (protoinfo_##id.retPassInfo.flags & SourceHook::PassInfo::PassFlag_OCtor) ? FindFuncAddr(&Ctor_Thunk<StripRef< ret_type >::type>::NormalConstructor) : NULL; \
|
||||
protoinfo_##id.retPassInfo2.pCopyCtor = (protoinfo_##id.retPassInfo.flags & SourceHook::PassInfo::PassFlag_CCtor) ? FindFuncAddr(&Ctor_Thunk<StripRef< ret_type >::type>::CopyConstructor) : NULL; \
|
||||
protoinfo_##id.retPassInfo2.pDtor = (protoinfo_##id.retPassInfo.flags & SourceHook::PassInfo::PassFlag_ODtor) ? FindFuncAddr(&Ctor_Thunk<StripRef< ret_type >::type>::Destructor) : NULL; \
|
||||
}
|
||||
|
||||
#define THGM_ADD_HOOK(id, num) \
|
||||
CAT4(hook, num, _, id) = g_SHPtr->AddHook(g_PLID, SourceHook::ISourceHook::Hook_Normal, reinterpret_cast<void*>(pTest##id), \
|
||||
0, myhookman##id, PtrBufPtr(new TestClass##id::Delegate##num), num >= 3);
|
||||
|
||||
#define THGM_REMOVE_HOOK(id, num) \
|
||||
g_SHPtr->RemoveHookByID(CAT4(hook, num, _, id));
|
||||
|
||||
#define THGM_CALLS_void(id, call_params) \
|
||||
pTest##id->Func call_params; \
|
||||
SH_CALL(pTest##id, &TestClass##id::Func) call_params;
|
||||
|
||||
#define THGM_DO_TEST_void(id, call_params) \
|
||||
setuppi_##id(); \
|
||||
g_Genc##id = new SourceHook::Impl::GenContext(&protoinfo_##id, 0, 0, g_SHPtr); \
|
||||
g_Genc_ad##id.set(g_Genc##id); \
|
||||
SourceHook::HookManagerPubFunc myhookman##id = g_Genc##id->Generate(); \
|
||||
int hook1_##id, hook2_##id, hook3_##id, hook4_##id; \
|
||||
\
|
||||
TestClass##id::ms_DoRecall = false; \
|
||||
TestClass##id *pTest##id = new TestClass##id; \
|
||||
CAutoPtrDestruction<TestClass##id> apd##id(pTest##id); \
|
||||
\
|
||||
/* no hooks - no hooks */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part1"); \
|
||||
\
|
||||
/* hook1 - no hooks */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part2"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
\
|
||||
/* no hooks - hook3 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part3"); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
\
|
||||
/* hook1 - hook3 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 1, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part4"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
\
|
||||
/* hook1, hook2 - hook3 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_ADD_HOOK(id, 2); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Deleg2_##id(pTest##id, 1, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 2, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part5"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
THGM_REMOVE_HOOK(id, 2); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
/* hook1, hook2 - hook3, hook4 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_ADD_HOOK(id, 2); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_ADD_HOOK(id, 4); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Deleg2_##id(pTest##id, 1, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 2, ParamState_m##id call_params), \
|
||||
new State_Deleg4_##id(pTest##id, 3, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part6"); \
|
||||
\
|
||||
/* hook1 - hook3, hook4, with recalls! */ \
|
||||
\
|
||||
TestClass##id::ms_DoRecall = true; \
|
||||
THGM_REMOVE_HOOK(id, 2); \
|
||||
THGM_CALLS_void(id, call_params); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params(0)), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params(1)), \
|
||||
new State_Deleg3_##id(pTest##id, 2, ParamState_m##id call_params(1)), \
|
||||
new State_Deleg4_##id(pTest##id, 3, ParamState_m##id call_params(2)), \
|
||||
/* sh_call one */ \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part7"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
THGM_REMOVE_HOOK(id, 4);
|
||||
|
||||
|
||||
#define THGM_CALLS(id, call_params, exp_ret_norm, exp_ret_shcall, err) \
|
||||
CHECK_COND(pTest##id->Func call_params == exp_ret_norm, err " /retcallnorm"); \
|
||||
CHECK_COND(SH_CALL(pTest##id, &TestClass##id::Func) call_params == exp_ret_shcall, err " /retcallshcall");
|
||||
|
||||
#define THGM_DO_TEST(id, call_params) \
|
||||
setuppi_##id(); \
|
||||
setupri_##id(); \
|
||||
g_Genc##id = new SourceHook::Impl::GenContext(&protoinfo_##id, 0, 0, g_SHPtr); \
|
||||
g_Genc_ad##id.set(g_Genc##id); \
|
||||
SourceHook::HookManagerPubFunc myhookman##id = g_Genc##id->Generate(); \
|
||||
int hook1_##id, hook2_##id, hook3_##id, hook4_##id; \
|
||||
\
|
||||
TestClass##id::ms_DoRecall = false; \
|
||||
TestClass##id *pTest##id = new TestClass##id; \
|
||||
CAutoPtrDestruction<TestClass##id> apd##id(pTest##id); \
|
||||
\
|
||||
/* no hooks - no hooks */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_CALLS(id, call_params, 0, 0, "Part1"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part1"); \
|
||||
\
|
||||
/* hook1 - no hooks */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_CALLS(id, call_params, 0, 0, "Part2"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part2"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
\
|
||||
/* no hooks - hook3 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_CALLS(id, call_params, 0, 0, "Part3"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part3"); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
\
|
||||
/* hook1 - hook3 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_CALLS(id, call_params, 0, 0, "Part4"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 1, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part4"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
\
|
||||
/* hook1, hook2 - hook3 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_ADD_HOOK(id, 2); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_CALLS(id, call_params, 2, 0, "Part5"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Deleg2_##id(pTest##id, 1, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 2, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part5"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
THGM_REMOVE_HOOK(id, 2); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
/* hook1, hook2 - hook3, hook4 */ \
|
||||
PtrBuf_Clear(); \
|
||||
THGM_ADD_HOOK(id, 1); \
|
||||
THGM_ADD_HOOK(id, 2); \
|
||||
THGM_ADD_HOOK(id, 3); \
|
||||
THGM_ADD_HOOK(id, 4); \
|
||||
THGM_CALLS(id, call_params, 4, 0, "Part6"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params), \
|
||||
new State_Deleg2_##id(pTest##id, 1, ParamState_m##id call_params), \
|
||||
new State_Deleg3_##id(pTest##id, 2, ParamState_m##id call_params), \
|
||||
new State_Deleg4_##id(pTest##id, 3, ParamState_m##id call_params), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part6"); \
|
||||
\
|
||||
/* hook1 - hook3, hook4, with recalls! */ \
|
||||
\
|
||||
TestClass##id::ms_DoRecall = true; \
|
||||
THGM_REMOVE_HOOK(id, 2); \
|
||||
THGM_CALLS(id, call_params, 4, 0, "Part7"); \
|
||||
CHECK_STATES((&g_States, \
|
||||
new State_Deleg1_##id(pTest##id, 0, ParamState_m##id call_params(0)), \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params(1)), \
|
||||
new State_Deleg3_##id(pTest##id, 2, ParamState_m##id call_params(1)), \
|
||||
new State_Deleg4_##id(pTest##id, 3, ParamState_m##id call_params(2)), \
|
||||
/* sh_call one */ \
|
||||
new State_Func##id(pTest##id, ParamState_m##id call_params), \
|
||||
NULL), "Test" #id " Part7"); \
|
||||
THGM_REMOVE_HOOK(id, 1); \
|
||||
THGM_REMOVE_HOOK(id, 3); \
|
||||
THGM_REMOVE_HOOK(id, 4);
|
||||
Loading…
Reference in New Issue
Block a user