3535#include " memory_function.h"
3636#include " memory_utilities.h"
3737#include " memory_hooks.h"
38+ #include " memory_wrap.h"
3839
3940// DynamicHooks
4041#include " conventions/x86MsCdecl.h"
@@ -80,7 +81,7 @@ int GetDynCallConvention(Convention_t eConv)
8081 case CONV_FASTCALL: return DC_CALL_C_X86_WIN32_FAST_MS;
8182#endif
8283 }
83-
84+
8485 BOOST_RAISE_EXCEPTION (PyExc_ValueError, " Unsupported calling convention." )
8586 return -1 ;
8687}
@@ -89,7 +90,7 @@ int GetDynCallConvention(Convention_t eConv)
8990// ============================================================================
9091// >> MakeDynamicHooksConvention
9192// ============================================================================
92- ICallingConvention* MakeDynamicHooksConvention (Convention_t eConv, std::vector<DataType_t> vecArgTypes, DataType_t returnType, int iAlignment= 4 )
93+ ICallingConvention* MakeDynamicHooksConvention (Convention_t eConv, std::vector<DataType_t> vecArgTypes, DataType_t returnType, int iAlignment)
9394{
9495#ifdef _WIN32
9596 switch (eConv)
@@ -145,27 +146,19 @@ CFunction::CFunction(unsigned long ulAddr, object oCallingConvention, object oAr
145146 // If this line succeeds the user wants to create a function with the built-in calling conventions
146147 m_eCallingConvention = extract<Convention_t>(oCallingConvention);
147148 m_pCallingConvention = MakeDynamicHooksConvention (m_eCallingConvention, ObjectToDataTypeVector (m_tArgs), m_eReturnType);
148-
149- // We allocated the calling convention, we are responsible to cleanup.
150- m_bAllocatedCallingConvention = true ;
149+ m_oCallingConvention = object ();
151150 }
152151 catch ( ... )
153152 {
154153 PyErr_Clear ();
155-
154+
156155 // A custom calling convention will be used...
157156 m_eCallingConvention = CONV_CUSTOM;
158- object _oCallingConvention = oCallingConvention (m_tArgs, m_eReturnType);
159-
160- // FIXME:
161- // This is required to fix a crash, but it will also cause a memory leak,
162- // because no calling convention object that is created via this method will ever be deleted.
163- // TODO: Pretty sure this was required due to the missing held type definition. It was added, but wasn't tested yet.
164- Py_INCREF (_oCallingConvention.ptr ());
165- m_pCallingConvention = extract<ICallingConvention*>(_oCallingConvention);
157+ m_oCallingConvention = oCallingConvention (m_tArgs, m_eReturnType);
158+ m_pCallingConvention = extract<ICallingConvention*>(m_oCallingConvention);
166159
167- // We didn't allocate the calling convention, someone else is responsible for it .
168- m_bAllocatedCallingConvention = false ;
160+ // Reserve a Python reference for DynamicHooks .
161+ Py_INCREF (m_oCallingConvention. ptr ()) ;
169162 }
170163
171164 // Step 4: Get the DynCall calling convention
@@ -179,9 +172,7 @@ CFunction::CFunction(unsigned long ulAddr, Convention_t eCallingConvention,
179172 m_eCallingConvention = eCallingConvention;
180173 m_iCallingConvention = iCallingConvention;
181174 m_pCallingConvention = NULL ;
182-
183- // We didn't allocate the calling convention, someone else is responsible for it.
184- m_bAllocatedCallingConvention = false ;
175+ m_oCallingConvention = object ();
185176
186177 m_tArgs = tArgs;
187178 m_eReturnType = eReturnType;
@@ -190,18 +181,24 @@ CFunction::CFunction(unsigned long ulAddr, Convention_t eCallingConvention,
190181
191182CFunction::~CFunction ()
192183{
193- // If we didn't allocate the calling convention, then it is not our responsibility.
194- if (!m_bAllocatedCallingConvention)
184+ if (!m_pCallingConvention)
195185 return ;
196186
197- CHook* pHook = GetHookManager ()->FindHook ((void *) m_ulAddr);
198-
199- // DynamicHooks will take care of it for us from there.
200- if (pHook && pHook->m_pCallingConvention == m_pCallingConvention)
201- return ;
187+ // If the convention isn't flagged as hooked, then we need to take care of it.
188+ if (!m_pCallingConvention->m_bHooked )
189+ {
190+ // If we don't have a Python instance, then we can safely delete it.
191+ if (m_oCallingConvention.is_none ())
192+ delete m_pCallingConvention;
193+ // Otherwise, just release our reference and let Python take care of it.
194+ else if (Py_REFCNT (m_oCallingConvention.ptr ()) > 1 )
195+ Py_DECREF (m_oCallingConvention.ptr ());
196+ }
197+ // If we are using a built-in convention that is currently hooked, let's flag it as no longer hooked
198+ // so that we know we are not bound to a CFunction anymore and can be deleted.
199+ else if (m_eCallingConvention != CONV_CUSTOM && !dynamic_cast <ICallingConventionWrapper *>(m_pCallingConvention))
200+ m_pCallingConvention->m_bHooked = false ;
202201
203- // Cleanup.
204- delete m_pCallingConvention;
205202 m_pCallingConvention = NULL ;
206203}
207204
@@ -287,7 +284,7 @@ object CFunction::Call(tuple args, dict kw)
287284
288285 dcArgPointer (g_pCallVM, ulAddr);
289286 break ;
290- }
287+ }
291288 case DATA_TYPE_STRING: dcArgPointer (g_pCallVM, (unsigned long ) (void *) extract<char *>(arg)); break ;
292289 default : BOOST_RAISE_EXCEPTION (PyExc_ValueError, " Unknown argument type." )
293290 }
@@ -357,10 +354,10 @@ void CFunction::AddHook(HookType_t eType, PyObject* pCallable)
357354{
358355 if (!IsHookable ())
359356 BOOST_RAISE_EXCEPTION (PyExc_ValueError, " Function is not hookable." )
360-
357+
361358 Validate ();
362359 CHook* pHook = GetHookManager ()->FindHook ((void *) m_ulAddr);
363-
360+
364361 // Prepare arguments for log message
365362 str type = str (eType);
366363 const char * szType = extract<const char *>(type);
@@ -391,16 +388,31 @@ void CFunction::AddHook(HookType_t eType, PyObject* pCallable)
391388
392389 if (!pHook) {
393390 pHook = HookFunctionHelper ((void *) m_ulAddr, m_pCallingConvention);
394-
395- // DynamicHooks will handle our convention from there, regardless if we allocated it or not.
396- m_bAllocatedCallingConvention = false ;
397391 }
398-
392+
399393 // Add the hook handler. If it's already added, it won't be added twice
400394 pHook->AddCallback (eType, (HookHandlerFn *) (void *) &SP_HookHandler);
401395 g_mapCallbacks[pHook][eType].push_back (object (handle<>(borrowed (pCallable))));
402396}
403397
398+ bool CFunction::AddHook (HookType_t eType, HookHandlerFn* pFunc)
399+ {
400+ if (!IsHookable ())
401+ return false ;
402+
403+ CHook* pHook = GetHookManager ()->FindHook ((void *) m_ulAddr);
404+
405+ if (!pHook) {
406+ pHook = GetHookManager ()->HookFunction ((void *) m_ulAddr, m_pCallingConvention);
407+
408+ if (!pHook)
409+ return false ;
410+ }
411+
412+ pHook->AddCallback (eType, pFunc);
413+ return true ;
414+ }
415+
404416void CFunction::RemoveHook (HookType_t eType, PyObject* pCallable)
405417{
406418 Validate ();
@@ -418,6 +430,26 @@ void CFunction::DeleteHook()
418430 return ;
419431
420432 g_mapCallbacks.erase (pHook);
433+
434+ ICallingConventionWrapper *pConv = dynamic_cast <ICallingConventionWrapper *>(pHook->m_pCallingConvention );
435+ if (pConv)
436+ {
437+ if (pConv->m_bHooked )
438+ {
439+ // Flag the convention as no longer hooked and being taken care of by DynamicHooks.
440+ pHook->m_pCallingConvention ->m_bHooked = false ;
441+
442+ // Release the Python reference we reserved for DynamicHooks.
443+ PyObject *pOwner = detail::wrapper_base_::owner (pConv);
444+ if (pOwner && Py_REFCNT (pOwner))
445+ Py_DECREF (pOwner);
446+ }
447+ }
448+ // If we are a built-in convention bound to a CHook instance but not flagged as hooked anymore, then that
449+ // means we are no longer bound to a CFunction instance and can be safely deleted.
450+ else if (!pHook->m_pCallingConvention ->m_bHooked )
451+ delete pHook->m_pCallingConvention ;
452+
421453 // Set the calling convention to NULL, because DynamicHooks will delete it otherwise.
422454 pHook->m_pCallingConvention = NULL ;
423455 GetHookManager ()->UnhookFunction ((void *) m_ulAddr);
0 commit comments