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
This commit is contained in:
parent
7ab8e2027b
commit
7c06d89b00
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8,00"
|
||||
Version="8.00"
|
||||
Name="sourcemod_mm"
|
||||
ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
|
||||
RootNamespace="sourcemod_mm"
|
||||
@ -713,6 +713,10 @@
|
||||
RelativePath="..\smn_events.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\smn_fakenatives.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\smn_filesystem.cpp"
|
||||
>
|
||||
|
430
core/smn_fakenatives.cpp
Normal file
430
core/smn_fakenatives.cpp
Normal file
@ -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 <sh_list.h>
|
||||
#include <sh_string.h>
|
||||
#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},
|
||||
};
|
@ -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<FakeNative *>::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;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <sh_list.h>
|
||||
#include <sh_stack.h>
|
||||
#include <sh_vector.h>
|
||||
#include <sh_string.h>
|
||||
#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<IPluginsListener *> m_listeners;
|
||||
List<CPlugin *> m_plugins;
|
||||
@ -389,6 +402,11 @@ private:
|
||||
Trie *m_LoadLookup;
|
||||
bool m_AllPluginsLoaded;
|
||||
IdentityToken_t *m_MyIdent;
|
||||
|
||||
/* Dynamic native stuff */
|
||||
List<FakeNative *> m_Natives;
|
||||
Trie *m_pNativeLookup;
|
||||
|
||||
};
|
||||
|
||||
extern CPluginManager g_PluginSys;
|
||||
|
205
plugins/include/functions.inc
Normal file
205
plugins/include/functions.inc
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ struct Plugin
|
||||
#include <clients>
|
||||
#include <usermessages>
|
||||
#include <events>
|
||||
#include <functions>
|
||||
|
||||
/**
|
||||
* Declare this as a struct in your plugin to expose its information.
|
||||
|
85
plugins/testsuite/fakenative1.sp
Normal file
85
plugins/testsuite/fakenative1.sp
Normal file
@ -0,0 +1,85 @@
|
||||
#include <sourcemod>
|
||||
|
||||
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<bytes; i++)
|
||||
{
|
||||
buffer[i] = buffer[i] + 1;
|
||||
}
|
||||
|
||||
SetNativeString(1, buffer, bytes+1);
|
||||
}
|
||||
|
||||
public __TestNative3(Handle:plugin, numParams)
|
||||
{
|
||||
new val1 = GetNativeCell(1);
|
||||
new val2 = GetNativeCellRef(2);
|
||||
|
||||
SetNativeCellRef(2, val1+val2);
|
||||
}
|
||||
|
||||
public __TestNative4(Handle:plugin, numParams)
|
||||
{
|
||||
new size = GetNativeCell(3);
|
||||
new local[size];
|
||||
|
||||
GetNativeArray(1, local, size);
|
||||
SetNativeArray(2, local, size);
|
||||
}
|
||||
|
||||
public __TestNative5(Handle:plugin, numParams)
|
||||
{
|
||||
new local = GetNativeCell(1);
|
||||
|
||||
if (local)
|
||||
{
|
||||
FormatNativeString(2, 4, 5, GetNativeCell(3));
|
||||
} else {
|
||||
new len = GetNativeCell(3);
|
||||
new fmt_len;
|
||||
new String:buffer[len];
|
||||
|
||||
GetNativeStringLength(4, fmt_len);
|
||||
|
||||
new String:format[fmt_len];
|
||||
|
||||
GetNativeString(2, buffer, len);
|
||||
GetNativeString(4, format, fmt_len);
|
||||
|
||||
FormatNativeString(0, 0, 5, len, _, buffer, format);
|
||||
}
|
||||
}
|
87
plugins/testsuite/fakenative2.sp
Normal file
87
plugins/testsuite/fakenative2.sp
Normal file
@ -0,0 +1,87 @@
|
||||
#include <sourcemod>
|
||||
|
||||
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;
|
||||
}
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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__
|
||||
|
@ -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)";
|
||||
|
@ -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);
|
||||
|
@ -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}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\jit_version.tpl"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\opcode_switch.inc"
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user