struct CAutoReleaseHookMan { SourceHook::HookManagerPubFunc m_Ptr; CAutoReleaseHookMan(SourceHook::HookManagerPubFunc ptr) : m_Ptr(ptr) { } ~CAutoReleaseHookMan() { g_HMAGPtr->ReleaseHookMan(m_Ptr); } }; // Strip & template struct StripRef { typedef T type; }; template struct StripRef { typedef T type; }; // Address of constructor/destructor // (using wrappers) template class Ctor_Thunk { public: void NormalConstructor() { new(this) T; } void CopyConstructor(const T &other) { new(this) T(other); } void Destructor() { reinterpret_cast(this)->~T(); } const T& AssignOp(const T &other) { return (*reinterpret_cast(this) = other); } }; template void *FindFuncAddr(T mfp) { union { T a; void *b; } u; u.a = mfp; return u.b; } // Reference carrier template struct MyRefCarrier { typedef T type; }; template struct MyRefCarrier { 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::type &other) { return m_StoredRef == other.m_StoredRef; } friend std::ostream& operator <<(std::ostream &os,const typename MyRefCarrier::type &obj) { os << *obj.m_StoredRef; return os; } }; }; #if SH_COMP==SH_COMP_GCC #define NO_OPTIMIZE __attribute__((noinline)) #else #define NO_OPTIMIZE #endif // Return value maker template struct MakeRet { static T Do(int a) { return a; } }; template struct MakeRet< POD > { static POD Do(int a) { POD x; memset(reinterpret_cast(x.x), a, SIZE); return x; } }; // Stores parameter status template bool EqualToMyFmtString(T sth) { return false; } bool EqualToMyFmtString(std::string &sth) { if (sth == "Hello %s%d%s") sth = "Hello BA1L!"; return true; } @[$1,0,$a: template struct ParamState$1 { @[$2,1,$1:typename MyRefCarrier::type m_$2; @] bool operator==(const ParamState$1 &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)@] { @[$2,1,$1: EqualToMyFmtString(m_$2); @] } ParamState$1 & operator() (int incrsteps) { @[$1!=0:int i;@] @[$2,1,$1: for (i = 0; i < incrsteps; ++i) Increment::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_4(State_Deleg_##id, int /*delegnumber*/, 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 Delegate : public MyDelegate \ { \ int m_DelegNumber; \ Delegate(int num) : m_DelegNumber(num) { } \ \ virtual void Call(@[$2,1,$1|, :param$2 p$2@]) \ { \ g_Inside_LeafFunc = true; \ ADD_STATE(State_Deleg_##id(m_DelegNumber, 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::type>::Incr(p$2);@] \ RETURN_META_NEWPARAMS((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE, &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \ } \ else \ RETURN_META((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE); \ } \ }; \ }; \ \ bool TestClass##id::ms_DoRecall = false; \ SourceHook::CProtoInfoBuilder protoinfo_##id(SourceHook::ProtoInfo::CallConv_ThisCall); #define THGM_MAKE_TEST$1(id, ret_type@[$2,1,$1:, param$2@]) \ struct TestClass##id; \ typedef ret_type RetType##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_4(State_Deleg_##id, int /*delegnumber*/, 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 MakeRet< ret_type >::Do(0); \ } \ \ struct Delegate : public MyDelegate \ { \ int m_DelegNumber; \ Delegate(int num) : m_DelegNumber(num) { } \ \ virtual ret_type Call(@[$2,1,$1|, :param$2 p$2@]) \ { \ g_Inside_LeafFunc = true; \ ADD_STATE(State_Deleg_##id(m_DelegNumber, 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::type>::Incr(p$2);@] \ RETURN_META_VALUE_NEWPARAMS((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE, MakeRet< ret_type >::Do(m_DelegNumber), &TestClass##id::Func, (@[$2,1,$1|, :p$2@])); \ } \ else \ RETURN_META_VALUE((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE, MakeRet< ret_type >::Do(m_DelegNumber)); \ } \ }; \ }; \ \ bool TestClass##id::ms_DoRecall = false; \ SourceHook::CProtoInfoBuilder protoinfo_##id(SourceHook::ProtoInfo::CallConv_ThisCall); #define THGM_MAKE_TEST$1_vafmt_void(id@[$2,1,$1:, param$2@]) \ struct TestClass##id; \ typedef ParamState@($1+1)<0@[$2,1,$1:, param$2@], std::string > ParamState_m##id; \ MAKE_STATE_2(State_Func##id, TestClass##id* /*thisptr*/, ParamState_m##id ); \ MAKE_STATE_4(State_Deleg_##id, int /*delegnumber*/, 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, @]const char *fmt, ...) \ { \ g_Inside_LeafFunc = true; \ \ char buf[9999]; \ va_list ap; \ va_start(ap, fmt); \ vsnprintf(buf, 9998, fmt, ap); \ buf[9998] = 0; \ va_end(ap); \ \ ADD_STATE(State_Func##id(this, ParamState_m##id(@[$2,1,$1:p$2, @]std::string(buf)))); \ g_Inside_LeafFunc = false; \ } \ \ struct Delegate : public MyDelegate \ { \ int m_DelegNumber; \ Delegate(int num) : m_DelegNumber(num) { } \ \ virtual void Call(@[$2,1,$1:param$2 p$2, @]const char *buf) \ { \ g_Inside_LeafFunc = true; \ ADD_STATE(State_Deleg_##id(m_DelegNumber, META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1:p$2, @]buf))); \ g_Inside_LeafFunc = false; \ if (ms_DoRecall) \ { \ @[$2,1,$1:Increment::type>::Incr(p$2);@] \ RETURN_META_NEWPARAMS((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE, &TestClass##id::Func, (@[$2,1,$1:p$2, @]"%s!", buf)); \ } \ else \ RETURN_META((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE); \ } \ }; \ }; \ \ bool TestClass##id::ms_DoRecall = false; \ SourceHook::CProtoInfoBuilder protoinfo_##id(SourceHook::ProtoInfo::CallConv_ThisCall | SourceHook::ProtoInfo::CallConv_HasVafmt); #define THGM_MAKE_TEST$1_vafmt(id, ret_type@[$2,1,$1:, param$2@]) \ struct TestClass##id; \ typedef ret_type RetType##id; \ typedef ParamState@($1+1)<0@[$2,1,$1:, param$2@], std::string > ParamState_m##id; \ MAKE_STATE_2(State_Func##id, TestClass##id* /*thisptr*/, ParamState_m##id ); \ MAKE_STATE_4(State_Deleg_##id, int /*delegnumber*/, 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, @]const char *fmt, ...) \ { \ g_Inside_LeafFunc = true; \ \ char buf[9999]; \ va_list ap; \ va_start(ap, fmt); \ vsnprintf(buf, 9998, fmt, ap); \ buf[9998] = 0; \ va_end(ap); \ \ ADD_STATE(State_Func##id(this, ParamState_m##id(@[$2,1,$1:p$2, @]std::string(buf)))); \ \ return MakeRet< ret_type >::Do(0); \ } \ \ struct Delegate : public MyDelegate \ { \ int m_DelegNumber; \ Delegate(int num) : m_DelegNumber(num) { } \ \ virtual ret_type Call(@[$2,1,$1:param$2 p$2, @]const char *buf) \ { \ g_Inside_LeafFunc = true; \ ADD_STATE(State_Deleg_##id(m_DelegNumber, META_IFACEPTR(TestClass##id), PtrBuf(this), ParamState_m##id(@[$2,1,$1:p$2, @]buf))); \ g_Inside_LeafFunc = false; \ if (ms_DoRecall) \ { \ @[$2,1,$1:Increment::type>::Incr(p$2);@] \ RETURN_META_VALUE_NEWPARAMS((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE, MakeRet< ret_type >::Do(m_DelegNumber), &TestClass##id::Func, (@[$2,1,$1:p$2, @]"%s!", buf)); \ } \ else \ RETURN_META_VALUE((m_DelegNumber & 1) ? MRES_IGNORED : MRES_SUPERCEDE, MakeRet< ret_type >::Do(m_DelegNumber)); \ } \ }; \ }; \ \ bool TestClass##id::ms_DoRecall = false; \ SourceHook::CProtoInfoBuilder protoinfo_##id(SourceHook::ProtoInfo::CallConv_ThisCall | SourceHook::ProtoInfo::CallConv_HasVafmt); #define THGM_SETUP_PI$1(id@[$2,1,$1:, p$2_type, p$2_passtype, p$2_flags@]) \ void setuppi_##id() \ { \ @[$2,1,$1: \ protoinfo_##id.AddParam(sizeof(p$2_type), p$2_passtype, p$2_flags, \ (p$2_flags & SourceHook::PassInfo::PassFlag_OCtor) ? FindFuncAddr(&Ctor_Thunk::type>::NormalConstructor) : NULL, \ (p$2_flags & SourceHook::PassInfo::PassFlag_CCtor) ? FindFuncAddr(&Ctor_Thunk::type>::CopyConstructor) : NULL, \ (p$2_flags & SourceHook::PassInfo::PassFlag_ODtor) ? FindFuncAddr(&Ctor_Thunk::type>::Destructor) : NULL, \ (p$2_flags & SourceHook::PassInfo::PassFlag_AssignOp) ? FindFuncAddr(&Ctor_Thunk::type>::AssignOp) : NULL \ ); \ @] \ } @] #define THGM_SETUP_RI(id, ret_type, ret_passtype, ret_flags) \ void setupri_##id() \ { \ protoinfo_##id.SetReturnType(sizeof(ret_type), ret_passtype, ret_flags, \ (ret_flags & SourceHook::PassInfo::PassFlag_OCtor) ? FindFuncAddr(&Ctor_Thunk::type>::NormalConstructor) : NULL, \ (ret_flags & SourceHook::PassInfo::PassFlag_CCtor) ? FindFuncAddr(&Ctor_Thunk::type>::CopyConstructor) : NULL, \ (ret_flags & SourceHook::PassInfo::PassFlag_ODtor) ? FindFuncAddr(&Ctor_Thunk::type>::Destructor) : NULL, \ (ret_flags & SourceHook::PassInfo::PassFlag_AssignOp) ? FindFuncAddr(&Ctor_Thunk::type>::AssignOp) : NULL \ ); \ } #define THGM_ADD_HOOK(id, num) \ CAT4(hook, num, _, id) = g_SHPtr->AddHook(g_PLID, SourceHook::ISourceHook::Hook_Normal, reinterpret_cast(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(); \ SourceHook::HookManagerPubFunc myhookman##id = g_HMAGPtr->MakeHookMan(protoinfo_##id, 0, 0); \ CAutoReleaseHookMan arhm_##id(myhookman##id); \ int hook1_##id, hook2_##id, hook3_##id, hook4_##id; \ \ TestClass##id::ms_DoRecall = false; \ TestClass##id *pTest##id = new TestClass##id; \ CAutoPtrDestruction 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 - 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_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params), \ new State_Func##id(pTest##id, ParamState_m##id call_params), \ new State_Deleg_##id(3, 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_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params), \ new State_Deleg_##id(2, pTest##id, 1, ParamState_m##id call_params), \ new State_Deleg_##id(3, pTest##id, 2, ParamState_m##id call_params), \ new State_Func##id(pTest##id, ParamState_m##id call_params), \ NULL), "Test" #id " Part5"); \ /* hook1, hook2 - hook3, hook4 */ \ THGM_ADD_HOOK(id, 4); \ THGM_CALLS_void(id, call_params); \ CHECK_STATES((&g_States, \ new State_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params), \ new State_Deleg_##id(2, pTest##id, 1, ParamState_m##id call_params), \ new State_Deleg_##id(3, pTest##id, 2, ParamState_m##id call_params), \ new State_Deleg_##id(4, 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_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params(0)), \ new State_Func##id(pTest##id, ParamState_m##id call_params(1)), \ new State_Deleg_##id(3, pTest##id, 2, ParamState_m##id call_params(1)), \ new State_Deleg_##id(4, 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); template T ComparableRef(T x) { return x; } template T* ComparableRef(T& x) { return &x; } #define THGM_CALLS(id, call_params, exp_ret_norm, exp_ret_shcall, err) \ CHECK_COND(ComparableRef(pTest##id->Func call_params) == ComparableRef(MakeRet< RetType##id >::Do(exp_ret_norm)), err " /retcallnorm"); \ CHECK_COND(ComparableRef(SH_CALL(pTest##id, &TestClass##id::Func) call_params) == ComparableRef(MakeRet< RetType##id >::Do(exp_ret_shcall)), err " /retcallshcall"); #define THGM_DO_TEST(id, call_params) \ setuppi_##id(); \ setupri_##id(); \ SourceHook::HookManagerPubFunc myhookman##id = g_HMAGPtr->MakeHookMan(protoinfo_##id, 0, 0); \ CAutoReleaseHookMan arhm_##id(myhookman##id); \ int hook1_##id, hook2_##id, hook3_##id, hook4_##id; \ \ TestClass##id::ms_DoRecall = false; \ TestClass##id *pTest##id = new TestClass##id; \ CAutoPtrDestruction 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 - 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_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params), \ new State_Func##id(pTest##id, ParamState_m##id call_params), \ new State_Deleg_##id(3, 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_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params), \ new State_Deleg_##id(2, pTest##id, 1, ParamState_m##id call_params), \ new State_Deleg_##id(3, pTest##id, 2, ParamState_m##id call_params), \ new State_Func##id(pTest##id, ParamState_m##id call_params), \ NULL), "Test" #id " Part5"); \ /* hook1, hook2 - hook3, hook4 */ \ THGM_ADD_HOOK(id, 4); \ THGM_CALLS(id, call_params, 4, 0, "Part6"); \ CHECK_STATES((&g_States, \ new State_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params), \ new State_Deleg_##id(2, pTest##id, 1, ParamState_m##id call_params), \ new State_Deleg_##id(3, pTest##id, 2, ParamState_m##id call_params), \ new State_Deleg_##id(4, 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_Deleg_##id(1, pTest##id, 0, ParamState_m##id call_params(0)), \ new State_Func##id(pTest##id, ParamState_m##id call_params(1)), \ new State_Deleg_##id(3, pTest##id, 2, ParamState_m##id call_params(1)), \ new State_Deleg_##id(4, 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);