|
| 1 | +/** |
| 2 | +* ============================================================================= |
| 3 | +* Source Python |
| 4 | +* Copyright (C) 2012 Source Python Development Team. All rights reserved. |
| 5 | +* ============================================================================= |
| 6 | +* |
| 7 | +* This program is free software; you can redistribute it and/or modify it under |
| 8 | +* the terms of the GNU General Public License, version 3.0, as published by the |
| 9 | +* Free Software Foundation. |
| 10 | +* |
| 11 | +* This program is distributed in the hope that it will be useful, but WITHOUT |
| 12 | +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 13 | +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| 14 | +* details. |
| 15 | +* |
| 16 | +* You should have received a copy of the GNU General Public License along with |
| 17 | +* this program. If not, see <http://www.gnu.org/licenses/>. |
| 18 | +* |
| 19 | +* As a special exception, the Source Python Team gives you permission |
| 20 | +* to link the code of this program (as well as its derivative works) to |
| 21 | +* "Half-Life 2," the "Source Engine," and any Game MODs that run on software |
| 22 | +* by the Valve Corporation. You must obey the GNU General Public License in |
| 23 | +* all respects for all other code used. Additionally, the Source.Python |
| 24 | +* Development Team grants this exception to all derivative works. |
| 25 | +*/ |
| 26 | + |
| 27 | +// ============================================================================ |
| 28 | +// >> INCLUDES |
| 29 | +// ============================================================================ |
| 30 | +#include "memory_callback.h" |
| 31 | +#include "utilities/wrap_macros.h" |
| 32 | +#include "utilities/call_python.h" |
| 33 | + |
| 34 | +#include "AsmJit.h" |
| 35 | +using namespace AsmJit; |
| 36 | + |
| 37 | +#include "utilities.h" |
| 38 | +#include "DynamicHooks.h" |
| 39 | +using namespace DynamicHooks; |
| 40 | + |
| 41 | + |
| 42 | +// ============================================================================ |
| 43 | +// >> MACROS |
| 44 | +// ============================================================================ |
| 45 | +#define GET_CALLBACK_CALLER(type) \ |
| 46 | + (void *) GET_FUNCTION(type, CallbackCaller<type>, CCallback*, unsigned long, unsigned long) |
| 47 | + |
| 48 | + |
| 49 | +// ============================================================================ |
| 50 | +// >> CLASSES |
| 51 | +// ============================================================================ |
| 52 | +CCallback::CCallback(object oCallback, Convention_t eConv, tuple args, object return_type, bool bAutoDealloc /* = true */) |
| 53 | + : CFunction(NULL, eConv, args, return_type) |
| 54 | +{ |
| 55 | + m_oCallback = oCallback; |
| 56 | + m_bAutoDealloc = bAutoDealloc; |
| 57 | + |
| 58 | + // Find the proper callback caller function |
| 59 | + void* pCallCallbackFunc = NULL; |
| 60 | + |
| 61 | + DataType_t RetType; |
| 62 | + try |
| 63 | + { |
| 64 | + RetType = extract<DataType_t>(m_oReturnType); |
| 65 | + } |
| 66 | + catch ( ... ) |
| 67 | + { |
| 68 | + PyErr_Clear(); |
| 69 | + RetType = DATA_TYPE_POINTER; |
| 70 | + } |
| 71 | + |
| 72 | + switch(RetType) |
| 73 | + { |
| 74 | + case DATA_TYPE_VOID: pCallCallbackFunc = GET_CALLBACK_CALLER(void); break; |
| 75 | + case DATA_TYPE_BOOL: pCallCallbackFunc = GET_CALLBACK_CALLER(bool); break; |
| 76 | + case DATA_TYPE_CHAR: pCallCallbackFunc = GET_CALLBACK_CALLER(char); break; |
| 77 | + case DATA_TYPE_UCHAR: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned char); break; |
| 78 | + case DATA_TYPE_SHORT: pCallCallbackFunc = GET_CALLBACK_CALLER(short); break; |
| 79 | + case DATA_TYPE_USHORT: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned short); break; |
| 80 | + case DATA_TYPE_INT: pCallCallbackFunc = GET_CALLBACK_CALLER(int); break; |
| 81 | + case DATA_TYPE_UINT: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned int); break; |
| 82 | + case DATA_TYPE_LONG: pCallCallbackFunc = GET_CALLBACK_CALLER(long); break; |
| 83 | + case DATA_TYPE_ULONG: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned long); break; |
| 84 | + case DATA_TYPE_LONGLONG: pCallCallbackFunc = GET_CALLBACK_CALLER(long long); break; |
| 85 | + case DATA_TYPE_ULONGLONG: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned long long); break; |
| 86 | + case DATA_TYPE_FLOAT: pCallCallbackFunc = GET_CALLBACK_CALLER(float); break; |
| 87 | + case DATA_TYPE_DOUBLE: pCallCallbackFunc = GET_CALLBACK_CALLER(double); break; |
| 88 | + case DATA_TYPE_POINTER: pCallCallbackFunc = GET_CALLBACK_CALLER(void *); break; |
| 89 | + case DATA_TYPE_STRING: pCallCallbackFunc = GET_CALLBACK_CALLER(char *); break; |
| 90 | + default: BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Unknown return type."); |
| 91 | + } |
| 92 | + |
| 93 | + // Generate the function |
| 94 | + Assembler a; |
| 95 | + |
| 96 | + // Epilog |
| 97 | + a.push(ebp); |
| 98 | + a.mov(ebp, esp); |
| 99 | + |
| 100 | + // Call callback caller |
| 101 | + a.push(ecx); |
| 102 | + a.push(ebp); |
| 103 | + a.push(imm((sysint_t) this)); |
| 104 | + a.call(pCallCallbackFunc); |
| 105 | + a.add(esp, imm(12)); |
| 106 | + |
| 107 | + // Prolog |
| 108 | + a.mov(esp, ebp); |
| 109 | + a.pop(ebp); |
| 110 | + |
| 111 | + // Return |
| 112 | + a.ret(imm(GetPopSize())); |
| 113 | + |
| 114 | + m_ulAddr = (unsigned long) a.make(); |
| 115 | +} |
| 116 | + |
| 117 | +int CCallback::GetPopSize() |
| 118 | +{ |
| 119 | + /* |
| 120 | + Who cleans up the stack? |
| 121 | +
|
| 122 | + Linux: Always the caller |
| 123 | +
|
| 124 | + Windows: |
| 125 | + CDECL: Caller |
| 126 | + STDCALL + THISCALL: Callee |
| 127 | + */ |
| 128 | + |
| 129 | +#ifdef _WIN32 |
| 130 | + if (m_eConv == CONV_CDECL) |
| 131 | + return 0; |
| 132 | + |
| 133 | + int i = 0; |
| 134 | + |
| 135 | + // Skip the this pointer. It's passed in an own register (ECX) |
| 136 | + if (m_eConv == CONV_THISCALL) |
| 137 | + i++; |
| 138 | + |
| 139 | + int size = 0; |
| 140 | + for(; i < len(m_Args); i++) |
| 141 | + size += GetTypeSize(extract<DataType_t>(m_Args[i])); |
| 142 | + |
| 143 | + return size; |
| 144 | +#endif |
| 145 | + return 0; |
| 146 | +} |
| 147 | + |
| 148 | +int CCallback::GetArgumentOffset(int iIndex) |
| 149 | +{ |
| 150 | + int offset = 8; |
| 151 | + if (iIndex == 0) |
| 152 | + return offset; |
| 153 | + |
| 154 | + for(int i=0; i <= iIndex - 1; i++) |
| 155 | + { |
| 156 | + offset += GetTypeSize(extract<DataType_t>(m_Args[i])); |
| 157 | + } |
| 158 | + |
| 159 | + // Subtract the this pointer on Windows |
| 160 | +#ifdef _WIN32 |
| 161 | + if (m_eConv == CONV_THISCALL) |
| 162 | + offset -= sizeof(void *); |
| 163 | +#endif |
| 164 | + |
| 165 | + return offset; |
| 166 | +} |
| 167 | + |
| 168 | +void CCallback::Dealloc() |
| 169 | +{ |
| 170 | + MemoryManager::getGlobal()->free((void *) m_ulAddr); |
| 171 | + m_ulAddr = 0; |
| 172 | +} |
| 173 | + |
| 174 | +CPointer* CCallback::Realloc(int iSize) |
| 175 | +{ |
| 176 | + BOOST_RAISE_EXCEPTION(PyExc_NotImplementedError, "This function is not implemented for callbacks.") |
| 177 | + return NULL; |
| 178 | +} |
| 179 | + |
| 180 | + |
| 181 | +// ============================================================================ |
| 182 | +// >> FUNCTIONS |
| 183 | +// ============================================================================ |
| 184 | +template<class T> |
| 185 | +T GetArgument(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX, int iIndex) |
| 186 | +{ |
| 187 | +#ifdef _WIN32 |
| 188 | + if (pCallback->m_eConv == CONV_THISCALL && iIndex == 0) |
| 189 | + return *(T *) &ulECX; |
| 190 | +#endif |
| 191 | + |
| 192 | + return *(T *) (ulEBP + pCallback->GetArgumentOffset(iIndex)); |
| 193 | +} |
| 194 | + |
| 195 | +object CallCallback(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX) |
| 196 | +{ |
| 197 | + // TODO: Make this crash proof |
| 198 | + BEGIN_BOOST_PY() |
| 199 | + list arg_list; |
| 200 | + for(int i=0; i < len(pCallback->m_Args); i++) |
| 201 | + { |
| 202 | + object val; |
| 203 | + switch(extract<DataType_t>(pCallback->m_Args[i])) |
| 204 | + { |
| 205 | + case DATA_TYPE_BOOL: val = object(GetArgument<bool>(pCallback, ulEBP, ulECX, i)); break; |
| 206 | + case DATA_TYPE_CHAR: val = object(GetArgument<char>(pCallback, ulEBP, ulECX, i)); break; |
| 207 | + case DATA_TYPE_UCHAR: val = object(GetArgument<unsigned char>(pCallback, ulEBP, ulECX, i)); break; |
| 208 | + case DATA_TYPE_SHORT: val = object(GetArgument<short>(pCallback, ulEBP, ulECX, i)); break; |
| 209 | + case DATA_TYPE_USHORT: val = object(GetArgument<unsigned short>(pCallback, ulEBP, ulECX, i)); break; |
| 210 | + case DATA_TYPE_INT: val = object(GetArgument<int>(pCallback, ulEBP, ulECX, i)); break; |
| 211 | + case DATA_TYPE_UINT: val = object(GetArgument<unsigned int>(pCallback, ulEBP, ulECX, i)); break; |
| 212 | + case DATA_TYPE_LONG: val = object(GetArgument<long>(pCallback, ulEBP, ulECX, i)); break; |
| 213 | + case DATA_TYPE_ULONG: val = object(GetArgument<unsigned long>(pCallback, ulEBP, ulECX, i)); break; |
| 214 | + case DATA_TYPE_LONGLONG: val = object(GetArgument<long long>(pCallback, ulEBP, ulECX, i)); break; |
| 215 | + case DATA_TYPE_ULONGLONG: val = object(GetArgument<unsigned long long>(pCallback, ulEBP, ulECX, i)); break; |
| 216 | + case DATA_TYPE_FLOAT: val = object(GetArgument<float>(pCallback, ulEBP, ulECX, i)); break; |
| 217 | + case DATA_TYPE_DOUBLE: val = object(GetArgument<double>(pCallback, ulEBP, ulECX, i)); break; |
| 218 | + case DATA_TYPE_POINTER: val = object(ptr(new CPointer(GetArgument<unsigned long>(pCallback, ulEBP, ulECX, i)))); break; |
| 219 | + case DATA_TYPE_STRING: val = object(GetArgument<const char*>(pCallback, ulEBP, ulECX, i)); break; |
| 220 | + default: BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Unknown argument type."); break; |
| 221 | + } |
| 222 | + arg_list.append(val); |
| 223 | + } |
| 224 | + arg_list.append(ptr(new CPointer((unsigned long) ulEBP))); |
| 225 | + return eval("lambda func, args: func(*args)")(pCallback->m_oCallback, arg_list); |
| 226 | + |
| 227 | + END_BOOST_PY_NORET() |
| 228 | + |
| 229 | + PythonLog(0, "An exception occured while calling the Python callback. The server will now crash!"); |
| 230 | + |
| 231 | + // Throw an exception. We will crash now :( |
| 232 | + throw; |
| 233 | +} |
0 commit comments