diff --git a/sourcepawn/include/sp_vm_api.h b/sourcepawn/include/sp_vm_api.h new file mode 100644 index 00000000..c469093a --- /dev/null +++ b/sourcepawn/include/sp_vm_api.h @@ -0,0 +1,133 @@ +#ifndef _INCLUDE_SOURCEPAWN_VM_API_H_ +#define _INCLUDE_SOURCEPAWN_VM_API_H_ + +#include <stdio.h> +#include "sp_vm_types.h" +#include "sp_vm_context.h" + +namespace SourcePawn +{ + class IPluginContext; + + /** + * Contains helper functions used by VMs and the host app + */ + class ISourcePawnEngine + { + public: + /** + * Loads a named file from a file pointer. + * Using this means base memory will be allocated by the VM. + * + * @param fp File pointer. May be at any offset. Not closed on return. + * @param err Optional error code pointer. + * @return A new plugin structure. + */ + virtual sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) =0; + + /** + * Loads a file from a base memory address. + * + * @param base Base address of the plugin's memory region. + * @param plugin If NULL, a new plugin pointer is returned. + * Otherwise, the passed pointer is used. + * @param err Optional error code pointer. + * @return The resulting plugin pointer. + */ + virtual sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) =0; + + /** + * Frees all of the memory associated with a plugin file. + * If allocated using SP_LoadFromMemory, the base and plugin pointer + * itself are not freed (so this may end up doing nothing). + */ + virtual int FreeFromMemory(sp_plugin_t *plugin) =0; + + /** + * Creates a new IContext from a context handle. + * + * @param ctx Context to use as a basis for the IPluginContext. + * @return New IPluginContext handle. + */ + virtual IPluginContext *CreateBaseContext(sp_context_t *ctx) =0; + + /** + * Frees a context. + * + * @param ctx Context pointer to free. + */ + virtual void FreeBaseContext(IPluginContext *ctx) =0; + + /** + * Allocates memory. + * + * @param size Size of memory to allocate. + * @return Pointer to memory, NULL if allocation failed. + */ + virtual void *BaseAlloc(size_t size) =0; + + /** + * Frees memory allocated with BaseAlloc. + * + * @param mem Memory address to free. + */ + virtual void BaseFree(void *memory) =0; + }; + + class ICompilation; + + class IVirtualMachine + { + public: + /** + * Returns the string name of a VM implementation. + */ + virtual const char *GetVMName() =0; + + /** + * Begins a new compilation + * + * @param plugin Pointer to a plugin structure. + * @return New compilation pointer. + */ + virtual ICompilation *StartCompilation(sp_plugin_t *plugin) =0; + + /** + * Sets a compilation option. + * + * @param co Pointer to a compilation. + * @param key Option key name. + * @param val Option value string. + * @return True if option could be set, false otherwise. + */ + virtual bool SetCompilationOption(ICompilation *co, const char *key, const char *val) =0; + + /** + * Finalizes a compilation into a new IContext. + * Note: This will free the ICompilation pointer. + * + * @param co Compilation pointer. + * @return New plugin context. + */ + virtual IPluginContext *CompileToContext(ICompilation *co) =0; + + /** + * Frees any internal variable usage on a context. + * + * @param ctx Context structure pointer. + */ + virtual void FreeContextVars(sp_context_t *ctx) =0; + + /** + * Calls the "execute" function on a context. + * + * @param ctx Executes a function in a context. + * @param code_idx Index into the code section. + * @param result Pointer to store result in. + * @return Error code (if any). + */ + virtual int ContextExecute(sp_context_t *ctx, uint32_t code_idx, cell_t *result) =0; + }; +}; + +#endif //_INCLUDE_SOURCEPAWN_VM_API_H_ diff --git a/sourcepawn/include/sp_vm_context.h b/sourcepawn/include/sp_vm_context.h new file mode 100644 index 00000000..e10dfca8 --- /dev/null +++ b/sourcepawn/include/sp_vm_context.h @@ -0,0 +1,309 @@ +#ifndef _INCLUDE_SOURCEPAWN_VM_CONTEXT_H_ +#define _INCLUDE_SOURCEPAWN_VM_CONTEXT_H_ + +#include "sp_vm_types.h" + +/***************** + ** Note that all functions return a non-zero error code on failure + * unless otherwise noted. + * All input pointers must be valid unless otherwise noted as optional. + * All output pointers on failure are undefined. + * All local address are guaranteed to be positive. However, they are stored + * as signed integers, because they must logically fit inside a cell. + */ + +namespace SourcePawn +{ + class IVirtualMachine; + + class IPluginDebugInfo + { + public: + /** + * Given a code pointer, finds the file it is associated with. + * + * @param addr Code address offset. + * @param filename Pointer to store filename pointer in. + */ + virtual int LookupFile(ucell_t addr, const char **filename) =0; + + /** + * Given a code pointer, finds the function it is associated with. + * + * @param addr Code address offset. + * @param name Pointer to store function name pointer in. + */ + virtual int LookupFunction(ucell_t addr, const char **name) =0; + + /** + * Given a code pointer, finds the line it is associated with. + * + * @param addr Code address offset. + * @param line Pointer to store line number in. + */ + virtual int LookupLine(ucell_t addr, uint32_t *line) =0; + }; + + class IPluginContext + { + public: + virtual ~IPluginContext() { }; + public: + /** + * Returns the parent IVirtualMachine. + * + * @return Parent virtual machine pointer. + */ + virtual IVirtualMachine *GetVirtualMachine() =0; + + /** + * Returns the child sp_context_t structure. + * + * @return Child sp_context_t structure. + */ + virtual sp_context_t *GetContext() =0; + + /** + * Returns true if the plugin is in debug mode. + * + * @return True if in debug mode, false otherwise. + */ + virtual bool IsDebugging() =0; + + /** + * Installs a debug break and returns the old one, if any. + * This will fail if the plugin is not debugging. + * + * @param newpfn New function pointer. + * @param oldpfn Pointer to retrieve old function pointer. + */ + virtual int SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn) =0; + + /** + * Returns debug info. + * + * @return IPluginDebugInfo, or NULL if no debug info found. + */ + virtual IPluginDebugInfo *GetDebugInfo() =0; + + /** + * Allocs memory on the secondary stack of a plugin. + * Note that although called a heap, it is in fact a stack. + * + * @param cells Number of cells to allocate. + * @param local_adddr Will be filled with data offset to heap. + * @param phys_addr Physical address to heap memory. + */ + virtual int HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr) =0; + + /** + * Pops a heap address off the heap stack. Use this to free memory allocated with + * SP_HeapAlloc(). + * Note that in SourcePawn, the heap is in fact a bottom-up stack. Deallocations + * with this native should be performed in precisely the REVERSE order. + * + * @param local_addr Local address to free. + */ + virtual int HeapPop(cell_t local_addr) =0; + + /** + * Releases a heap address using a different method than SP_HeapPop(). + * This allows you to release in any order. However, if you allocate N + * objects, release only some of them, then begin allocating again, + * you cannot go back and starting freeing the originals. + * In other words, for each chain of allocations, if you start deallocating, + * then allocating more in a chain, you must only deallocate from the current + * allocation chain. This is basically HeapPop() except on a larger scale. + * + * @param local_addr Local address to free. + */ + virtual int HeapRelease(cell_t local_addr) =0; + + /** + * Finds a native by name. + * + * @param name Name of native. + * @param index Optionally filled with native index number. + */ + virtual int FindNativeByName(const char *name, uint32_t *index) =0; + + /** + * Gets native info by index. + * + * @param index Index number of native. + * @param native Optionally filled with pointer to native structure. + */ + virtual int GetNativeByIndex(uint32_t index, sp_native_t **native) =0; + + /** + * Gets the number of natives. + * + * @return Filled with the number of natives. + */ + virtual uint32_t GetNativesNum() =0; + + /** + * Finds a public function by name. + * + * @param name Name of public + * @param index Optionally filled with public index number. + */ + virtual int FindPublicByName(const char *name, uint32_t *index) =0; + + /** + * Gets public function info by index. + * + * @param index Public function index number. + * @param pblic Optionally filled with pointer to public structure. + */ + virtual int GetPublicByIndex(uint32_t index, sp_public_t **publicptr) =0; + + /** + * Gets the number of public functions. + * + * @return Filled with the number of public functions. + */ + virtual uint32_t GetPublicsNum() =0; + + /** + * Gets public variable info by index. + * @param index Public variable index number. + * @param pubvar Optionally filled with pointer to pubvar structure. + */ + virtual int GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar) =0; + + /** + * Finds a public variable by name. + * + * @param name Name of pubvar + * @param index Optionally filled with pubvar index number. + * @param local_addr Optionally filled with local address offset. + * @param phys_addr Optionally filled with relocated physical address. + */ + virtual int FindPubvarByName(const char *name, uint32_t *index) =0; + + /** + * Gets the addresses of a public variable. + * + * @param index Index of public variable. + * @param local_addr Address to store local address in. + * @param phys_addr Address to store physically relocated in. + */ + virtual int GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr) =0; + + /** + * Returns the number of public variables. + * + * @return Number of public variables. + */ + virtual uint32_t GetPubVarsNum() =0; + + /** + * Round-about method of converting a plugin reference to a physical address + * + * @param local_addr Local address in plugin. + * @param phys_addr Optionally filled with relocated physical address. + */ + virtual int LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr) =0; + + /** + * Converts a local address to a physical string. + * Note that SourcePawn does not support packed strings, as such this function is + * 'cell to char' only. + * + * @param local_addr Local address in plugin. + * @param buffer Destination output buffer. + * @param maxlength Maximum length of output buffer, including null terminator. + * @param chars Optionally filled with the number of characters written. + */ + virtual int LocalToString(cell_t local_addr, char *buffer, size_t maxlength, int *chars) =0; + + /** + * Converts a physical string to a local address. + * Note that SourcePawn does not support packed strings. + * + * @param local_addr Local address in plugin. + * @param chars Number of chars to write, including NULL terminator. + * @param source Source string to copy. + */ + virtual int StringToLocal(cell_t local_addr, size_t chars, const char *source) =0; + + /** + * Pushes a cell onto the stack. Increases the parameter count by one. + * + * @param value Cell value. + */ + virtual int PushCell(cell_t value) =0; + + /** + * Pushes an array of cells onto the stack. Increases the parameter count by one. + * If the function returns an error it will fail entirely, releasing anything allocated in the process. + * Note that this does not release the heap, so you should release it after + * calling Execute(). + * + * @param local_addr Filled with local address to release. + * @param phys_addr Optionally filled with physical address of new array. + * @param array Cell array to copy. + * @param numcells Number of cells in the array to copy. + */ + virtual int PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells) =0; + + /** + * Pushes a string onto the stack (by reference) and increases the parameter count by one. + * Note that this does not release the heap, so you should release it after + * calling Execute(). + * + * @param local_addr Filled with local address to release. + * @param phys_addr Optionally filled with physical address of new array. + * @param array Cell array to copy. + * @param numcells Number of cells in the array to copy. + */ + virtual int PushString(cell_t *local_addr, cell_t **phys_addr, const char *string) =0; + + /** + * Individually pushes each cell of an array of cells onto the stack. Increases the + * parameter count by the number of cells pushed. + * If the function returns an error it will fail entirely, releasing anything allocated in the process. + * + * @param array Array of cells to read from. + * @param numcells Number of cells to read. + */ + virtual int PushCellsFromArray(cell_t array[], unsigned int numcells) =0; + + /** + * Binds a list of native names and their function pointers to a context. + * If num is 0, the list is read until an entry with a NULL name is reached. + * All natives are assigned a status of SP_NATIVE_OKAY by default. + * If overwrite is non-zero, already registered natives will be overwritten. + * + * @param natives Array of natives. + * @param num Number of natives in array. + */ + virtual int BindNatives(sp_nativeinfo_t *natives, unsigned int num, int overwrite) =0; + + /** + * Binds a single native. Overwrites any existing bind. + * If the context does not contain the native that will be binded the function will return + * with a SP_ERR_NOT_FOUND error. + * + * @param native Pointer to native. + * @param status Status value to set (should be SP_NATIVE_OKAY). + */ + virtual int BindNative(sp_nativeinfo_t *native, uint32_t status) =0; + + /** + * Binds a single native to any non-registered or pending native. + * Status is automatically set to pending. + * + * @param native Native to bind. + */ + virtual int BindNativeToAny(SPVM_NATIVE_FUNC native) =0; + + /** + * Executes a public function. + */ + virtual int Execute(uint32_t public_func, cell_t *result) =0; + }; +}; + +#endif //_INCLUDE_SOURCEPAWN_VM_CONTEXT_H_ diff --git a/sourcepawn/include/sp_vm_types.h b/sourcepawn/include/sp_vm_types.h index f3f9c618..bb133369 100644 --- a/sourcepawn/include/sp_vm_types.h +++ b/sourcepawn/include/sp_vm_types.h @@ -19,6 +19,7 @@ typedef int32_t cell_t; #define SP_ERR_INDEX 7 /* Invalid index parameter */ #define SP_ERR_NATIVE_PENDING 8 /* A script tried to exec an unbound native */ #define SP_ERR_STACKERR 9 /* Stack/Heap collision */ +#define SP_ERR_NOTDEBUGGING 10 /* Debug mode was not on or debug section not found */ /********************************************** *** The following structures are reference structures. @@ -125,6 +126,15 @@ typedef struct sp_native_s uint32_t status; /* status flags */ } sp_native_t; +/** + * Used for setting natives from modules/host apps. + */ +typedef struct sp_nativeinfo_s +{ + const char *name; + SPVM_NATIVE_FUNC func; +} sp_nativeinfo_t; + /** * Debug file table */ @@ -159,49 +169,39 @@ typedef struct sp_debug_symbol_s sp_fdbg_symbol_t *sym; /* pointer to original symbol */ } sp_debug_symbol_t; -/** - * Executes a Context. - * @sp_context_s - Execution Context - * @uint32_t - Offset from code pointer - * @res - return value of function - * @return - error code (0=none) - */ -typedef int (*SPVM_EXEC)(struct sp_context_s *, - uint32_t, - cell_t *res); - /** * Breaks into a debugger */ typedef int (*SPVM_DEBUGBREAK)(struct sp_context_s *); -#define SP_CONTEXT_DEBUG (1<<0) /* in debug mode */ -#define SP_CONTEXT_INHERIT_MEMORY (1<<1) /* inherits memory pointers */ -#define SP_CONTEXT_INHERIT_CODE (1<<2) /* inherits code pointers */ +#define SPFLAG_PLUGIN_DEBUG (1<<0) /* plugin is in debug mode */ /** * This is the heart of the VM. It contains all of the runtime * information about a plugin context. - * It is split into three sections. + * Note that user[0..3] can be used for any user based pointers. + * vm[0..3] should not be touched, as it is reserved for the VM. */ typedef struct sp_context_s { /* general/parent information */ void *base; /* base of generated code and memory */ sp_plugin_t *plugin; /* pointer back to parent information */ - struct sp_context_s *parent; /* pointer to parent context */ - uint32_t flags; /* context flags */ + void *context; /* pointer to IPluginContext */ + void *vmbase; /* pointer to IVirtualMachine */ + void *user[4]; /* user specific pointers */ + void *vm[4]; /* VM specific pointers */ + uint32_t flags; /* compilation flags */ SPVM_DEBUGBREAK dbreak; /* debug break function */ - void *user; /* user specific pointer */ - /* execution specific data */ - SPVM_EXEC exec; /* execution base */ - cell_t pri; /* PRI register */ - cell_t alt; /* ALT register */ + /* context runtime information */ + ucell_t memory; /* total memory size; */ uint8_t *data; /* data chunk */ cell_t heapbase; /* heap base */ + /* execution specific data */ + cell_t pri; /* PRI register */ + cell_t alt; /* ALT register */ cell_t hp; /* heap pointer */ cell_t sp; /* stack pointer */ - ucell_t memory; /* total memory size; */ int32_t err; /* error code */ uint32_t pushcount; /* push count */ /* context rebased database */ diff --git a/sourcepawn/vm/msvc8/vm.vcproj b/sourcepawn/vm/msvc8/vm.vcproj index 2516a305..9fd1a5ce 100644 --- a/sourcepawn/vm/msvc8/vm.vcproj +++ b/sourcepawn/vm/msvc8/vm.vcproj @@ -176,7 +176,11 @@ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > <File - RelativePath="..\sp_vm.c" + RelativePath="..\sp_vm_basecontext.cpp" + > + </File> + <File + RelativePath="..\sp_vm_engine.cpp" > </File> </Filter> @@ -186,19 +190,11 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > <File - RelativePath="..\..\include\sp_file_headers.h" + RelativePath="..\sp_vm_basecontext.h" > </File> <File - RelativePath="..\sp_vm.h" - > - </File> - <File - RelativePath="..\sp_vm_debug.h" - > - </File> - <File - RelativePath="..\..\include\sp_vm_types.h" + RelativePath="..\sp_vm_engine.h" > </File> </Filter> @@ -308,6 +304,26 @@ </File> </Filter> </Filter> + <Filter + Name="SDK" + > + <File + RelativePath="..\..\include\sp_file_headers.h" + > + </File> + <File + RelativePath="..\..\include\sp_vm_api.h" + > + </File> + <File + RelativePath="..\..\include\sp_vm_context.h" + > + </File> + <File + RelativePath="..\..\include\sp_vm_types.h" + > + </File> + </Filter> </Files> <Globals> </Globals> diff --git a/sourcepawn/vm/sp_vm.c b/sourcepawn/vm/sp_vm.c deleted file mode 100644 index 303b92c3..00000000 --- a/sourcepawn/vm/sp_vm.c +++ /dev/null @@ -1,648 +0,0 @@ -#include <limits.h> -#include <string.h> -#include <assert.h> -#include <malloc.h> -#include "sp_vm.h" - -#define CELLBOUNDMAX (INT_MAX/sizeof(cell_t)) -#define STACKMARGIN ((cell_t)(16*sizeof(cell_t))) - -/*int main() -{ - /** temporary testing area */ - /*sp_context_t ctx; - cell_t l, *p; - cell_t arr1[] = {1,3,3,7}; - cell_t arr2[] = {123,1234,12345,123456}; - const char *str = "hat hat"; - char buf[20]; - - ctx.data = (uint8_t *)malloc(50000); - ctx.memory = 50000; - ctx.heapbase = 200; - ctx.hp = ctx.heapbase; - ctx.sp = 45000; - - assert(SP_HeapAlloc(&ctx, 500, &l, &p) == SP_ERR_NONE); - assert(SP_HeapPop(&ctx, l) == SP_ERR_NONE); - assert(SP_HeapRelease(&ctx, l) == SP_ERR_NONE); - assert(SP_HeapRelease(&ctx, 4) == SP_ERR_INVALID_ADDRESS); - assert(SP_HeapAlloc(&ctx, 500, &l, &p) == SP_ERR_NONE); - assert(SP_HeapRelease(&ctx, l) == SP_ERR_NONE); - assert(SP_PushCell(&ctx, 1337) == SP_ERR_NONE); - assert(SP_PushCellArray(&ctx, &l, &p, arr1, 4) == SP_ERR_NONE); - assert(SP_HeapRelease(&ctx, l) == SP_ERR_NONE); - assert(SP_PushCellsFromArray(&ctx, arr2, 4) == SP_ERR_NONE); - assert(SP_PushString(&ctx, &l, &p, str) == SP_ERR_NONE); - assert(SP_LocalToString(&ctx, l, NULL, buf, 20) == SP_ERR_NONE); - assert(SP_HeapRelease(&ctx, l) == SP_ERR_NONE); - - return 0; -}*/ - -int SP_HeapAlloc(sp_context_t *ctx, unsigned int cells, cell_t *local_addr, cell_t **phys_addr) -{ - cell_t *addr; - ucell_t realmem; - -#if 0 - if (cells > CELLBOUNDMAX) - { - return SP_ERR_PARAM; - } -#else - assert(cells < CELLBOUNDMAX); -#endif - - realmem = cells * sizeof(cell_t); - - /** - * Check if the space between the heap and stack is sufficient. - */ - if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN) - { - return SP_ERR_HEAPLOW; - } - - addr = (cell_t *)(ctx->data + ctx->hp); - /* store size of allocation in cells */ - *addr = (cell_t)cells; - addr++; - ctx->hp += sizeof(cell_t); - - *local_addr = ctx->hp; - - if (phys_addr) - { - *phys_addr = addr; - } - - ctx->hp += realmem; - - return SP_ERR_NONE; -} - -int SP_HeapPop(sp_context_t *ctx, cell_t local_addr) -{ - cell_t cellcount; - cell_t *addr; - - /* check the bounds of this address */ - local_addr -= sizeof(cell_t); - if (local_addr < ctx->heapbase || local_addr >= ctx->sp) - { - return SP_ERR_INVALID_ADDRESS; - } - - addr = (cell_t *)(ctx->data + local_addr); - cellcount = (*addr) * sizeof(cell_t); - /* check if this memory count looks valid */ - if (ctx->hp - cellcount - sizeof(cell_t) != local_addr) - { - return SP_ERR_INVALID_ADDRESS; - } - - ctx->hp = local_addr; - - return SP_ERR_NONE; -} - -int SP_HeapRelease(sp_context_t *ctx, cell_t local_addr) -{ - if (local_addr < ctx->heapbase) - { - return SP_ERR_INVALID_ADDRESS; - } - - ctx->hp = local_addr - sizeof(cell_t); - - return SP_ERR_NONE; -} - -int SP_FindNativeByName(sp_context_t *ctx, const char *name, uint32_t *index) -{ - int diff, high, low; - uint32_t mid; - - high = ctx->plugin->info.natives_num - 1; - low = 0; - - while (low <= high) - { - mid = (low + high) / 2; - diff = strcmp(ctx->natives[mid].name, name); - if (diff == 0) - { - if (index) - { - *index = mid; - } - return SP_ERR_NONE; - } else if (diff < 0) { - low = mid + 1; - } else { - high = mid - 1; - } - } - - return SP_ERR_NOT_FOUND; -} - -int SP_GetNativeByIndex(sp_context_t *ctx, uint32_t index, sp_native_t **native) -{ - if (index >= ctx->plugin->info.natives_num) - { - return SP_ERR_INDEX; - } - - if (native) - { - *native = &(ctx->natives[index]); - } - - return SP_ERR_NONE; -} - -int SP_GetNativesNum(sp_context_t *ctx, uint32_t *num) -{ - *num = ctx->plugin->info.natives_num; - - return SP_ERR_NONE; -} - -int SP_FindPublicByName(sp_context_t *ctx, const char *name, uint32_t *index) -{ - int diff, high, low; - uint32_t mid; - - high = ctx->plugin->info.publics_num - 1; - low = 0; - - while (low <= high) - { - mid = (low + high) / 2; - diff = strcmp(ctx->publics[mid].name, name); - if (diff == 0) - { - if (index) - { - *index = mid; - } - return SP_ERR_NONE; - } else if (diff < 0) { - low = mid + 1; - } else { - high = mid - 1; - } - } - - return SP_ERR_NOT_FOUND; -} - -int SP_GetPublicByIndex(sp_context_t *ctx, uint32_t index, sp_public_t **pblic) -{ - if (index >= ctx->plugin->info.publics_num) - { - return SP_ERR_INDEX; - } - - if (pblic) - { - *pblic = &(ctx->publics[index]); - } - - return SP_ERR_NONE; -} - -int SP_GetPublicsNum(sp_context_t *ctx, uint32_t *num) -{ - *num = ctx->plugin->info.publics_num; - - return SP_ERR_NONE; -} - -int SP_GetPubvarByIndex(sp_context_t *ctx, uint32_t index, sp_pubvar_t **pubvar) -{ - if (index >= ctx->plugin->info.pubvars_num) - { - return SP_ERR_INDEX; - } - - if (pubvar) - { - *pubvar = &(ctx->pubvars[index]); - } - - return SP_ERR_NONE; -} - -int SP_FindPubvarByName(sp_context_t *ctx, const char *name, uint32_t *index) -{ - int diff, high, low; - uint32_t mid; - - high = ctx->plugin->info.pubvars_num - 1; - low = 0; - - while (low <= high) - { - mid = (low + high) / 2; - diff = strcmp(ctx->pubvars[mid].name, name); - if (diff == 0) - { - if (index) - { - *index = mid; - } - return SP_ERR_NONE; - } else if (diff < 0) { - low = mid + 1; - } else { - high = mid - 1; - } - } - - return SP_ERR_NOT_FOUND; -} - -int SP_GetPubvarAddrs(sp_context_t *ctx, uint32_t index, cell_t *local_addr, cell_t **phys_addr) -{ - if (index >= ctx->plugin->info.pubvars_num) - { - return SP_ERR_INDEX; - } - - *local_addr = ctx->plugin->info.pubvars[index].address; - *phys_addr = ctx->pubvars[index].offs; - - return SP_ERR_NONE; -} - -int SP_GetPubVarsNum(sp_context_t *ctx, uint32_t *num) -{ - *num = ctx->plugin->info.pubvars_num; - - return SP_ERR_NONE; -} - -int SP_BindNatives(sp_context_t *ctx, sp_nativeinfo_t *natives, unsigned int num, int overwrite) -{ - uint32_t i, j, max; - - max = ctx->plugin->info.natives_num; - - for (i=0; i<max; i++) - { - if ((ctx->natives[i].status == SP_NATIVE_OKAY) && !overwrite) - { - continue; - } - - for (j=0; (natives[j].name) && (!num || j<num); j++) - { - if (!strcmp(ctx->natives[i].name, natives[j].name)) - { - ctx->natives[i].pfn = natives[j].func; - ctx->natives[i].status = SP_NATIVE_OKAY; - } - } - } - - return SP_ERR_NONE; -} - -int SP_BindNative(sp_context_t *ctx, sp_nativeinfo_t *native, uint32_t status) -{ - uint32_t index; - int err; - - if ((err = SP_FindNativeByName(ctx, native->name, &index)) != SP_ERR_NONE) - { - return err; - } - - ctx->natives[index].pfn = native->func; - ctx->natives[index].status = status; - - return SP_ERR_NONE; -} - -int SP_BindNativeToAny(sp_context_t *ctx, SPVM_NATIVE_FUNC native) -{ - uint32_t nativesnum, i; - - nativesnum = ctx->plugin->info.natives_num; - - for (i=0; i<nativesnum; i++) - { - if (ctx->natives[i].status != SP_NATIVE_OKAY) - { - ctx->natives[i].pfn = native; - ctx->natives[i].status = SP_NATIVE_PENDING; - } - } - - return SP_ERR_NONE; -} - -int SP_LocalToPhysAddr(sp_context_t *ctx, cell_t local_addr, cell_t **phys_addr) -{ - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->memory)) - { - return SP_ERR_INVALID_ADDRESS; - } - - if (phys_addr) - { - *phys_addr = (cell_t *)(ctx->data + local_addr); - } - - return SP_ERR_NONE; -} - -int SP_PushCell(sp_context_t *ctx, cell_t value) -{ - if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t))) - { - return SP_ERR_STACKERR; - } - - ctx->sp -= sizeof(cell_t); - *(cell_t *)(ctx->data + ctx->sp) = value; - ctx->pushcount++; - - return SP_ERR_NONE; -} - -int SP_PushCellsFromArray(sp_context_t *ctx, cell_t array[], unsigned int numcells) -{ - unsigned int i; - int err; - - for (i=0; i<numcells; i++) - { - if ((err = SP_PushCell(ctx, array[i])) != SP_ERR_NONE) - { - ctx->sp += (cell_t)(i * sizeof(cell_t)); - ctx->pushcount -= i; - return err; - } - } - - return SP_ERR_NONE; -} - -int SP_PushCellArray(sp_context_t *ctx, cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells) -{ - cell_t *ph_addr; - int err; - - if ((err = SP_HeapAlloc(ctx, numcells, local_addr, &ph_addr)) != SP_ERR_NONE) - { - return err; - } - - memcpy(ph_addr, array, numcells * sizeof(cell_t)); - - if ((err = SP_PushCell(ctx, *local_addr)) != SP_ERR_NONE) - { - SP_HeapRelease(ctx, *local_addr); - return err; - } - - if (phys_addr) - { - *phys_addr = ph_addr; - } - - return SP_ERR_NONE; -} - -int SP_LocalToString(sp_context_t *ctx, cell_t local_addr, int *chars, char *buffer, size_t maxlength) -{ - int len = 0; - cell_t *src; - - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->memory)) - { - return SP_ERR_INVALID_ADDRESS; - } - - src = (cell_t *)(ctx->data + local_addr); - while ((*src != '\0') && ((size_t)len < maxlength)) - { - buffer[len++] = (char)*src++; - } - - if ((size_t)len >= maxlength) - { - len = maxlength - 1; - } - if (len >= 0) - { - buffer[len] = '\0'; - } - - if (chars) - { - *chars = len; - } - - return SP_ERR_NONE; -} - -int SP_PushString(sp_context_t *ctx, cell_t *local_addr, cell_t **phys_addr, const char *string) -{ - cell_t *ph_addr; - int err; - unsigned int i, numcells = strlen(string); - - if ((err = SP_HeapAlloc(ctx, numcells+1, local_addr, &ph_addr)) != SP_ERR_NONE) - { - return err; - } - - for (i=0; i<numcells; i++) - { - ph_addr[i] = (cell_t)string[i]; - } - ph_addr[numcells] = '\0'; - - if ((err = SP_PushCell(ctx, *local_addr)) != SP_ERR_NONE) - { - SP_HeapRelease(ctx, *local_addr); - return err; - } - - if (phys_addr) - { - *phys_addr = ph_addr; - } - - return SP_ERR_NONE; -} - -int SP_StringToLocal(sp_context_t *ctx, cell_t local_addr, size_t chars, const char *source) -{ - cell_t *dest; - int i, len; - - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->memory)) - { - return SP_ERR_INVALID_ADDRESS; - } - - len = strlen(source); - dest = (cell_t *)(ctx->data + local_addr); - - if ((size_t)len >= chars) - { - len = chars - 1; - } - - for (i=0; i<len; i++) - { - dest[i] = (cell_t)source[i]; - } - dest[len] = '\0'; - - return SP_ERR_NONE; -} - -int SP_CreateBaseContext(sp_plugin_t *plugin, sp_context_t **ctx) -{ - uint32_t iter, max; - uint8_t *dat, *cursor; - const char *strbase; - sp_fdbg_symbol_t *sym; - sp_fdbg_arraydim_t *arr; - sp_context_t *context; - - context = (sp_context_t *)malloc(sizeof(sp_context_t)); - memset(context, 0, sizeof(sp_context_t)); - - context->base = plugin->base; - context->plugin = plugin; - context->flags = plugin->flags; - - context->data = (uint8_t *)malloc(plugin->memory); - memcpy(context->data, plugin->data, plugin->data_size); - context->memory = plugin->memory; - context->heapbase = (cell_t)(plugin->data_size); - - strbase = plugin->info.stringbase; - - if (max = plugin->info.publics_num) - { - context->publics = (sp_public_t *)malloc(sizeof(sp_public_t) * max); - for (iter=0; iter<max; iter++) - { - context->publics[iter].name = strbase + plugin->info.publics[iter].name; - context->publics[iter].offs = plugin->info.publics[iter].address; - } - } - - if (max = plugin->info.pubvars_num) - { - dat = plugin->data; - context->pubvars = (sp_pubvar_t *)malloc(sizeof(sp_pubvar_t) * max); - for (iter=0; iter<max; iter++) - { - context->pubvars[iter].name = strbase + plugin->info.pubvars[iter].name; - context->pubvars[iter].offs = (cell_t *)(dat + plugin->info.pubvars[iter].address); - } - } - - if (max = plugin->info.natives_num) - { - context->natives = (sp_native_t *)malloc(sizeof(sp_native_t) * max); - for (iter=0; iter<max; iter++) - { - context->natives[iter].name = strbase + plugin->info.natives[iter].name; - context->natives[iter].pfn = SP_NoExecNative; - context->natives[iter].status = SP_NATIVE_NONE; - } - } - - strbase = plugin->debug.stringbase; - - if (plugin->flags & SP_FLAG_DEBUG) - { - max = plugin->debug.files_num; - context->files = (sp_debug_file_t *)malloc(sizeof(sp_debug_file_t) * max); - for (iter=0; iter<max; iter++) - { - context->files[iter].addr = plugin->debug.files[iter].addr; - context->files[iter].name = strbase + plugin->debug.files[iter].name; - } - - max = plugin->debug.lines_num; - context->lines = (sp_debug_line_t *)malloc(sizeof(sp_debug_line_t) * max); - for (iter=0; iter<max; iter++) - { - context->lines[iter].addr = plugin->debug.lines[iter].addr; - context->lines[iter].line = plugin->debug.lines[iter].line; - } - - cursor = (uint8_t *)(plugin->debug.symbols); - max = plugin->debug.syms_num; - context->symbols = (sp_debug_symbol_t *)malloc(sizeof(sp_debug_symbol_t) * max); - for (iter=0; iter<max; iter++) - { - sym = (sp_fdbg_symbol_t *)cursor; - - context->symbols[iter].codestart = sym->codestart; - context->symbols[iter].codeend = sym->codeend; - context->symbols[iter].name = strbase + sym->name; - context->symbols[iter].sym = sym; - - if (sym->dimcount > 0) - { - cursor += sizeof(sp_fdbg_symbol_t); - arr = (sp_fdbg_arraydim_t *)cursor; - context->symbols[iter].dims = arr; - cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount; - continue; - } - - context->symbols[iter].dims = NULL; - cursor += sizeof(sp_fdbg_symbol_t); - } - } - - *ctx = context; - return SP_ERR_NONE; -} - -int SP_FreeBaseContext(sp_context_t *ctx) -{ - if (ctx->flags & SP_FLAG_DEBUG) - { - free(ctx->symbols); - free(ctx->lines); - free(ctx->files); - } - if (ctx->plugin->info.natives) - { - free(ctx->natives); - } - if (ctx->plugin->info.pubvars_num) - { - free(ctx->pubvars); - } - if (ctx->plugin->info.publics_num) - { - free(ctx->publics); - } - free(ctx->data); - free(ctx); - - return SP_ERR_NONE; -} - -cell_t SP_NoExecNative(sp_context_t *ctx, cell_t *params) -{ - ctx->err = SP_ERR_NATIVE_PENDING; - - return 0; -} \ No newline at end of file diff --git a/sourcepawn/vm/sp_vm.h b/sourcepawn/vm/sp_vm.h deleted file mode 100644 index 8ed41cb2..00000000 --- a/sourcepawn/vm/sp_vm.h +++ /dev/null @@ -1,322 +0,0 @@ -#ifndef _INCLUDE_SOURCEPAWN_VM_H_ -#define _INCLUDE_SOURCEPAWN_VM_H_ - -#include <stdio.h> -#include "sp_vm_types.h" - -/***************** - ** Note that all functions return a non-zero error code on failure - * unless otherwise noted. - * All input pointers must be valid unless otherwise noted as optional. - * All output pointers on failure are undefined. - * All local address are guaranteed to be positive. However, they are stored - * as signed integers, because they must logically fit inside a cell. - */ - -typedef struct sp_nativeinfo_s -{ - const char *name; - SPVM_NATIVE_FUNC func; -} sp_nativeinfo_t; - -/** - * Loads a named file from a file pointer. - * Using this means base memory will be allocated by the VM. - * - * @param fp File pointer. May be at any offset. Not closed on return. - * @param err Optional error code pointer. - * @return A new plugin structure. - */ -sp_plugin_t *SP_LoadFromFilePointer(FILE *fp, int *err); - -/** - * Loads a file from a base memory address. - * - * @param base Base address of the plugin's memory region. - * @param plugin If NULL, a new plugin pointer is returned. - * Otherwise, the passed pointer is used. - * @param err Optional error code pointer. - * @return The resulting plugin pointer. - */ -sp_plugin_t *SP_LoadFromMemory(void *base, sp_plugin_t *plugin, int *err); - -/** - * Frees all of the memory associated with a plugin file. - * If allocated using SP_LoadFromMemory, the base and plugin pointer - * itself are not freed (so this may end up doing nothing). - */ -int SP_FreeFromMemory(sp_plugin_t *plugin); - -/** - * Allocs memory on the secondary stack of a plugin. - * Note that although called a heap, it is in fact a stack. - * - * @param ctx Context pointer. - * @param cells Number of cells to allocate. - * @param local_adddr Will be filled with data offset to heap. - * @param phys_addr Physical address to heap memory. - */ -int SP_HeapAlloc(sp_context_t *ctx, unsigned int cells, cell_t *local_addr, cell_t **phys_addr); - -/** - * Pops a heap address off the heap stack. Use this to free memory allocated with - * SP_HeapAlloc(). - * Note that in SourcePawn, the heap is in fact a bottom-up stack. Deallocations - * with this native should be performed in precisely the REVERSE order. - */ -int SP_HeapPop(sp_context_t *ctx, cell_t local_addr); - -/** - * Releases a heap address using a different method than SP_HeapPop(). - * This allows you to release in any order. However, if you allocate N - * objects, release only some of them, then begin allocating again, - * you cannot go back and starting freeing the originals. - * In other words, for each chain of allocations, if you start deallocating, - * then allocating more in a chain, you must only deallocate from the current - * allocation chain. This is basically SP_HeapPop() except on a larger scale. - */ -int SP_HeapRelease(sp_context_t *ctx, cell_t local_addr); - -/** - * Finds a native by name. - * - * @param ctx Context pointer. - * @param name Name of native. - * @param index Optionally filled with native index number. - */ -int SP_FindNativeByName(sp_context_t *ctx, const char *name, uint32_t *index); - -/** - * Gets native info by index. - * - * @param ctx Context pointer. - * @param index Index number of native. - * @param native Optionally filled with pointer to native structure. - */ -int SP_GetNativeByIndex(sp_context_t *ctx, uint32_t index, sp_native_t **native); - -/** - * Gets the number of natives. - * - * @param ctx Context pointer. - * @param num Filled with the number of natives. - */ -int SP_GetNativesNum(sp_context_t *ctx, uint32_t *num); - -/** - * Finds a public function by name. - * - * @param ctx Context pointer. - * @param name Name of public - * @param index Optionally filled with public index number. - */ -int SP_FindPublicByName(sp_context_t *ctx, const char *name, uint32_t *index); - - -/** - * Gets public function info by index. - * - * @param ctx Context pointer. - * @param index Public function index number. - * @param pblic Optionally filled with pointer to public structure. - */ -int SP_GetPublicByIndex(sp_context_t *ctx, uint32_t index, sp_public_t **pblic); - -/** - * Gets the number of public functions. - * - * @param ctx Context pointer. - * @param num Filled with the number of public functions. - */ -int SP_GetPublicsNum(sp_context_t *ctx, uint32_t *num); - -/** - * Gets public variable info by index. - * @param ctx Context pointer. - * @param index Public variable index number. - * @param pubvar Optionally filled with pointer to pubvar structure. - */ -int SP_GetPubvarByIndex(sp_context_t *ctx, uint32_t index, sp_pubvar_t **pubvar); - -/** - * Finds a public variable by name. - * - * @param ctx Context pointer. - * @param name Name of pubvar - * @param index Optionally filled with pubvar index number. - * @param local_addr Optionally filled with local address offset. - * @param phys_addr Optionally filled with relocated physical address. - */ -int SP_FindPubvarByName(sp_context_t *ctx, const char *name, uint32_t *index); - -//:TODO: fill in the info of this function, hi -int SP_GetPubvarAddrs(sp_context_t *ctx, uint32_t index, cell_t *local_addr, cell_t **phys_addr); - -/** -* Gets the number of public variables. -* -* @param ctx Context pointer. -* @param num Filled with the number of public variables. -*/ -int SP_GetPubVarsNum(sp_context_t *ctx, uint32_t *num); - -/** - * Round-about method of converting a plugin reference to a physical address - * - * @param ctx Context pointer. - * @param local_addr Local address in plugin. - * @param phys_addr Optionally filled with relocated physical address. - */ -int SP_LocalToPhysAddr(sp_context_t *ctx, cell_t local_addr, cell_t **phys_addr); - -/** - * Converts a local address to a physical string. - * Note that SourcePawn does not support packed strings, as such this function is - * 'cell to char' only. - * - * @param ctx Context pointer. - * @param local_addr Local address in plugin. - * @param chars Optionally filled with the number of characters written. - * @param buffer Destination output buffer. - * @param maxlength Maximum length of output buffer, including null terminator. - */ -int SP_LocalToString(sp_context_t *ctx, - cell_t local_addr, - int *chars, - char *buffer, - size_t maxlength); - -/** - * Converts a physical string to a local address. - * Note that SourcePawn does not support packed strings. - * @param ctx Context pointer - * @param local_addr Local address in plugin. - * @param chars Number of chars to write, including NULL terminator. - * @param source Source string to copy. - */ -int SP_StringToLocal(sp_context_t *ctx, - cell_t local_addr, - size_t chars, - const char *source); - -/** - * Pushes a cell onto the stack. Increases the parameter count by one. - * - * @param ctx Context pointer. - * @param value Cell value. - */ -int SP_PushCell(sp_context_t *ctx, cell_t value); - -/** - * Pushes an array of cells onto the stack. Increases the parameter count by one. - * If the function returns an error it will fail entirely, releasing anything allocated in the process. - * Note that this does not release the heap, so you should release it after - * calling SP_Execute(). - * - * @param ctx Context pointer. - * @param local_addr Filled with local address to release. - * @param phys_addr Optionally filled with physical address of new array. - * @param array Cell array to copy. - * @param numcells Number of cells in the array to copy. - */ -int SP_PushCellArray(sp_context_t *ctx, - cell_t *local_addr, - cell_t **phys_addr, - cell_t array[], - unsigned int numcells); - -/** - * Pushes a string onto the stack (by reference) and increases the parameter count by one. - * Note that this does not release the heap, so you should release it after - * calling SP_Execute(). - * - * @param ctx Context pointer. - * @param local_addr Filled with local address to release. - * @param phys_addr Optionally filled with physical address of new array. - * @param array Cell array to copy. - * @param numcells Number of cells in the array to copy. - */ -int SP_PushString(sp_context_t *ctx, - cell_t *local_addr, - cell_t **phys_addr, - const char *string); - -/** - * Individually pushes each cell of an array of cells onto the stack. Increases the - * parameter count by the number of cells pushed. - * If the function returns an error it will fail entirely, releasing anything allocated in the process. - * - * @param ctx Context pointer. - * @param array Array of cells to read from. - * @param numcells Number of cells to read. - */ -int SP_PushCellsFromArray(sp_context_t *ctx, cell_t array[], unsigned int numcells); - -/** - * Binds a list of native names and their function pointers to a context. - * If num is 0, the list is read until an entry with a NULL name is reached. - * All natives are assigned a status of SP_NATIVE_OKAY by default. - * If overwrite is non-zero, already registered natives will be overwritten. - * - * @param ctx Context pointer. - * @param natives Array of natives. - * @param num Number of natives in array. - */ -int SP_BindNatives(sp_context_t *ctx, sp_nativeinfo_t *natives, unsigned int num, int overwrite); - -/** - * Binds a single native. Overwrites any existing bind. - * If the context does not contain the native that will be binded the function will return - * with a SP_ERR_NOT_FOUND error. - * - * @param ctx Context pointer. - * @param native Pointer to native. - * @param status Status value to set (should be SP_NATIVE_OKAY). - */ -int SP_BindNative(sp_context_t *ctx, sp_nativeinfo_t *native, uint32_t status); - -/** - * Binds a single native to any non-registered or pending native. - * Status is automatically set to pending. - * - * @param ctx Context pointer. - */ -int SP_BindNativeToAny(sp_context_t *ctx, SPVM_NATIVE_FUNC native); - -/** - * Executes a public function in a context. - * The parameter count is set to zero during execution. - * All context-specific variables that are modified are saved before execution, - * thus allowing nested calls to SP_Execute(). - * - * @param ctx Context pointer. - * @param idx Public function index number. - * @param result Optional pointer to store return value. - */ -int SP_Execute(sp_context_t *ctx, uint32_t idx, cell_t *result); - -/** - * Creates a base context. The context is not bound to any JIT, and thus - * inherits the parent code pointer of the file structure. It does, - * however, have relocated info+debug tables (even though the code address - * do not need to be relocated). - * It is guaranteed to have a newly allocated and copied memory layout - * of the data, heap and stack, and thus relevant address in the info/debug - * tables must be relocated. - * - * @param plugin Plugin file structure to build a context form. - * @param ctx Pointer to store newly created context pointer. - */ -int SP_CreateBaseContext(sp_plugin_t *plugin, sp_context_t **ctx); - -/** - * Frees a base context. - * - * @param ctx Context pointer. - */ -int SP_FreeBaseContext(sp_context_t *ctx); - -//:TODO: fill in this -cell_t SP_NoExecNative(sp_context_t *ctx, cell_t *params); - -#endif //_INCLUDE_SOURCEPAWN_VM_H_ diff --git a/sourcepawn/vm/sp_vm_basecontext.cpp b/sourcepawn/vm/sp_vm_basecontext.cpp new file mode 100644 index 00000000..bc38cc82 --- /dev/null +++ b/sourcepawn/vm/sp_vm_basecontext.cpp @@ -0,0 +1,611 @@ +#include <string.h> +#include <assert.h> +#include <limits.h> +#include "sp_vm_api.h" +#include "sp_vm_basecontext.h" + +using namespace SourcePawn; + +#define CELLBOUNDMAX (INT_MAX/sizeof(cell_t)) +#define STACKMARGIN ((cell_t)(16*sizeof(cell_t))) + +BaseContext::BaseContext(sp_context_t *_ctx) +{ + ctx = _ctx; +} + +IVirtualMachine *BaseContext::GetVirtualMachine() +{ + return (IVirtualMachine *)ctx->vmbase; +} + +sp_context_t *BaseContext::GetContext() +{ + return ctx; +} + +bool BaseContext::IsDebugging() +{ + return (ctx->flags & SPFLAG_PLUGIN_DEBUG); +} + +int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn) +{ + if (!IsDebugging()) + { + return SP_ERR_NOTDEBUGGING; + } + + *oldpfn = ctx->dbreak; + ctx->dbreak = newpfn; + + return SP_ERR_NONE; +} + +IPluginDebugInfo *BaseContext::GetDebugInfo() +{ + return this; +} + +int BaseContext::Execute(uint32_t public_func, cell_t *result) +{ + IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase; + + int err; + sp_public_t *pubfunc; + if ((err=GetPublicByIndex(public_func, &pubfunc)) != SP_ERR_NONE) + { + return err; + } + + return vm->ContextExecute(ctx, pubfunc->offs, result); +} + +int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr) +{ + cell_t *addr; + ucell_t realmem; + +#if 0 + if (cells > CELLBOUNDMAX) + { + return SP_ERR_PARAM; + } +#else + assert(cells < CELLBOUNDMAX); +#endif + + realmem = cells * sizeof(cell_t); + + /** + * Check if the space between the heap and stack is sufficient. + */ + if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN) + { + return SP_ERR_HEAPLOW; + } + + addr = (cell_t *)(ctx->data + ctx->hp); + /* store size of allocation in cells */ + *addr = (cell_t)cells; + addr++; + ctx->hp += sizeof(cell_t); + + *local_addr = ctx->hp; + + if (phys_addr) + { + *phys_addr = addr; + } + + ctx->hp += realmem; + + return SP_ERR_NONE; +} + +int BaseContext::HeapPop(cell_t local_addr) +{ + cell_t cellcount; + cell_t *addr; + + /* check the bounds of this address */ + local_addr -= sizeof(cell_t); + if (local_addr < ctx->heapbase || local_addr >= ctx->sp) + { + return SP_ERR_INVALID_ADDRESS; + } + + addr = (cell_t *)(ctx->data + local_addr); + cellcount = (*addr) * sizeof(cell_t); + /* check if this memory count looks valid */ + if (ctx->hp - cellcount - sizeof(cell_t) != local_addr) + { + return SP_ERR_INVALID_ADDRESS; + } + + ctx->hp = local_addr; + + return SP_ERR_NONE; +} + + +int BaseContext::HeapRelease(cell_t local_addr) +{ + if (local_addr < ctx->heapbase) + { + return SP_ERR_INVALID_ADDRESS; + } + + ctx->hp = local_addr - sizeof(cell_t); + + return SP_ERR_NONE; +} + +int BaseContext::FindNativeByName(const char *name, uint32_t *index) +{ + int diff, high, low; + uint32_t mid; + + high = ctx->plugin->info.natives_num - 1; + low = 0; + + while (low <= high) + { + mid = (low + high) / 2; + diff = strcmp(ctx->natives[mid].name, name); + if (diff == 0) + { + if (index) + { + *index = mid; + } + return SP_ERR_NONE; + } else if (diff < 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return SP_ERR_NOT_FOUND; +} + +int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native) +{ + if (index >= ctx->plugin->info.natives_num) + { + return SP_ERR_INDEX; + } + + if (native) + { + *native = &(ctx->natives[index]); + } + + return SP_ERR_NONE; +} + + +uint32_t BaseContext::GetNativesNum() +{ + return ctx->plugin->info.natives_num; +} + +int BaseContext::FindPublicByName(const char *name, uint32_t *index) +{ + int diff, high, low; + uint32_t mid; + + high = ctx->plugin->info.publics_num - 1; + low = 0; + + while (low <= high) + { + mid = (low + high) / 2; + diff = strcmp(ctx->publics[mid].name, name); + if (diff == 0) + { + if (index) + { + *index = mid; + } + return SP_ERR_NONE; + } else if (diff < 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return SP_ERR_NOT_FOUND; +} + +int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic) +{ + if (index >= ctx->plugin->info.publics_num) + { + return SP_ERR_INDEX; + } + + if (pblic) + { + *pblic = &(ctx->publics[index]); + } + + return SP_ERR_NONE; +} + +uint32_t BaseContext::GetPublicsNum() +{ + return ctx->plugin->info.publics_num; +} + +int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar) +{ + if (index >= ctx->plugin->info.pubvars_num) + { + return SP_ERR_INDEX; + } + + if (pubvar) + { + *pubvar = &(ctx->pubvars[index]); + } + + return SP_ERR_NONE; +} + +int BaseContext::FindPubvarByName(const char *name, uint32_t *index) +{ + int diff, high, low; + uint32_t mid; + + high = ctx->plugin->info.pubvars_num - 1; + low = 0; + + while (low <= high) + { + mid = (low + high) / 2; + diff = strcmp(ctx->pubvars[mid].name, name); + if (diff == 0) + { + if (index) + { + *index = mid; + } + return SP_ERR_NONE; + } else if (diff < 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return SP_ERR_NOT_FOUND; +} + +int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr) +{ + if (index >= ctx->plugin->info.pubvars_num) + { + return SP_ERR_INDEX; + } + + *local_addr = ctx->plugin->info.pubvars[index].address; + *phys_addr = ctx->pubvars[index].offs; + + return SP_ERR_NONE; +} + +uint32_t BaseContext::GetPubVarsNum() +{ + return ctx->plugin->info.pubvars_num; +} + +int BaseContext::BindNatives(sp_nativeinfo_t *natives, unsigned int num, int overwrite) +{ + uint32_t i, j, max; + + max = ctx->plugin->info.natives_num; + + for (i=0; i<max; i++) + { + if ((ctx->natives[i].status == SP_NATIVE_OKAY) && !overwrite) + { + continue; + } + + for (j=0; (natives[j].name) && (!num || j<num); j++) + { + if (!strcmp(ctx->natives[i].name, natives[j].name)) + { + ctx->natives[i].pfn = natives[j].func; + ctx->natives[i].status = SP_NATIVE_OKAY; + } + } + } + + return SP_ERR_NONE; +} + + + +int BaseContext::BindNative(sp_nativeinfo_t *native, uint32_t status) +{ + uint32_t index; + int err; + + if ((err = FindNativeByName(native->name, &index)) != SP_ERR_NONE) + { + return err; + } + + ctx->natives[index].pfn = native->func; + ctx->natives[index].status = status; + + return SP_ERR_NONE; +} + +int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native) +{ + uint32_t nativesnum, i; + + nativesnum = ctx->plugin->info.natives_num; + + for (i=0; i<nativesnum; i++) + { + if (ctx->natives[i].status != SP_NATIVE_OKAY) + { + ctx->natives[i].pfn = native; + ctx->natives[i].status = SP_NATIVE_PENDING; + } + } + + return SP_ERR_NONE; +} + +int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr) +{ + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->memory)) + { + return SP_ERR_INVALID_ADDRESS; + } + + if (phys_addr) + { + *phys_addr = (cell_t *)(ctx->data + local_addr); + } + + return SP_ERR_NONE; +} + +int BaseContext::PushCell(cell_t value) +{ + if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t))) + { + return SP_ERR_STACKERR; + } + + ctx->sp -= sizeof(cell_t); + *(cell_t *)(ctx->data + ctx->sp) = value; + ctx->pushcount++; + + return SP_ERR_NONE; +} + +int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells) +{ + unsigned int i; + int err; + + for (i=0; i<numcells; i++) + { + if ((err = PushCell(array[i])) != SP_ERR_NONE) + { + ctx->sp += (cell_t)(i * sizeof(cell_t)); + ctx->pushcount -= i; + return err; + } + } + + return SP_ERR_NONE; +} + +int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells) +{ + cell_t *ph_addr; + int err; + + if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERR_NONE) + { + return err; + } + + memcpy(ph_addr, array, numcells * sizeof(cell_t)); + + if ((err = PushCell(*local_addr)) != SP_ERR_NONE) + { + HeapRelease(*local_addr); + return err; + } + + if (phys_addr) + { + *phys_addr = ph_addr; + } + + return SP_ERR_NONE; +} + +int BaseContext::LocalToString(cell_t local_addr, char *buffer, size_t maxlength, int *chars) +{ + int len = 0; + cell_t *src; + + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->memory)) + { + return SP_ERR_INVALID_ADDRESS; + } + + src = (cell_t *)(ctx->data + local_addr); + while ((*src != '\0') && ((size_t)len < maxlength)) + { + buffer[len++] = (char)*src++; + } + + if ((size_t)len >= maxlength) + { + len = maxlength - 1; + } + if (len >= 0) + { + buffer[len] = '\0'; + } + + if (chars) + { + *chars = len; + } + + return SP_ERR_NONE; +} + +int BaseContext::PushString(cell_t *local_addr, cell_t **phys_addr, const char *string) +{ + cell_t *ph_addr; + int err; + unsigned int i, numcells = strlen(string); + + if ((err = HeapAlloc(numcells+1, local_addr, &ph_addr)) != SP_ERR_NONE) + { + return err; + } + + for (i=0; i<numcells; i++) + { + ph_addr[i] = (cell_t)string[i]; + } + ph_addr[numcells] = '\0'; + + if ((err = PushCell(*local_addr)) != SP_ERR_NONE) + { + HeapRelease(*local_addr); + return err; + } + + if (phys_addr) + { + *phys_addr = ph_addr; + } + + return SP_ERR_NONE; +} + +int BaseContext::StringToLocal(cell_t local_addr, size_t chars, const char *source) +{ + cell_t *dest; + int i, len; + + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->memory)) + { + return SP_ERR_INVALID_ADDRESS; + } + + len = strlen(source); + dest = (cell_t *)(ctx->data + local_addr); + + if ((size_t)len >= chars) + { + len = chars - 1; + } + + for (i=0; i<len; i++) + { + dest[i] = (cell_t)source[i]; + } + dest[len] = '\0'; + + return SP_ERR_NONE; +} + +#define USHR(x) ((unsigned int)(x)>>1) + +int BaseContext::LookupFile(ucell_t addr, const char **filename) +{ + int high, low, mid; + + high = ctx->plugin->debug.files_num; + low = -1; + + while (high - low > 1) + { + mid = USHR(low + high); + if (ctx->files[mid].addr <= addr) + { + low = mid; + } else { + high = mid; + } + } + + if (low == -1) + { + return SP_ERR_NOT_FOUND; + } + + *filename = ctx->files[low].name; + + return SP_ERR_NONE; +} + +int BaseContext::LookupFunction(ucell_t addr, const char **name) +{ + uint32_t iter, max = ctx->plugin->debug.syms_num; + + for (iter=0; iter<max; iter++) + { + if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) + && (ctx->symbols[iter].codestart <= addr) + && (ctx->symbols[iter].codeend > addr)) + { + break; + } + } + + if (iter >= max) + { + return SP_ERR_NOT_FOUND; + } + + *name = ctx->symbols[iter].name; + + return SP_ERR_NONE; +} + +int BaseContext::LookupLine(ucell_t addr, uint32_t *line) +{ + int high, low, mid; + + high = ctx->plugin->debug.lines_num; + low = -1; + + while (high - low > 1) + { + mid = USHR(low + high); + if (ctx->lines[mid].addr <= addr) + { + low = mid; + } else { + high = mid; + } + } + + if (low == -1) + { + return SP_ERR_NOT_FOUND; + } + + *line = ctx->lines[low].line; + + return SP_ERR_NONE; +} diff --git a/sourcepawn/vm/sp_vm_basecontext.h b/sourcepawn/vm/sp_vm_basecontext.h new file mode 100644 index 00000000..e49972e4 --- /dev/null +++ b/sourcepawn/vm/sp_vm_basecontext.h @@ -0,0 +1,53 @@ +#ifndef _INCLUDE_SOURCEPAWN_BASECONTEXT_H_ +#define _INCLUDE_SOURCEPAWN_BASECONTEXT_H_ + +#include "sp_vm_context.h" + +namespace SourcePawn +{ + class BaseContext : + public IPluginContext, + public IPluginDebugInfo + { + public: + BaseContext(sp_context_t *ctx); + public: //IPluginContext + IVirtualMachine *GetVirtualMachine(); + sp_context_t *GetContext(); + bool IsDebugging(); + int SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn); + IPluginDebugInfo *GetDebugInfo(); + virtual int HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr); + virtual int HeapPop(cell_t local_addr); + virtual int HeapRelease(cell_t local_addr); + virtual int FindNativeByName(const char *name, uint32_t *index); + virtual int GetNativeByIndex(uint32_t index, sp_native_t **native); + virtual uint32_t GetNativesNum(); + virtual int FindPublicByName(const char *name, uint32_t *index); + virtual int GetPublicByIndex(uint32_t index, sp_public_t **publicptr); + virtual uint32_t GetPublicsNum(); + virtual int GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar); + virtual int FindPubvarByName(const char *name, uint32_t *index); + virtual int GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr); + virtual uint32_t GetPubVarsNum(); + virtual int LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr); + virtual int LocalToString(cell_t local_addr, char *buffer, size_t maxlength, int *chars); + virtual int StringToLocal(cell_t local_addr, size_t chars, const char *source); + virtual int PushCell(cell_t value); + virtual int PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells); + virtual int PushString(cell_t *local_addr, cell_t **phys_addr, const char *string); + virtual int PushCellsFromArray(cell_t array[], unsigned int numcells); + virtual int BindNatives(sp_nativeinfo_t *natives, unsigned int num, int overwrite); + virtual int BindNative(sp_nativeinfo_t *native, uint32_t status); + virtual int BindNativeToAny(SPVM_NATIVE_FUNC native); + virtual int Execute(uint32_t public_func, cell_t *result); + public: //IPluginDebugInfo + virtual int LookupFile(ucell_t addr, const char **filename); + virtual int LookupFunction(ucell_t addr, const char **name); + virtual int LookupLine(ucell_t addr, uint32_t *line); + private: + sp_context_t *ctx; + }; +}; + +#endif //_INCLUDE_SOURCEPAWN_BASECONTEXT_H_ diff --git a/sourcepawn/vm/sp_vm_debug.c b/sourcepawn/vm/sp_vm_debug.c deleted file mode 100644 index 862af16d..00000000 --- a/sourcepawn/vm/sp_vm_debug.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "sp_vm.h" -#include "sp_vm_debug.h" - -#define USHR(x) ((unsigned int)(x)>>1) - -int SP_DbgLookupFile(sp_context_t *ctx, ucell_t addr, const char **filename) -{ - int high, low, mid; - - high = ctx->plugin->debug.files_num; - low = -1; - - while (high - low > 1) - { - mid = USHR(low + high); - if (ctx->files[mid].addr <= addr) - { - low = mid; - } else { - high = mid; - } - } - - if (low == -1) - { - return SP_ERR_NOT_FOUND; - } - - *filename = ctx->files[low].name; - - return SP_ERR_NONE; -} - -int SP_DbgLookupFunction(sp_context_t *ctx, ucell_t addr, const char **name) -{ - uint32_t iter, max = ctx->plugin->debug.syms_num; - - for (iter=0; iter<max; iter++) - { - if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) - && (ctx->symbols[iter].codestart <= addr) - && (ctx->symbols[iter].codeend > addr)) - { - break; - } - } - - if (iter >= max) - { - return SP_ERR_NOT_FOUND; - } - - *name = ctx->symbols[iter].name; - - return SP_ERR_NONE; -} - -int SP_DbgLookupLine(sp_context_t *ctx, ucell_t addr, uint32_t *line) -{ - int high, low, mid; - - high = ctx->plugin->debug.lines_num; - low = -1; - - while (high - low > 1) - { - mid = USHR(low + high); - if (ctx->lines[mid].addr <= addr) - { - low = mid; - } else { - high = mid; - } - } - - if (low == -1) - { - return SP_ERR_NOT_FOUND; - } - - *line = ctx->lines[low].line; - - return SP_ERR_NONE; -} - -int SP_DbgInstallBreak(sp_context_t *ctx, SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn) -{ - if (ctx->dbreak) - *oldpfn = ctx->dbreak; - - ctx->dbreak = newpfn; - - return SP_ERR_NONE; -} diff --git a/sourcepawn/vm/sp_vm_debug.h b/sourcepawn/vm/sp_vm_debug.h deleted file mode 100644 index 901f955c..00000000 --- a/sourcepawn/vm/sp_vm_debug.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _INCLUDE_SOURCEPAWN_VM_DEBUG_H -#define _INCLUDE_SOURCEPAWN_VM_DEBUG_H - -/***************** - ** Note that all functions return a non-zero error code on failure - * unless otherwise noted. - * All input pointers must be valid unless otherwise noted as optional. - * All output pointers on failure are undefined. - * All local address are guaranteed to be positive. However, they are stored - * as signed integers, because they must logically fit inside a cell. - */ - -/** - * Given a code pointer, finds the file it is associated with. - * - * @param ctx Context pointer. - * @param addr Code address offset. - * @param filename Pointer to store filename pointer in. - */ -int SP_DbgLookupFile(sp_context_t *ctx, ucell_t addr, const char **filename); - -/** - * Given a code pointer, finds the function it is associated with. - * - * @param ctx Context pointer. - * @param addr Code address offset. - * @param name Pointer to store function name pointer in. - */ -int SP_DbgLookupFunction(sp_context_t *ctx, ucell_t addr, const char **name); - -/** - * Given a code pointer, finds the line it is associated with. - * - * @param ctx Context pointer. - * @param addr Code address offset. - * @param line Pointer to store line number in. - */ -int SP_DbgLookupLine(sp_context_t *ctx, ucell_t addr, uint32_t *line); - -/** - * Installs a debug break and returns the old one, if any. - * - * @param ctx Context pointer. - * @param newpfn New function pointer. - * @param oldpfn Pointer to retrieve old function pointer. - */ -int SP_DbgInstallBreak(sp_context_t *ctx, - SPVM_DEBUGBREAK newpfn, - SPVM_DEBUGBREAK *oldpfn); - - -#endif //_INCLUDE_SOURCEPAWN_VM_DEBUG_H diff --git a/sourcepawn/vm/sp_reader.c b/sourcepawn/vm/sp_vm_engine.cpp similarity index 74% rename from sourcepawn/vm/sp_reader.c rename to sourcepawn/vm/sp_vm_engine.cpp index 7f6b17c6..5bae6220 100644 --- a/sourcepawn/vm/sp_reader.c +++ b/sourcepawn/vm/sp_vm_engine.cpp @@ -1,7 +1,37 @@ #include <malloc.h> #include <string.h> -#include "sp_vm.h" +#include "sp_file_headers.h" +#include "sp_vm_types.h" +#include "sp_vm_engine.h" #include "zlib/zlib.h" +#include "sp_vm_basecontext.h" + +using namespace SourcePawn; + +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) +{ + sp_context_t *_ctx = ctx->GetContext(); + IVirtualMachine *vm = ctx->GetVirtualMachine(); + + vm->FreeContextVars(_ctx); + + delete ctx; +} sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err) { @@ -15,7 +45,7 @@ sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, while (sectnum < hdr->sections) { - nameptr = base + hdr->stringtab + secptr->nameoffs; + nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs); if (!(plugin->pcode) && !strcmp(nameptr, ".code")) { @@ -48,7 +78,7 @@ sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, } else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names")) { - plugin->info.stringbase = base + secptr->dataoffs; + plugin->info.stringbase = (const char *)(base + secptr->dataoffs); } else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files")) { @@ -71,7 +101,7 @@ sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, } else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings")) { - plugin->debug.stringbase = base + secptr->dataoffs; + plugin->debug.stringbase = (const char *)(base + secptr->dataoffs); } secptr++; @@ -104,7 +134,7 @@ return_error: return NULL; } -sp_plugin_t *SP_LoadFromFilePointer(FILE *fp, int *err) +sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err) { sp_file_hdr_t hdr; sp_plugin_t *plugin; @@ -184,6 +214,8 @@ sp_plugin_t *SP_LoadFromFilePointer(FILE *fp, int *err) return NULL; } + plugin->allocflags = 0; + return plugin; return_error: @@ -194,7 +226,7 @@ return_error: return NULL; } -sp_plugin_t *SP_LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) +sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) { sp_file_hdr_t hdr; uint8_t noptr = 0; @@ -207,7 +239,7 @@ sp_plugin_t *SP_LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) noptr = 1; } - if (!_ReadPlugin(&hdr, base, plugin, err)) + if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err)) { if (noptr) { @@ -216,5 +248,26 @@ sp_plugin_t *SP_LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) 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_ERR_NONE; +} diff --git a/sourcepawn/vm/sp_vm_engine.h b/sourcepawn/vm/sp_vm_engine.h new file mode 100644 index 00000000..13b18557 --- /dev/null +++ b/sourcepawn/vm/sp_vm_engine.h @@ -0,0 +1,73 @@ +#ifndef _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ +#define _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ + +#include "sp_vm_api.h" + +namespace SourcePawn +{ + class SourcePawnEngine : public ISourcePawnEngine + { + public: + /** + * Loads a named file from a file pointer. + * Using this means base memory will be allocated by the VM. + * Note: The file handle position may be undefined on entry, and is + * always undefined on conclusion. + * + * @param fp File pointer. May be at any offset. Not closed on return. + * @param err Optional error code pointer. + * @return A new plugin structure. + */ + sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err); + + /** + * Loads a file from a base memory address. + * + * @param base Base address of the plugin's memory region. + * @param plugin If NULL, a new plugin pointer is returned. + * Otherwise, the passed pointer is used. + * @param err Optional error code pointer. + * @return The resulting plugin pointer. + */ + sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err); + + /** + * Frees all of the memory associated with a plugin file. + * If allocated using SP_LoadFromMemory, the base and plugin pointer + * itself are not freed (so this may end up doing nothing). + */ + int FreeFromMemory(sp_plugin_t *plugin); + + /** + * Creates a new IContext from a context handle. + * + * @param ctx Context to use as a basis for the IPluginContext. + * @return New IPluginContext handle. + */ + IPluginContext *CreateBaseContext(sp_context_t *ctx); + + /** + * Frees a context. + * + * @param ctx Context pointer to free. + */ + void FreeBaseContext(IPluginContext *ctx); + + /** + * Allocates memory. + * + * @param size Size of memory to allocate. + * @return Pointer to memory, NULL if allocation failed. + */ + void *BaseAlloc(size_t size); + + /** + * Frees memory allocated with BaseAlloc. + * + * @param mem Memory address to free. + */ + void BaseFree(void *memory); + }; +}; + +#endif //_INCLUDE_SOURCEPAWN_VM_ENGINE_H_