jit refactoring branch
--HG-- branch : refac-jit extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/refac-jit%402369
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEPAWN_BASECONTEXT_H_
|
||||
#define _INCLUDE_SOURCEPAWN_BASECONTEXT_H_
|
||||
|
||||
#include "sp_vm_api.h"
|
||||
#include "sp_vm_function.h"
|
||||
|
||||
/**
|
||||
* :TODO: Make functions allocate as a lump instead of individual allocations!
|
||||
*/
|
||||
|
||||
extern IProfiler *sm_profiler;
|
||||
|
||||
namespace SourcePawn
|
||||
{
|
||||
class BaseContext :
|
||||
public IPluginContext,
|
||||
public IPluginDebugInfo
|
||||
{
|
||||
public:
|
||||
BaseContext(sp_context_t *ctx);
|
||||
~BaseContext();
|
||||
public: //IPluginContext
|
||||
IVirtualMachine *GetVirtualMachine();
|
||||
sp_context_t *GetContext();
|
||||
bool IsDebugging();
|
||||
int SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn);
|
||||
IPluginDebugInfo *GetDebugInfo();
|
||||
int HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr);
|
||||
int HeapPop(cell_t local_addr);
|
||||
int HeapRelease(cell_t local_addr);
|
||||
int FindNativeByName(const char *name, uint32_t *index);
|
||||
int GetNativeByIndex(uint32_t index, sp_native_t **native);
|
||||
uint32_t GetNativesNum();
|
||||
int FindPublicByName(const char *name, uint32_t *index);
|
||||
int GetPublicByIndex(uint32_t index, sp_public_t **publicptr);
|
||||
uint32_t GetPublicsNum();
|
||||
int GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar);
|
||||
int FindPubvarByName(const char *name, uint32_t *index);
|
||||
int GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr);
|
||||
uint32_t GetPubVarsNum();
|
||||
int LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr);
|
||||
int LocalToString(cell_t local_addr, char **addr);
|
||||
int StringToLocal(cell_t local_addr, size_t chars, const char *source);
|
||||
int StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes);
|
||||
int PushCell(cell_t value);
|
||||
int PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells);
|
||||
int PushString(cell_t *local_addr, char **phys_addr, const char *string);
|
||||
int PushCellsFromArray(cell_t array[], unsigned int numcells);
|
||||
int BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite);
|
||||
int BindNative(const sp_nativeinfo_t *native);
|
||||
int BindNativeToAny(SPVM_NATIVE_FUNC native);
|
||||
int Execute(uint32_t code_addr, cell_t *result);
|
||||
cell_t ThrowNativeErrorEx(int error, const char *msg, ...);
|
||||
cell_t ThrowNativeError(const char *msg, ...);
|
||||
IPluginFunction *GetFunctionByName(const char *public_name);
|
||||
IPluginFunction *GetFunctionById(funcid_t func_id);
|
||||
#if defined SOURCEMOD_BUILD
|
||||
SourceMod::IdentityToken_t *GetIdentity();
|
||||
void SetIdentity(SourceMod::IdentityToken_t *token);
|
||||
cell_t *GetNullRef(SP_NULL_TYPE type);
|
||||
int LocalToStringNULL(cell_t local_addr, char **addr);
|
||||
#endif
|
||||
int BindNativeToIndex(uint32_t index, SPVM_NATIVE_FUNC native);
|
||||
public: //IPluginDebugInfo
|
||||
int LookupFile(ucell_t addr, const char **filename);
|
||||
int LookupFunction(ucell_t addr, const char **name);
|
||||
int LookupLine(ucell_t addr, uint32_t *line);
|
||||
public:
|
||||
void SetContext(sp_context_t *_ctx);
|
||||
bool IsInExec();
|
||||
private:
|
||||
void SetErrorMessage(const char *msg, va_list ap);
|
||||
void FlushFunctionCache();
|
||||
void RefreshFunctionCache();
|
||||
private:
|
||||
sp_context_t *ctx;
|
||||
#if defined SOURCEMOD_BUILD
|
||||
SourceMod::IdentityToken_t *m_pToken;
|
||||
cell_t *m_pNullVec;
|
||||
cell_t *m_pNullString;
|
||||
#endif
|
||||
char m_MsgCache[1024];
|
||||
bool m_CustomMsg;
|
||||
bool m_InExec;
|
||||
unsigned int m_funcsnum;
|
||||
#if 0
|
||||
CFunction **m_priv_funcs;
|
||||
#endif
|
||||
CFunction **m_pub_funcs;
|
||||
};
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_BASECONTEXT_H_
|
||||
@@ -0,0 +1,704 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sp_vm_types.h"
|
||||
#include <sh_memory.h>
|
||||
/* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */
|
||||
#if !defined SH_ASSERT
|
||||
#define SH_ASSERT(x, info)
|
||||
#include <sh_pagealloc.h>
|
||||
#undef SH_ASSERT
|
||||
#else
|
||||
#include <sh_pagealloc.h>
|
||||
#endif
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "sp_file_headers.h"
|
||||
#include "sp_vm_engine.h"
|
||||
#include "zlib/zlib.h"
|
||||
#include "sp_vm_basecontext.h"
|
||||
|
||||
#if defined WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#elif defined __GNUC__
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#define INVALID_CIP 0xFFFFFFFF
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
SourceHook::CPageAlloc g_ExeMemory(16);
|
||||
|
||||
#define ERROR_MESSAGE_MAX 25
|
||||
static const char *g_ErrorMsgTable[] =
|
||||
{
|
||||
NULL,
|
||||
"Unrecognizable file format",
|
||||
"Decompressor was not found",
|
||||
"Not enough space on the heap",
|
||||
"Invalid parameter or parameter type",
|
||||
"Invalid plugin address",
|
||||
"Object or index not found",
|
||||
"Invalid index or index not found",
|
||||
"Not enough space on the stack",
|
||||
"Debug section not found or debug not enabled",
|
||||
"Invalid instruction",
|
||||
"Invalid memory access",
|
||||
"Stack went below stack boundary",
|
||||
"Heap went below heap boundary",
|
||||
"Divide by zero",
|
||||
"Array index is out of bounds",
|
||||
"Instruction contained invalid parameter",
|
||||
"Stack memory leaked by native",
|
||||
"Heap memory leaked by native",
|
||||
"Dynamic array is too big",
|
||||
"Tracker stack is out of bounds",
|
||||
"Native is not bound",
|
||||
"Maximum number of parameters reached",
|
||||
"Native detected error",
|
||||
"Plugin not runnable",
|
||||
"Call was aborted",
|
||||
};
|
||||
|
||||
const char *GetSourcePawnErrorMessage(int error)
|
||||
{
|
||||
if (error < 1 || error > ERROR_MESSAGE_MAX)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_ErrorMsgTable[error];
|
||||
}
|
||||
|
||||
SourcePawnEngine::SourcePawnEngine()
|
||||
{
|
||||
m_pDebugHook = NULL;
|
||||
m_CallStack = NULL;
|
||||
m_FreedCalls = NULL;
|
||||
m_CurChain = 0;
|
||||
#if 0
|
||||
m_pFreeFuncs = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
SourcePawnEngine::~SourcePawnEngine()
|
||||
{
|
||||
TracedCall *pTemp;
|
||||
while (m_FreedCalls)
|
||||
{
|
||||
pTemp = m_FreedCalls->next;
|
||||
delete m_FreedCalls;
|
||||
m_FreedCalls = pTemp;
|
||||
}
|
||||
|
||||
#if 0
|
||||
CFunction *pNext;
|
||||
while (m_pFreeFuncs)
|
||||
{
|
||||
pNext = m_pFreeFuncs->m_pNext;
|
||||
delete m_pFreeFuncs;
|
||||
m_pFreeFuncs = pNext;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SourcePawnEngine::ExecAlloc(size_t size)
|
||||
{
|
||||
#if defined WIN32
|
||||
return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#elif defined __GNUC__
|
||||
void *base = memalign(sysconf(_SC_PAGESIZE), size);
|
||||
if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
|
||||
{
|
||||
free(base);
|
||||
return NULL;
|
||||
}
|
||||
return base;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SourcePawnEngine::AllocatePageMemory(size_t size)
|
||||
{
|
||||
return g_ExeMemory.Alloc(size);
|
||||
}
|
||||
|
||||
void SourcePawnEngine::SetReadExecute(void *ptr)
|
||||
{
|
||||
g_ExeMemory.SetRE(ptr);
|
||||
}
|
||||
|
||||
void SourcePawnEngine::SetReadWrite(void *ptr)
|
||||
{
|
||||
g_ExeMemory.SetRW(ptr);
|
||||
}
|
||||
|
||||
void SourcePawnEngine::FreePageMemory(void *ptr)
|
||||
{
|
||||
g_ExeMemory.Free(ptr);
|
||||
}
|
||||
|
||||
void SourcePawnEngine::ExecFree(void *address)
|
||||
{
|
||||
#if defined WIN32
|
||||
VirtualFree(address, 0, MEM_RELEASE);
|
||||
#elif defined __GNUC__
|
||||
free(address);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SourcePawnEngine::BaseAlloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void SourcePawnEngine::BaseFree(void *memory)
|
||||
{
|
||||
free(memory);
|
||||
}
|
||||
|
||||
IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
|
||||
{
|
||||
return new BaseContext(ctx);
|
||||
}
|
||||
|
||||
void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
|
||||
{
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
|
||||
{
|
||||
char *nameptr;
|
||||
uint8_t sectnum = 0;
|
||||
sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
|
||||
|
||||
memset(plugin, 0, sizeof(sp_plugin_t));
|
||||
|
||||
plugin->base = base;
|
||||
|
||||
while (sectnum < hdr->sections)
|
||||
{
|
||||
nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
|
||||
|
||||
if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
|
||||
{
|
||||
sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
|
||||
plugin->pcode = base + secptr->dataoffs + cod->code;
|
||||
plugin->pcode_size = cod->codesize;
|
||||
plugin->flags = cod->flags;
|
||||
}
|
||||
else if (!(plugin->data) && !strcmp(nameptr, ".data"))
|
||||
{
|
||||
sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
|
||||
plugin->data = base + secptr->dataoffs + dat->data;
|
||||
plugin->data_size = dat->datasize;
|
||||
plugin->memory = dat->memsize;
|
||||
}
|
||||
else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
|
||||
{
|
||||
plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
|
||||
plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
|
||||
{
|
||||
plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
|
||||
plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
|
||||
{
|
||||
plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
|
||||
plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
|
||||
{
|
||||
plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
|
||||
{
|
||||
plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
|
||||
{
|
||||
plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
|
||||
{
|
||||
plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
|
||||
}
|
||||
else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
|
||||
{
|
||||
sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
|
||||
plugin->debug.files_num = inf->num_files;
|
||||
plugin->debug.lines_num = inf->num_lines;
|
||||
plugin->debug.syms_num = inf->num_syms;
|
||||
}
|
||||
else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
|
||||
{
|
||||
plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
|
||||
}
|
||||
|
||||
secptr++;
|
||||
sectnum++;
|
||||
}
|
||||
|
||||
if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
|
||||
{
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
|
||||
{
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
*err = SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
return plugin;
|
||||
|
||||
return_error:
|
||||
if (err)
|
||||
{
|
||||
*err = SP_ERROR_FILE_FORMAT;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
|
||||
{
|
||||
sp_file_hdr_t hdr;
|
||||
sp_plugin_t *plugin;
|
||||
uint8_t *base;
|
||||
int z_result;
|
||||
int error;
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
error = SP_ERROR_NOT_FOUND;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
/* Rewind for safety */
|
||||
rewind(fp);
|
||||
fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
|
||||
|
||||
if (hdr.magic != SPFILE_MAGIC)
|
||||
{
|
||||
error = SP_ERROR_FILE_FORMAT;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
switch (hdr.compression)
|
||||
{
|
||||
case SPFILE_COMPRESSION_GZ:
|
||||
{
|
||||
uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
|
||||
uint32_t compsize = hdr.disksize - hdr.dataoffs;
|
||||
uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
|
||||
uLongf destlen = uncompsize;
|
||||
|
||||
char *tempbuf = (char *)malloc(compsize);
|
||||
void *uncompdata = malloc(uncompsize);
|
||||
void *sectheader = malloc(sectsize);
|
||||
|
||||
fread(sectheader, sectsize, 1, fp);
|
||||
fread(tempbuf, compsize, 1, fp);
|
||||
|
||||
z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
|
||||
free(tempbuf);
|
||||
if (z_result != Z_OK)
|
||||
{
|
||||
free(sectheader);
|
||||
free(uncompdata);
|
||||
error = SP_ERROR_DECOMPRESSOR;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
base = (uint8_t *)malloc(hdr.imagesize);
|
||||
memcpy(base, &hdr, sizeof(sp_file_hdr_t));
|
||||
memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
|
||||
free(sectheader);
|
||||
memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
|
||||
free(uncompdata);
|
||||
break;
|
||||
}
|
||||
case SPFILE_COMPRESSION_NONE:
|
||||
{
|
||||
base = (uint8_t *)malloc(hdr.imagesize);
|
||||
rewind(fp);
|
||||
fread(base, hdr.imagesize, 1, fp);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error = SP_ERROR_DECOMPRESSOR;
|
||||
goto return_error;
|
||||
}
|
||||
}
|
||||
|
||||
plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
|
||||
if (!_ReadPlugin(&hdr, base, plugin, err))
|
||||
{
|
||||
free(plugin);
|
||||
free(base);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plugin->allocflags = 0;
|
||||
|
||||
return plugin;
|
||||
|
||||
return_error:
|
||||
if (err)
|
||||
{
|
||||
*err = error;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
|
||||
{
|
||||
sp_file_hdr_t hdr;
|
||||
uint8_t noptr = 0;
|
||||
|
||||
memcpy(&hdr, base, sizeof(sp_file_hdr_t));
|
||||
|
||||
if (!plugin)
|
||||
{
|
||||
plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
|
||||
noptr = 1;
|
||||
}
|
||||
|
||||
if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
|
||||
{
|
||||
if (noptr)
|
||||
{
|
||||
free(plugin);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!noptr)
|
||||
{
|
||||
plugin->allocflags |= SP_FA_SELF_EXTERNAL;
|
||||
}
|
||||
plugin->allocflags |= SP_FA_BASE_EXTERNAL;
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
|
||||
{
|
||||
if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
|
||||
{
|
||||
free(plugin->base);
|
||||
plugin->base = NULL;
|
||||
}
|
||||
if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
|
||||
{
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void SourcePawnEngine::ReleaseFunctionToPool(CFunction *func)
|
||||
{
|
||||
if (!func)
|
||||
{
|
||||
return;
|
||||
}
|
||||
func->Cancel();
|
||||
func->m_pNext = m_pFreeFuncs;
|
||||
m_pFreeFuncs = func;
|
||||
}
|
||||
|
||||
CFunction *SourcePawnEngine::GetFunctionFromPool(funcid_t f, IPluginContext *plugin)
|
||||
{
|
||||
if (!m_pFreeFuncs)
|
||||
{
|
||||
return new CFunction(f, plugin);
|
||||
} else {
|
||||
CFunction *pFunc = m_pFreeFuncs;
|
||||
m_pFreeFuncs = m_pFreeFuncs->m_pNext;
|
||||
pFunc->Set(f, plugin);
|
||||
return pFunc;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
|
||||
{
|
||||
IDebugListener *old = m_pDebugHook;
|
||||
|
||||
m_pDebugHook = pListener;
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
unsigned int SourcePawnEngine::GetContextCallCount()
|
||||
{
|
||||
if (!m_CallStack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_CallStack->chain;
|
||||
}
|
||||
|
||||
TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
|
||||
{
|
||||
TracedCall *pCall;
|
||||
|
||||
if (!m_FreedCalls)
|
||||
{
|
||||
pCall = new TracedCall;
|
||||
} else {
|
||||
/* Unlink the head node from the free list */
|
||||
pCall = m_FreedCalls;
|
||||
m_FreedCalls = m_FreedCalls->next;
|
||||
}
|
||||
|
||||
/* Link as the head node into the call stack */
|
||||
pCall->next = m_CallStack;
|
||||
|
||||
if (new_chain)
|
||||
{
|
||||
pCall->chain = ++m_CurChain;
|
||||
} else {
|
||||
pCall->chain = m_CurChain;
|
||||
}
|
||||
|
||||
m_CallStack = pCall;
|
||||
|
||||
return pCall;
|
||||
}
|
||||
|
||||
void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
|
||||
{
|
||||
/* Check if this is the top of the call stack */
|
||||
if (pCall == m_CallStack)
|
||||
{
|
||||
m_CallStack = m_CallStack->next;
|
||||
}
|
||||
|
||||
/* Add this to our linked list of freed calls */
|
||||
if (!m_FreedCalls)
|
||||
{
|
||||
m_FreedCalls = pCall;
|
||||
m_FreedCalls->next = NULL;
|
||||
} else {
|
||||
pCall->next = m_FreedCalls;
|
||||
m_FreedCalls = pCall;
|
||||
}
|
||||
}
|
||||
|
||||
void SourcePawnEngine::PushTracer(sp_context_t *ctx)
|
||||
{
|
||||
TracedCall *pCall = MakeTracedCall(true);
|
||||
|
||||
pCall->cip = INVALID_CIP;
|
||||
pCall->ctx = ctx;
|
||||
pCall->frm = INVALID_CIP;
|
||||
}
|
||||
|
||||
void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
|
||||
{
|
||||
assert(m_CallStack != NULL);
|
||||
assert(m_CallStack->ctx == ctx);
|
||||
assert(m_CallStack->chain == m_CurChain);
|
||||
|
||||
if (m_CallStack->cip == INVALID_CIP)
|
||||
{
|
||||
/* We aren't logging anything yet, so begin the trace */
|
||||
m_CallStack->cip = codeip;
|
||||
m_CallStack->frm = frame;
|
||||
} else {
|
||||
if (m_CallStack->frm > frame)
|
||||
{
|
||||
/* The last frame has moved down the stack,
|
||||
* so we have to push a new call onto our list.
|
||||
*/
|
||||
TracedCall *pCall = MakeTracedCall(false);
|
||||
pCall->ctx = ctx;
|
||||
pCall->frm = frame;
|
||||
} else if (m_CallStack->frm < frame) {
|
||||
/* The last frame has moved up the stack,
|
||||
* so we have to pop the call from our list.
|
||||
*/
|
||||
FreeTracedCall(m_CallStack);
|
||||
}
|
||||
/* no matter where we are, update the cip */
|
||||
m_CallStack->cip = codeip;
|
||||
}
|
||||
}
|
||||
|
||||
void SourcePawnEngine::PopTracer(int error, const char *msg)
|
||||
{
|
||||
assert(m_CallStack != NULL);
|
||||
|
||||
if (error != SP_ERROR_NONE && m_pDebugHook)
|
||||
{
|
||||
uint32_t native = INVALID_CIP;
|
||||
|
||||
if (m_CallStack->ctx->n_err)
|
||||
{
|
||||
native = m_CallStack->ctx->n_idx;
|
||||
}
|
||||
|
||||
CContextTrace trace(m_CallStack, error, msg, native);
|
||||
m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
|
||||
}
|
||||
|
||||
/* Now pop the error chain */
|
||||
while (m_CallStack && m_CallStack->chain == m_CurChain)
|
||||
{
|
||||
FreeTracedCall(m_CallStack);
|
||||
}
|
||||
|
||||
m_CurChain--;
|
||||
}
|
||||
|
||||
unsigned int SourcePawnEngine::GetEngineAPIVersion()
|
||||
{
|
||||
return SOURCEPAWN_ENGINE_API_VERSION;
|
||||
}
|
||||
|
||||
CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) :
|
||||
m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
|
||||
{
|
||||
}
|
||||
|
||||
bool CContextTrace::DebugInfoAvailable()
|
||||
{
|
||||
return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
|
||||
}
|
||||
|
||||
const char *CContextTrace::GetCustomErrorString()
|
||||
{
|
||||
return m_pMsg;
|
||||
}
|
||||
|
||||
int CContextTrace::GetErrorCode()
|
||||
{
|
||||
return m_Error;
|
||||
}
|
||||
|
||||
const char *CContextTrace::GetErrorString()
|
||||
{
|
||||
if (m_Error > ERROR_MESSAGE_MAX ||
|
||||
m_Error < 1)
|
||||
{
|
||||
return "Invalid error code";
|
||||
} else {
|
||||
return g_ErrorMsgTable[m_Error];
|
||||
}
|
||||
}
|
||||
|
||||
void CContextTrace::ResetTrace()
|
||||
{
|
||||
m_pIterator = m_pStart;
|
||||
}
|
||||
|
||||
bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
|
||||
{
|
||||
if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_pIterator->cip == INVALID_CIP)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IPluginContext *pContext = m_pIterator->ctx->context;
|
||||
IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!trace)
|
||||
{
|
||||
m_pIterator = m_pIterator->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
|
||||
{
|
||||
trace->filename = NULL;
|
||||
}
|
||||
|
||||
if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
|
||||
{
|
||||
trace->function = NULL;
|
||||
}
|
||||
|
||||
if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
|
||||
{
|
||||
trace->line = 0;
|
||||
}
|
||||
|
||||
m_pIterator = m_pIterator->next;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *CContextTrace::GetLastNative(uint32_t *index)
|
||||
{
|
||||
if (m_Native == INVALID_CIP)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sp_native_t *native;
|
||||
if (m_pIterator->ctx->context->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (index)
|
||||
{
|
||||
*index = m_Native;
|
||||
}
|
||||
|
||||
return native->name;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEPAWN_VM_ENGINE_H_
|
||||
#define _INCLUDE_SOURCEPAWN_VM_ENGINE_H_
|
||||
|
||||
#include "sp_vm_api.h"
|
||||
#include "sp_vm_function.h"
|
||||
|
||||
struct TracedCall
|
||||
{
|
||||
uint32_t cip;
|
||||
uint32_t frm;
|
||||
sp_context_t *ctx;
|
||||
TracedCall *next;
|
||||
unsigned int chain;
|
||||
};
|
||||
|
||||
class CContextTrace : public IContextTrace
|
||||
{
|
||||
public:
|
||||
CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native);
|
||||
public:
|
||||
int GetErrorCode();
|
||||
const char *GetErrorString();
|
||||
bool DebugInfoAvailable();
|
||||
const char *GetCustomErrorString();
|
||||
bool GetTraceInfo(CallStackInfo *trace);
|
||||
void ResetTrace();
|
||||
const char *GetLastNative(uint32_t *index);
|
||||
private:
|
||||
int m_Error;
|
||||
const char *m_pMsg;
|
||||
TracedCall *m_pStart;
|
||||
TracedCall *m_pIterator;
|
||||
uint32_t m_Native;
|
||||
};
|
||||
|
||||
class SourcePawnEngine : public ISourcePawnEngine
|
||||
{
|
||||
public:
|
||||
SourcePawnEngine();
|
||||
~SourcePawnEngine();
|
||||
public: //ISourcePawnEngine
|
||||
sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err);
|
||||
sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err);
|
||||
int FreeFromMemory(sp_plugin_t *plugin);
|
||||
IPluginContext *CreateBaseContext(sp_context_t *ctx);
|
||||
void FreeBaseContext(IPluginContext *ctx);
|
||||
void *BaseAlloc(size_t size);
|
||||
void BaseFree(void *memory);
|
||||
void *ExecAlloc(size_t size);
|
||||
void ExecFree(void *address);
|
||||
IDebugListener *SetDebugListener(IDebugListener *pListener);
|
||||
unsigned int GetContextCallCount();
|
||||
unsigned int GetEngineAPIVersion();
|
||||
void *AllocatePageMemory(size_t size);
|
||||
void SetReadWrite(void *ptr);
|
||||
void SetReadExecute(void *ptr);
|
||||
void FreePageMemory(void *ptr);
|
||||
public: //Debugger Stuff
|
||||
/**
|
||||
* @brief Pushes a context onto the top of the call tracer.
|
||||
*
|
||||
* @param ctx Plugin context.
|
||||
*/
|
||||
void PushTracer(sp_context_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Pops a plugin off the call tracer.
|
||||
*/
|
||||
void PopTracer(int error, const char *msg);
|
||||
|
||||
/**
|
||||
* @brief Runs tracer from a debug break.
|
||||
*/
|
||||
void RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip);
|
||||
public: //Plugin function stuff
|
||||
CFunction *GetFunctionFromPool(funcid_t f, IPluginContext *plugin);
|
||||
void ReleaseFunctionToPool(CFunction *func);
|
||||
private:
|
||||
TracedCall *MakeTracedCall(bool new_chain);
|
||||
void FreeTracedCall(TracedCall *pCall);
|
||||
private:
|
||||
IDebugListener *m_pDebugHook;
|
||||
TracedCall *m_FreedCalls;
|
||||
TracedCall *m_CallStack;
|
||||
unsigned int m_CurChain;
|
||||
//CFunction *m_pFreeFuncs;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_VM_ENGINE_H_
|
||||
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sp_vm_function.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
/********************
|
||||
* FUNCTION CALLING *
|
||||
********************/
|
||||
|
||||
void CFunction::Set(uint32_t code_addr, IPluginContext *plugin, funcid_t id, uint32_t pub_id)
|
||||
{
|
||||
m_codeaddr = code_addr;
|
||||
m_pContext = plugin;
|
||||
m_curparam = 0;
|
||||
m_errorstate = SP_ERROR_NONE;
|
||||
m_Invalid = false;
|
||||
m_pCtx = plugin ? plugin->GetContext() : NULL;
|
||||
m_FnId = id;
|
||||
|
||||
m_pContext->GetPublicByIndex(pub_id, &m_pPublic);
|
||||
}
|
||||
|
||||
bool CFunction::IsRunnable()
|
||||
{
|
||||
return ((m_pCtx->flags & SPFLAG_PLUGIN_PAUSED) != SPFLAG_PLUGIN_PAUSED);
|
||||
}
|
||||
|
||||
int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
int ir, serial;
|
||||
|
||||
if (!IsRunnable())
|
||||
{
|
||||
return SP_ERROR_NOT_RUNNABLE;
|
||||
}
|
||||
|
||||
if ((m_pCtx->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS
|
||||
&& m_pPublic != NULL)
|
||||
{
|
||||
serial = m_pCtx->profiler->OnCallbackBegin(m_pContext, m_pPublic);
|
||||
}
|
||||
|
||||
while (num_params--)
|
||||
{
|
||||
m_pContext->PushCell(params[num_params]);
|
||||
}
|
||||
|
||||
ir = m_pContext->Execute(m_codeaddr, result);
|
||||
|
||||
if ((m_pCtx->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS
|
||||
&& m_pPublic != NULL)
|
||||
{
|
||||
m_pCtx->profiler->OnCallbackEnd(serial);
|
||||
}
|
||||
|
||||
return ir;
|
||||
}
|
||||
|
||||
IPluginContext *CFunction::GetParentContext()
|
||||
{
|
||||
return m_pContext;
|
||||
}
|
||||
|
||||
CFunction::CFunction(uint32_t code_addr, IPluginContext *plugin, funcid_t id, uint32_t pub_id) :
|
||||
m_codeaddr(code_addr), m_pContext(plugin), m_curparam(0),
|
||||
m_errorstate(SP_ERROR_NONE), m_FnId(id)
|
||||
{
|
||||
m_Invalid = false;
|
||||
if (plugin)
|
||||
{
|
||||
m_pCtx = plugin->GetContext();
|
||||
}
|
||||
m_pContext->GetPublicByIndex(pub_id, &m_pPublic);
|
||||
}
|
||||
|
||||
int CFunction::PushCell(cell_t cell)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
m_info[m_curparam].marked = false;
|
||||
m_params[m_curparam] = cell;
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int CFunction::PushCellByRef(cell_t *cell, int flags)
|
||||
{
|
||||
return PushArray(cell, 1, flags);
|
||||
}
|
||||
|
||||
int CFunction::PushFloat(float number)
|
||||
{
|
||||
cell_t val = *(cell_t *)&number;
|
||||
|
||||
return PushCell(val);
|
||||
}
|
||||
|
||||
int CFunction::PushFloatByRef(float *number, int flags)
|
||||
{
|
||||
return PushCellByRef((cell_t *)number, flags);
|
||||
}
|
||||
|
||||
int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
ParamInfo *info = &m_info[m_curparam];
|
||||
|
||||
info->flags = inarray ? copyback : 0;
|
||||
info->marked = true;
|
||||
info->size = cells;
|
||||
info->str.is_sz = false;
|
||||
info->orig_addr = inarray;
|
||||
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int CFunction::PushString(const char *string)
|
||||
{
|
||||
return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
|
||||
}
|
||||
|
||||
int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
|
||||
{
|
||||
return _PushString(buffer, sz_flags, cp_flags, length);
|
||||
}
|
||||
|
||||
int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
ParamInfo *info = &m_info[m_curparam];
|
||||
|
||||
info->marked = true;
|
||||
info->orig_addr = (cell_t *)string;
|
||||
info->flags = cp_flags;
|
||||
info->size = len;
|
||||
info->str.sz_flags = sz_flags;
|
||||
info->str.is_sz = true;
|
||||
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
void CFunction::Cancel()
|
||||
{
|
||||
if (!m_curparam)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_errorstate = SP_ERROR_NONE;
|
||||
m_curparam = 0;
|
||||
}
|
||||
|
||||
int CFunction::Execute(cell_t *result)
|
||||
{
|
||||
int err = SP_ERROR_NONE;
|
||||
|
||||
if (!IsRunnable())
|
||||
{
|
||||
m_errorstate = SP_ERROR_NOT_RUNNABLE;
|
||||
}
|
||||
|
||||
if (m_errorstate != SP_ERROR_NONE)
|
||||
{
|
||||
err = m_errorstate;
|
||||
Cancel();
|
||||
return err;
|
||||
}
|
||||
|
||||
//This is for re-entrancy!
|
||||
cell_t temp_params[SP_MAX_EXEC_PARAMS];
|
||||
ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int numparams = m_curparam;
|
||||
unsigned int i;
|
||||
bool docopies = true;
|
||||
|
||||
if (numparams)
|
||||
{
|
||||
//Save the info locally, then reset it for re-entrant calls.
|
||||
memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
|
||||
}
|
||||
m_curparam = 0;
|
||||
|
||||
/* Browse the parameters and build arrays */
|
||||
for (i=0; i<numparams; i++)
|
||||
{
|
||||
/* Is this marked as an array? */
|
||||
if (temp_info[i].marked)
|
||||
{
|
||||
if (!temp_info[i].str.is_sz)
|
||||
{
|
||||
/* Allocate a normal/generic array */
|
||||
if ((err=m_pContext->HeapAlloc(temp_info[i].size,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr)))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (temp_info[i].orig_addr)
|
||||
{
|
||||
memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate cells required for the string */
|
||||
size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
|
||||
|
||||
/* Allocate the buffer */
|
||||
if ((err=m_pContext->HeapAlloc(cells,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr)))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Copy original string if necessary */
|
||||
if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
|
||||
{
|
||||
/* Cut off UTF-8 properly */
|
||||
if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
|
||||
{
|
||||
if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr,
|
||||
NULL))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Copy a binary blob */
|
||||
else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
|
||||
{
|
||||
memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
|
||||
}
|
||||
/* Copy ASCII characters */
|
||||
else
|
||||
{
|
||||
if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* End array/string calculation */
|
||||
/* Update the pushed parameter with the byref local address */
|
||||
temp_params[i] = temp_info[i].local_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just copy the value normally */
|
||||
temp_params[i] = m_params[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the call if we can */
|
||||
if (err == SP_ERROR_NONE)
|
||||
{
|
||||
if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
|
||||
{
|
||||
docopies = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
docopies = false;
|
||||
}
|
||||
|
||||
/* i should be equal to the last valid parameter + 1 */
|
||||
while (i--)
|
||||
{
|
||||
if (!temp_info[i].marked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
|
||||
{
|
||||
if (temp_info[i].orig_addr)
|
||||
{
|
||||
if (temp_info[i].str.is_sz)
|
||||
{
|
||||
memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temp_info[i].size == 1)
|
||||
{
|
||||
*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(temp_info[i].orig_addr,
|
||||
temp_info[i].phys_addr,
|
||||
temp_info[i].size * sizeof(cell_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
funcid_t CFunction::GetFunctionID()
|
||||
{
|
||||
return m_FnId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_BASEFUNCTION_H_
|
||||
#define _INCLUDE_SOURCEMOD_BASEFUNCTION_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
|
||||
struct ParamInfo
|
||||
{
|
||||
int flags; /* Copy-back flags */
|
||||
bool marked; /* Whether this is marked as being used */
|
||||
cell_t local_addr; /* Local address to free */
|
||||
cell_t *phys_addr; /* Physical address of our copy */
|
||||
cell_t *orig_addr; /* Original address to copy back to */
|
||||
ucell_t size; /* Size of array in bytes */
|
||||
struct
|
||||
{
|
||||
bool is_sz; /* is a string */
|
||||
int sz_flags; /* has sz flags */
|
||||
} str;
|
||||
};
|
||||
|
||||
class CPlugin;
|
||||
|
||||
class CFunction : public IPluginFunction
|
||||
{
|
||||
friend class SourcePawnEngine;
|
||||
public:
|
||||
CFunction(uint32_t code_addr,
|
||||
IPluginContext *pContext,
|
||||
funcid_t fnid,
|
||||
uint32_t pub_id);
|
||||
public:
|
||||
virtual int PushCell(cell_t cell);
|
||||
virtual int PushCellByRef(cell_t *cell, int flags);
|
||||
virtual int PushFloat(float number);
|
||||
virtual int PushFloatByRef(float *number, int flags);
|
||||
virtual int PushArray(cell_t *inarray, unsigned int cells, int copyback);
|
||||
virtual int PushString(const char *string);
|
||||
virtual int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
|
||||
virtual int Execute(cell_t *result);
|
||||
virtual void Cancel();
|
||||
virtual int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
|
||||
virtual IPluginContext *GetParentContext();
|
||||
inline bool IsInvalidated()
|
||||
{
|
||||
return m_Invalid;
|
||||
}
|
||||
inline void Invalidate()
|
||||
{
|
||||
m_Invalid = true;
|
||||
}
|
||||
bool IsRunnable();
|
||||
funcid_t GetFunctionID();
|
||||
public:
|
||||
void Set(uint32_t code_addr, IPluginContext *plugin, funcid_t fnid, uint32_t pub_id);
|
||||
private:
|
||||
int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
|
||||
inline int SetError(int err)
|
||||
{
|
||||
m_errorstate = err;
|
||||
return err;
|
||||
}
|
||||
private:
|
||||
uint32_t m_codeaddr;
|
||||
IPluginContext *m_pContext;
|
||||
sp_context_t *m_pCtx;
|
||||
cell_t m_params[SP_MAX_EXEC_PARAMS];
|
||||
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int m_curparam;
|
||||
int m_errorstate;
|
||||
CFunction *m_pNext;
|
||||
bool m_Invalid;
|
||||
funcid_t m_FnId;
|
||||
sp_public_t *m_pPublic;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_BASEFUNCTION_H_
|
||||
Reference in New Issue
Block a user