From 7c06d89b00a19e8a002a654ffa093a11016f106c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 12 Mar 2007 07:08:05 +0000 Subject: [PATCH] initial import of dynamic native code for both the JIT and plugins note: dependency resolution is not done yet! --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40607 --- core/msvc8/sourcemod_mm.vcproj | 6 +- core/smn_fakenatives.cpp | 430 ++++++++++++++++++++++++ core/systems/PluginSys.cpp | 55 ++- core/systems/PluginSys.h | 18 + plugins/include/functions.inc | 205 +++++++++++ plugins/include/sourcemod.inc | 1 + plugins/testsuite/fakenative1.sp | 85 +++++ plugins/testsuite/fakenative2.sp | 87 +++++ public/sourcepawn/sp_vm_api.h | 19 +- public/sourcepawn/sp_vm_types.h | 6 + sourcepawn/jit/x86/dll_exports.cpp | 4 +- sourcepawn/jit/x86/jit_x86.cpp | 46 +++ sourcepawn/jit/x86/jit_x86.h | 2 + sourcepawn/jit/x86/msvc8/jit-x86.vcproj | 4 + 14 files changed, 963 insertions(+), 5 deletions(-) create mode 100644 core/smn_fakenatives.cpp create mode 100644 plugins/include/functions.inc create mode 100644 plugins/testsuite/fakenative1.sp create mode 100644 plugins/testsuite/fakenative2.sp diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 99409e11..6a66d17d 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -1,7 +1,7 @@ + + diff --git a/core/smn_fakenatives.cpp b/core/smn_fakenatives.cpp new file mode 100644 index 00000000..5b90816b --- /dev/null +++ b/core/smn_fakenatives.cpp @@ -0,0 +1,430 @@ +/** + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include +#include +#include "sm_trie.h" +#include "sm_globals.h" +#include "PluginSys.h" +#include "sourcemod.h" +#include "sm_stringutil.h" + +using namespace SourceHook; + +static cell_t s_curparams[SP_MAX_EXEC_PARAMS+1]; +static FakeNative *s_curnative = NULL; +static IPluginContext *s_curcaller = NULL; + +cell_t FakeNativeRouter(IPluginContext *pContext, const cell_t *params, void *pData) +{ + FakeNative *native = (FakeNative *)pData; + + /* Check if too many parameters were passed */ + if (params[0] > SP_MAX_EXEC_PARAMS) + { + return pContext->ThrowNativeError("Called native with too many parameters (%d>%d)", params[9], SP_MAX_EXEC_PARAMS); + } + + /* Check if the native is paused */ + sp_context_t *pNativeCtx = native->ctx->GetContext(); + if ((pNativeCtx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED) + { + return pContext->ThrowNativeError("Plugin owning this native is currently paused."); + } + + CPlugin *pCaller = g_PluginSys.GetPluginByCtx(pContext->GetContext()); + + /* Save any old data on the stack */ + FakeNative *pSaveNative = s_curnative; + IPluginContext *pSaveCaller = s_curcaller; + cell_t save_params[SP_MAX_EXEC_PARAMS+1]; + if (pSaveNative != NULL) + { + /* Copy all old parameters */ + for (cell_t i=0; i<=s_curparams[0]; i++) + { + save_params[i] = s_curparams[i]; + } + } + + /* Save the current parameters */ + s_curnative = native; + s_curcaller = pContext; + for (cell_t i=0; i<=params[0]; i++) + { + s_curparams[i] = params[i]; + } + + /* Push info and execute. */ + cell_t result = 0; + native->call->PushCell(pCaller->GetMyHandle()); + native->call->PushCell(params[0]); + if (native->call->Execute(&result) != SP_ERROR_NONE) + { + if (pContext->GetContext()->n_err == SP_ERROR_NONE) + { + pContext->ThrowNativeError("Error encountered while processing a dynamic native"); + } + } + + /* Restore everything from the stack if necessary */ + if (pSaveNative != NULL) + { + s_curnative = pSaveNative; + s_curcaller = pSaveCaller; + for (cell_t i=0; i<=save_params[0]; i++) + { + s_curparams[i] = save_params[i]; + } + } + + return result; +} + +static cell_t CreateNative(IPluginContext *pContext, const cell_t *params) +{ + char *name; + pContext->LocalToString(params[1], &name); + + IPluginFunction *pFunction = pContext->GetFunctionById(params[2]); + if (!pFunction) + { + return pContext->ThrowNativeError("Function %x is not a valid function", params[2]); + } + + if (!g_PluginSys.AddFakeNative(pFunction, name, FakeNativeRouter)) + { + return pContext->ThrowNativeError("Fatal error creating dynamic native!"); + } + + return 1; +} + +static cell_t ThrowNativeError(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + char buffer[512]; + + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + s_curcaller->ThrowNativeError("Error encountered while processing a dynamic native"); + } else { + s_curcaller->ThrowNativeErrorEx(params[1], "%s", buffer); + } + + return 0; +} + +static cell_t GetNativeStringLength(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + int err; + char *str; + if ((err=s_curcaller->LocalToString(s_curparams[param], &str)) != SP_ERROR_NONE) + { + return err; + } + + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + *addr = (cell_t)strlen(str); + + return SP_ERROR_NONE; +} + +static cell_t GetNativeString(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + int err; + char *str; + if ((err=s_curcaller->LocalToString(s_curparams[param], &str)) != SP_ERROR_NONE) + { + return err; + } + + size_t bytes = 0; + pContext->StringToLocalUTF8(params[2], params[3], str, &bytes); + + cell_t *addr; + pContext->LocalToPhysAddr(params[4], &addr); + *addr = (cell_t)bytes; + + return SP_ERROR_NONE; +} + +static cell_t SetNativeString(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + char *str; + pContext->LocalToString(params[2], &str); + + int err; + size_t bytes = 0; + if (params[4]) + { + err = s_curcaller->StringToLocalUTF8(s_curparams[param], params[3], str, &bytes); + } else { + err = s_curcaller->StringToLocal(s_curparams[param], params[3], str); + /* Eww */ + bytes = strlen(str); + if (bytes >= (size_t)params[3]) + { + bytes = params[3] - 1; + } + } + + if (err != SP_ERROR_NONE) + { + return err; + } + + cell_t *addr; + pContext->LocalToPhysAddr(params[5], &addr); + *addr = (cell_t)bytes; + + return SP_ERROR_NONE; +} + +static cell_t GetNativeCell(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + return s_curparams[param]; +} + +static cell_t GetNativeCellRef(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + cell_t *addr; + if (s_curcaller->LocalToPhysAddr(s_curparams[param], &addr) != SP_ERROR_NONE) + { + return s_curcaller->ThrowNativeErrorEx(SP_ERROR_INVALID_ADDRESS, "Invalid address value"); + } + + return *addr; +} + +static cell_t SetNativeCellRef(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + cell_t *addr; + if (s_curcaller->LocalToPhysAddr(s_curparams[param], &addr) != SP_ERROR_NONE) + { + return s_curcaller->ThrowNativeErrorEx(SP_ERROR_INVALID_ADDRESS, "Invalid address value"); + } + + *addr = params[2]; + + return 1; +} + +static cell_t GetNativeArray(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + int err; + cell_t *addr; + if ((err=s_curcaller->LocalToPhysAddr(s_curparams[param], &addr)) != SP_ERROR_NONE) + { + return err; + } + + cell_t *src; + pContext->LocalToPhysAddr(params[2], &src); + + memcpy(src, addr, sizeof(cell_t) * params[3]); + + return SP_ERROR_NONE; +} + +static cell_t SetNativeArray(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + int err; + cell_t *addr; + if ((err=s_curcaller->LocalToPhysAddr(s_curparams[param], &addr)) != SP_ERROR_NONE) + { + return err; + } + + cell_t *src; + pContext->LocalToPhysAddr(params[2], &src); + + memcpy(addr, src, sizeof(cell_t) * params[3]); + + return SP_ERROR_NONE; +} + +static cell_t FormatNativeString(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + int out_param = params[1]; + int fmt_param = params[2]; + int var_param = params[3]; + + /* Validate input */ + if (out_param && (out_param < 1 || out_param > s_curparams[0])) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", out_param); + } + if (fmt_param && (fmt_param < 1 || fmt_param > s_curparams[0])) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", fmt_param); + } + if (var_param && (var_param < 1 || var_param > s_curparams[0] + 1)) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", fmt_param); + } + + /* Get buffer information */ + int err; + char *output_buffer; + char *format_buffer; + + if (out_param) + { + if ((err=s_curcaller->LocalToString(s_curparams[out_param], &output_buffer)) != SP_ERROR_NONE) + { + return err; + } + } else { + pContext->LocalToString(params[6], &output_buffer); + } + + if (fmt_param) + { + if ((err=s_curcaller->LocalToString(s_curparams[fmt_param], &format_buffer)) != SP_ERROR_NONE) + { + return err; + } + } else { + pContext->LocalToString(params[7], &format_buffer); + } + + /* Get maximum length */ + size_t maxlen = (size_t)params[4]; + + /* Do the format */ + size_t written = atcprintf(output_buffer, maxlen, format_buffer, s_curcaller, s_curparams, &var_param); + + cell_t *addr; + pContext->LocalToPhysAddr(params[5], &addr); + *addr = (cell_t)written; + + return s_curcaller->GetContext()->n_err; +} + +//tee hee +REGISTER_NATIVES(nativeNatives) +{ + {"CreateNative", CreateNative}, + {"GetNativeArray", GetNativeArray}, + {"GetNativeCell", GetNativeCell}, + {"GetNativeCellRef", GetNativeCellRef}, + {"GetNativeString", GetNativeString}, + {"GetNativeStringLength", GetNativeStringLength}, + {"FormatNativeString", FormatNativeString}, + {"ThrowNativeError", ThrowNativeError}, + {"SetNativeArray", SetNativeArray}, + {"SetNativeCellRef", SetNativeCellRef}, + {"SetNativeString", SetNativeString}, + {NULL, NULL}, +}; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 0b38ff04..6864afba 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -588,6 +588,7 @@ CPluginManager::CPluginManager() m_LoadLookup = sm_trie_create(); m_AllPluginsLoaded = false; m_MyIdent = NULL; + m_pNativeLookup = sm_trie_create(); } CPluginManager::~CPluginManager() @@ -598,9 +599,9 @@ CPluginManager::~CPluginManager() * will crash anyway. YAY */ sm_trie_destroy(m_LoadLookup); + sm_trie_destroy(m_pNativeLookup); } - void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir) { /* First read in the database of plugin settings */ @@ -958,6 +959,7 @@ bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxleng /* Bind all extra natives */ g_Extensions.BindAllNativesToPlugin(pPlugin); + AddFakeNativesToPlugin(pPlugin); /* Find any unbound natives * Right now, these are not allowed @@ -1013,6 +1015,32 @@ void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin) } } +void CPluginManager::AddFakeNativesToPlugin(CPlugin *pPlugin) +{ + IPluginContext *pContext = pPlugin->GetBaseContext(); + sp_nativeinfo_t native; + + List::iterator iter; + FakeNative *pNative; + sp_context_t *ctx; + for (iter = m_Natives.begin(); iter != m_Natives.end(); iter++) + { + pNative = (*iter); + ctx = pNative->ctx->GetContext(); + if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED) + { + /* Ignore natives in paused plugins */ + continue; + } + native.name = pNative->name.c_str(); + native.func = pNative->func; + if (pContext->BindNative(&native) == SP_ERROR_NONE) + { + /* :TODO: add to dependency list */ + } + } +} + bool CPluginManager::UnloadPlugin(IPlugin *plugin) { CPlugin *pPlugin = (CPlugin *)plugin; @@ -1675,3 +1703,28 @@ void CPluginManager::_SetPauseState(CPlugin *pl, bool paused) } } +bool CPluginManager::AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func) +{ + if (sm_trie_retrieve(m_pNativeLookup, name, NULL)) + { + return false; + } + + FakeNative *pNative = new FakeNative; + + pNative->func = g_pVM->CreateFakeNative(func, pNative); + if (!pNative->func) + { + delete pNative; + return false; + } + + pNative->call = pFunction; + pNative->name.assign(name); + pNative->ctx = pFunction->GetParentContext(); + + m_Natives.push_back(pNative); + sm_trie_insert(m_pNativeLookup, name,pNative); + + return true; +} diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index 05838db0..1eb2cec9 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "sm_globals.h" #include "vm/sp_vm_basecontext.h" #include "PluginInfoDatabase.h" @@ -232,6 +233,14 @@ private: Trie *m_pProps; }; +struct FakeNative +{ + IPluginContext *ctx; + IPluginFunction *call; + String name; + SPVM_NATIVE_FUNC func; +}; + class CPluginManager : public IPluginManager, public SMGlobalClass, @@ -380,6 +389,10 @@ protected: { return m_MyIdent; } +public: + bool AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func); +private: + void AddFakeNativesToPlugin(CPlugin *pPlugin); private: List m_listeners; List m_plugins; @@ -389,6 +402,11 @@ private: Trie *m_LoadLookup; bool m_AllPluginsLoaded; IdentityToken_t *m_MyIdent; + + /* Dynamic native stuff */ + List m_Natives; + Trie *m_pNativeLookup; + }; extern CPluginManager g_PluginSys; diff --git a/plugins/include/functions.inc b/plugins/include/functions.inc new file mode 100644 index 00000000..2cea70a0 --- /dev/null +++ b/plugins/include/functions.inc @@ -0,0 +1,205 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is part of the SourceMod/SourcePawn SDK. This file may only be used + * or modified under the Terms and Conditions of its License Agreement, which is found + * in LICENSE.txt. The Terms and Conditions for making SourceMod extensions/plugins + * may change at any time. To view the latest information, see: + * http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#define SP_ERROR_NONE 0 /**< No error occurred */ +#define SP_ERROR_FILE_FORMAT 1 /**< File format unrecognized */ +#define SP_ERROR_DECOMPRESSOR 2 /**< A decompressor was not found */ +#define SP_ERROR_HEAPLOW 3 /**< Not enough space left on the heap */ +#define SP_ERROR_PARAM 4 /**< Invalid parameter or parameter type */ +#define SP_ERROR_INVALID_ADDRESS 5 /**< A memory address was not valid */ +#define SP_ERROR_NOT_FOUND 6 /**< The object in question was not found */ +#define SP_ERROR_INDEX 7 /**< Invalid index parameter */ +#define SP_ERROR_STACKLOW 8 /**< Nnot enough space left on the stack */ +#define SP_ERROR_NOTDEBUGGING 9 /**< Debug mode was not on or debug section not found */ +#define SP_ERROR_INVALID_INSTRUCTION 10 /**< Invalid instruction was encountered */ +#define SP_ERROR_MEMACCESS 11 /**< Invalid memory access */ +#define SP_ERROR_STACKMIN 12 /**< Stack went beyond its minimum value */ +#define SP_ERROR_HEAPMIN 13 /**< Heap went beyond its minimum value */ +#define SP_ERROR_DIVIDE_BY_ZERO 14 /**< Division by zero */ +#define SP_ERROR_ARRAY_BOUNDS 15 /**< Array index is out of bounds */ +#define SP_ERROR_INSTRUCTION_PARAM 16 /**< Instruction had an invalid parameter */ +#define SP_ERROR_STACKLEAK 17 /**< A native leaked an item on the stack */ +#define SP_ERROR_HEAPLEAK 18 /**< A native leaked an item on the heap */ +#define SP_ERROR_ARRAY_TOO_BIG 19 /**< A dynamic array is too big */ +#define SP_ERROR_TRACKER_BOUNDS 20 /**< Tracker stack is out of bounds */ +#define SP_ERROR_INVALID_NATIVE 21 /**< Native was pending or invalid */ +#define SP_ERROR_PARAMS_MAX 22 /**< Maximum number of parameters reached */ +#define SP_ERROR_NATIVE 23 /**< Error originates from a native */ +#define SP_ERROR_NOT_RUNNABLE 24 /**< Function or plugin is not runnable */ +#define SP_ERROR_ABORTED 25 /**< Function call was aborted */ + +/** + * Defines a native function. + * + * It is not necessary to validate the parameter count + * + * @param plugin Handle of the calling plugin. + * @param numParams Number of parameters passed to the native. + * @return Value for the native call to return. + */ +functag NativeCall public(Handle:plugin, numParams); + +/** + * Creates a dynamic native. This should only be called in AskPluginLoad(), or + * else you risk not having your native shared with other plugins. + * + * @param name Name of the dynamic native; must be unique amongst + * all other registered dynamic native.s + * @param func Function to use as the dynamic native. + * @noreturn + */ +native CreateNative(const String:name[], NativeCall:func); + +/** + * Throws an error in the calling plugin of a native, instead of your own plugin. + * + * @param error Error code to use. + * @param fmt Error message format. + * @param ... Format arguments. + */ +native ThrowNativeError(error, const String:fmt[], {Handle,Float,String,_}:...); + +/** + * Retrieves the string length from a native parameter string. This is useful + * fetching the entire string using dynamic arrays. + * @note If this function succeeds, Get/SetNativeString will also succeed. + * + * @param param Parameter number, starting from 1. + * @param length Stores the length of the string. + * @return SP_ERROR_NONE on sucecss, any other integer on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeStringLength(param, &length); + +/** + * Retrieves a string from a native parameter. + * @note Output conditions are undefined on failure. + * + * @param param Parameter number, starting from 1. + * @param buffer Buffer to store the string in. + * @param maxlength Maximum length of the buffer. + * @param bytes Optionally store the number of bytes written. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeString(param, String:buffer[], maxlength, &bytes=0); + +/** + * Sets a string in a native parameter. + * @note Output conditions are undefined on failure. + * + * @param param Parameter number, starting from 1. + * @param source Source string to use. + * @param maxlength Maximum number of bytes to write. + * @param utf8 If false, string will not be written + * with UTF8 safety. + * @param bytes Optionally store the number of bytes written. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native SetNativeString(param, const String:source[], maxlength, bool:utf8=true, &bytes=0); + +/** + * Gets a cell from a native parameter. + * + * @param param Parameter number, starting from 1. + * @return Cell value at the parameter number. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeCell(param); + +/** + * Gets a cell from a native parameter, by reference. + * + * @param param Parameter number, starting from 1. + * @return Cell value at the parameter number. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeCellRef(param); + +/** + * Sets a cell from a native parameter, by reference. + * + * @param param Parameter number, starting from 1. + * @param value Cell value at the parameter number to set by reference. + * @noreturn + * @error Invalid parameter number or calling from a non-native function. + */ +native SetNativeCellRef(param, {Float,Handle,_}:value); + +/** + * Gets an array from a native parameter (always by reference). + * + * @param param Parameter number, starting from 1. + * @param local Local array to copy into. + * @param size Maximum size of local array. + * @return SP_ERROR_NONE on success, anything else on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeArray(param, {Float,Handle,_}:local[], size); + +/** + * Copies a local array into a native parameter array (always by reference). + * + * @param param Parameter number, starting from 1. + * @param local Local array to copy from. + * @param size Size of the local array to copy. + * @return SP_ERROR_NONE on success, anything else on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native SetNativeArray(param, const {Float,Handle,_}:local[], size); + +/** + * Formats a string using parameters from a native. + * + * @note All parameter indexes start at 1. + * @note If the input and output buffers overlap, the contents + * of the output buffer at the end is undefined. + * + * @param out_param Output parameter number to write to. If 0, out_string is used. + * @param fmt_param Format parameter number. If 0, fmt_string is used. + * @param vararg_param First variable parameter number. + * @param out_len Output string buffer maximum length (always required). + * @param written Optionally stores the number of bytes written. + * @param out_string Output string buffer to use if out_param is not used. + * @param fmt_string Format string to use if fmt_param is not used. + * @return SP_ERROR_NONE on success, anything else on failure. + */ +native FormatNativeString(out_param, + fmt_param, + vararg_param, + out_len, + &written=0, + String:out_string[]="", + const String:fmt_string[]=""); + +stock Float:GetNativeFloat(param, bool:byref) +{ + if (!byref) + { + return Float:GetNativeCell(param); + } else { + return Float:GetNativeCellRef(param); + } +} +stock Handle:GetNativeHandle(param, bool:byref) +{ + if (!byref) + { + return Handle:GetNativeCell(param); + } else { + return Handle:GetNativeCellRef(param); + } +} diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 67cd32b8..ba0d0a00 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -39,6 +39,7 @@ struct Plugin #include #include #include +#include /** * Declare this as a struct in your plugin to expose its information. diff --git a/plugins/testsuite/fakenative1.sp b/plugins/testsuite/fakenative1.sp new file mode 100644 index 00000000..3c747695 --- /dev/null +++ b/plugins/testsuite/fakenative1.sp @@ -0,0 +1,85 @@ +#include + +public Plugin:myinfo = +{ + name = "FakeNative Testing Lab #1", + author = "AlliedModders LLC", + description = "Test suite #1 for dynamic natives", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +{ + CreateNative("TestNative1", __TestNative1); + CreateNative("TestNative2", __TestNative2); + CreateNative("TestNative3", __TestNative3); + CreateNative("TestNative4", __TestNative4); + CreateNative("TestNative5", __TestNative5); + return true; +} + +public __TestNative1(Handle:plugin, numParams) +{ + PrintToServer("TestNative1: Plugin: %x params: %d", plugin, numParams); + if (numParams == 4) + { + ThrowNativeError(SP_ERROR_NATIVE, "Four parameters ARE NOT ALLOWED lol"); + } + return 5; +} + +public __TestNative2(Handle:plugin, numParams) +{ + new String:buffer[512]; + new bytes; + + GetNativeString(1, buffer, sizeof(buffer), bytes); + + for (new i=0; i + +native TestNative1(gaben, ...); +native TestNative2(String:buffer[]); +native TestNative3(value1, &value2); +native TestNative4(const input[], output[], size); +native TestNative5(bool:local, String:buffer[], maxlength, const String:fmt[], {Handle,Float,String,_}:...); + +public Plugin:myinfo = +{ + name = "FakeNative Testing Lab #2", + author = "AlliedModders LLC", + description = "Test suite #2 for dynamic natives", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + RegServerCmd("test_native1", Test_Native1); + RegServerCmd("test_native2", Test_Native2); + RegServerCmd("test_native3", Test_Native3); + RegServerCmd("test_native4", Test_Native4); + RegServerCmd("test_native5", Test_Native5); +} + +public Action:Test_Native1(args) +{ + PrintToServer("Returned: %d", TestNative1(1)); + PrintToServer("Returned: %d", TestNative1(1,2)); + PrintToServer("Returned: %d", TestNative1(1,2,3,4)); + + return Plugin_Handled; +} + +public Action:Test_Native2(args) +{ + new String:buffer[] = "IBM"; + + PrintToServer("Before: %s", buffer); + + TestNative2(buffer); + + PrintToServer("AfteR: %s", buffer); + + return Plugin_Handled; +} + +public Action:Test_Native3(args) +{ + new value1 = 5, value2 = 6 + + PrintToServer("Before: %d, %d", value1, value2); + + TestNative3(value1, value2); + + PrintToServer("After: %d, %d", value1, value2); + + return Plugin_Handled; +} + +public Action:Test_Native4(args) +{ + new input[5] = {0, 1, 2, 3, 4}; + new output[5]; + + TestNative4(input, output, 5); + + PrintToServer("[0=%d] [1=%d] [2=%d] [3=%d] [4=%d]", input[0], input[1], input[2], input[3], input[4]); + PrintToServer("[0=%d] [1=%d] [2=%d] [3=%d] [4=%d]", output[0], output[1], output[2], output[3], output[4]); + + return Plugin_Handled; +} + +public Action:Test_Native5(args) +{ + new String:buffer1[512]; + new String:buffer2[512]; + + TestNative5(true, buffer1, sizeof(buffer1), "%d gabens in the %s", 50, "gabpark"); + TestNative5(true, buffer2, sizeof(buffer2), "%d gabens in the %s", 50, "gabpark"); + + PrintToServer("Test 1: %s", buffer1); + PrintToServer("Test 2: %s", buffer2); + + return Plugin_Handled; +} diff --git a/public/sourcepawn/sp_vm_api.h b/public/sourcepawn/sp_vm_api.h index 3db0b0bc..832de0b1 100644 --- a/public/sourcepawn/sp_vm_api.h +++ b/public/sourcepawn/sp_vm_api.h @@ -28,7 +28,7 @@ #include "sp_vm_types.h" /** SourcePawn VM API Version */ -#define SOURCEPAWN_VM_API_VERSION 1 +#define SOURCEPAWN_VM_API_VERSION 2 #if defined SOURCEMOD_BUILD namespace SourceMod @@ -820,6 +820,23 @@ namespace SourcePawn * @return True if code index is valid, false otherwise. */ virtual bool FunctionPLookup(const sp_context_t *ctx, uint32_t code_addr, unsigned int *result) =0; + + /** + * @brief Creates a fake native and binds it to a general callback function. + * + * @param callback Callback function to bind the native to. + * @param pData Private data to pass to the callback when the native is invoked. + * @return A new fake native function as a wrapper around the callback. + */ + virtual SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) =0; + + /** + * @brief Destroys a fake native function wrapper. + * + * @param function Pointer to the fake native created by CreateFakeNative. + * @noreturn + */ + virtual void DestroyFakenative(SPVM_NATIVE_FUNC func) =0; }; }; diff --git a/public/sourcepawn/sp_vm_types.h b/public/sourcepawn/sp_vm_types.h index 2ba21ffd..7d73bf33 100644 --- a/public/sourcepawn/sp_vm_types.h +++ b/public/sourcepawn/sp_vm_types.h @@ -136,6 +136,12 @@ struct sp_context_s; */ typedef cell_t (*SPVM_NATIVE_FUNC)(SourcePawn::IPluginContext *, const cell_t *); +/** + * @brief Fake native callback prototype, passed a context, parameter stack, and private data. + * A cell must be returned. + */ +typedef cell_t (*SPVM_FAKENATIVE_FUNC)(SourcePawn::IPluginContext *, const cell_t *, void *); + /********************************************** *** The following structures are bound to the VM/JIT. *** Changing them will result in necessary recompilation. diff --git a/sourcepawn/jit/x86/dll_exports.cpp b/sourcepawn/jit/x86/dll_exports.cpp index 6ac0a56c..bd54f789 100644 --- a/sourcepawn/jit/x86/dll_exports.cpp +++ b/sourcepawn/jit/x86/dll_exports.cpp @@ -17,7 +17,7 @@ #include "dll_exports.h" SourcePawn::ISourcePawnEngine *engine = NULL; -JITX86 jit; +JITX86 g_jit; EXPORTFUNC int GiveEnginePointer(SourcePawn::ISourcePawnEngine *engine_p) { @@ -45,7 +45,7 @@ EXPORTFUNC SourcePawn::IVirtualMachine *GetExport(unsigned int exportnum) return NULL; } - return &jit; + return &g_jit; } #if defined __linux__ diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 7c9e5e89..cc030b1e 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -2188,6 +2188,52 @@ jit_rewind: return ctx; } +SPVM_NATIVE_FUNC JITX86::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) +{ + JitWriter jw; + JitWriter *jit = &jw; + + jw.outbase = NULL; + jw.outptr = NULL; + jw.data = NULL; + + /* First pass, calculate size */ +rewind: + //push pData ;push pData + //push [esp+12] ;push params + //push [esp+12] ;push ctx + //call [callback] ;invoke the meta-callback + //add esp, 12 ;restore the stack + //ret ;return + IA32_Push_Imm32(jit, (jit_int32_t)pData); + IA32_Push_Rm_Disp8_ESP(jit, 12); + IA32_Push_Rm_Disp8_ESP(jit, 12); + uint32_t call = IA32_Call_Imm32(jit, 0); + IA32_Write_Jump32_Abs(jit, call, (void *)callback); + IA32_Add_Rm_Imm8(jit, REG_ESP, 12, MOD_REG); + IA32_Return(jit); + + if (jw.outbase == NULL) + { + /* Second pass: Actually write */ + jw.outbase = (jitcode_t)engine->ExecAlloc(jw.get_outputpos()); + if (!jw.outbase) + { + return NULL; + } + jw.outptr = jw.outbase; + + goto rewind; + } + + return (SPVM_NATIVE_FUNC)jw.outbase; +} + +void JITX86::DestroyFakenative(SPVM_NATIVE_FUNC func) +{ + engine->ExecFree(func); +} + const char *JITX86::GetVMName() { return "JIT (x86)"; diff --git a/sourcepawn/jit/x86/jit_x86.h b/sourcepawn/jit/x86/jit_x86.h index 4d65745a..02801438 100644 --- a/sourcepawn/jit/x86/jit_x86.h +++ b/sourcepawn/jit/x86/jit_x86.h @@ -98,6 +98,8 @@ public: unsigned int FunctionCount(const sp_context_t *ctx); const char *GetVersionString(); const char *GetCPUOptimizations(); + SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData); + void DestroyFakenative(SPVM_NATIVE_FUNC func); }; cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params); diff --git a/sourcepawn/jit/x86/msvc8/jit-x86.vcproj b/sourcepawn/jit/x86/msvc8/jit-x86.vcproj index f2972a94..e5e623e2 100644 --- a/sourcepawn/jit/x86/msvc8/jit-x86.vcproj +++ b/sourcepawn/jit/x86/msvc8/jit-x86.vcproj @@ -234,6 +234,10 @@ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" > + +