Merge pull request #285 from alliedmodders/frames
Implement a new stack and error handling model for the SourcePawn VM.
This commit is contained in:
commit
715a51d01f
@ -218,6 +218,7 @@ ResultType ConCmdManager::DispatchClientCommand(int client, const char *cmd, int
|
||||
hook->pf->PushCell(args);
|
||||
|
||||
cell_t tempres = result;
|
||||
|
||||
if (hook->pf->Execute(&tempres) == SP_ERROR_NONE)
|
||||
{
|
||||
if (tempres > result)
|
||||
|
@ -62,7 +62,6 @@ binary.sources += [
|
||||
'PluginSys.cpp',
|
||||
'HandleSys.cpp',
|
||||
'NativeOwner.cpp',
|
||||
'NativeInvoker.cpp',
|
||||
'ExtensionSys.cpp',
|
||||
'DebugReporter.cpp',
|
||||
'Database.cpp',
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
@ -132,51 +132,6 @@ void DebugReport::GenerateCodeError(IPluginContext *pContext, uint32_t code_addr
|
||||
}
|
||||
}
|
||||
|
||||
void DebugReport::OnContextExecuteError(IPluginContext *ctx, IContextTrace *error)
|
||||
{
|
||||
const char *lastname;
|
||||
const char *plname = pluginsys->FindPluginByContext(ctx->GetContext())->GetFilename();
|
||||
int n_err = error->GetErrorCode();
|
||||
|
||||
if (n_err != SP_ERROR_NATIVE)
|
||||
{
|
||||
g_Logger.LogError("[SM] Plugin encountered error %d: %s",
|
||||
n_err,
|
||||
error->GetErrorString());
|
||||
}
|
||||
|
||||
if ((lastname=error->GetLastNative(NULL)) != NULL)
|
||||
{
|
||||
const char *custerr;
|
||||
if ((custerr=error->GetCustomErrorString()) != NULL)
|
||||
{
|
||||
g_Logger.LogError("[SM] Native \"%s\" reported: %s", lastname, custerr);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Native \"%s\" encountered a generic error.", lastname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error->DebugInfoAvailable())
|
||||
{
|
||||
g_Logger.LogError("[SM] Debug mode is not enabled for \"%s\"", plname);
|
||||
g_Logger.LogError("[SM] To enable debug mode, edit plugin_settings.cfg, or type: sm plugins debug %d on",
|
||||
_GetPluginIndex(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
CallStackInfo stk_info;
|
||||
int i = 0;
|
||||
g_Logger.LogError("[SM] Displaying call stack trace for plugin \"%s\":", plname);
|
||||
while (error->GetTraceInfo(&stk_info))
|
||||
{
|
||||
g_Logger.LogError("[SM] [%d] Line %d, %s::%s()",
|
||||
i++,
|
||||
stk_info.line,
|
||||
stk_info.filename,
|
||||
stk_info.function);
|
||||
}
|
||||
}
|
||||
|
||||
int DebugReport::_GetPluginIndex(IPluginContext *ctx)
|
||||
{
|
||||
int id = 1;
|
||||
@ -199,3 +154,46 @@ int DebugReport::_GetPluginIndex(IPluginContext *ctx)
|
||||
return pluginsys->GetPluginCount() + 1;
|
||||
}
|
||||
|
||||
void DebugReport::ReportError(const IErrorReport &report, IFrameIterator &iter)
|
||||
{
|
||||
// Find the nearest plugin to blame.
|
||||
const char *blame = nullptr;
|
||||
for (; !iter.Done(); iter.Next()) {
|
||||
if (iter.IsScriptedFrame()) {
|
||||
IPlugin *plugin = pluginsys->FindPluginByContext(iter.Context()->GetContext());
|
||||
if (plugin)
|
||||
blame = plugin->GetFilename();
|
||||
else
|
||||
blame = iter.Context()->GetRuntime()->GetFilename();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
iter.Reset();
|
||||
|
||||
g_Logger.LogError("[SM] Exception reported: %s", report.Message());
|
||||
if (blame)
|
||||
g_Logger.LogError("[SM] Blaming plugin: %s", blame);
|
||||
g_Logger.LogError("[SM] Call stack trace:");
|
||||
|
||||
for (int index = 0; !iter.Done(); iter.Next(), index++) {
|
||||
const char *fn = iter.FunctionName();
|
||||
if (!fn)
|
||||
fn = "<unknown function>";
|
||||
|
||||
if (iter.IsNativeFrame()) {
|
||||
g_Logger.LogError("[SM] [%d] %s", index, fn);
|
||||
continue;
|
||||
}
|
||||
if (iter.IsScriptedFrame()) {
|
||||
const char *file = iter.FilePath();
|
||||
if (!file)
|
||||
file = "<unknown>";
|
||||
g_Logger.LogError("[SM] [%d] Line %d, %s::%s()",
|
||||
index,
|
||||
iter.LineNumber(),
|
||||
file,
|
||||
fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class DebugReport :
|
||||
public: // SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
public: // IDebugListener
|
||||
void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error);
|
||||
void ReportError(const IErrorReport &report, IFrameIterator &iter);
|
||||
void OnDebugSpew(const char *msg, ...);
|
||||
public:
|
||||
void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
|
||||
|
@ -1,360 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "NativeInvoker.h"
|
||||
#include "ShareSys.h"
|
||||
|
||||
NativeInterface g_NInvoke;
|
||||
|
||||
NativeInvoker::NativeInvoker()
|
||||
{
|
||||
}
|
||||
|
||||
NativeInvoker::~NativeInvoker()
|
||||
{
|
||||
}
|
||||
|
||||
const char *NativeInterface::GetInterfaceName()
|
||||
{
|
||||
return SMINTERFACE_NINVOKE_NAME;
|
||||
}
|
||||
|
||||
unsigned int NativeInterface::GetInterfaceVersion()
|
||||
{
|
||||
return SMINTERFACE_NINVOKE_VERSION;
|
||||
}
|
||||
|
||||
void NativeInterface::OnSourceModAllInitialized()
|
||||
{
|
||||
sharesys->AddInterface(NULL, &g_NInvoke);
|
||||
}
|
||||
|
||||
IPluginRuntime *NativeInterface::CreateRuntime(const char *name, size_t bytes)
|
||||
{
|
||||
return g_pSourcePawn2->CreateEmptyRuntime(name, bytes);
|
||||
}
|
||||
|
||||
INativeInvoker *NativeInterface::CreateInvoker()
|
||||
{
|
||||
return new NativeInvoker();
|
||||
}
|
||||
|
||||
bool NativeInvoker::Start(IPluginContext *pContext, const char *name)
|
||||
{
|
||||
ke::Ref<Native> entry = g_ShareSys.FindNative(name);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (!entry->owner)
|
||||
return false;
|
||||
|
||||
native_ = entry->func();
|
||||
context_ = pContext;
|
||||
|
||||
m_curparam = 0;
|
||||
m_errorstate = SP_ERROR_NONE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cell_t NativeInvoker::PushCell(cell_t cell)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
m_info[m_curparam].marked = false;
|
||||
m_params[m_curparam] = cell;
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int NativeInvoker::PushCellByRef(cell_t *cell, int flags)
|
||||
{
|
||||
return PushArray(cell, 1, flags);
|
||||
}
|
||||
|
||||
int NativeInvoker::PushFloat(float number)
|
||||
{
|
||||
cell_t val = *(cell_t *)&number;
|
||||
|
||||
return PushCell(val);
|
||||
}
|
||||
|
||||
int NativeInvoker::PushFloatByRef(float *number, int flags)
|
||||
{
|
||||
return PushCellByRef((cell_t *)number, flags);
|
||||
}
|
||||
|
||||
int NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
ParamInfo *info = &m_info[m_curparam];
|
||||
|
||||
info->flags = inarray ? copyback : 0;
|
||||
info->marked = true;
|
||||
info->size = cells;
|
||||
info->str.is_sz = false;
|
||||
info->orig_addr = inarray;
|
||||
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int NativeInvoker::PushString(const char *string)
|
||||
{
|
||||
return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
|
||||
}
|
||||
|
||||
int NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
|
||||
{
|
||||
return _PushString(buffer, sz_flags, cp_flags, length);
|
||||
}
|
||||
|
||||
int NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
ParamInfo *info = &m_info[m_curparam];
|
||||
|
||||
info->marked = true;
|
||||
info->orig_addr = (cell_t *)string;
|
||||
info->flags = cp_flags;
|
||||
info->size = len;
|
||||
info->str.sz_flags = sz_flags;
|
||||
info->str.is_sz = true;
|
||||
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
void NativeInvoker::Cancel()
|
||||
{
|
||||
if (context_ == NULL)
|
||||
return;
|
||||
|
||||
m_errorstate = SP_ERROR_NONE;
|
||||
m_curparam = 0;
|
||||
context_ = NULL;
|
||||
native_ = NULL;
|
||||
}
|
||||
|
||||
int NativeInvoker::SetError(int err)
|
||||
{
|
||||
m_errorstate = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
int NativeInvoker::Invoke(cell_t *result)
|
||||
{
|
||||
int err = SP_ERROR_NONE;
|
||||
|
||||
if (context_ == NULL)
|
||||
return SP_ERROR_INVALID_NATIVE;
|
||||
|
||||
if (m_errorstate != SP_ERROR_NONE)
|
||||
{
|
||||
err = m_errorstate;
|
||||
Cancel();
|
||||
return err;
|
||||
}
|
||||
|
||||
cell_t tresult;
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
result = &tresult;
|
||||
}
|
||||
|
||||
//This is for re-entrancy!
|
||||
IPluginContext *ctx = context_;
|
||||
cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
|
||||
cell_t *temp_params = &_temp_params[1];
|
||||
ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int numparams = m_curparam;
|
||||
unsigned int i;
|
||||
bool docopies = true;
|
||||
|
||||
if (numparams)
|
||||
{
|
||||
//Save the info locally, then reset it for re-entrant calls.
|
||||
memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
|
||||
}
|
||||
m_curparam = 0;
|
||||
context_ = NULL;
|
||||
|
||||
/* Initialize 0th parameter */
|
||||
_temp_params[0] = numparams;
|
||||
|
||||
/* Browse the parameters and build arrays */
|
||||
for (i = 0; i < numparams; i++)
|
||||
{
|
||||
/* Is this marked as an array? */
|
||||
if (temp_info[i].marked)
|
||||
{
|
||||
if (!temp_info[i].str.is_sz)
|
||||
{
|
||||
/* Allocate a normal/generic array */
|
||||
if ((err = ctx->HeapAlloc(temp_info[i].size,
|
||||
&temp_info[i].local_addr,
|
||||
&temp_info[i].phys_addr))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (temp_info[i].orig_addr)
|
||||
{
|
||||
memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate cells required for the string */
|
||||
size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
|
||||
|
||||
/* Allocate the buffer */
|
||||
if ((err = ctx->HeapAlloc(cells,
|
||||
&temp_info[i].local_addr,
|
||||
&temp_info[i].phys_addr))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Copy original string if necessary */
|
||||
if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
|
||||
{
|
||||
/* Cut off UTF-8 properly */
|
||||
if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
|
||||
{
|
||||
if ((err = ctx->StringToLocalUTF8(temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr,
|
||||
NULL))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Copy a binary blob */
|
||||
else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
|
||||
{
|
||||
memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
|
||||
}
|
||||
/* Copy ASCII characters */
|
||||
else
|
||||
{
|
||||
if ((err = ctx->StringToLocal(temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* End array/string calculation */
|
||||
/* Update the pushed parameter with the byref local address */
|
||||
temp_params[i] = temp_info[i].local_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just copy the value normally */
|
||||
temp_params[i] = m_params[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the call if we can */
|
||||
if (err == SP_ERROR_NONE)
|
||||
{
|
||||
*result = native_(ctx, _temp_params);
|
||||
if (ctx->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
docopies = false;
|
||||
ctx->ClearLastNativeError();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
docopies = false;
|
||||
}
|
||||
|
||||
/* i should be equal to the last valid parameter + 1 */
|
||||
while (i--)
|
||||
{
|
||||
if (!temp_info[i].marked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
|
||||
{
|
||||
if (temp_info[i].orig_addr)
|
||||
{
|
||||
if (temp_info[i].str.is_sz)
|
||||
{
|
||||
memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temp_info[i].size == 1)
|
||||
{
|
||||
*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(temp_info[i].orig_addr,
|
||||
temp_info[i].phys_addr,
|
||||
temp_info[i].size * sizeof(cell_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
|
||||
#define _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <INativeInvoker.h>
|
||||
#include "common_logic.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
using namespace SourcePawn;
|
||||
|
||||
struct ParamInfo
|
||||
{
|
||||
int flags; /* Copy-back flags */
|
||||
bool marked; /* Whether this is marked as being used */
|
||||
cell_t local_addr; /* Local address to free */
|
||||
cell_t *phys_addr; /* Physical address of our copy */
|
||||
cell_t *orig_addr; /* Original address to copy back to */
|
||||
ucell_t size; /* Size of array in bytes */
|
||||
struct
|
||||
{
|
||||
bool is_sz; /* is a string */
|
||||
int sz_flags; /* has sz flags */
|
||||
} str;
|
||||
};
|
||||
|
||||
class NativeInvoker : public INativeInvoker
|
||||
{
|
||||
public:
|
||||
NativeInvoker();
|
||||
~NativeInvoker();
|
||||
public: /* ICallable */
|
||||
int PushCell(cell_t cell);
|
||||
int PushCellByRef(cell_t *cell, int flags=SM_PARAM_COPYBACK);
|
||||
int PushFloat(float number);
|
||||
int PushFloatByRef(float *number, int flags=SM_PARAM_COPYBACK);
|
||||
int PushArray(cell_t *inarray, unsigned int cells, int flags=0);
|
||||
int PushString(const char *string);
|
||||
int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
|
||||
void Cancel();
|
||||
public: /* INativeInvoker */
|
||||
bool Start(IPluginContext *pContext, const char *name);
|
||||
int Invoke(cell_t *result);
|
||||
private:
|
||||
int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
|
||||
int SetError(int err);
|
||||
private:
|
||||
IPluginContext *context_;
|
||||
SPVM_NATIVE_FUNC native_;
|
||||
cell_t m_params[SP_MAX_EXEC_PARAMS];
|
||||
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int m_curparam;
|
||||
int m_errorstate;
|
||||
};
|
||||
|
||||
class NativeInterface :
|
||||
public INativeInterface,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public: /* SMGlobalClass */
|
||||
void OnSourceModAllInitialized();
|
||||
public: /* SMInterface */
|
||||
unsigned int GetInterfaceVersion();
|
||||
const char *GetInterfaceName();
|
||||
public: /* INativeInvoker */
|
||||
IPluginRuntime *CreateRuntime(const char *name, size_t bytes);
|
||||
INativeInvoker *CreateInvoker();
|
||||
};
|
||||
|
||||
extern NativeInterface g_NInvoke;
|
||||
|
||||
#endif /* _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_ */
|
@ -1175,13 +1175,9 @@ bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size
|
||||
if ((pFunc=pBase->GetFunctionByName(buffer)))
|
||||
{
|
||||
cell_t res;
|
||||
pFunc->Execute(&res);
|
||||
if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
if (pFunc->Execute(&res) != SP_ERROR_NONE) {
|
||||
if (error)
|
||||
{
|
||||
smcore.Format(error, maxlength, "Fatal error during initializing plugin load");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1303,13 +1299,9 @@ bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass
|
||||
if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
|
||||
{
|
||||
cell_t res;
|
||||
pFunc->Execute(&res);
|
||||
if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
if (pFunc->Execute(&res) != SP_ERROR_NONE) {
|
||||
if (error)
|
||||
{
|
||||
smcore.Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -150,10 +150,11 @@ static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
char buffer[1024];
|
||||
size_t len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 1);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
size_t len;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -171,10 +172,11 @@ static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *par
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
char buffer[1024];
|
||||
size_t len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 1);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
size_t len;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -211,10 +213,11 @@ static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(params[1]);
|
||||
|
||||
char buffer[256];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
size_t len;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -240,10 +243,10 @@ static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(params[1]);
|
||||
|
||||
char buffer[256];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -258,10 +261,12 @@ static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
/* Build the format string */
|
||||
char buffer[1024];
|
||||
size_t len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
size_t len;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
@ -130,13 +130,14 @@ void LogAction(Handle_t hndl, int type, int client, int target, const char *mess
|
||||
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
|
||||
if (pContext->GetLastNativeError() == SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->ReportError("%s", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -388,16 +389,16 @@ static cell_t SetFailState(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char buffer[2048];
|
||||
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
if (eh.HasException()) {
|
||||
pPlugin->SetErrorState(Plugin_Failed, "%s", str);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "Formatting error (%s)", str);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPlugin->SetErrorState(Plugin_Failed, "%s", buffer);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
|
||||
pContext->ReportFatalError("%s", buffer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,10 +508,10 @@ static cell_t sm_LogAction(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char buffer[2048];
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -536,14 +537,15 @@ static cell_t LogToFile(IPluginContext *pContext, const cell_t *params)
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
if (eh.HasException()) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
|
||||
|
||||
@ -569,14 +571,15 @@ static cell_t LogToFileEx(IPluginContext *pContext, const cell_t *params)
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
if (eh.HasException()) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(fp, "%s", buffer);
|
||||
|
||||
@ -653,14 +656,20 @@ static cell_t RequireFeature(IPluginContext *pContext, const cell_t *params)
|
||||
char default_message[255];
|
||||
SMPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
|
||||
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE || buffer[0] == '\0')
|
||||
{
|
||||
if (eh.HasException())
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (buffer[0] == '\0') {
|
||||
g_pSM->Format(default_message, sizeof(default_message), "Feature \"%s\" not available", name);
|
||||
msg = default_message;
|
||||
}
|
||||
pPlugin->SetErrorState(Plugin_Error, "%s", msg);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", msg);
|
||||
|
||||
if (!eh.HasException())
|
||||
pContext->ReportFatalError("%s", msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -125,7 +125,6 @@ static cell_t smn_WritePackString(IPluginContext *pContext, const cell_t *params
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
IDataPack *pDataPack;
|
||||
int err;
|
||||
|
||||
sec.pOwner = pContext->GetIdentity();
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
@ -137,12 +136,7 @@ static cell_t smn_WritePackString(IPluginContext *pContext, const cell_t *params
|
||||
}
|
||||
|
||||
char *str;
|
||||
if ((err=pContext->LocalToString(params[2], &str)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->LocalToString(params[2], &str);
|
||||
pDataPack->PackString(str);
|
||||
|
||||
return 1;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet:
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
@ -81,18 +81,12 @@ cell_t FakeNativeRouter(IPluginContext *pContext, const cell_t *params, void *pD
|
||||
s_curparams[i] = params[i];
|
||||
}
|
||||
|
||||
/* Push info and execute. */
|
||||
// Push info and execute. If Invoke() fails, the error will propagate up.
|
||||
// We still carry on below to clear our global state.
|
||||
cell_t result = 0;
|
||||
native->call->PushCell(pCaller->GetMyHandle());
|
||||
native->call->PushCell(params[0]);
|
||||
int error;
|
||||
if ((error=native->call->Execute(&result)) != SP_ERROR_NONE)
|
||||
{
|
||||
if (pContext->GetLastNativeError() == SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(error, "Error encountered while processing a dynamic native");
|
||||
}
|
||||
}
|
||||
native->call->Invoke(&result);
|
||||
|
||||
/* Restore everything from the stack if necessary */
|
||||
s_curnative = pSaveNative;
|
||||
@ -141,15 +135,15 @@ static cell_t ThrowNativeError(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
char buffer[512];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
s_curcaller->ThrowNativeError("Error encountered while processing a dynamic native");
|
||||
} else {
|
||||
s_curcaller->ThrowNativeErrorEx(params[1], "%s", buffer);
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->ReportError("%s", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -402,36 +396,32 @@ static cell_t FormatNativeString(IPluginContext *pContext, const cell_t *params)
|
||||
char *format_buffer;
|
||||
|
||||
if (out_param)
|
||||
{
|
||||
if ((err=s_curcaller->LocalToString(s_curparams[out_param], &output_buffer)) != SP_ERROR_NONE)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
s_curcaller->LocalToString(s_curparams[out_param], &output_buffer);
|
||||
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 {
|
||||
s_curcaller->LocalToString(s_curparams[fmt_param], &format_buffer);
|
||||
else
|
||||
pContext->LocalToString(params[7], &format_buffer);
|
||||
}
|
||||
|
||||
/* Get maximum length */
|
||||
size_t maxlen = (size_t)params[4];
|
||||
|
||||
/* Do the format */
|
||||
size_t written = smcore.atcprintf(output_buffer, maxlen, format_buffer, s_curcaller, s_curparams, &var_param);
|
||||
size_t written;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
written = smcore.atcprintf(output_buffer, maxlen, format_buffer, s_curcaller, s_curparams, &var_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[5], &addr);
|
||||
*addr = (cell_t)written;
|
||||
|
||||
return s_curcaller->GetLastNativeError();
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
//tee hee
|
||||
|
@ -765,9 +765,12 @@ static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
int arg = 3;
|
||||
char buffer[2048];
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
smcore.atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SystemFile *sysfile = file->AsSystemFile()) {
|
||||
fprintf(sysfile->fp(), "%s\n", buffer);
|
||||
@ -797,9 +800,12 @@ static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
|
||||
pContext->LocalToString(params[2], &buffer);
|
||||
pContext->LocalToString(params[4], &fmt);
|
||||
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
smcore.atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g_pSM->BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
|
||||
}
|
||||
@ -808,11 +814,12 @@ static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
size_t len;
|
||||
char buffer[1024];
|
||||
size_t len = g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -835,10 +842,10 @@ static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
char buffer[1024];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -853,10 +860,10 @@ static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
char buffer[1024];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -900,9 +907,12 @@ static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
char buffer[2048];
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
|
||||
g_Logger.LogToOpenFile(sysfile->fp(), "[%s] %s", pPlugin->GetFilename(), buffer);
|
||||
@ -922,9 +932,12 @@ static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
char buffer[2048];
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(sysfile->fp(), "%s", buffer);
|
||||
return 1;
|
||||
|
@ -564,6 +564,7 @@ static cell_t sm_CallFinish(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
pContext->LocalToPhysAddr(params[1], &result);
|
||||
|
||||
// Note: Execute() swallows exceptions, so this is okay.
|
||||
if (s_pFunction)
|
||||
{
|
||||
IPluginFunction *pFunction = s_pFunction;
|
||||
|
@ -1087,10 +1087,11 @@ static cell_t _ShowActivity(IPluginContext *pContext,
|
||||
if (replyto == SM_REPLY_CONSOLE)
|
||||
{
|
||||
g_pSM->SetGlobalTarget(client);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1102,10 +1103,11 @@ static cell_t _ShowActivity(IPluginContext *pContext,
|
||||
else
|
||||
{
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1141,10 +1143,11 @@ static cell_t _ShowActivity(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1165,10 +1168,11 @@ static cell_t _ShowActivity(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1210,10 +1214,10 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
|
||||
}
|
||||
|
||||
g_pSM->SetGlobalTarget(client);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1227,10 +1231,10 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
|
||||
else
|
||||
{
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1266,10 +1270,11 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1290,10 +1295,11 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1350,10 +1356,10 @@ static cell_t KickClient(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(client);
|
||||
|
||||
char buffer[256];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1387,10 +1393,10 @@ static cell_t KickClientEx(IPluginContext *pContext, const cell_t *params)
|
||||
g_pSM->SetGlobalTarget(client);
|
||||
|
||||
char buffer[256];
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -232,6 +232,7 @@ static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
|
||||
if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_CurStringArray = array;
|
||||
@ -282,12 +283,16 @@ struct sort_info
|
||||
cell_t array_addr;
|
||||
cell_t *array_base;
|
||||
cell_t *array_remap;
|
||||
ExceptionHandler *eh;
|
||||
};
|
||||
|
||||
sort_info g_SortInfo;
|
||||
|
||||
int sort1d_amx_custom(const void *elem1, const void *elem2)
|
||||
{
|
||||
if (g_SortInfo.eh->HasException())
|
||||
return 0;
|
||||
|
||||
cell_t c1 = *(cell_t *)elem1;
|
||||
cell_t c2 = *(cell_t *)elem2;
|
||||
|
||||
@ -297,7 +302,7 @@ int sort1d_amx_custom(const void *elem1, const void *elem2)
|
||||
pf->PushCell(c2);
|
||||
pf->PushCell(g_SortInfo.array_addr);
|
||||
pf->PushCell(g_SortInfo.hndl);
|
||||
pf->Execute(&result);
|
||||
pf->Invoke(&result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -317,22 +322,25 @@ static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
sort_info oldinfo = g_SortInfo;
|
||||
|
||||
|
||||
DetectExceptions eh(pContext);
|
||||
g_SortInfo.hndl = params[4];
|
||||
g_SortInfo.array_addr = params[1];
|
||||
g_SortInfo.array_remap = NULL;
|
||||
g_SortInfo.array_base = NULL;
|
||||
g_SortInfo.pFunc = pFunction;
|
||||
g_SortInfo.eh = &eh;
|
||||
|
||||
qsort(array, array_size, sizeof(cell_t), sort1d_amx_custom);
|
||||
|
||||
g_SortInfo = oldinfo;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sort2d_amx_custom(const void *elem1, const void *elem2)
|
||||
{
|
||||
if (g_SortInfo.eh->HasException())
|
||||
return 0;
|
||||
|
||||
cell_t c1 = *(cell_t *)elem1;
|
||||
cell_t c2 = *(cell_t *)elem2;
|
||||
|
||||
@ -349,7 +357,7 @@ int sort2d_amx_custom(const void *elem1, const void *elem2)
|
||||
g_SortInfo.pFunc->PushCell(c2_addr);
|
||||
g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
|
||||
g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
|
||||
g_SortInfo.pFunc->Execute(&result);
|
||||
g_SortInfo.pFunc->Invoke(&result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -378,9 +386,11 @@ static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
sort_info oldinfo = g_SortInfo;
|
||||
|
||||
DetectExceptions eh(pContext);
|
||||
g_SortInfo.pFunc = pFunction;
|
||||
g_SortInfo.hndl = params[4];
|
||||
g_SortInfo.array_addr = params[1];
|
||||
g_SortInfo.eh = &eh;
|
||||
|
||||
/** Same process as in strings, back up the old indices for later fixup */
|
||||
g_SortInfo.array_base = array;
|
||||
@ -511,19 +521,23 @@ struct sort_infoADT
|
||||
cell_t array_bsize;
|
||||
Handle_t array_hndl;
|
||||
Handle_t hndl;
|
||||
ExceptionHandler *eh;
|
||||
};
|
||||
|
||||
sort_infoADT g_SortInfoADT;
|
||||
|
||||
int sort_adtarray_custom(const void *elem1, const void *elem2)
|
||||
{
|
||||
if (g_SortInfoADT.eh->HasException())
|
||||
return 0;
|
||||
|
||||
cell_t result = 0;
|
||||
IPluginFunction *pf = g_SortInfoADT.pFunc;
|
||||
pf->PushCell(((cell_t) ((cell_t *) elem1 - g_SortInfoADT.array_base)) / g_SortInfoADT.array_bsize);
|
||||
pf->PushCell(((cell_t) ((cell_t *) elem2 - g_SortInfoADT.array_base)) / g_SortInfoADT.array_bsize);
|
||||
pf->PushCell(g_SortInfoADT.array_hndl);
|
||||
pf->PushCell(g_SortInfoADT.hndl);
|
||||
pf->Execute(&result);
|
||||
pf->Invoke(&result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -552,11 +566,13 @@ static cell_t sm_SortADTArrayCustom(IPluginContext *pContext, const cell_t *para
|
||||
|
||||
sort_infoADT oldinfo = g_SortInfoADT;
|
||||
|
||||
DetectExceptions eh(pContext);
|
||||
g_SortInfoADT.pFunc = pFunction;
|
||||
g_SortInfoADT.array_base = array;
|
||||
g_SortInfoADT.array_bsize = (cell_t) blocksize;
|
||||
g_SortInfoADT.array_hndl = params[1];
|
||||
g_SortInfoADT.hndl = params[3];
|
||||
g_SortInfoADT.eh = &eh;
|
||||
|
||||
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_custom);
|
||||
|
||||
|
@ -1180,12 +1180,7 @@ reswitch:
|
||||
{
|
||||
CHECK_ARGS(0);
|
||||
char *str;
|
||||
int err;
|
||||
if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, "Could not deference string");
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToString(params[arg], &str);
|
||||
AddString(&buf_p, llen, str, width, prec);
|
||||
arg++;
|
||||
break;
|
||||
|
@ -194,7 +194,6 @@ static cell_t smn_BfWriteString(IPluginContext *pCtx, const cell_t *params)
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
int err;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
@ -206,11 +205,7 @@ static cell_t smn_BfWriteString(IPluginContext *pCtx, const cell_t *params)
|
||||
}
|
||||
|
||||
char *str;
|
||||
if ((err=pCtx->LocalToString(params[2], &str)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToString(params[2], &str);
|
||||
|
||||
pBitBuf->WriteString(str);
|
||||
|
||||
|
@ -909,10 +909,11 @@ static cell_t sm_ServerCommandEx(IPluginContext *pContext, const cell_t *params)
|
||||
g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
char buffer[1024];
|
||||
size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 3);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
size_t len;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -965,10 +966,10 @@ static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params
|
||||
g_SourceMod.SetGlobalTarget(params[1]);
|
||||
|
||||
char buffer[256];
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
|
||||
@ -321,11 +321,11 @@ static cell_t PrintToChat(IPluginContext *pContext, const cell_t *params)
|
||||
g_SourceMod.SetGlobalTarget(client);
|
||||
|
||||
char buffer[192];
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
/* Check for an error before printing to the client */
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -355,11 +355,11 @@ static cell_t PrintCenterText(IPluginContext *pContext, const cell_t *params)
|
||||
g_SourceMod.SetGlobalTarget(client);
|
||||
|
||||
char buffer[192];
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
/* Check for an error before printing to the client */
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -389,11 +389,10 @@ static cell_t PrintHintText(IPluginContext *pContext, const cell_t *params)
|
||||
g_SourceMod.SetGlobalTarget(client);
|
||||
|
||||
char buffer[192];
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
/* Check for an error before printing to the client */
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet:
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
@ -415,9 +415,11 @@ static cell_t ShowSyncHudText(IPluginContext *pContext, const cell_t *params)
|
||||
}
|
||||
|
||||
g_SourceMod.SetGlobalTarget(client);
|
||||
g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -488,9 +490,11 @@ static cell_t ShowHudText(IPluginContext *pContext, const cell_t *params)
|
||||
}
|
||||
|
||||
g_SourceMod.SetGlobalTarget(client);
|
||||
g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -52,13 +52,8 @@
|
||||
|
||||
// Assumes message field name is param 2, gets as strField
|
||||
#define GET_FIELD_NAME_OR_ERR() \
|
||||
int err; \
|
||||
char *strField; \
|
||||
if ((err=pCtx->LocalToString(params[2], &strField)) != SP_ERROR_NONE) \
|
||||
{ \
|
||||
pCtx->ThrowNativeErrorEx(err, NULL); \
|
||||
return 0; \
|
||||
}
|
||||
pCtx->LocalToString(params[2], &strField);
|
||||
|
||||
static cell_t smn_PbReadInt(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
@ -387,11 +382,7 @@ static cell_t smn_PbSetString(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
char *strValue;
|
||||
if ((err=pCtx->LocalToString(params[3], &strValue)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToString(params[3], &strValue);
|
||||
|
||||
int index = params[0] >= 4 ? params[4] : -1;
|
||||
if (index < 0)
|
||||
@ -418,11 +409,7 @@ static cell_t smn_PbSetColor(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *clrParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &clrParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &clrParams);
|
||||
|
||||
Color clr(
|
||||
clrParams[0],
|
||||
@ -455,11 +442,7 @@ static cell_t smn_PbSetAngle(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *angParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &angParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &angParams);
|
||||
|
||||
QAngle ang(
|
||||
sp_ctof(angParams[0]),
|
||||
@ -491,11 +474,7 @@ static cell_t smn_PbSetVector(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *vecParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &vecParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &vecParams);
|
||||
|
||||
Vector vec(
|
||||
sp_ctof(vecParams[0]),
|
||||
@ -527,11 +506,7 @@ static cell_t smn_PbSetVector2D(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *vecParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &vecParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &vecParams);
|
||||
|
||||
Vector2D vec(
|
||||
sp_ctof(vecParams[0]),
|
||||
@ -602,11 +577,7 @@ static cell_t smn_PbAddString(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
char *strValue;
|
||||
if ((err=pCtx->LocalToString(params[3], &strValue)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToString(params[3], &strValue);
|
||||
|
||||
if (!msg->AddString(strField, strValue))
|
||||
{
|
||||
@ -622,11 +593,7 @@ static cell_t smn_PbAddColor(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *clrParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &clrParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &clrParams);
|
||||
|
||||
Color clr(
|
||||
clrParams[0],
|
||||
@ -648,11 +615,7 @@ static cell_t smn_PbAddAngle(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *angParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &angParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &angParams);
|
||||
|
||||
QAngle ang(
|
||||
sp_ctof(angParams[0]),
|
||||
@ -673,11 +636,7 @@ static cell_t smn_PbAddVector(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *vecParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &vecParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &vecParams);
|
||||
|
||||
Vector vec(
|
||||
sp_ctof(vecParams[0]),
|
||||
@ -698,11 +657,7 @@ static cell_t smn_PbAddVector2D(IPluginContext *pCtx, const cell_t *params)
|
||||
GET_FIELD_NAME_OR_ERR();
|
||||
|
||||
cell_t *vecParams;
|
||||
if ((err=pCtx->LocalToPhysAddr(params[3], &vecParams)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
pCtx->LocalToPhysAddr(params[3], &vecParams);
|
||||
|
||||
Vector2D vec(
|
||||
sp_ctof(vecParams[0]),
|
||||
|
@ -290,8 +290,7 @@ DataStatus DecodeValveParam(IPluginContext *pContext,
|
||||
case Valve_Vector:
|
||||
{
|
||||
cell_t *addr;
|
||||
int err;
|
||||
err = pContext->LocalToPhysAddr(param, &addr);
|
||||
pContext->LocalToPhysAddr(param, &addr);
|
||||
|
||||
unsigned char *mem = (unsigned char *)buffer;
|
||||
if (data->type == PassType_Basic)
|
||||
@ -317,12 +316,6 @@ DataStatus DecodeValveParam(IPluginContext *pContext,
|
||||
}
|
||||
}
|
||||
|
||||
if (err != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
|
||||
return Data_Fail;
|
||||
}
|
||||
|
||||
/* Use placement new to initialize the object cleanly
|
||||
* This has no destructor so we don't need to do
|
||||
* DestroyValveParam() or something :]
|
||||
|
@ -1,107 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_INATIVEINVOKER_H_
|
||||
#define _INCLUDE_SOURCEMOD_INATIVEINVOKER_H_
|
||||
|
||||
/**
|
||||
* @file INativeInvoker.h
|
||||
* @brief Interface for invoking natives.
|
||||
*/
|
||||
|
||||
#include <IShareSys.h>
|
||||
#include <sp_vm_api.h>
|
||||
|
||||
#define SMINTERFACE_NINVOKE_NAME "INativeInterface"
|
||||
#define SMINTERFACE_NINVOKE_VERSION 1
|
||||
|
||||
#define NINVOKE_DEFAULT_MEMORY 16384
|
||||
|
||||
namespace SourceMod
|
||||
{
|
||||
class INativeInvoker : public SourcePawn::ICallable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Virtual destructor - use delete to free this.
|
||||
*/
|
||||
virtual ~INativeInvoker()
|
||||
{
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Begins a native call.
|
||||
*
|
||||
* During a call's preparation, no new calls may be started.
|
||||
*
|
||||
* @param pContext Context to invoke native under.
|
||||
* @param name Name of native.
|
||||
* @return True if native was found, false otherwise.
|
||||
*/
|
||||
virtual bool Start(SourcePawn::IPluginContext *pContext, const char *name) = 0;
|
||||
|
||||
/**
|
||||
* @brief Invokes the native. The preparation state is cleared immediately, meaning that
|
||||
* this object can be re-used after or even from inside the native being called.
|
||||
*
|
||||
* @param result Optional pointer to retrieve a result.
|
||||
* @return SP_ERROR return code.
|
||||
*/
|
||||
virtual int Invoke(cell_t *result) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Factory for dealing with native invocations.
|
||||
*/
|
||||
class INativeInterface : public SMInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a virtual plugin. This can be used as an environment to invoke natives.
|
||||
*
|
||||
* IPluginRuntime objects must be freed with the delete operator.
|
||||
*
|
||||
* @param name Name, or NULL for anonymous.
|
||||
* @param bytes Number of bytes for memory (NINVOKE_DEFAULT_MEMORY recommended).
|
||||
* @return New runtime, or NULL on failure.
|
||||
*/
|
||||
virtual SourcePawn::IPluginRuntime *CreateRuntime(const char *name, size_t bytes) = 0;
|
||||
|
||||
/**
|
||||
* @brief Creates an object that can be used to invoke a single native code.
|
||||
*
|
||||
* @return New native invoker (free with delete).
|
||||
*/
|
||||
virtual INativeInvoker *CreateInvoker() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE_SOURCEMOD_INATIVEINVOKER_H_ */
|
@ -85,6 +85,10 @@ class FixedArray : public AllocPolicy
|
||||
data_[index] = t;
|
||||
}
|
||||
|
||||
T *buffer() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
FixedArray(const FixedArray &other) KE_DELETE;
|
||||
FixedArray &operator =(const FixedArray &other) KE_DELETE;
|
||||
|
@ -230,13 +230,24 @@ class Label
|
||||
assert(this->offset() == offset);
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
// Note that 0 as an invalid offset is okay, because the offset we save for
|
||||
// pending jumps are after the jump opcode itself, and therefore 0 is never
|
||||
// valid, since there are no 0-byte jumps.
|
||||
uint32_t status_;
|
||||
};
|
||||
|
||||
// Label that suppresses its assert, for non-stack use.
|
||||
class SilentLabel : public Label
|
||||
{
|
||||
public:
|
||||
SilentLabel()
|
||||
{}
|
||||
~SilentLabel() {
|
||||
status_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// A DataLabel is a special form of Label intended for absolute addresses that
|
||||
// are within the code buffer, and thus aren't known yet, and will be
|
||||
// automatically fixed up when calling emitToExecutableMemory().
|
||||
|
@ -1,4 +1,4 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
// vim: set ts=4 sw=4 tw=99 noet:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
@ -19,15 +19,19 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "sp_vm_types.h"
|
||||
|
||||
/** SourcePawn Engine API Versions */
|
||||
#define SOURCEPAWN_ENGINE2_API_VERSION 9
|
||||
#define SOURCEPAWN_API_VERSION 0x0209
|
||||
#define SOURCEPAWN_ENGINE2_API_VERSION 0xA
|
||||
#define SOURCEPAWN_API_VERSION 0x020A
|
||||
|
||||
namespace SourceMod {
|
||||
struct IdentityToken_t;
|
||||
};
|
||||
namespace sp {
|
||||
class Environment;
|
||||
};
|
||||
|
||||
struct sp_context_s;
|
||||
typedef struct sp_context_s sp_context_t;
|
||||
@ -36,6 +40,8 @@ namespace SourcePawn
|
||||
{
|
||||
class IVirtualMachine;
|
||||
class IPluginRuntime;
|
||||
class ISourcePawnEngine2;
|
||||
class ISourcePawnEnvironment;
|
||||
|
||||
/* Parameter flags */
|
||||
#define SM_PARAM_COPYBACK (1<<0) /**< Copy an array/reference back after call */
|
||||
@ -162,21 +168,23 @@ namespace SourcePawn
|
||||
* @brief Executes the function, resets the pushed parameter list, and
|
||||
* performs any copybacks.
|
||||
*
|
||||
* The exception state is reset upon entering and leaving this
|
||||
* function. Callers that want to propagate exceptions from Execute()
|
||||
* should use Invoke(). ReportError is not preferred since it would
|
||||
* lose any custom exception messages.
|
||||
*
|
||||
* @param result Pointer to store return value in.
|
||||
* @return Error code, if any.
|
||||
*/
|
||||
virtual int Execute(cell_t *result) =0;
|
||||
|
||||
/**
|
||||
* @brief Executes the function with the given parameter array.
|
||||
* Parameters are read in forward order (i.e. index 0 is parameter #1)
|
||||
* NOTE: You will get an error if you attempt to use CallFunction() with
|
||||
* previously pushed parameters.
|
||||
* @brief This function is deprecated. If invoked, it reports an error.
|
||||
*
|
||||
* @param params Array of cell parameters.
|
||||
* @param num_params Number of parameters to push.
|
||||
* @param result Pointer to store result of function on return.
|
||||
* @return SourcePawn error code (if any).
|
||||
* @param params Unused.
|
||||
* @param num_params Unused.
|
||||
* @param result Unused.
|
||||
* @return SP_ERROR_ABORTED.
|
||||
*/
|
||||
virtual int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result) =0;
|
||||
|
||||
@ -204,30 +212,22 @@ namespace SourcePawn
|
||||
virtual funcid_t GetFunctionID() =0;
|
||||
|
||||
/**
|
||||
* @brief Executes the forward, resets the pushed parameter list, and
|
||||
* performs any copybacks.
|
||||
* @brief This function is deprecated. If invoked, it reports an error.
|
||||
*
|
||||
* Note: A function can only be executed given a runtime it was created in.
|
||||
*
|
||||
* @param ctx Context to execute the function in.
|
||||
* @param result Pointer to store return value in.
|
||||
* @return Error code, if any.
|
||||
* @param ctx Unused.
|
||||
* @param result Unused.
|
||||
* @return SP_ERROR_ABORTED.
|
||||
*/
|
||||
virtual int Execute2(IPluginContext *ctx, cell_t *result) =0;
|
||||
|
||||
/**
|
||||
* @brief Executes the function with the given parameter array.
|
||||
* Parameters are read in forward order (i.e. index 0 is parameter #1)
|
||||
* NOTE: You will get an error if you attempt to use CallFunction() with
|
||||
* previously pushed parameters.
|
||||
* @brief This function is deprecated. If invoked, it reports an error.
|
||||
*
|
||||
* Note: A function can only be executed given a runtime it was created in.
|
||||
*
|
||||
* @param ctx Context to execute the function in.
|
||||
* @param params Array of cell parameters.
|
||||
* @param num_params Number of parameters to push.
|
||||
* @param result Pointer to store result of function on return.
|
||||
* @return SourcePawn error code (if any).
|
||||
* @param ctx Unused.
|
||||
* @param params Unused.
|
||||
* @param num_params Unused.
|
||||
* @param result Unused.
|
||||
* @return SP_ERROR_ABORTED.
|
||||
*/
|
||||
virtual int CallFunction2(IPluginContext *ctx,
|
||||
const cell_t *params,
|
||||
@ -240,6 +240,20 @@ namespace SourcePawn
|
||||
* @return IPluginRuntime pointer.
|
||||
*/
|
||||
virtual IPluginRuntime *GetParentRuntime() =0;
|
||||
|
||||
/**
|
||||
* @brief Executes the function, resets the pushed parameter list, and
|
||||
* performs any copybacks.
|
||||
*
|
||||
* Unlike Execute(), this does not reset the exception state. It is
|
||||
* illegal to call Invoke() while an exception is unhandled. If it
|
||||
* returns false, an exception has been thrown, and must either be
|
||||
* handled via ExceptionHandler or propagated to its caller.
|
||||
*
|
||||
* @param result Pointer to store return value in.
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
virtual bool Invoke(cell_t *rval = nullptr) = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -464,6 +478,11 @@ namespace SourcePawn
|
||||
* @return Native pointer, or NULL on failure.
|
||||
*/
|
||||
virtual const sp_native_t *GetNative(uint32_t index) = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the file or location this plugin was loaded from.
|
||||
*/
|
||||
virtual const char *GetFilename() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -734,6 +753,8 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Throws a error and halts any current execution.
|
||||
*
|
||||
* This function is deprecated. Use ReportError() instead.
|
||||
*
|
||||
* @param error The error number to set.
|
||||
* @param msg Custom error message format. NULL to use default.
|
||||
* @param ... Message format arguments, if any.
|
||||
@ -744,6 +765,8 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Throws a generic native error and halts any current execution.
|
||||
*
|
||||
* This function is deprecated. Use ReportError() instead.
|
||||
*
|
||||
* @param msg Custom error message format. NULL to set no message.
|
||||
* @param ... Message format arguments, if any.
|
||||
* @return 0 for convenience.
|
||||
@ -816,14 +839,13 @@ namespace SourcePawn
|
||||
virtual IPluginRuntime *GetRuntime() =0;
|
||||
|
||||
/**
|
||||
* @brief Executes a function in the context. The function must be
|
||||
* a member of the context's parent runtime.
|
||||
* @brief This function is deprecated. If invoked, it reports an error.
|
||||
*
|
||||
* @param function Function.
|
||||
* @param params Parameters.
|
||||
* @param num_params Number of parameters in the parameter array.
|
||||
* @param result Optional pointer to store the result on success.
|
||||
* @return Error code.
|
||||
* @param function Unused.
|
||||
* @param params Unused.
|
||||
* @param num_params Unused.
|
||||
* @param result Unused.
|
||||
* @return SP_ERROR_ABORTED.
|
||||
*/
|
||||
virtual int Execute2(IPluginFunction *function,
|
||||
const cell_t *params,
|
||||
@ -833,8 +855,11 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Returns whether a context is in an error state.
|
||||
*
|
||||
* This function is deprecated. Use DetectExceptions instead.
|
||||
*
|
||||
* This should only be used inside natives to determine whether
|
||||
* a prior call failed.
|
||||
* a prior call failed. The return value should only be compared
|
||||
* against SP_ERROR_NONE.
|
||||
*/
|
||||
virtual int GetLastNativeError() =0;
|
||||
|
||||
@ -870,81 +895,171 @@ namespace SourcePawn
|
||||
virtual bool GetKey(int k, void **value) =0;
|
||||
|
||||
/**
|
||||
* @brief Clears the last native error.
|
||||
* @brief If an exception is pending, this removes the exception. It
|
||||
* is deprecated and should not be used.
|
||||
*/
|
||||
virtual void ClearLastNativeError() =0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Information about a position in a call stack.
|
||||
* @brief Return a pointer to the ISourcePawnEngine2 that is active.
|
||||
* This is a convenience function.
|
||||
*
|
||||
* @return API pointer.
|
||||
*/
|
||||
struct CallStackInfo
|
||||
{
|
||||
const char *filename; /**< NULL if not found */
|
||||
unsigned int line; /**< 0 if not found */
|
||||
const char *function; /**< NULL if not found */
|
||||
virtual ISourcePawnEngine2 *APIv2() = 0;
|
||||
|
||||
/**
|
||||
* @brief Report an error.
|
||||
*
|
||||
* @param message Error message format.
|
||||
* @param ... Formatting arguments.
|
||||
*/
|
||||
virtual void ReportError(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Report an error with variadic arguments.
|
||||
*
|
||||
* @param message Error message format.
|
||||
* @param ap Formatting arguments.
|
||||
*/
|
||||
virtual void ReportErrorVA(const char *fmt, va_list ap) = 0;
|
||||
|
||||
/**
|
||||
* @brief Report a fatal error. Fatal errors cannot be caught by any
|
||||
* exception handler.
|
||||
*
|
||||
* @param message Error message format.
|
||||
* @param ... Formatting arguments.
|
||||
*/
|
||||
virtual void ReportFatalError(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Report a fatal error with variadic arguments. Fatal errors
|
||||
* cannot be caught by any exception handler.
|
||||
*
|
||||
* @param message Error message format.
|
||||
* @param ap Formatting arguments.
|
||||
*/
|
||||
virtual void ReportFatalErrorVA(const char *fmt, va_list ap) = 0;
|
||||
|
||||
/**
|
||||
* @brief Report an error by its builtin number.
|
||||
*
|
||||
* @param number Error number.
|
||||
*/
|
||||
virtual void ReportErrorNumber(int error) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Retrieves error information from a debug hook.
|
||||
* @brief Removed.
|
||||
*/
|
||||
class IContextTrace
|
||||
class IContextTrace;
|
||||
|
||||
/**
|
||||
* @brief Information about a reported error.
|
||||
*/
|
||||
class IErrorReport
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the integer error code.
|
||||
* @brief Return the message of the error report.
|
||||
*
|
||||
* @return Integer error code.
|
||||
* @return Message string.
|
||||
*/
|
||||
virtual int GetErrorCode() =0;
|
||||
virtual const char *Message() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns a string describing the error.
|
||||
* @brief True if the error is fatal and cannot be handled (though
|
||||
* reporting can be suppressed).
|
||||
*
|
||||
* @return Error string.
|
||||
* @return True if fatal, false otherwise.
|
||||
*/
|
||||
virtual const char *GetErrorString() =0;
|
||||
virtual bool IsFatal() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether debug info is available.
|
||||
* @brief Return the plugin context that caused the error.
|
||||
*
|
||||
* @return True if debug info is available, false otherwise.
|
||||
* @return Plugin context.
|
||||
*/
|
||||
virtual bool DebugInfoAvailable() =0;
|
||||
|
||||
/**
|
||||
* @brief Returns a custom error message.
|
||||
*
|
||||
* @return A pointer to a custom error message, or NULL otherwise.
|
||||
*/
|
||||
virtual const char *GetCustomErrorString() =0;
|
||||
|
||||
/**
|
||||
* @brief Returns trace info for a specific point in the backtrace, if any.
|
||||
* The next subsequent call to GetTraceInfo() will return the next item in the call stack.
|
||||
* Calls are retrieved in descending order (i.e. the first item is at the top of the stack/call sequence).
|
||||
*
|
||||
* @param trace An ErrorTraceInfo buffer to store information (NULL to ignore).
|
||||
* @return True if successful, false if there are no more traces.
|
||||
*/
|
||||
virtual bool GetTraceInfo(CallStackInfo *trace) =0;
|
||||
|
||||
/**
|
||||
* @brief Resets the trace to its original position (the call on the top of the stack).
|
||||
*/
|
||||
virtual void ResetTrace() =0;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the name of the last native called.
|
||||
* Returns NULL if there was no native that caused the error.
|
||||
*
|
||||
* @param index Optional pointer to store index.
|
||||
* @return Native name, or NULL if none.
|
||||
*/
|
||||
virtual const char *GetLastNative(uint32_t *index) =0;
|
||||
virtual IPluginContext *Context() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allows inspecting the stack frames of the SourcePawn environment.
|
||||
*
|
||||
* Invoking VM functions while iterating frames will cause the iterator
|
||||
* to become corrupt.
|
||||
*
|
||||
* Frames iterate in most-recent to least-recent order.
|
||||
*/
|
||||
class IFrameIterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Returns whether or not there are more frames to read.
|
||||
*
|
||||
* @return True if there are more frames to read, false otherwise.
|
||||
*/
|
||||
virtual bool Done() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Advances to the next frame.
|
||||
*
|
||||
* Note that the iterator starts at either a valid frame or no frame.
|
||||
*/
|
||||
virtual void Next() = 0;
|
||||
|
||||
/**
|
||||
* @brief Resets the iterator to the top of the stack.
|
||||
*/
|
||||
virtual void Reset() = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the context owning the current frame, if any.
|
||||
*
|
||||
* @return Context, or null.
|
||||
*/
|
||||
virtual IPluginContext *Context() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not the current frame is a native frame. If it
|
||||
* is, line numbers and file paths are not available.
|
||||
*
|
||||
* @return True if a native frame, false otherwise.
|
||||
*/
|
||||
virtual bool IsNativeFrame() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the frame is a scripted frame.
|
||||
*
|
||||
* @return True if a scripted frame, false otherwise.
|
||||
*/
|
||||
virtual bool IsScriptedFrame() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the line number of the current frame, or 0 if none is
|
||||
* available.
|
||||
*
|
||||
* @return Line number on success, 0 on failure.
|
||||
*/
|
||||
virtual unsigned LineNumber() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the function name of the current frame, or null if
|
||||
* none could be computed.
|
||||
*
|
||||
* @return Function name on success, null on failure.
|
||||
*/
|
||||
virtual const char *FunctionName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the file path of the function of the current frame,
|
||||
* or none could be computed.
|
||||
*
|
||||
* @return File path on success, null on failure.
|
||||
*/
|
||||
virtual const char *FilePath() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides callbacks for debug information.
|
||||
@ -953,12 +1068,13 @@ namespace SourcePawn
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Invoked on a context execution error.
|
||||
* @brief No longer invoked.
|
||||
*
|
||||
* @param ctx Context.
|
||||
* @param error Object holding error information and a backtrace.
|
||||
* @param ctx Unused.
|
||||
* @param error Unused.
|
||||
*/
|
||||
virtual void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error) =0;
|
||||
virtual void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Called on debug spew.
|
||||
@ -967,6 +1083,15 @@ namespace SourcePawn
|
||||
* @param fmt Message formatting arguments (printf-style).
|
||||
*/
|
||||
virtual void OnDebugSpew(const char *msg, ...) =0;
|
||||
|
||||
/**
|
||||
* @brief Called when an error is reported and no exception
|
||||
* handler was available.
|
||||
*
|
||||
* @param report Error report object.
|
||||
* @param iter Stack frame iterator.
|
||||
*/
|
||||
virtual void ReportError(const IErrorReport &report, IFrameIterator &iter) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1114,10 +1239,10 @@ namespace SourcePawn
|
||||
virtual void ExecFree(void *address) =0;
|
||||
|
||||
/**
|
||||
* @brief Sets the debug listener. This should only be called once.
|
||||
* If called successively (using manual chaining), only the last function should
|
||||
* attempt to call back into the same plugin. Otherwise, globally cached states
|
||||
* can be accidentally overwritten.
|
||||
* @brief Sets the debug listener.
|
||||
*
|
||||
* This should be called once on application startup. It is
|
||||
* not considered part of the userland API and may change at any time.
|
||||
*
|
||||
* @param listener Pointer to an IDebugListener.
|
||||
* @return Old IDebugListener, or NULL if none.
|
||||
@ -1166,6 +1291,8 @@ namespace SourcePawn
|
||||
virtual void FreePageMemory(void *ptr) =0;
|
||||
};
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
/**
|
||||
* @brief Outlines the interface a Virtual Machine (JIT) must expose
|
||||
*/
|
||||
@ -1228,10 +1355,10 @@ namespace SourcePawn
|
||||
virtual void DestroyFakeNative(SPVM_NATIVE_FUNC func) =0;
|
||||
|
||||
/**
|
||||
* @brief Sets the debug listener. This should only be called once.
|
||||
* If called successively (using manual chaining), only the last function should
|
||||
* attempt to call back into the same plugin. Otherwise, globally cached states
|
||||
* can be accidentally overwritten.
|
||||
* @brief Sets the debug listener.
|
||||
*
|
||||
* This should be called once on application startup. It is
|
||||
* not considered part of the userland API and may change at any time.
|
||||
*
|
||||
* @param listener Pointer to an IDebugListener.
|
||||
* @return Old IDebugListener, or NULL if none.
|
||||
@ -1248,6 +1375,9 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Returns the string representation of an error message.
|
||||
*
|
||||
* This function is deprecated and should not be used. The exception
|
||||
* handling API should be used instead.
|
||||
*
|
||||
* @param err Error code.
|
||||
* @return Error string, or NULL if not found.
|
||||
*/
|
||||
@ -1326,6 +1456,11 @@ namespace SourcePawn
|
||||
* @return New runtime pointer, or NULL on failure.
|
||||
*/
|
||||
virtual IPluginRuntime *LoadBinaryFromFile(const char *file, char *error, size_t maxlength) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the environment.
|
||||
*/
|
||||
virtual ISourcePawnEnvironment *Environment() = 0;
|
||||
};
|
||||
|
||||
// @brief This class is the v3 API for SourcePawn. It provides access to
|
||||
@ -1351,6 +1486,23 @@ namespace SourcePawn
|
||||
// all plugin memory. This should not be called while plugins have
|
||||
// active code running on the stack.
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
// @brief Enters an exception handling scope. This is intended to be
|
||||
// used on the stack and must have a corresponding call to
|
||||
// LeaveExceptionHandlingScope. When in an exception handling scope,
|
||||
// exceptions are not immediately reported. Instead the caller is
|
||||
// responsible for propagation them or clearing them.
|
||||
virtual void EnterExceptionHandlingScope(ExceptionHandler *handler) = 0;
|
||||
|
||||
// @brief Leaves the most recent exception handling scope. The handler
|
||||
// is provided as a sanity check.
|
||||
virtual void LeaveExceptionHandlingScope(ExceptionHandler *handler) = 0;
|
||||
|
||||
// @brief Returns whether or not an exception is currently pending.
|
||||
virtual bool HasPendingException(const ExceptionHandler *handler) = 0;
|
||||
|
||||
// @brief Returns the message of the pending exception.
|
||||
virtual const char *GetPendingExceptionMessage(const ExceptionHandler *handler) = 0;
|
||||
};
|
||||
|
||||
// @brief This class is the entry-point to using SourcePawn from a DLL.
|
||||
@ -1371,6 +1523,96 @@ namespace SourcePawn
|
||||
// @brief A function named "GetSourcePawnFactory" is exported from the
|
||||
// SourcePawn DLL, conforming to the following signature:
|
||||
typedef ISourcePawnFactory *(*GetSourcePawnFactoryFn)(int apiVersion);
|
||||
|
||||
// @brief A helper class for handling exceptions.
|
||||
//
|
||||
// ExceptionHandlers can be used to detect, catch, and re-throw VM errors
|
||||
// within C++ code.
|
||||
//
|
||||
// When throwing errors, SourcePawn creates an exception object. The
|
||||
// exception object is global state. As long as an exception is present,
|
||||
// all scripted code should immediately abort and return to their callers,
|
||||
// all native code should exit, all code should propagate any error states
|
||||
// until the exception is handled.
|
||||
//
|
||||
// In some cases, an error code is not available. For example, if a native
|
||||
// detects an exception, it does not have an error status to propagate. It
|
||||
// should simply return instead. The return value will be ignored; the VM
|
||||
// knows to abort the script.
|
||||
class ExceptionHandler
|
||||
{
|
||||
friend class sp::Environment;
|
||||
|
||||
public:
|
||||
ExceptionHandler(ISourcePawnEngine2 *api)
|
||||
: env_(api->Environment()),
|
||||
catch_(true)
|
||||
{
|
||||
env_->EnterExceptionHandlingScope(this);
|
||||
}
|
||||
ExceptionHandler(IPluginContext *ctx)
|
||||
: env_(ctx->APIv2()->Environment()),
|
||||
catch_(true)
|
||||
{
|
||||
env_->EnterExceptionHandlingScope(this);
|
||||
}
|
||||
~ExceptionHandler()
|
||||
{
|
||||
env_->LeaveExceptionHandlingScope(this);
|
||||
}
|
||||
|
||||
virtual uint32_t ApiVersion() const {
|
||||
return SOURCEPAWN_API_VERSION;
|
||||
}
|
||||
|
||||
// Propagates the exception instead of catching it. After calling this,
|
||||
// no more SourcePawn code should be executed until the exception is
|
||||
// handled. Callers should return immediately.
|
||||
void Rethrow() {
|
||||
assert(catch_ && HasException());
|
||||
catch_ = false;
|
||||
}
|
||||
|
||||
bool HasException() const {
|
||||
return env_->HasPendingException(this);
|
||||
}
|
||||
|
||||
const char *Message() const {
|
||||
return env_->GetPendingExceptionMessage(this);
|
||||
}
|
||||
|
||||
private:
|
||||
// Don't allow heap construction.
|
||||
ExceptionHandler(const ExceptionHandler &other);
|
||||
void operator =(const ExceptionHandler &other);
|
||||
void *operator new(size_t size);
|
||||
void operator delete(void *, size_t);
|
||||
|
||||
private:
|
||||
ISourcePawnEnvironment *env_;
|
||||
ExceptionHandler *next_;
|
||||
|
||||
protected:
|
||||
// If true, the exception will be swallowed.
|
||||
bool catch_;
|
||||
};
|
||||
|
||||
// @brief An implementation of ExceptionHandler that simply collects
|
||||
// whether an exception was thrown.
|
||||
class DetectExceptions : public ExceptionHandler
|
||||
{
|
||||
public:
|
||||
DetectExceptions(ISourcePawnEngine2 *api)
|
||||
: ExceptionHandler(api)
|
||||
{
|
||||
catch_ = false;
|
||||
}
|
||||
DetectExceptions(IPluginContext *ctx)
|
||||
: ExceptionHandler(ctx)
|
||||
{
|
||||
catch_ = false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_VM_API_H_
|
||||
|
@ -88,6 +88,9 @@ typedef uint32_t funcid_t; /**< Function index code */
|
||||
#define SP_ERROR_OUT_OF_MEMORY 28 /**< Out of memory */
|
||||
#define SP_ERROR_INTEGER_OVERFLOW 29 /**< Integer overflow (-INT_MIN / -1) */
|
||||
#define SP_ERROR_TIMEOUT 30 /**< Timeout */
|
||||
#define SP_ERROR_USER 31 /**< Custom message */
|
||||
#define SP_ERROR_FATAL 32 /**< Custom fatal message */
|
||||
#define SP_MAX_ERROR_CODES 33
|
||||
//Hey you! Update the string table if you add to the end of me! */
|
||||
|
||||
/**********************************************
|
||||
|
@ -36,13 +36,13 @@ library.sources += [
|
||||
'code-allocator.cpp',
|
||||
'code-stubs.cpp',
|
||||
'compiled-function.cpp',
|
||||
'debug-trace.cpp',
|
||||
'environment.cpp',
|
||||
'file-utils.cpp',
|
||||
'opcodes.cpp',
|
||||
'plugin-context.cpp',
|
||||
'plugin-runtime.cpp',
|
||||
'scripted-invoker.cpp',
|
||||
'stack-frames.cpp',
|
||||
'smx-v1-image.cpp',
|
||||
'watchdog_timer.cpp',
|
||||
'x86/code-stubs-x86.cpp',
|
||||
|
@ -178,14 +178,10 @@ SourcePawnEngine2::SourcePawnEngine2()
|
||||
{
|
||||
}
|
||||
|
||||
static size_t
|
||||
UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
||||
size_t
|
||||
sp::UTIL_FormatVA(char *buffer, size_t maxlength, const char *fmt, va_list ap)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
size_t len = vsnprintf(buffer, maxlength, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len >= maxlength) {
|
||||
buffer[maxlength - 1] = '\0';
|
||||
@ -194,6 +190,18 @@ UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t
|
||||
sp::UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
size_t len = UTIL_FormatVA(buffer, maxlength, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
IPluginRuntime *
|
||||
SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err)
|
||||
{
|
||||
@ -256,13 +264,13 @@ SourcePawnEngine2::LoadBinaryFromFile(const char *file, char *error, size_t maxl
|
||||
# endif
|
||||
)
|
||||
{
|
||||
pRuntime->SetName(&file[i + 1]);
|
||||
pRuntime->SetNames(file, &file[i + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pRuntime->Name())
|
||||
pRuntime->SetName(file);
|
||||
pRuntime->SetNames(file, file);
|
||||
|
||||
return pRuntime;
|
||||
}
|
||||
@ -341,7 +349,9 @@ SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rt->SetName(name != NULL ? name : "<anonymous>");
|
||||
if (!name)
|
||||
name = "<anonymous>";
|
||||
rt->SetNames(name, name);
|
||||
return rt;
|
||||
}
|
||||
|
||||
@ -387,3 +397,9 @@ SourcePawnEngine2::SetProfilingTool(IProfilingTool *tool)
|
||||
{
|
||||
Environment::get()->SetProfiler(tool);
|
||||
}
|
||||
|
||||
ISourcePawnEnvironment *
|
||||
SourcePawnEngine2::Environment()
|
||||
{
|
||||
return Environment::get();
|
||||
}
|
||||
|
@ -68,8 +68,12 @@ class SourcePawnEngine2 : public ISourcePawnEngine2
|
||||
void DisableProfiling() KE_OVERRIDE;
|
||||
void SetProfilingTool(IProfilingTool *tool) KE_OVERRIDE;
|
||||
IPluginRuntime *LoadBinaryFromFile(const char *file, char *error, size_t maxlength) KE_OVERRIDE;
|
||||
ISourcePawnEnvironment *Environment() KE_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace SourcePawn
|
||||
extern size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
|
||||
extern size_t UTIL_FormatVA(char *buffer, size_t maxlength, const char *fmt, va_list ap);
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif // _include_sourcepawn_vm_api_h_
|
||||
|
@ -18,8 +18,7 @@ using namespace sp;
|
||||
CodeStubs::CodeStubs(Environment *env)
|
||||
: env_(env),
|
||||
invoke_stub_(nullptr),
|
||||
return_stub_(nullptr),
|
||||
timeout_stub_(nullptr)
|
||||
return_stub_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,6 @@ class CodeStubs
|
||||
void *ReturnStub() const {
|
||||
return return_stub_;
|
||||
}
|
||||
void *TimeoutStub() const {
|
||||
return return_stub_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool InitializeFeatureDetection();
|
||||
@ -52,7 +49,6 @@ class CodeStubs
|
||||
Environment *env_;
|
||||
void *invoke_stub_;
|
||||
void *return_stub_; // Owned by invoke_stub_.
|
||||
void *timeout_stub_; // Owned by invoke_stub_.
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,10 +16,15 @@
|
||||
|
||||
using namespace sp;
|
||||
|
||||
CompiledFunction::CompiledFunction(void *entry_addr, cell_t pcode_offs, FixedArray<LoopEdge> *edges)
|
||||
CompiledFunction::CompiledFunction(void *entry_addr, size_t code_length,
|
||||
cell_t pcode_offs,
|
||||
FixedArray<LoopEdge> *edges,
|
||||
FixedArray<CipMapEntry> *cipmap)
|
||||
: entry_(entry_addr),
|
||||
code_length_(code_length),
|
||||
code_offset_(pcode_offs),
|
||||
edges_(edges)
|
||||
edges_(edges),
|
||||
cip_map_(cipmap)
|
||||
{
|
||||
}
|
||||
|
||||
@ -27,3 +32,40 @@ CompiledFunction::~CompiledFunction()
|
||||
{
|
||||
Environment::get()->FreeCode(entry_);
|
||||
}
|
||||
|
||||
static int cip_map_entry_cmp(const void *a1, const void *aEntry)
|
||||
{
|
||||
uint32_t pcoffs = (uint32_t)a1;
|
||||
const CipMapEntry *entry = reinterpret_cast<const CipMapEntry *>(aEntry);
|
||||
if (pcoffs < entry->pcoffs)
|
||||
return -1;
|
||||
if (pcoffs == entry->pcoffs)
|
||||
return 0;
|
||||
return pcoffs > entry->pcoffs;
|
||||
}
|
||||
|
||||
ucell_t
|
||||
CompiledFunction::FindCipByPc(void *pc)
|
||||
{
|
||||
if (uintptr_t(pc) < uintptr_t(entry_))
|
||||
return kInvalidCip;
|
||||
|
||||
uint32_t pcoffs = intptr_t(pc) - intptr_t(entry_);
|
||||
if (pcoffs > code_length_)
|
||||
return kInvalidCip;
|
||||
|
||||
void *ptr = bsearch(
|
||||
(void *)pcoffs,
|
||||
cip_map_->buffer(),
|
||||
cip_map_->length(),
|
||||
sizeof(CipMapEntry),
|
||||
cip_map_entry_cmp);
|
||||
assert(ptr);
|
||||
|
||||
if (!ptr) {
|
||||
// Shouldn't happen, but fail gracefully.
|
||||
return kInvalidCip;
|
||||
}
|
||||
|
||||
return code_offset_ + reinterpret_cast<CipMapEntry *>(ptr)->cipoffs;
|
||||
}
|
||||
|
@ -23,14 +23,31 @@ using namespace ke;
|
||||
|
||||
struct LoopEdge
|
||||
{
|
||||
// Offset to the patchable jump instruction, such that (base + offset - 4)
|
||||
// yields a patchable location.
|
||||
uint32_t offset;
|
||||
// The displacement to either the timeout routine or the original
|
||||
// displacement, depending on the timeout state.
|
||||
int32_t disp32;
|
||||
};
|
||||
|
||||
struct CipMapEntry {
|
||||
// Offset from the first cip of the function.
|
||||
uint32_t cipoffs;
|
||||
// Offset from the first pc of the function.
|
||||
uint32_t pcoffs;
|
||||
};
|
||||
|
||||
static const ucell_t kInvalidCip = 0xffffffff;
|
||||
|
||||
class CompiledFunction
|
||||
{
|
||||
public:
|
||||
CompiledFunction(void *entry_addr, cell_t pcode_offs, FixedArray<LoopEdge> *edges);
|
||||
CompiledFunction(void *entry_addr,
|
||||
size_t code_length,
|
||||
cell_t pcode_offs,
|
||||
FixedArray<LoopEdge> *edges,
|
||||
FixedArray<CipMapEntry> *cip_map);
|
||||
~CompiledFunction();
|
||||
|
||||
public:
|
||||
@ -43,14 +60,18 @@ class CompiledFunction
|
||||
uint32_t NumLoopEdges() const {
|
||||
return edges_->length();
|
||||
}
|
||||
const LoopEdge &GetLoopEdge(size_t i) const {
|
||||
LoopEdge &GetLoopEdge(size_t i) {
|
||||
return edges_->at(i);
|
||||
}
|
||||
|
||||
ucell_t FindCipByPc(void *pc);
|
||||
|
||||
private:
|
||||
void *entry_;
|
||||
size_t code_length_;
|
||||
cell_t code_offset_;
|
||||
AutoPtr<FixedArray<LoopEdge>> edges_;
|
||||
AutoPtr<FixedArray<CipMapEntry>> cip_map_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,123 +0,0 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
#include "debug-trace.h"
|
||||
#include "plugin-context.h"
|
||||
#include "environment.h"
|
||||
#include "plugin-runtime.h"
|
||||
|
||||
using namespace ke;
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
|
||||
CContextTrace::CContextTrace(PluginRuntime *pRuntime, int err, const char *errstr, cell_t start_rp)
|
||||
: m_pRuntime(pRuntime),
|
||||
context_(pRuntime->GetBaseContext()),
|
||||
m_Error(err),
|
||||
m_pMsg(errstr),
|
||||
m_StartRp(start_rp),
|
||||
m_Level(0)
|
||||
{
|
||||
m_pDebug = m_pRuntime->GetDebugInfo();
|
||||
}
|
||||
|
||||
bool
|
||||
CContextTrace::DebugInfoAvailable()
|
||||
{
|
||||
return (m_pDebug != NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
CContextTrace::GetCustomErrorString()
|
||||
{
|
||||
return m_pMsg;
|
||||
}
|
||||
|
||||
int
|
||||
CContextTrace::GetErrorCode()
|
||||
{
|
||||
return m_Error;
|
||||
}
|
||||
|
||||
const char *
|
||||
CContextTrace::GetErrorString()
|
||||
{
|
||||
return Environment::get()->GetErrorString(m_Error);
|
||||
}
|
||||
|
||||
void
|
||||
CContextTrace::ResetTrace()
|
||||
{
|
||||
m_Level = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
CContextTrace::GetTraceInfo(CallStackInfo *trace)
|
||||
{
|
||||
cell_t cip;
|
||||
|
||||
if (m_Level == 0) {
|
||||
cip = context_->cip();
|
||||
} else if (context_->rp() > 0) {
|
||||
/* Entries go from ctx.rp - 1 to m_StartRp */
|
||||
cell_t offs, start, end;
|
||||
|
||||
offs = m_Level - 1;
|
||||
start = context_->rp() - 1;
|
||||
end = m_StartRp;
|
||||
|
||||
if (start - offs < end)
|
||||
return false;
|
||||
|
||||
cip = context_->getReturnStackCip(start - offs);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trace == NULL) {
|
||||
m_Level++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_pDebug->LookupFile(cip, &(trace->filename)) != SP_ERROR_NONE)
|
||||
trace->filename = NULL;
|
||||
|
||||
if (m_pDebug->LookupFunction(cip, &(trace->function)) != SP_ERROR_NONE)
|
||||
trace->function = NULL;
|
||||
|
||||
if (m_pDebug->LookupLine(cip, &(trace->line)) != SP_ERROR_NONE)
|
||||
trace->line = 0;
|
||||
|
||||
m_Level++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *
|
||||
CContextTrace::GetLastNative(uint32_t *index)
|
||||
{
|
||||
if (context_->GetLastNativeError() == SP_ERROR_NONE)
|
||||
return NULL;
|
||||
|
||||
int lastNative = context_->lastNative();
|
||||
if (lastNative < 0)
|
||||
return NULL;
|
||||
|
||||
const sp_native_t *native = m_pRuntime->GetNative(lastNative);
|
||||
if (!native)
|
||||
return NULL;
|
||||
|
||||
if (index)
|
||||
*index = lastNative;
|
||||
|
||||
return native->name;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
#ifndef _include_sourcepawn_vm_debug_trace_h_
|
||||
#define _include_sourcepawn_vm_debug_trace_h_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
class PluginRuntime;
|
||||
class PluginContext;
|
||||
|
||||
class CContextTrace : public IContextTrace
|
||||
{
|
||||
public:
|
||||
CContextTrace(PluginRuntime *pRuntime, int err, const char *errstr, cell_t start_rp);
|
||||
|
||||
public:
|
||||
int GetErrorCode();
|
||||
const char *GetErrorString();
|
||||
bool DebugInfoAvailable();
|
||||
const char *GetCustomErrorString();
|
||||
bool GetTraceInfo(CallStackInfo *trace);
|
||||
void ResetTrace();
|
||||
const char *GetLastNative(uint32_t *index);
|
||||
|
||||
private:
|
||||
PluginRuntime *m_pRuntime;
|
||||
PluginContext *context_;
|
||||
int m_Error;
|
||||
const char *m_pMsg;
|
||||
cell_t m_StartRp;
|
||||
cell_t m_Level;
|
||||
IPluginDebugInfo *m_pDebug;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _include_sourcepawn_vm_debug_trace_h_
|
@ -35,6 +35,7 @@
|
||||
#include <am-utility.h> // Replace with am-cxx later.
|
||||
#include "dll_exports.h"
|
||||
#include "environment.h"
|
||||
#include "stack-frames.h"
|
||||
|
||||
using namespace ke;
|
||||
using namespace sp;
|
||||
@ -55,72 +56,36 @@ public:
|
||||
} sFactory;
|
||||
|
||||
#ifdef SPSHELL
|
||||
template <typename T> class AutoT
|
||||
{
|
||||
public:
|
||||
AutoT(T *t)
|
||||
: t_(t)
|
||||
{
|
||||
}
|
||||
~AutoT()
|
||||
{
|
||||
delete t_;
|
||||
}
|
||||
|
||||
operator T *() const {
|
||||
return t_;
|
||||
}
|
||||
bool operator !() const {
|
||||
return !t_;
|
||||
}
|
||||
T * operator ->() const {
|
||||
return t_;
|
||||
}
|
||||
private:
|
||||
T *t_;
|
||||
};
|
||||
|
||||
Environment *sEnv;
|
||||
|
||||
static void
|
||||
DumpStack(IFrameIterator &iter)
|
||||
{
|
||||
int index = 0;
|
||||
for (; !iter.Done(); iter.Next(), index++) {
|
||||
const char *name = iter.FunctionName();
|
||||
if (!name) {
|
||||
fprintf(stdout, " [%d] <unknown>\n", index);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter.IsScriptedFrame()) {
|
||||
const char *file = iter.FilePath();
|
||||
if (!file)
|
||||
file = "<unknown>";
|
||||
fprintf(stdout, " [%d] %s::%s, line %d\n", index, file, name, iter.LineNumber());
|
||||
} else {
|
||||
fprintf(stdout, " [%d] %s()\n", index, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ShellDebugListener : public IDebugListener
|
||||
{
|
||||
public:
|
||||
void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error) {
|
||||
int n_err = error->GetErrorCode();
|
||||
|
||||
if (n_err != SP_ERROR_NATIVE)
|
||||
{
|
||||
fprintf(stderr, "plugin error: %s\n", error->GetErrorString());
|
||||
}
|
||||
|
||||
if (const char *lastname = error->GetLastNative(NULL))
|
||||
{
|
||||
if (const char *custerr = error->GetCustomErrorString())
|
||||
{
|
||||
fprintf(stderr, "Native \"%s\" reported: %s", lastname, custerr);
|
||||
} else {
|
||||
fprintf(stderr, "Native \"%s\" encountered a generic error.", lastname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error->DebugInfoAvailable())
|
||||
{
|
||||
fprintf(stderr, "Debug info not available!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
CallStackInfo stk_info;
|
||||
int i = 0;
|
||||
fprintf(stderr, "Displaying call stack trace:\n");
|
||||
while (error->GetTraceInfo(&stk_info))
|
||||
{
|
||||
fprintf(stderr,
|
||||
" [%d] Line %d, %s::%s()\n",
|
||||
i++,
|
||||
stk_info.line,
|
||||
stk_info.filename,
|
||||
stk_info.function);
|
||||
}
|
||||
void ReportError(const IErrorReport &report, IFrameIterator &iter) KE_OVERRIDE {
|
||||
fprintf(stdout, "Exception thrown: %s\n", report.Message());
|
||||
DumpStack(iter);
|
||||
}
|
||||
|
||||
void OnDebugSpew(const char *msg, ...) {
|
||||
@ -181,18 +146,43 @@ static cell_t PrintFloat(IPluginContext *cx, const cell_t *params)
|
||||
return printf("%f\n", sp_ctof(params[1]));
|
||||
}
|
||||
|
||||
static cell_t DoExecute(IPluginContext *cx, const cell_t *params)
|
||||
{
|
||||
int32_t ok = 0;
|
||||
for (size_t i = 0; i < size_t(params[2]); i++) {
|
||||
if (IPluginFunction *fn = cx->GetFunctionById(params[1])) {
|
||||
if (fn->Execute(nullptr) != SP_ERROR_NONE)
|
||||
continue;
|
||||
ok++;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static cell_t DoInvoke(IPluginContext *cx, const cell_t *params)
|
||||
{
|
||||
for (size_t i = 0; i < size_t(params[2]); i++) {
|
||||
if (IPluginFunction *fn = cx->GetFunctionById(params[1])) {
|
||||
if (!fn->Invoke())
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t DumpStackTrace(IPluginContext *cx, const cell_t *params)
|
||||
{
|
||||
FrameIterator iter;
|
||||
DumpStack(iter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Execute(const char *file)
|
||||
{
|
||||
ICompilation *co = sEnv->APIv2()->StartCompilation();
|
||||
if (!co) {
|
||||
fprintf(stderr, "Could not create a compilation context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int err;
|
||||
AutoT<IPluginRuntime> rt(sEnv->APIv2()->LoadPlugin(co, file, &err));
|
||||
char error[255];
|
||||
AutoPtr<IPluginRuntime> rt(sEnv->APIv2()->LoadBinaryFromFile(file, error, sizeof(error)));
|
||||
if (!rt) {
|
||||
fprintf(stderr, "Could not load plugin: %s\n", sEnv->GetErrorString(err));
|
||||
fprintf(stderr, "Could not load plugin: %s\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -201,6 +191,9 @@ static int Execute(const char *file)
|
||||
BindNative(rt, "printnums", PrintNums);
|
||||
BindNative(rt, "printfloat", PrintFloat);
|
||||
BindNative(rt, "donothing", DoNothing);
|
||||
BindNative(rt, "execute", DoExecute);
|
||||
BindNative(rt, "invoke", DoInvoke);
|
||||
BindNative(rt, "dump_stack_trace", DumpStackTrace);
|
||||
|
||||
IPluginFunction *fun = rt->GetFunctionByName("main");
|
||||
if (!fun)
|
||||
@ -208,11 +201,14 @@ static int Execute(const char *file)
|
||||
|
||||
IPluginContext *cx = rt->GetDefaultContext();
|
||||
|
||||
int result = fun->Execute2(cx, &err);
|
||||
if (err != SP_ERROR_NONE) {
|
||||
fprintf(stderr, "Error executing main(): %s\n", sEnv->GetErrorString(err));
|
||||
int result;
|
||||
{
|
||||
ExceptionHandler eh(cx);
|
||||
if (!fun->Invoke(&result)) {
|
||||
fprintf(stderr, "Error executing main: %s\n", eh.Message());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "environment.h"
|
||||
#include "x86/jit_x86.h"
|
||||
#include "watchdog_timer.h"
|
||||
#include "debug-trace.h"
|
||||
#include "api.h"
|
||||
#include "code-stubs.h"
|
||||
#include "watchdog_timer.h"
|
||||
@ -25,10 +24,12 @@ static Environment *sEnvironment = nullptr;
|
||||
|
||||
Environment::Environment()
|
||||
: debugger_(nullptr),
|
||||
exception_code_(SP_ERROR_NONE),
|
||||
profiler_(nullptr),
|
||||
jit_enabled_(true),
|
||||
profiling_enabled_(false),
|
||||
code_pool_(nullptr)
|
||||
code_pool_(nullptr),
|
||||
top_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -150,7 +151,9 @@ static const char *sErrorMsgTable[] =
|
||||
"Plugin format is too new",
|
||||
"Out of memory",
|
||||
"Integer overflow",
|
||||
"Script execution timed out"
|
||||
"Script execution timed out",
|
||||
"Custom error",
|
||||
"Fatal error"
|
||||
};
|
||||
|
||||
const char *
|
||||
@ -161,17 +164,6 @@ Environment::GetErrorString(int error)
|
||||
return sErrorMsgTable[error];
|
||||
}
|
||||
|
||||
void
|
||||
Environment::ReportError(PluginRuntime *runtime, int err, const char *errstr, cell_t rp_start)
|
||||
{
|
||||
if (!debugger_)
|
||||
return;
|
||||
|
||||
CContextTrace trace(runtime, err, errstr, rp_start);
|
||||
|
||||
debugger_->OnContextExecuteError(runtime->GetDefaultContext(), &trace);
|
||||
}
|
||||
|
||||
void *
|
||||
Environment::AllocateCode(size_t size)
|
||||
{
|
||||
@ -198,6 +190,15 @@ Environment::DeregisterRuntime(PluginRuntime *rt)
|
||||
runtimes_.remove(rt);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SwapLoopEdge(uint8_t *code, LoopEdge &e)
|
||||
{
|
||||
int32_t *loc = reinterpret_cast<int32_t *>(code + e.offset - 4);
|
||||
int32_t new_disp32 = e.disp32;
|
||||
e.disp32 = *loc;
|
||||
*loc = new_disp32;
|
||||
}
|
||||
|
||||
void
|
||||
Environment::PatchAllJumpsForTimeout()
|
||||
{
|
||||
@ -208,11 +209,8 @@ Environment::PatchAllJumpsForTimeout()
|
||||
CompiledFunction *fun = rt->GetJitFunction(i);
|
||||
uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
|
||||
|
||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
||||
const LoopEdge &e = fun->GetLoopEdge(j);
|
||||
int32_t diff = intptr_t(code_stubs_->TimeoutStub()) - intptr_t(base + e.offset);
|
||||
*reinterpret_cast<int32_t *>(base + e.offset - 4) = diff;
|
||||
}
|
||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++)
|
||||
SwapLoopEdge(base, fun->GetLoopEdge(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,10 +225,8 @@ Environment::UnpatchAllJumpsFromTimeout()
|
||||
CompiledFunction *fun = rt->GetJitFunction(i);
|
||||
uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
|
||||
|
||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
||||
const LoopEdge &e = fun->GetLoopEdge(j);
|
||||
*reinterpret_cast<int32_t *>(base + e.offset - 4) = e.disp32;
|
||||
}
|
||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++)
|
||||
SwapLoopEdge(base, fun->GetLoopEdge(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,16 +234,168 @@ Environment::UnpatchAllJumpsFromTimeout()
|
||||
int
|
||||
Environment::Invoke(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result)
|
||||
{
|
||||
// Must be in an invoke frame.
|
||||
assert(top_ && top_->cx() == runtime->GetBaseContext());
|
||||
|
||||
PluginContext *cx = runtime->GetBaseContext();
|
||||
|
||||
// Note that cip, hp, sp are saved and restored by Execute2().
|
||||
*cx->addressOfCip() = fn->GetCodeOffset();
|
||||
|
||||
InvokeStubFn invoke = code_stubs_->InvokeStub();
|
||||
|
||||
EnterInvoke();
|
||||
int err = invoke(cx, fn->GetEntryAddress(), result);
|
||||
LeaveInvoke();
|
||||
|
||||
return err;
|
||||
return invoke(cx, fn->GetEntryAddress(), result);
|
||||
}
|
||||
|
||||
void
|
||||
Environment::ReportError(int code)
|
||||
{
|
||||
const char *message = GetErrorString(code);
|
||||
if (!message) {
|
||||
char buffer[255];
|
||||
UTIL_Format(buffer, sizeof(buffer), "Unknown error code %d", code);
|
||||
ReportError(code, buffer);
|
||||
} else {
|
||||
ReportError(code, message);
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorReport : public SourcePawn::IErrorReport
|
||||
{
|
||||
public:
|
||||
ErrorReport(int code, const char *message, PluginContext *cx)
|
||||
: code_(code),
|
||||
message_(message),
|
||||
context_(cx)
|
||||
{}
|
||||
|
||||
const char *Message() const KE_OVERRIDE {
|
||||
return message_;
|
||||
}
|
||||
bool IsFatal() const KE_OVERRIDE {
|
||||
switch (code_) {
|
||||
case SP_ERROR_HEAPLOW:
|
||||
case SP_ERROR_INVALID_ADDRESS:
|
||||
case SP_ERROR_STACKLOW:
|
||||
case SP_ERROR_INVALID_INSTRUCTION:
|
||||
case SP_ERROR_MEMACCESS:
|
||||
case SP_ERROR_STACKMIN:
|
||||
case SP_ERROR_HEAPMIN:
|
||||
case SP_ERROR_INSTRUCTION_PARAM:
|
||||
case SP_ERROR_STACKLEAK:
|
||||
case SP_ERROR_HEAPLEAK:
|
||||
case SP_ERROR_TRACKER_BOUNDS:
|
||||
case SP_ERROR_PARAMS_MAX:
|
||||
case SP_ERROR_ABORTED:
|
||||
case SP_ERROR_OUT_OF_MEMORY:
|
||||
case SP_ERROR_FATAL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
IPluginContext *Context() const KE_OVERRIDE {
|
||||
return context_;
|
||||
}
|
||||
|
||||
private:
|
||||
int code_;
|
||||
const char *message_;
|
||||
PluginContext *context_;
|
||||
};
|
||||
|
||||
void
|
||||
Environment::ReportErrorVA(const char *fmt, va_list ap)
|
||||
{
|
||||
ReportErrorVA(SP_ERROR_USER, fmt, ap);
|
||||
}
|
||||
|
||||
void
|
||||
Environment::ReportErrorVA(int code, const char *fmt, va_list ap)
|
||||
{
|
||||
// :TODO: right-size the string rather than rely on this buffer.
|
||||
char buffer[1024];
|
||||
UTIL_FormatVA(buffer, sizeof(buffer), fmt, ap);
|
||||
ReportError(code, buffer);
|
||||
}
|
||||
|
||||
void
|
||||
Environment::ReportErrorFmt(int code, const char *message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
ReportErrorVA(code, message, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
Environment::ReportError(int code, const char *message)
|
||||
{
|
||||
FrameIterator iter;
|
||||
ErrorReport report(code, message, top_ ? top_->cx() : nullptr);
|
||||
|
||||
// If this fires, someone forgot to propagate an error.
|
||||
assert(!hasPendingException());
|
||||
|
||||
// Save the exception state.
|
||||
if (eh_top_) {
|
||||
exception_code_ = code;
|
||||
UTIL_Format(exception_message_, sizeof(exception_message_), "%s", message);
|
||||
}
|
||||
|
||||
// For now, we always report exceptions even if they might be handled.
|
||||
if (debugger_)
|
||||
debugger_->ReportError(report, iter);
|
||||
}
|
||||
|
||||
void
|
||||
Environment::EnterExceptionHandlingScope(ExceptionHandler *handler)
|
||||
{
|
||||
handler->next_ = eh_top_;
|
||||
eh_top_ = handler;
|
||||
}
|
||||
|
||||
void
|
||||
Environment::LeaveExceptionHandlingScope(ExceptionHandler *handler)
|
||||
{
|
||||
assert(handler == eh_top_);
|
||||
eh_top_ = eh_top_->next_;
|
||||
|
||||
// To preserve compatibility with older API, we clear the exception state
|
||||
// when there is no EH handler.
|
||||
if (!eh_top_ || handler->catch_)
|
||||
exception_code_ = SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
bool
|
||||
Environment::HasPendingException(const ExceptionHandler *handler)
|
||||
{
|
||||
// Note here and elsewhere - this is not a sanity assert. In the future, the
|
||||
// API may need to query the handler.
|
||||
assert(handler == eh_top_);
|
||||
return hasPendingException();
|
||||
}
|
||||
|
||||
const char *
|
||||
Environment::GetPendingExceptionMessage(const ExceptionHandler *handler)
|
||||
{
|
||||
// Note here and elsewhere - this is not a sanity assert. In the future, the
|
||||
// API may need to query the handler.
|
||||
assert(handler == eh_top_);
|
||||
assert(HasPendingException(handler));
|
||||
return exception_message_;
|
||||
}
|
||||
|
||||
bool
|
||||
Environment::hasPendingException() const
|
||||
{
|
||||
return exception_code_ != SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
Environment::clearPendingException()
|
||||
{
|
||||
exception_code_ = SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
Environment::getPendingExceptionCode() const
|
||||
{
|
||||
return exception_code_;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <am-thread-utils.h>
|
||||
#include "code-allocator.h"
|
||||
#include "plugin-runtime.h"
|
||||
#include "stack-frames.h"
|
||||
|
||||
namespace sp {
|
||||
|
||||
@ -54,9 +55,18 @@ class Environment : public ISourcePawnEnvironment
|
||||
|
||||
bool InstallWatchdogTimer(int timeout_ms);
|
||||
|
||||
void EnterExceptionHandlingScope(ExceptionHandler *handler) KE_OVERRIDE;
|
||||
void LeaveExceptionHandlingScope(ExceptionHandler *handler) KE_OVERRIDE;
|
||||
bool HasPendingException(const ExceptionHandler *handler) KE_OVERRIDE;
|
||||
const char *GetPendingExceptionMessage(const ExceptionHandler *handler) KE_OVERRIDE;
|
||||
|
||||
// Runtime functions.
|
||||
const char *GetErrorString(int err);
|
||||
void ReportError(PluginRuntime *runtime, int err, const char *errstr, cell_t rp_start);
|
||||
void ReportError(int code);
|
||||
void ReportError(int code, const char *message);
|
||||
void ReportErrorFmt(int code, const char *message, ...);
|
||||
void ReportErrorVA(const char *fmt, va_list ap);
|
||||
void ReportErrorVA(int code, const char *fmt, va_list ap);
|
||||
|
||||
// Allocate and free executable memory.
|
||||
void *AllocateCode(size_t size);
|
||||
@ -104,19 +114,40 @@ class Environment : public ISourcePawnEnvironment
|
||||
return watchdog_timer_;
|
||||
}
|
||||
|
||||
bool hasPendingException() const;
|
||||
void clearPendingException();
|
||||
int getPendingExceptionCode() const;
|
||||
|
||||
// These are indicators used for the watchdog timer.
|
||||
uintptr_t FrameId() const {
|
||||
return frame_id_;
|
||||
}
|
||||
bool RunningCode() const {
|
||||
return invoke_depth_ != 0;
|
||||
return !!top_;
|
||||
}
|
||||
void EnterInvoke() {
|
||||
if (invoke_depth_++ == 0)
|
||||
void enterInvoke(InvokeFrame *frame) {
|
||||
if (!top_)
|
||||
frame_id_++;
|
||||
top_ = frame;
|
||||
}
|
||||
void LeaveInvoke() {
|
||||
invoke_depth_--;
|
||||
void leaveInvoke() {
|
||||
exit_frame_ = top_->prev_exit_frame();
|
||||
top_ = top_->prev();
|
||||
}
|
||||
|
||||
InvokeFrame *top() const {
|
||||
return top_;
|
||||
}
|
||||
const ExitFrame &exit_frame() const {
|
||||
return exit_frame_;
|
||||
}
|
||||
|
||||
public:
|
||||
static inline size_t offsetOfTopFrame() {
|
||||
return offsetof(Environment, top_);
|
||||
}
|
||||
static inline size_t offsetOfExceptionCode() {
|
||||
return offsetof(Environment, exception_code_);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -129,6 +160,10 @@ class Environment : public ISourcePawnEnvironment
|
||||
ke::Mutex mutex_;
|
||||
|
||||
IDebugListener *debugger_;
|
||||
ExceptionHandler *eh_top_;
|
||||
int exception_code_;
|
||||
char exception_message_[1024];
|
||||
|
||||
IProfilingTool *profiler_;
|
||||
bool jit_enabled_;
|
||||
bool profiling_enabled_;
|
||||
@ -137,9 +172,11 @@ class Environment : public ISourcePawnEnvironment
|
||||
ke::InlineList<PluginRuntime> runtimes_;
|
||||
|
||||
uintptr_t frame_id_;
|
||||
uintptr_t invoke_depth_;
|
||||
|
||||
ke::AutoPtr<CodeStubs> code_stubs_;
|
||||
|
||||
InvokeFrame *top_;
|
||||
ExitFrame exit_frame_;
|
||||
};
|
||||
|
||||
class EnterProfileScope
|
||||
|
@ -30,14 +30,13 @@ using namespace SourcePawn;
|
||||
static const size_t kMinHeapSize = 16384;
|
||||
|
||||
PluginContext::PluginContext(PluginRuntime *pRuntime)
|
||||
: m_pRuntime(pRuntime),
|
||||
: env_(Environment::get()),
|
||||
m_pRuntime(pRuntime),
|
||||
memory_(nullptr),
|
||||
data_size_(m_pRuntime->data().length),
|
||||
mem_size_(m_pRuntime->image()->HeapSize()),
|
||||
m_pNullVec(nullptr),
|
||||
m_pNullString(nullptr),
|
||||
m_CustomMsg(false),
|
||||
m_InExec(false)
|
||||
m_pNullString(nullptr)
|
||||
{
|
||||
// Compute and align a minimum memory amount.
|
||||
if (mem_size_ < data_size_)
|
||||
@ -52,9 +51,6 @@ PluginContext::PluginContext(PluginRuntime *pRuntime)
|
||||
hp_ = data_size_;
|
||||
sp_ = mem_size_ - sizeof(cell_t);
|
||||
frm_ = sp_;
|
||||
rp_ = 0;
|
||||
last_native_ = -1;
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
|
||||
tracker_.pBase = (ucell_t *)malloc(1024);
|
||||
tracker_.pCur = tracker_.pBase;
|
||||
@ -133,56 +129,23 @@ PluginContext::Execute(uint32_t code_addr, cell_t *result)
|
||||
return SP_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::SetErrorMessage(const char *msg, va_list ap)
|
||||
{
|
||||
m_CustomMsg = true;
|
||||
|
||||
vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::_SetErrorMessage(const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
SetErrorMessage(msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
cell_t
|
||||
PluginContext::ThrowNativeErrorEx(int error, const char *msg, ...)
|
||||
{
|
||||
if (!m_InExec)
|
||||
return 0;
|
||||
|
||||
native_error_ = error;
|
||||
|
||||
if (msg) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
SetErrorMessage(msg, ap);
|
||||
env_->ReportErrorVA(error, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t
|
||||
PluginContext::ThrowNativeError(const char *msg, ...)
|
||||
{
|
||||
if (!m_InExec)
|
||||
return 0;
|
||||
|
||||
native_error_ = SP_ERROR_NATIVE;
|
||||
|
||||
if (msg) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
SetErrorMessage(msg, ap);
|
||||
env_->ReportErrorVA(SP_ERROR_NATIVE, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -540,133 +503,124 @@ PluginContext::GetNullRef(SP_NULL_TYPE type)
|
||||
bool
|
||||
PluginContext::IsInExec()
|
||||
{
|
||||
return m_InExec;
|
||||
for (InvokeFrame *ivk = env_->top(); ivk; ivk = ivk->prev()) {
|
||||
if (ivk->cx() == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
int ir;
|
||||
int serial;
|
||||
cell_t *sp;
|
||||
CompiledFunction *fn;
|
||||
cell_t _ignore_result;
|
||||
ReportErrorNumber(SP_ERROR_ABORTED);
|
||||
return SP_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginContext::Invoke(funcid_t fnid, const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
EnterProfileScope profileScope("SourcePawn", "EnterJIT");
|
||||
|
||||
if (!Environment::get()->watchdog()->HandleInterrupt())
|
||||
return SP_ERROR_TIMEOUT;
|
||||
if (!env_->watchdog()->HandleInterrupt()) {
|
||||
ReportErrorNumber(SP_ERROR_TIMEOUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
funcid_t fnid = function->GetFunctionID();
|
||||
if (!(fnid & 1))
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
assert((fnid & 1) != 0);
|
||||
|
||||
unsigned public_id = fnid >> 1;
|
||||
ScriptedInvoker *cfun = m_pRuntime->GetPublicFunction(public_id);
|
||||
if (!cfun)
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
if (!cfun) {
|
||||
ReportErrorNumber(SP_ERROR_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_pRuntime->IsPaused())
|
||||
return SP_ERROR_NOT_RUNNABLE;
|
||||
if (m_pRuntime->IsPaused()) {
|
||||
ReportErrorNumber(SP_ERROR_NOT_RUNNABLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((cell_t)(hp_ + 16*sizeof(cell_t)) > (cell_t)(sp_ - (sizeof(cell_t) * (num_params + 1))))
|
||||
return SP_ERROR_STACKLOW;
|
||||
if ((cell_t)(hp_ + 16*sizeof(cell_t)) > (cell_t)(sp_ - (sizeof(cell_t) * (num_params + 1)))) {
|
||||
ReportErrorNumber(SP_ERROR_STACKLOW);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Yuck. We have to do this for compatibility, otherwise something like
|
||||
// ForwardSys or any sort of multi-callback-fire code would die. Later,
|
||||
// we'll expose an Invoke() or something that doesn't do this.
|
||||
env_->clearPendingException();
|
||||
|
||||
cell_t ignore_result;
|
||||
if (result == NULL)
|
||||
result = &_ignore_result;
|
||||
result = &ignore_result;
|
||||
|
||||
/* We got this far. It's time to start profiling. */
|
||||
EnterProfileScope scriptScope("SourcePawn", cfun->FullName());
|
||||
|
||||
/* See if we have to compile the callee. */
|
||||
if (Environment::get()->IsJitEnabled()) {
|
||||
CompiledFunction *fn = nullptr;
|
||||
if (env_->IsJitEnabled()) {
|
||||
/* We might not have to - check pcode offset. */
|
||||
if ((fn = cfun->cachedCompiledFunction()) == nullptr) {
|
||||
fn = m_pRuntime->GetJittedFunctionByOffset(cfun->Public()->code_offs);
|
||||
if (!fn) {
|
||||
if ((fn = CompileFunction(m_pRuntime, cfun->Public()->code_offs, &ir)) == NULL)
|
||||
return ir;
|
||||
int err = SP_ERROR_NONE;
|
||||
if ((fn = CompileFunction(m_pRuntime, cfun->Public()->code_offs, &err)) == NULL) {
|
||||
ReportErrorNumber(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cfun->setCachedCompiledFunction(fn);
|
||||
}
|
||||
} else {
|
||||
ReportError("JIT is not enabled!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Save our previous state. */
|
||||
|
||||
bool save_exec;
|
||||
uint32_t save_n_idx;
|
||||
cell_t save_sp, save_hp, save_rp, save_cip;
|
||||
|
||||
save_sp = sp_;
|
||||
save_hp = hp_;
|
||||
save_exec = m_InExec;
|
||||
save_n_idx = last_native_;
|
||||
save_rp = rp_;
|
||||
save_cip = cip_;
|
||||
cell_t save_sp = sp_;
|
||||
cell_t save_hp = hp_;
|
||||
|
||||
/* Push parameters */
|
||||
|
||||
sp_ -= sizeof(cell_t) * (num_params + 1);
|
||||
sp = (cell_t *)(memory_ + sp_);
|
||||
cell_t *sp = (cell_t *)(memory_ + sp_);
|
||||
|
||||
sp[0] = num_params;
|
||||
for (unsigned int i = 0; i < num_params; i++)
|
||||
sp[i + 1] = params[i];
|
||||
|
||||
/* Clear internal state */
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
last_native_ = -1;
|
||||
m_MsgCache[0] = '\0';
|
||||
m_CustomMsg = false;
|
||||
m_InExec = true;
|
||||
|
||||
// Enter the execution engine.
|
||||
Environment *env = Environment::get();
|
||||
int ir;
|
||||
{
|
||||
InvokeFrame ivkframe(this, fn->GetCodeOffset());
|
||||
Environment *env = env_;
|
||||
ir = env->Invoke(m_pRuntime, fn, result);
|
||||
|
||||
/* Restore some states, stop the frame tracer */
|
||||
|
||||
m_InExec = save_exec;
|
||||
}
|
||||
|
||||
if (ir == SP_ERROR_NONE) {
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
// Verify that our state is still sane.
|
||||
if (sp_ != save_sp) {
|
||||
ir = SP_ERROR_STACKLEAK;
|
||||
_SetErrorMessage("Stack leak detected: sp:%d should be %d!",
|
||||
env_->ReportErrorFmt(
|
||||
SP_ERROR_STACKLEAK,
|
||||
"Stack leak detected: sp:%d should be %d!",
|
||||
sp_,
|
||||
save_sp);
|
||||
return false;
|
||||
}
|
||||
if (hp_ != save_hp) {
|
||||
ir = SP_ERROR_HEAPLEAK;
|
||||
_SetErrorMessage("Heap leak detected: hp:%d should be %d!",
|
||||
env_->ReportErrorFmt(
|
||||
SP_ERROR_HEAPLEAK,
|
||||
"Heap leak detected: hp:%d should be %d!",
|
||||
hp_,
|
||||
save_hp);
|
||||
}
|
||||
if (rp_ != save_rp) {
|
||||
ir = SP_ERROR_STACKLEAK;
|
||||
_SetErrorMessage("Return stack leak detected: rp:%d should be %d!",
|
||||
rp_,
|
||||
save_rp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ir == SP_ERROR_TIMEOUT)
|
||||
Environment::get()->watchdog()->NotifyTimeoutReceived();
|
||||
|
||||
if (ir != SP_ERROR_NONE)
|
||||
Environment::get()->ReportError(m_pRuntime, ir, m_MsgCache, save_rp);
|
||||
|
||||
sp_ = save_sp;
|
||||
hp_ = save_hp;
|
||||
rp_ = save_rp;
|
||||
|
||||
cip_ = save_cip;
|
||||
last_native_ = save_n_idx;
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
m_MsgCache[0] = '\0';
|
||||
m_CustomMsg = false;
|
||||
|
||||
return ir;
|
||||
return ir == SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
IPluginRuntime *
|
||||
@ -678,7 +632,10 @@ PluginContext::GetRuntime()
|
||||
int
|
||||
PluginContext::GetLastNativeError()
|
||||
{
|
||||
return native_error_;
|
||||
Environment *env = env_;
|
||||
if (!env->hasPendingException())
|
||||
return SP_ERROR_NONE;
|
||||
return env->getPendingExceptionCode();
|
||||
}
|
||||
|
||||
cell_t *
|
||||
@ -710,7 +667,8 @@ PluginContext::GetKey(int k, void **value)
|
||||
void
|
||||
PluginContext::ClearLastNativeError()
|
||||
{
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
if (env_->hasPendingException())
|
||||
env_->clearPendingException();
|
||||
}
|
||||
|
||||
int
|
||||
@ -757,28 +715,24 @@ PluginContext::invokeNative(ucell_t native_idx, cell_t *params)
|
||||
cell_t save_sp = sp_;
|
||||
cell_t save_hp = hp_;
|
||||
|
||||
// Note: Invoke() saves the last native, so we don't need to here.
|
||||
last_native_ = native_idx;
|
||||
|
||||
const sp_native_t *native = m_pRuntime->GetNative(native_idx);
|
||||
|
||||
if (native->status == SP_NATIVE_UNBOUND) {
|
||||
native_error_ = SP_ERROR_INVALID_NATIVE;
|
||||
ReportErrorNumber(SP_ERROR_INVALID_NATIVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t result = native->pfn(this, params);
|
||||
|
||||
if (native_error_ != SP_ERROR_NONE)
|
||||
return result;
|
||||
|
||||
if (save_sp != sp_) {
|
||||
native_error_ = SP_ERROR_STACKLEAK;
|
||||
return result;
|
||||
if (!env_->hasPendingException())
|
||||
ReportErrorNumber(SP_ERROR_STACKLEAK);
|
||||
return 0;
|
||||
}
|
||||
if (save_hp != hp_) {
|
||||
native_error_ = SP_ERROR_HEAPLEAK;
|
||||
return result;
|
||||
if (!env_->hasPendingException())
|
||||
ReportErrorNumber(SP_ERROR_HEAPLEAK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -792,15 +746,14 @@ PluginContext::invokeBoundNative(SPVM_NATIVE_FUNC pfn, cell_t *params)
|
||||
|
||||
cell_t result = pfn(this, params);
|
||||
|
||||
if (native_error_ != SP_ERROR_NONE)
|
||||
return result;
|
||||
|
||||
if (save_sp != sp_) {
|
||||
native_error_ = SP_ERROR_STACKLEAK;
|
||||
if (!env_->hasPendingException())
|
||||
ReportErrorNumber(SP_ERROR_STACKLEAK);
|
||||
return result;
|
||||
}
|
||||
if (save_hp != hp_) {
|
||||
native_error_ = SP_ERROR_HEAPLEAK;
|
||||
if (!env_->hasPendingException())
|
||||
ReportErrorNumber(SP_ERROR_HEAPLEAK);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -957,3 +910,44 @@ PluginContext::generateArray(cell_t dims, cell_t *stk, bool autozero)
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
ISourcePawnEngine2 *
|
||||
PluginContext::APIv2()
|
||||
{
|
||||
return env_->APIv2();
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::ReportError(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
env_->ReportErrorVA(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::ReportErrorVA(const char *fmt, va_list ap)
|
||||
{
|
||||
env_->ReportErrorVA(fmt, ap);
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::ReportFatalError(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
env_->ReportErrorVA(SP_ERROR_FATAL, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::ReportFatalErrorVA(const char *fmt, va_list ap)
|
||||
{
|
||||
env_->ReportErrorVA(SP_ERROR_FATAL, fmt, ap);
|
||||
}
|
||||
|
||||
void
|
||||
PluginContext::ReportErrorNumber(int error)
|
||||
{
|
||||
env_->ReportError(error);
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ struct HeapTracker
|
||||
|
||||
static const size_t SP_MAX_RETURN_STACK = 1024;
|
||||
|
||||
class Environment;
|
||||
class PluginContext;
|
||||
|
||||
class PluginContext : public IPluginContext
|
||||
{
|
||||
public:
|
||||
@ -88,6 +91,14 @@ class PluginContext : public IPluginContext
|
||||
bool GetKey(int k, void **value);
|
||||
void Refresh();
|
||||
void ClearLastNativeError();
|
||||
ISourcePawnEngine2 *APIv2() KE_OVERRIDE;
|
||||
void ReportError(const char *fmt, ...) KE_OVERRIDE;
|
||||
void ReportErrorVA(const char *fmt, va_list ap) KE_OVERRIDE;
|
||||
void ReportFatalError(const char *fmt, ...) KE_OVERRIDE;
|
||||
void ReportFatalErrorVA(const char *fmt, va_list ap) KE_OVERRIDE;
|
||||
void ReportErrorNumber(int error) KE_OVERRIDE;
|
||||
|
||||
bool Invoke(funcid_t fnid, const cell_t *params, unsigned int num_params, cell_t *result);
|
||||
|
||||
size_t HeapSize() const {
|
||||
return mem_size_;
|
||||
@ -98,25 +109,16 @@ class PluginContext : public IPluginContext
|
||||
size_t DataSize() const {
|
||||
return data_size_;
|
||||
}
|
||||
PluginRuntime *runtime() const {
|
||||
return m_pRuntime;
|
||||
}
|
||||
|
||||
public:
|
||||
bool IsInExec();
|
||||
|
||||
static inline size_t offsetOfRp() {
|
||||
return offsetof(PluginContext, rp_);
|
||||
}
|
||||
static inline size_t offsetOfRstkCips() {
|
||||
return offsetof(PluginContext, rstk_cips_);
|
||||
}
|
||||
static inline size_t offsetOfTracker() {
|
||||
return offsetof(PluginContext, tracker_);
|
||||
}
|
||||
static inline size_t offsetOfLastNative() {
|
||||
return offsetof(PluginContext, last_native_);
|
||||
}
|
||||
static inline size_t offsetOfNativeError() {
|
||||
return offsetof(PluginContext, native_error_);
|
||||
}
|
||||
static inline size_t offsetOfSp() {
|
||||
return offsetof(PluginContext, sp_);
|
||||
}
|
||||
@ -127,9 +129,6 @@ class PluginContext : public IPluginContext
|
||||
return offsetof(PluginContext, memory_);
|
||||
}
|
||||
|
||||
int32_t *addressOfCip() {
|
||||
return &cip_;
|
||||
}
|
||||
int32_t *addressOfSp() {
|
||||
return &sp_;
|
||||
}
|
||||
@ -140,9 +139,6 @@ class PluginContext : public IPluginContext
|
||||
return &hp_;
|
||||
}
|
||||
|
||||
int32_t cip() const {
|
||||
return cip_;
|
||||
}
|
||||
cell_t frm() const {
|
||||
return frm_;
|
||||
}
|
||||
@ -150,25 +146,6 @@ class PluginContext : public IPluginContext
|
||||
return hp_;
|
||||
}
|
||||
|
||||
// Return stack logic.
|
||||
bool pushReturnCip(cell_t cip) {
|
||||
if (rp_ >= SP_MAX_RETURN_STACK)
|
||||
return false;
|
||||
rstk_cips_[rp_++] = cip;
|
||||
return true;
|
||||
}
|
||||
void popReturnCip() {
|
||||
assert(rp_ > 0);
|
||||
rp_--;
|
||||
}
|
||||
cell_t rp() const {
|
||||
return rp_;
|
||||
}
|
||||
cell_t getReturnStackCip(int index) {
|
||||
assert(index >= 0 && index < SP_MAX_RETURN_STACK);
|
||||
return rstk_cips_[index];
|
||||
}
|
||||
|
||||
int popTrackerAndSetHeap();
|
||||
int pushTracker(uint32_t amount);
|
||||
|
||||
@ -176,9 +153,6 @@ class PluginContext : public IPluginContext
|
||||
int generateFullArray(uint32_t argc, cell_t *argv, int autozero);
|
||||
cell_t invokeNative(ucell_t native_idx, cell_t *params);
|
||||
cell_t invokeBoundNative(SPVM_NATIVE_FUNC pfn, cell_t *params);
|
||||
int lastNative() const {
|
||||
return last_native_;
|
||||
}
|
||||
|
||||
inline bool checkAddress(cell_t *stk, cell_t addr) {
|
||||
if (uint32_t(addr) >= mem_size_)
|
||||
@ -194,10 +168,7 @@ class PluginContext : public IPluginContext
|
||||
}
|
||||
|
||||
private:
|
||||
void SetErrorMessage(const char *msg, va_list ap);
|
||||
void _SetErrorMessage(const char *msg, ...);
|
||||
|
||||
private:
|
||||
Environment *env_;
|
||||
PluginRuntime *m_pRuntime;
|
||||
uint8_t *memory_;
|
||||
uint32_t data_size_;
|
||||
@ -205,26 +176,12 @@ class PluginContext : public IPluginContext
|
||||
|
||||
cell_t *m_pNullVec;
|
||||
cell_t *m_pNullString;
|
||||
char m_MsgCache[1024];
|
||||
bool m_CustomMsg;
|
||||
bool m_InExec;
|
||||
void *m_keys[4];
|
||||
bool m_keys_set[4];
|
||||
|
||||
// Tracker for local HEA growth.
|
||||
HeapTracker tracker_;
|
||||
|
||||
// Return stack.
|
||||
cell_t rp_;
|
||||
cell_t rstk_cips_[SP_MAX_RETURN_STACK];
|
||||
|
||||
// Track the currently executing native index, and any error it throws.
|
||||
int32_t last_native_;
|
||||
int native_error_;
|
||||
|
||||
// Most recent CIP.
|
||||
int32_t cip_;
|
||||
|
||||
// Stack, heap, and frame pointer.
|
||||
cell_t sp_;
|
||||
cell_t hp_;
|
||||
|
@ -161,11 +161,10 @@ PluginRuntime::GetNativeReplacement(size_t index)
|
||||
}
|
||||
|
||||
void
|
||||
PluginRuntime::SetName(const char *name)
|
||||
PluginRuntime::SetNames(const char *fullname, const char *name)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
name_ = new char[len + 1];
|
||||
strcpy(name_, name);
|
||||
name_ = name;
|
||||
full_name_ = name;
|
||||
}
|
||||
|
||||
static cell_t
|
||||
@ -180,19 +179,21 @@ PluginRuntime::AddJittedFunction(CompiledFunction *fn)
|
||||
m_JitFunctions.append(fn);
|
||||
|
||||
ucell_t pcode_offset = fn->GetCodeOffset();
|
||||
{
|
||||
FunctionMap::Insert p = function_map_.findForAdd(pcode_offset);
|
||||
assert(!p.found());
|
||||
|
||||
function_map_.add(p, pcode_offset, fn);
|
||||
}
|
||||
}
|
||||
|
||||
CompiledFunction *
|
||||
PluginRuntime::GetJittedFunctionByOffset(cell_t pcode_offset)
|
||||
{
|
||||
FunctionMap::Result r = function_map_.find(pcode_offset);
|
||||
if (r.found())
|
||||
return r->value;
|
||||
if (!r.found())
|
||||
return nullptr;
|
||||
return r->value;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <am-vector.h>
|
||||
#include <am-string.h>
|
||||
#include <am-inlinelist.h>
|
||||
#include <am-hashmap.h>
|
||||
#include "compiled-function.h"
|
||||
@ -71,7 +72,7 @@ class PluginRuntime
|
||||
virtual unsigned char *GetDataHash();
|
||||
CompiledFunction *GetJittedFunctionByOffset(cell_t pcode_offset);
|
||||
void AddJittedFunction(CompiledFunction *fn);
|
||||
void SetName(const char *name);
|
||||
void SetNames(const char *fullname, const char *name);
|
||||
unsigned GetNativeReplacement(size_t index);
|
||||
ScriptedInvoker *GetPublicFunction(size_t index);
|
||||
int UpdateNativeBinding(uint32_t index, SPVM_NATIVE_FUNC pfn, uint32_t flags, void *data) KE_OVERRIDE;
|
||||
@ -79,6 +80,9 @@ class PluginRuntime
|
||||
int LookupLine(ucell_t addr, uint32_t *line) KE_OVERRIDE;
|
||||
int LookupFunction(ucell_t addr, const char **name) KE_OVERRIDE;
|
||||
int LookupFile(ucell_t addr, const char **filename) KE_OVERRIDE;
|
||||
const char *GetFilename() KE_OVERRIDE {
|
||||
return full_name_.chars();
|
||||
}
|
||||
|
||||
PluginContext *GetBaseContext();
|
||||
|
||||
@ -89,11 +93,7 @@ class PluginRuntime
|
||||
return m_JitFunctions[i];
|
||||
}
|
||||
const char *Name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
static inline size_t offsetToPlugin() {
|
||||
return 0x0fff0000;
|
||||
return name_.chars();
|
||||
}
|
||||
|
||||
public:
|
||||
@ -117,7 +117,8 @@ class PluginRuntime
|
||||
ke::AutoPtr<sp::LegacyImage> image_;
|
||||
ke::AutoArray<uint8_t> aligned_code_;
|
||||
ke::AutoArray<floattbl_t> float_table_;
|
||||
ke::AutoArray<char> name_;
|
||||
ke::AString name_;
|
||||
ke::AString full_name_;
|
||||
Code code_;
|
||||
Data data_;
|
||||
ke::AutoArray<sp_native_t> natives_;
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <string.h>
|
||||
#include "scripted-invoker.h"
|
||||
#include "plugin-runtime.h"
|
||||
#include "environment.h"
|
||||
#include "plugin-context.h"
|
||||
|
||||
/********************
|
||||
* FUNCTION CALLING *
|
||||
@ -23,43 +25,14 @@
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
|
||||
ScriptedInvoker::~ScriptedInvoker()
|
||||
{
|
||||
delete [] full_name_;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedInvoker::IsRunnable()
|
||||
{
|
||||
return !m_pRuntime->IsPaused();
|
||||
}
|
||||
|
||||
int
|
||||
ScriptedInvoker::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
return CallFunction2(m_pRuntime->GetDefaultContext(), params, num_params, result);
|
||||
}
|
||||
|
||||
int
|
||||
ScriptedInvoker::CallFunction2(IPluginContext *pContext, const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
return pContext->Execute2(this, params, num_params, result);
|
||||
}
|
||||
|
||||
IPluginContext *
|
||||
ScriptedInvoker::GetParentContext()
|
||||
{
|
||||
return m_pRuntime->GetDefaultContext();
|
||||
}
|
||||
|
||||
ScriptedInvoker::ScriptedInvoker(PluginRuntime *runtime, funcid_t id, uint32_t pub_id)
|
||||
: m_curparam(0),
|
||||
: env_(Environment::get()),
|
||||
context_(runtime->GetBaseContext()),
|
||||
m_curparam(0),
|
||||
m_errorstate(SP_ERROR_NONE),
|
||||
m_FnId(id),
|
||||
cc_function_(nullptr)
|
||||
{
|
||||
m_pRuntime = runtime;
|
||||
|
||||
runtime->GetPublicByIndex(pub_id, &public_);
|
||||
|
||||
size_t rt_len = strlen(runtime->Name());
|
||||
@ -71,6 +44,36 @@ ScriptedInvoker::ScriptedInvoker(PluginRuntime *runtime, funcid_t id, uint32_t p
|
||||
strcpy(&full_name_[rt_len + 2], public_->name);
|
||||
}
|
||||
|
||||
ScriptedInvoker::~ScriptedInvoker()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedInvoker::IsRunnable()
|
||||
{
|
||||
return !context_->runtime()->IsPaused();
|
||||
}
|
||||
|
||||
int
|
||||
ScriptedInvoker::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
Environment::get()->ReportError(SP_ERROR_ABORTED);
|
||||
return SP_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
int
|
||||
ScriptedInvoker::CallFunction2(IPluginContext *pContext, const cell_t *params, unsigned int num_params, cell_t *result)
|
||||
{
|
||||
Environment::get()->ReportError(SP_ERROR_ABORTED);
|
||||
return SP_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
IPluginContext *
|
||||
ScriptedInvoker::GetParentContext()
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
int ScriptedInvoker::PushCell(cell_t cell)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
@ -169,21 +172,41 @@ ScriptedInvoker::Cancel()
|
||||
int
|
||||
ScriptedInvoker::Execute(cell_t *result)
|
||||
{
|
||||
return Execute2(m_pRuntime->GetDefaultContext(), result);
|
||||
Environment *env = Environment::get();
|
||||
env->clearPendingException();
|
||||
|
||||
// For backward compatibility, we have to clear the exception state.
|
||||
// Otherwise code like this:
|
||||
//
|
||||
// static cell_t native(cx, params) {
|
||||
// for (auto callback : callbacks) {
|
||||
// callback->Execute();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Could unintentionally leak a pending exception back to the caller,
|
||||
// which wouldn't have happened before the Great Exception Refactoring.
|
||||
ExceptionHandler eh(context_);
|
||||
if (!Invoke(result)) {
|
||||
assert(env->hasPendingException());
|
||||
return env->getPendingExceptionCode();
|
||||
}
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
bool
|
||||
ScriptedInvoker::Invoke(cell_t *result)
|
||||
{
|
||||
int err = SP_ERROR_NONE;
|
||||
|
||||
if (!IsRunnable())
|
||||
m_errorstate = SP_ERROR_NOT_RUNNABLE;
|
||||
|
||||
if (m_errorstate != SP_ERROR_NONE) {
|
||||
err = m_errorstate;
|
||||
if (!IsRunnable()) {
|
||||
Cancel();
|
||||
return err;
|
||||
env_->ReportError(SP_ERROR_NOT_RUNNABLE);
|
||||
return false;
|
||||
}
|
||||
if (int err = m_errorstate) {
|
||||
Cancel();
|
||||
env_->ReportError(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This is for re-entrancy!
|
||||
@ -191,7 +214,6 @@ ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int numparams = m_curparam;
|
||||
unsigned int i;
|
||||
bool docopies = true;
|
||||
|
||||
if (numparams)
|
||||
{
|
||||
@ -201,16 +223,19 @@ ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
m_curparam = 0;
|
||||
|
||||
/* Browse the parameters and build arrays */
|
||||
bool ok = true;
|
||||
for (i=0; i<numparams; i++) {
|
||||
/* Is this marked as an array? */
|
||||
if (temp_info[i].marked) {
|
||||
if (!temp_info[i].str.is_sz) {
|
||||
/* Allocate a normal/generic array */
|
||||
if ((err=ctx->HeapAlloc(temp_info[i].size,
|
||||
int err = context_->HeapAlloc(
|
||||
temp_info[i].size,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr)))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
&(temp_info[i].phys_addr));
|
||||
if (err != SP_ERROR_NONE) {
|
||||
env_->ReportError(err);
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (temp_info[i].orig_addr)
|
||||
@ -222,26 +247,26 @@ ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
|
||||
|
||||
/* Allocate the buffer */
|
||||
if ((err=ctx->HeapAlloc(cells,
|
||||
int err = context_->HeapAlloc(
|
||||
cells,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr)))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
&(temp_info[i].phys_addr));
|
||||
if (err != SP_ERROR_NONE) {
|
||||
env_->ReportError(err);
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy original string if necessary */
|
||||
if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
|
||||
{
|
||||
/* Cut off UTF-8 properly */
|
||||
if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8) {
|
||||
if ((err=ctx->StringToLocalUTF8(temp_info[i].local_addr,
|
||||
context_->StringToLocalUTF8(
|
||||
temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr,
|
||||
NULL))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
NULL);
|
||||
}
|
||||
/* Copy a binary blob */
|
||||
else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
|
||||
@ -251,13 +276,10 @@ ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
/* Copy ASCII characters */
|
||||
else
|
||||
{
|
||||
if ((err=ctx->StringToLocal(temp_info[i].local_addr,
|
||||
context_->StringToLocal(
|
||||
temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
(const char *)temp_info[i].orig_addr);
|
||||
}
|
||||
}
|
||||
} /* End array/string calculation */
|
||||
@ -270,14 +292,11 @@ ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
}
|
||||
|
||||
/* Make the call if we can */
|
||||
if (err == SP_ERROR_NONE) {
|
||||
if ((err = CallFunction2(ctx, temp_params, numparams, result)) != SP_ERROR_NONE)
|
||||
docopies = false;
|
||||
} else {
|
||||
docopies = false;
|
||||
}
|
||||
if (ok)
|
||||
ok = context_->Invoke(m_FnId, temp_params, numparams, result);
|
||||
|
||||
/* i should be equal to the last valid parameter + 1 */
|
||||
bool docopies = ok;
|
||||
while (i--) {
|
||||
if (!temp_info[i].marked)
|
||||
continue;
|
||||
@ -299,17 +318,24 @@ ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
}
|
||||
}
|
||||
|
||||
if ((err=ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
|
||||
return err;
|
||||
if (int err = context_->HeapPop(temp_info[i].local_addr))
|
||||
env_->ReportError(err);
|
||||
}
|
||||
|
||||
return err;
|
||||
return !env_->hasPendingException();
|
||||
}
|
||||
|
||||
int
|
||||
ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result)
|
||||
{
|
||||
Environment::get()->ReportError(SP_ERROR_ABORTED);
|
||||
return SP_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
IPluginRuntime *
|
||||
ScriptedInvoker::GetParentRuntime()
|
||||
{
|
||||
return m_pRuntime;
|
||||
return context_->runtime();
|
||||
}
|
||||
|
||||
funcid_t
|
||||
|
@ -14,12 +14,14 @@
|
||||
#define _INCLUDE_SOURCEMOD_BASEFUNCTION_H_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <am-utility.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
class PluginRuntime;
|
||||
class PluginContext;
|
||||
class CompiledFunction;
|
||||
|
||||
struct ParamInfo
|
||||
@ -43,17 +45,18 @@ class ScriptedInvoker : public IPluginFunction
|
||||
~ScriptedInvoker();
|
||||
|
||||
public:
|
||||
virtual int PushCell(cell_t cell);
|
||||
virtual int PushCellByRef(cell_t *cell, int flags);
|
||||
virtual int PushFloat(float number);
|
||||
virtual int PushFloatByRef(float *number, int flags);
|
||||
virtual int PushArray(cell_t *inarray, unsigned int cells, int copyback);
|
||||
virtual int PushString(const char *string);
|
||||
virtual int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
|
||||
virtual int Execute(cell_t *result);
|
||||
virtual void Cancel();
|
||||
virtual int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
|
||||
virtual IPluginContext *GetParentContext();
|
||||
int PushCell(cell_t cell);
|
||||
int PushCellByRef(cell_t *cell, int flags);
|
||||
int PushFloat(float number);
|
||||
int PushFloatByRef(float *number, int flags);
|
||||
int PushArray(cell_t *inarray, unsigned int cells, int copyback);
|
||||
int PushString(const char *string);
|
||||
int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
|
||||
int Execute(cell_t *result);
|
||||
void Cancel();
|
||||
int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
|
||||
IPluginContext *GetParentContext();
|
||||
bool Invoke(cell_t *result);
|
||||
bool IsRunnable();
|
||||
funcid_t GetFunctionID();
|
||||
int Execute2(IPluginContext *ctx, cell_t *result);
|
||||
@ -83,13 +86,15 @@ class ScriptedInvoker : public IPluginFunction
|
||||
int SetError(int err);
|
||||
|
||||
private:
|
||||
Environment *env_;
|
||||
PluginRuntime *m_pRuntime;
|
||||
PluginContext *context_;
|
||||
cell_t m_params[SP_MAX_EXEC_PARAMS];
|
||||
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int m_curparam;
|
||||
int m_errorstate;
|
||||
funcid_t m_FnId;
|
||||
char *full_name_;
|
||||
ke::AutoArray<char> full_name_;
|
||||
sp_public_t *public_;
|
||||
CompiledFunction *cc_function_;
|
||||
};
|
||||
|
227
sourcepawn/jit/stack-frames.cpp
Normal file
227
sourcepawn/jit/stack-frames.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
#include "environment.h"
|
||||
#include "plugin-runtime.h"
|
||||
#include "plugin-context.h"
|
||||
#include "stack-frames.h"
|
||||
#include "x86/frames-x86.h"
|
||||
#include "compiled-function.h"
|
||||
|
||||
using namespace ke;
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
|
||||
InvokeFrame::InvokeFrame(PluginContext *cx, cell_t entry_cip)
|
||||
: prev_(Environment::get()->top()),
|
||||
cx_(cx),
|
||||
prev_exit_frame_(Environment::get()->exit_frame()),
|
||||
entry_cip_(0),
|
||||
entry_sp_(nullptr)
|
||||
{
|
||||
Environment::get()->enterInvoke(this);
|
||||
}
|
||||
|
||||
InvokeFrame::~InvokeFrame()
|
||||
{
|
||||
assert(Environment::get()->top() == this);
|
||||
Environment::get()->leaveInvoke();
|
||||
}
|
||||
|
||||
FrameIterator::FrameIterator()
|
||||
: ivk_(Environment::get()->top()),
|
||||
exit_frame_(Environment::get()->exit_frame()),
|
||||
runtime_(nullptr),
|
||||
sp_iter_(nullptr),
|
||||
sp_stop_(nullptr),
|
||||
function_cip_(kInvalidCip),
|
||||
cip_(kInvalidCip),
|
||||
pc_(nullptr)
|
||||
{
|
||||
if (!ivk_)
|
||||
return;
|
||||
|
||||
nextInvokeFrame();
|
||||
}
|
||||
|
||||
void
|
||||
FrameIterator::nextInvokeFrame()
|
||||
{
|
||||
assert(exit_frame_.exit_sp());
|
||||
sp_iter_ = exit_frame_.exit_sp();
|
||||
|
||||
// Inside an exit frame, the stack looks like this:
|
||||
// .. C++ ..
|
||||
// ----------- <-- entry_sp
|
||||
// return addr to C++
|
||||
// entry cip for frame #0
|
||||
// alignment
|
||||
// -----------
|
||||
// return addr to frame #0
|
||||
// entry cip for frame #1
|
||||
// alignment
|
||||
// ----------- <-- exit sp
|
||||
// saved regs
|
||||
// return addr
|
||||
// .. InvokeNativeBoundHelper() ..
|
||||
//
|
||||
// We are guaranteed to always have one frame. We subtract one frame from
|
||||
// the entry sp so we hit the stopping point correctly.
|
||||
assert(ivk_->entry_sp());
|
||||
assert(ke::IsAligned(sizeof(JitFrame), sizeof(intptr_t)));
|
||||
sp_stop_ = ivk_->entry_sp() - (sizeof(JitFrame) / sizeof(intptr_t));
|
||||
assert(sp_stop_ >= sp_iter_);
|
||||
|
||||
runtime_ = ivk_->cx()->runtime();
|
||||
function_cip_ = -1;
|
||||
pc_ = nullptr;
|
||||
cip_ = kInvalidCip;
|
||||
|
||||
if (!exit_frame_.has_exit_native()) {
|
||||
// We have an exit frame, but it's not for natives. automatically advance
|
||||
// to the most recent scripted frame.
|
||||
const JitExitFrameForHelper *exit =
|
||||
JitExitFrameForHelper::FromExitSp(exit_frame_.exit_sp());
|
||||
exit_frame_ = ExitFrame();
|
||||
|
||||
// If we haven't compiled the function yet, but threw an error, then the
|
||||
// return address will be null.
|
||||
pc_ = exit->return_address;
|
||||
assert(pc_ || exit->isCompileFunction());
|
||||
|
||||
// The function owning pc_ is in the previous frame.
|
||||
const JitFrame *frame = exit->prev();
|
||||
function_cip_ = frame->function_cip;
|
||||
sp_iter_ = reinterpret_cast<const intptr_t *>(frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FrameIterator::Next()
|
||||
{
|
||||
void *pc = nullptr;
|
||||
if (exit_frame_.has_exit_native()) {
|
||||
// If we're at an exit frame, the return address will yield the current pc.
|
||||
const JitExitFrameForNative *exit =
|
||||
JitExitFrameForNative::FromExitSp(exit_frame_.exit_sp());
|
||||
exit_frame_ = ExitFrame();
|
||||
|
||||
pc_ = exit->return_address;
|
||||
cip_ = kInvalidCip;
|
||||
|
||||
// The function owning pc_ is in the previous frame.
|
||||
const JitFrame *frame = JitFrame::FromSp(sp_iter_);
|
||||
function_cip_ = frame->function_cip;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sp_iter_ >= sp_stop_) {
|
||||
// Jump to the next invoke frame.
|
||||
exit_frame_ = ivk_->prev_exit_frame();
|
||||
ivk_ = ivk_->prev();
|
||||
if (!ivk_)
|
||||
return;
|
||||
|
||||
nextInvokeFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
pc_ = JitFrame::FromSp(sp_iter_)->return_address;
|
||||
assert(pc_);
|
||||
|
||||
// Advance, and find the function cip the pc belongs to.
|
||||
sp_iter_ = reinterpret_cast<const intptr_t *>(JitFrame::FromSp(sp_iter_) + 1);
|
||||
function_cip_ = JitFrame::FromSp(sp_iter_)->function_cip;
|
||||
cip_ = kInvalidCip;
|
||||
}
|
||||
|
||||
void
|
||||
FrameIterator::Reset()
|
||||
{
|
||||
*this = FrameIterator();
|
||||
}
|
||||
|
||||
cell_t
|
||||
FrameIterator::findCip() const
|
||||
{
|
||||
CompiledFunction *fn = runtime_->GetJittedFunctionByOffset(function_cip_);
|
||||
if (!fn)
|
||||
return 0;
|
||||
|
||||
if (cip_ == kInvalidCip) {
|
||||
if (pc_)
|
||||
cip_ = fn->FindCipByPc(pc_);
|
||||
else
|
||||
cip_ = function_cip_;
|
||||
}
|
||||
return cip_;
|
||||
}
|
||||
|
||||
unsigned
|
||||
FrameIterator::LineNumber() const
|
||||
{
|
||||
cell_t cip = findCip();
|
||||
if (cip == kInvalidCip)
|
||||
return 0;
|
||||
|
||||
uint32_t line;
|
||||
if (!runtime_->image()->LookupLine(cip, &line))
|
||||
return 0;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
const char *
|
||||
FrameIterator::FilePath() const
|
||||
{
|
||||
cell_t cip = findCip();
|
||||
if (cip == kInvalidCip)
|
||||
return runtime_->image()->LookupFile(function_cip_);
|
||||
|
||||
return runtime_->image()->LookupFile(cip);
|
||||
}
|
||||
|
||||
const char *
|
||||
FrameIterator::FunctionName() const
|
||||
{
|
||||
assert(ivk_);
|
||||
if (exit_frame_.has_exit_native()) {
|
||||
uint32_t native_index = exit_frame_.exit_native();
|
||||
const sp_native_t *native = runtime_->GetNative(native_index);
|
||||
if (!native)
|
||||
return nullptr;
|
||||
return native->name;
|
||||
}
|
||||
|
||||
return runtime_->image()->LookupFunction(function_cip_);
|
||||
}
|
||||
|
||||
bool
|
||||
FrameIterator::IsNativeFrame() const
|
||||
{
|
||||
return exit_frame_.has_exit_native();
|
||||
}
|
||||
|
||||
bool
|
||||
FrameIterator::IsScriptedFrame() const
|
||||
{
|
||||
return !IsNativeFrame() && ivk_;
|
||||
}
|
||||
|
||||
IPluginContext *
|
||||
FrameIterator::Context() const
|
||||
{
|
||||
if (!ivk_)
|
||||
return nullptr;
|
||||
return ivk_->cx();
|
||||
}
|
135
sourcepawn/jit/stack-frames.h
Normal file
135
sourcepawn/jit/stack-frames.h
Normal file
@ -0,0 +1,135 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
#ifndef _include_sourcepawn_vm_stack_frames_h_
|
||||
#define _include_sourcepawn_vm_stack_frames_h_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
class PluginContext;
|
||||
class PluginRuntime;
|
||||
|
||||
// An ExitFrame represents the state of the most recent exit from VM state to
|
||||
// the outside world. Because this transition is on a critical path, we declare
|
||||
// exactly one ExitFrame and save/restore it in InvokeFrame(). Anytime we're in
|
||||
// the VM, we are guaranteed to have an ExitFrame for each InvokeFrame().
|
||||
class ExitFrame
|
||||
{
|
||||
public:
|
||||
ExitFrame()
|
||||
: exit_sp_(nullptr),
|
||||
exit_native_(-1)
|
||||
{}
|
||||
|
||||
public:
|
||||
const intptr_t *exit_sp() const {
|
||||
return exit_sp_;
|
||||
}
|
||||
bool has_exit_native() const {
|
||||
return exit_native_ != -1;
|
||||
}
|
||||
uint32_t exit_native() const {
|
||||
assert(has_exit_native());
|
||||
return exit_native_;
|
||||
}
|
||||
|
||||
public:
|
||||
static inline size_t offsetOfExitSp() {
|
||||
return offsetof(ExitFrame, exit_sp_);
|
||||
}
|
||||
static inline size_t offsetOfExitNative() {
|
||||
return offsetof(ExitFrame, exit_native_);
|
||||
}
|
||||
|
||||
private:
|
||||
const intptr_t *exit_sp_;
|
||||
int exit_native_;
|
||||
};
|
||||
|
||||
// An InvokeFrame represents one activation of Execute2().
|
||||
class InvokeFrame
|
||||
{
|
||||
public:
|
||||
InvokeFrame(PluginContext *cx, cell_t cip);
|
||||
~InvokeFrame();
|
||||
|
||||
InvokeFrame *prev() const {
|
||||
return prev_;
|
||||
}
|
||||
PluginContext *cx() const {
|
||||
return cx_;
|
||||
}
|
||||
|
||||
const ExitFrame &prev_exit_frame() const {
|
||||
return prev_exit_frame_;
|
||||
}
|
||||
const intptr_t *entry_sp() const {
|
||||
return entry_sp_;
|
||||
}
|
||||
cell_t entry_cip() const {
|
||||
return entry_cip_;
|
||||
}
|
||||
|
||||
public:
|
||||
static inline size_t offsetOfEntrySp() {
|
||||
return offsetof(InvokeFrame, entry_sp_);
|
||||
}
|
||||
|
||||
private:
|
||||
InvokeFrame *prev_;
|
||||
PluginContext *cx_;
|
||||
ExitFrame prev_exit_frame_;
|
||||
cell_t entry_cip_;
|
||||
const intptr_t *entry_sp_;
|
||||
};
|
||||
|
||||
class FrameIterator : public SourcePawn::IFrameIterator
|
||||
{
|
||||
public:
|
||||
FrameIterator();
|
||||
|
||||
bool Done() const KE_OVERRIDE {
|
||||
return !ivk_;
|
||||
}
|
||||
void Next() KE_OVERRIDE;
|
||||
void Reset() KE_OVERRIDE;
|
||||
|
||||
bool IsNativeFrame() const KE_OVERRIDE;
|
||||
bool IsScriptedFrame() const KE_OVERRIDE;
|
||||
const char *FunctionName() const KE_OVERRIDE;
|
||||
const char *FilePath() const KE_OVERRIDE;
|
||||
unsigned LineNumber() const KE_OVERRIDE;
|
||||
IPluginContext *Context() const KE_OVERRIDE;
|
||||
|
||||
private:
|
||||
void nextInvokeFrame();
|
||||
cell_t findCip() const;
|
||||
|
||||
private:
|
||||
InvokeFrame *ivk_;
|
||||
ExitFrame exit_frame_;
|
||||
PluginRuntime *runtime_;
|
||||
const intptr_t *sp_iter_;
|
||||
const intptr_t *sp_stop_;
|
||||
cell_t function_cip_;
|
||||
mutable cell_t cip_;
|
||||
void *pc_;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif // _include_sourcepawn_vm_stack_frames_h_
|
@ -14,6 +14,7 @@
|
||||
#include "code-stubs.h"
|
||||
#include "x86-utils.h"
|
||||
#include "jit_x86.h"
|
||||
#include "environment.h"
|
||||
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
@ -64,6 +65,12 @@ CodeStubs::CompileInvokeStub()
|
||||
// Align the stack.
|
||||
__ andl(esp, 0xfffffff0);
|
||||
|
||||
// Set up the last piece of the invoke frame. This lets us find the bounds
|
||||
// of the call stack.
|
||||
__ movl(eax, intptr_t(Environment::get()));
|
||||
__ movl(eax, Operand(eax, Environment::offsetOfTopFrame()));
|
||||
__ movl(Operand(eax, InvokeFrame::offsetOfEntrySp()), esp);
|
||||
|
||||
// Call into plugin (align the stack first).
|
||||
__ call(ecx);
|
||||
|
||||
@ -98,17 +105,11 @@ CodeStubs::CompileInvokeStub()
|
||||
__ movl(ecx, Operand(ebp, 8 + 4 * 0)); // ret-path expects ecx = ctx
|
||||
__ jmp(&ret);
|
||||
|
||||
Label timeout;
|
||||
__ bind(&timeout);
|
||||
__ movl(eax, SP_ERROR_TIMEOUT);
|
||||
__ jmp(&error);
|
||||
|
||||
invoke_stub_ = LinkCode(env_, masm);
|
||||
if (!invoke_stub_)
|
||||
return false;
|
||||
|
||||
return_stub_ = reinterpret_cast<uint8_t *>(invoke_stub_) + error.offset();
|
||||
timeout_stub_ = reinterpret_cast<uint8_t *>(invoke_stub_) + timeout.offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
83
sourcepawn/jit/x86/frames-x86.h
Normal file
83
sourcepawn/jit/x86/frames-x86.h
Normal file
@ -0,0 +1,83 @@
|
||||
// vim: set ts=8 sts=2 sw=2 tw=99 et:
|
||||
//
|
||||
// This file is part of SourcePawn.
|
||||
//
|
||||
// SourcePawn is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// SourcePawn is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef _include_sourcepawn_jit_frames_x86_h_
|
||||
#define _include_sourcepawn_jit_frames_x86_h_
|
||||
|
||||
#include <sp_vm_types.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
class PluginContext;
|
||||
|
||||
// This is the layout of the stack in between each scripted function call.
|
||||
struct JitFrame
|
||||
{
|
||||
intptr_t align0;
|
||||
intptr_t align1;
|
||||
ucell_t function_cip;
|
||||
void *return_address;
|
||||
|
||||
static inline const JitFrame *FromSp(const intptr_t *sp) {
|
||||
return reinterpret_cast<const JitFrame *>(sp);
|
||||
}
|
||||
};
|
||||
|
||||
// When we're about to call a native, the stack pointer we store in the exit
|
||||
// frame is such that (sp + sizeof(JitExitFrameForNative)) conforms to this
|
||||
// structure.
|
||||
//
|
||||
// Note that it looks reversed compared to JitFrame because we capture the sp
|
||||
// before saving registers and pushing arguments.
|
||||
struct JitExitFrameForNative
|
||||
{
|
||||
void *return_address;
|
||||
PluginContext *cx;
|
||||
union {
|
||||
uint32_t native_index;
|
||||
SPVM_NATIVE_FUNC fn;
|
||||
} arg;
|
||||
const cell_t *params;
|
||||
cell_t saved_alt;
|
||||
|
||||
static inline const JitExitFrameForNative *FromExitSp(const intptr_t *exit_sp) {
|
||||
return reinterpret_cast<const JitExitFrameForNative *>(
|
||||
reinterpret_cast<const uint8_t *>(exit_sp) - sizeof(JitExitFrameForNative));
|
||||
}
|
||||
};
|
||||
|
||||
// Unlke native frames, the exit_sp for these is created at the base address.
|
||||
struct JitExitFrameForHelper
|
||||
{
|
||||
void *return_address;
|
||||
|
||||
static inline const JitExitFrameForHelper *FromExitSp(const intptr_t *exit_sp) {
|
||||
return reinterpret_cast<const JitExitFrameForHelper *>(exit_sp);
|
||||
}
|
||||
|
||||
bool isCompileFunction() const {
|
||||
return !!return_address;
|
||||
}
|
||||
const JitFrame *prev() const {
|
||||
return reinterpret_cast<const JitFrame *>(this + 1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif // _include_sourcepawn_jit_frames_x86_h_
|
@ -236,6 +236,9 @@ Compiler::emit(int *errp)
|
||||
// an opcode, we bind its corresponding label.
|
||||
__ bind(&jump_map_[cip_ - codeseg]);
|
||||
|
||||
// Save the start of the opcode for emitCipMap().
|
||||
op_cip_ = cip_;
|
||||
|
||||
OPCODE op = (OPCODE)readCell();
|
||||
if (!emitOp(op) || error_ != SP_ERROR_NONE) {
|
||||
*errp = (error_ == SP_ERROR_NONE) ? SP_ERROR_OUT_OF_MEMORY : error_;
|
||||
@ -244,6 +247,17 @@ Compiler::emit(int *errp)
|
||||
}
|
||||
|
||||
emitCallThunks();
|
||||
|
||||
// For each backward jump, emit a little thunk so we can exit from a timeout.
|
||||
// Track the offset of where the thunk is, so the watchdog timer can patch it.
|
||||
for (size_t i = 0; i < backward_jumps_.length(); i++) {
|
||||
BackwardJump &jump = backward_jumps_[i];
|
||||
jump.timeout_offset = masm.pc();
|
||||
__ call(&throw_timeout_);
|
||||
emitCipMapping(jump.cip);
|
||||
}
|
||||
|
||||
// This has to come last.
|
||||
emitErrorPaths();
|
||||
|
||||
uint8_t *code = LinkCode(env_, masm);
|
||||
@ -255,44 +269,69 @@ Compiler::emit(int *errp)
|
||||
AutoPtr<FixedArray<LoopEdge>> edges(
|
||||
new FixedArray<LoopEdge>(backward_jumps_.length()));
|
||||
for (size_t i = 0; i < backward_jumps_.length(); i++) {
|
||||
edges->at(i).offset = backward_jumps_[i];
|
||||
edges->at(i).disp32 = *reinterpret_cast<int32_t *>(code + edges->at(i).offset - 4);
|
||||
const BackwardJump &jump = backward_jumps_[i];
|
||||
edges->at(i).offset = jump.pc;
|
||||
edges->at(i).disp32 = int32_t(jump.timeout_offset) - int32_t(jump.pc);
|
||||
}
|
||||
|
||||
return new CompiledFunction(code, pcode_start_, edges.take());
|
||||
AutoPtr<FixedArray<CipMapEntry>> cipmap(
|
||||
new FixedArray<CipMapEntry>(cip_map_.length()));
|
||||
memcpy(cipmap->buffer(), cip_map_.buffer(), cip_map_.length() * sizeof(CipMapEntry));
|
||||
|
||||
return new CompiledFunction(code, masm.length(), pcode_start_, edges.take(), cipmap.take());
|
||||
}
|
||||
|
||||
// Helpers for invoking context members.
|
||||
// No exit frame - error code is returned directly.
|
||||
static int
|
||||
InvokePushTracker(PluginContext *cx, uint32_t amount)
|
||||
{
|
||||
return cx->pushTracker(amount);
|
||||
}
|
||||
|
||||
// No exit frame - error code is returned directly.
|
||||
static int
|
||||
InvokePopTrackerAndSetHeap(PluginContext *cx)
|
||||
{
|
||||
return cx->popTrackerAndSetHeap();
|
||||
}
|
||||
|
||||
// Error code must be checked in the environment.
|
||||
static cell_t
|
||||
InvokeNativeHelper(PluginContext *cx, ucell_t native_idx, cell_t *params)
|
||||
{
|
||||
return cx->invokeNative(native_idx, params);
|
||||
}
|
||||
|
||||
// Error code must be checked in the environment.
|
||||
static cell_t
|
||||
InvokeBoundNativeHelper(PluginContext *cx, SPVM_NATIVE_FUNC fn, cell_t *params)
|
||||
{
|
||||
return cx->invokeBoundNative(fn, params);
|
||||
}
|
||||
|
||||
// No exit frame - error code is returned directly.
|
||||
static int
|
||||
InvokeGenerateFullArray(PluginContext *cx, uint32_t argc, cell_t *argv, int autozero)
|
||||
{
|
||||
return cx->generateFullArray(argc, argv, autozero);
|
||||
}
|
||||
|
||||
// Exit frame is a JitExitFrameForHelper.
|
||||
static void
|
||||
InvokeReportError(int err)
|
||||
{
|
||||
Environment::get()->ReportError(err);
|
||||
}
|
||||
|
||||
// Exit frame is a JitExitFrameForHelper. This is a special function since we
|
||||
// have to notify the watchdog timer that we're unblocked.
|
||||
static void
|
||||
InvokeReportTimeout()
|
||||
{
|
||||
Environment::get()->watchdog()->NotifyTimeoutReceived();
|
||||
InvokeReportError(SP_ERROR_TIMEOUT);
|
||||
}
|
||||
|
||||
bool
|
||||
Compiler::emitOp(OPCODE op)
|
||||
{
|
||||
@ -450,8 +489,16 @@ Compiler::emitOp(OPCODE op)
|
||||
__ subl(tmp, dat);
|
||||
__ movl(Operand(frmAddr()), tmp);
|
||||
|
||||
// Align the stack to 16-bytes (each call adds 4 bytes).
|
||||
__ subl(esp, 12);
|
||||
// Store the function cip for stack traces.
|
||||
__ push(pcode_start_);
|
||||
|
||||
// Align the stack to 16-bytes (each call adds 8 bytes).
|
||||
__ subl(esp, 8);
|
||||
#if defined(DEBUG)
|
||||
// Debug guards.
|
||||
__ movl(Operand(esp, 0), 0xffaaee00);
|
||||
__ movl(Operand(esp, 4), 0xffaaee04);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case OP_IDXADDR_B:
|
||||
@ -769,14 +816,14 @@ Compiler::emitOp(OPCODE op)
|
||||
|
||||
// Guard against divide-by-zero.
|
||||
__ testl(divisor, divisor);
|
||||
__ j(zero, &error_divide_by_zero_);
|
||||
jumpOnError(zero, SP_ERROR_DIVIDE_BY_ZERO);
|
||||
|
||||
// A more subtle case; -INT_MIN / -1 yields an overflow exception.
|
||||
Label ok;
|
||||
__ cmpl(divisor, -1);
|
||||
__ j(not_equal, &ok);
|
||||
__ cmpl(dividend, 0x80000000);
|
||||
__ j(equal, &error_integer_overflow_);
|
||||
jumpOnError(equal, SP_ERROR_INTEGER_OVERFLOW);
|
||||
__ bind(&ok);
|
||||
|
||||
// Now we can actually perform the divide.
|
||||
@ -1078,13 +1125,13 @@ Compiler::emitOp(OPCODE op)
|
||||
if (amount > 0) {
|
||||
// Check if the stack went beyond the stack top - usually a compiler error.
|
||||
__ cmpl(stk, intptr_t(context_->memory() + context_->HeapSize()));
|
||||
__ j(not_below, &error_stack_min_);
|
||||
jumpOnError(not_below, SP_ERROR_STACKMIN);
|
||||
} else {
|
||||
// Check if the stack is going to collide with the heap.
|
||||
__ movl(tmp, Operand(hpAddr()));
|
||||
__ lea(tmp, Operand(dat, ecx, NoScale, STACK_MARGIN));
|
||||
__ cmpl(stk, tmp);
|
||||
__ j(below, &error_stack_low_);
|
||||
jumpOnError(below, SP_ERROR_STACKLOW);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1097,12 +1144,12 @@ Compiler::emitOp(OPCODE op)
|
||||
|
||||
if (amount < 0) {
|
||||
__ cmpl(Operand(hpAddr()), context_->DataSize());
|
||||
__ j(below, &error_heap_min_);
|
||||
jumpOnError(below, SP_ERROR_HEAPMIN);
|
||||
} else {
|
||||
__ movl(tmp, Operand(hpAddr()));
|
||||
__ lea(tmp, Operand(dat, ecx, NoScale, STACK_MARGIN));
|
||||
__ cmpl(tmp, stk);
|
||||
__ j(above, &error_heap_low_);
|
||||
jumpOnError(above, SP_ERROR_HEAPLOW);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1114,7 +1161,7 @@ Compiler::emitOp(OPCODE op)
|
||||
return false;
|
||||
if (target->bound()) {
|
||||
__ jmp32(target);
|
||||
backward_jumps_.append(masm.pc());
|
||||
backward_jumps_.append(BackwardJump(masm.pc(), op_cip_));
|
||||
} else {
|
||||
__ jmp(target);
|
||||
}
|
||||
@ -1131,7 +1178,7 @@ Compiler::emitOp(OPCODE op)
|
||||
__ testl(pri, pri);
|
||||
if (target->bound()) {
|
||||
__ j32(cc, target);
|
||||
backward_jumps_.append(masm.pc());
|
||||
backward_jumps_.append(BackwardJump(masm.pc(), op_cip_));
|
||||
} else {
|
||||
__ j(cc, target);
|
||||
}
|
||||
@ -1152,7 +1199,7 @@ Compiler::emitOp(OPCODE op)
|
||||
__ cmpl(pri, alt);
|
||||
if (target->bound()) {
|
||||
__ j32(cc, target);
|
||||
backward_jumps_.append(masm.pc());
|
||||
backward_jumps_.append(BackwardJump(masm.pc(), op_cip_));
|
||||
} else {
|
||||
__ j(cc, target);
|
||||
}
|
||||
@ -1171,7 +1218,7 @@ Compiler::emitOp(OPCODE op)
|
||||
__ call(ExternalAddress((void *)InvokePushTracker));
|
||||
__ addl(esp, 8);
|
||||
__ testl(eax, eax);
|
||||
__ j(not_zero, &extern_error_);
|
||||
jumpOnError(not_zero);
|
||||
|
||||
__ pop(alt);
|
||||
__ pop(pri);
|
||||
@ -1189,31 +1236,32 @@ Compiler::emitOp(OPCODE op)
|
||||
__ call(ExternalAddress((void *)InvokePopTrackerAndSetHeap));
|
||||
__ addl(esp, 4);
|
||||
__ testl(eax, eax);
|
||||
__ j(not_zero, &extern_error_);
|
||||
jumpOnError(not_zero);
|
||||
|
||||
__ pop(alt);
|
||||
__ pop(pri);
|
||||
break;
|
||||
}
|
||||
|
||||
// This opcode is used to note where line breaks occur. We don't support
|
||||
// live debugging, and if we did, we could build this map from the lines
|
||||
// table. So we don't generate any code here.
|
||||
case OP_BREAK:
|
||||
{
|
||||
cell_t cip = uintptr_t(cip_ - 1) - uintptr_t(rt_->code().bytes);
|
||||
__ movl(Operand(cipAddr()), cip);
|
||||
break;
|
||||
}
|
||||
|
||||
// This should never be hit.
|
||||
case OP_HALT:
|
||||
__ align(16);
|
||||
__ movl(pri, readCell());
|
||||
__ jmp(&extern_error_);
|
||||
__ testl(eax, eax);
|
||||
jumpOnError(not_zero);
|
||||
break;
|
||||
|
||||
case OP_BOUNDS:
|
||||
{
|
||||
cell_t value = readCell();
|
||||
__ cmpl(eax, value);
|
||||
__ j(above, &error_bounds_);
|
||||
jumpOnError(above, SP_ERROR_ARRAY_BOUNDS);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1281,7 +1329,7 @@ Compiler::emitCheckAddress(Register reg)
|
||||
{
|
||||
// Check if we're in memory bounds.
|
||||
__ cmpl(reg, context_->HeapSize());
|
||||
__ j(not_below, &error_memaccess_);
|
||||
jumpOnError(not_below, SP_ERROR_MEMACCESS);
|
||||
|
||||
// Check if we're in the invalid region between hp and sp.
|
||||
Label done;
|
||||
@ -1289,7 +1337,7 @@ Compiler::emitCheckAddress(Register reg)
|
||||
__ j(below, &done);
|
||||
__ lea(tmp, Operand(dat, reg, NoScale));
|
||||
__ cmpl(tmp, stk);
|
||||
__ j(below, &error_memaccess_);
|
||||
jumpOnError(below, SP_ERROR_MEMACCESS);
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
@ -1308,7 +1356,7 @@ Compiler::emitGenArray(bool autozero)
|
||||
__ movl(Operand(hpAddr()), alt);
|
||||
__ addl(alt, dat);
|
||||
__ cmpl(alt, stk);
|
||||
__ j(not_below, &error_heap_low_);
|
||||
jumpOnError(not_below, SP_ERROR_HEAPLOW);
|
||||
|
||||
__ shll(tmp, 2);
|
||||
__ push(tmp);
|
||||
@ -1318,7 +1366,7 @@ Compiler::emitGenArray(bool autozero)
|
||||
__ pop(tmp);
|
||||
__ shrl(tmp, 2);
|
||||
__ testl(eax, eax);
|
||||
__ j(not_zero, &extern_error_);
|
||||
jumpOnError(not_zero);
|
||||
|
||||
if (autozero) {
|
||||
// Note - tmp is ecx and still intact.
|
||||
@ -1347,7 +1395,7 @@ Compiler::emitGenArray(bool autozero)
|
||||
__ pop(tmp);
|
||||
|
||||
__ testl(eax, eax);
|
||||
__ j(not_zero, &extern_error_);
|
||||
jumpOnError(not_zero);
|
||||
|
||||
// Move tmp back to pri, remove pushed args.
|
||||
__ movl(pri, tmp);
|
||||
@ -1367,25 +1415,6 @@ Compiler::emitCall()
|
||||
return false;
|
||||
}
|
||||
|
||||
// eax = context
|
||||
// ecx = rp
|
||||
__ movl(eax, intptr_t(rt_->GetBaseContext()));
|
||||
__ movl(ecx, Operand(eax, PluginContext::offsetOfRp()));
|
||||
|
||||
// Check if the return stack is used up.
|
||||
__ cmpl(ecx, SP_MAX_RETURN_STACK);
|
||||
__ j(not_below, &error_stack_low_);
|
||||
|
||||
// Add to the return stack.
|
||||
uintptr_t cip = uintptr_t(cip_ - 2) - uintptr_t(rt_->code().bytes);
|
||||
__ movl(Operand(eax, ecx, ScaleFour, PluginContext::offsetOfRstkCips()), cip);
|
||||
|
||||
// Increment the return stack pointer.
|
||||
__ addl(Operand(eax, PluginContext::offsetOfRp()), 1);
|
||||
|
||||
// Store the CIP of the function we're about to call.
|
||||
__ movl(Operand(cipAddr()), offset);
|
||||
|
||||
CompiledFunction *fun = rt_->GetJittedFunctionByOffset(offset);
|
||||
if (!fun) {
|
||||
// Need to emit a delayed thunk.
|
||||
@ -1398,12 +1427,8 @@ Compiler::emitCall()
|
||||
__ call(ExternalAddress(fun->GetEntryAddress()));
|
||||
}
|
||||
|
||||
// Restore the last cip.
|
||||
__ movl(Operand(cipAddr()), cip);
|
||||
|
||||
// Mark us as leaving the last frame.
|
||||
__ movl(tmp, intptr_t(rt_->GetBaseContext()));
|
||||
__ subl(Operand(tmp, PluginContext::offsetOfRp()), 1);
|
||||
// Map the return address to the cip that started this call.
|
||||
emitCipMapping(op_cip_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1415,15 +1440,27 @@ Compiler::emitCallThunks()
|
||||
|
||||
Label error;
|
||||
__ bind(&thunk->call);
|
||||
// Huge hack - get the return address, since that is the call that we
|
||||
// need to patch.
|
||||
|
||||
// Get the return address, since that is the call that we need to patch.
|
||||
__ movl(eax, Operand(esp, 0));
|
||||
|
||||
// Push an OP_PROC frame as if we already called the function. This helps
|
||||
// error reporting.
|
||||
__ push(thunk->pcode_offset);
|
||||
__ subl(esp, 8);
|
||||
|
||||
// Create the exit frame, then align the stack.
|
||||
__ push(0);
|
||||
__ movl(ecx, intptr_t(&Environment::get()->exit_frame()));
|
||||
__ movl(Operand(ecx, ExitFrame::offsetOfExitNative()), -1);
|
||||
__ movl(Operand(ecx, ExitFrame::offsetOfExitSp()), esp);
|
||||
|
||||
// We need to push 4 arguments, and one of them will need an extra word
|
||||
// on the stack. Allocate a big block so we're aligned, subtracting
|
||||
// 4 because we got here via a call.
|
||||
// on the stack. Allocate a big block so we're aligned.
|
||||
//
|
||||
// Note: we add 12 since the push above misaligned the stack.
|
||||
static const size_t kStackNeeded = 5 * sizeof(void *);
|
||||
static const size_t kStackReserve = ke::Align(kStackNeeded, 16) - sizeof(void *);
|
||||
static const size_t kStackReserve = ke::Align(kStackNeeded, 16) + 3 * sizeof(void *);
|
||||
__ subl(esp, kStackReserve);
|
||||
|
||||
// Set arguments.
|
||||
@ -1435,14 +1472,10 @@ Compiler::emitCallThunks()
|
||||
|
||||
__ call(ExternalAddress((void *)CompileFromThunk));
|
||||
__ movl(edx, Operand(esp, 4 * sizeof(void *)));
|
||||
__ addl(esp, kStackReserve);
|
||||
__ addl(esp, kStackReserve + 4 * sizeof(void *)); // Drop the exit frame and fake frame.
|
||||
__ testl(eax, eax);
|
||||
__ j(not_zero, &error);
|
||||
jumpOnError(not_zero);
|
||||
__ jmp(edx);
|
||||
|
||||
__ bind(&error);
|
||||
__ movl(Operand(cipAddr()), thunk->pcode_offset);
|
||||
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1482,18 +1515,23 @@ Compiler::emitNativeCall(OPCODE op)
|
||||
__ subl(stk, 4);
|
||||
}
|
||||
|
||||
// Create the exit frame. This is a JitExitFrameForNative, so everything we
|
||||
// push up to the return address of the call instruction is reflected in
|
||||
// that structure.
|
||||
__ movl(eax, intptr_t(&Environment::get()->exit_frame()));
|
||||
__ movl(Operand(eax, ExitFrame::offsetOfExitNative()), native_index);
|
||||
__ movl(Operand(eax, ExitFrame::offsetOfExitSp()), esp);
|
||||
|
||||
// Save registers.
|
||||
__ push(edx);
|
||||
|
||||
// Push the last parameter for the C++ function.
|
||||
__ push(stk);
|
||||
|
||||
__ movl(eax, intptr_t(rt_->GetBaseContext()));
|
||||
__ movl(Operand(eax, PluginContext::offsetOfLastNative()), native_index);
|
||||
|
||||
// Relocate our absolute stk to be dat-relative, and update the context's
|
||||
// view.
|
||||
__ subl(stk, dat);
|
||||
__ movl(eax, intptr_t(context_));
|
||||
__ movl(Operand(eax, PluginContext::offsetOfSp()), stk);
|
||||
|
||||
const sp_native_t *native = rt_->GetNative(native_index);
|
||||
@ -1512,11 +1550,14 @@ Compiler::emitNativeCall(OPCODE op)
|
||||
__ call(ExternalAddress((void *)InvokeBoundNativeHelper));
|
||||
}
|
||||
|
||||
// Check for errors.
|
||||
__ movl(ecx, intptr_t(rt_->GetBaseContext()));
|
||||
__ movl(ecx, Operand(ecx, PluginContext::offsetOfNativeError()));
|
||||
__ testl(ecx, ecx);
|
||||
__ j(not_zero, &extern_error_);
|
||||
// Map the return address to the cip that initiated this call.
|
||||
emitCipMapping(op_cip_);
|
||||
|
||||
// Check for errors. Note we jump directly to the return stub since the
|
||||
// error has already been reported.
|
||||
__ movl(ecx, intptr_t(Environment::get()));
|
||||
__ cmpl(Operand(ecx, Environment::offsetOfExceptionCode()), 0);
|
||||
__ j(not_zero, &return_reported_error_);
|
||||
|
||||
// Restore local state.
|
||||
__ addl(stk, dat);
|
||||
@ -1631,16 +1672,6 @@ Compiler::emitSwitch()
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Compiler::emitErrorPath(Label *dest, int code)
|
||||
{
|
||||
if (dest->used()) {
|
||||
__ bind(dest);
|
||||
__ movl(eax, code);
|
||||
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Compiler::emitFloatCmp(ConditionCode cc)
|
||||
{
|
||||
@ -1691,22 +1722,112 @@ Compiler::emitFloatCmp(ConditionCode cc)
|
||||
__ addl(stk, 8);
|
||||
}
|
||||
|
||||
void
|
||||
Compiler::jumpOnError(ConditionCode cc, int err)
|
||||
{
|
||||
// Note: we accept 0 for err. In this case we expect the error to be in eax.
|
||||
{
|
||||
ErrorPath path(op_cip_, err);
|
||||
error_paths_.append(path);
|
||||
}
|
||||
|
||||
ErrorPath &path = error_paths_.back();
|
||||
__ j(cc, &path.label);
|
||||
}
|
||||
|
||||
void
|
||||
Compiler::emitErrorPaths()
|
||||
{
|
||||
emitErrorPath(&error_divide_by_zero_, SP_ERROR_DIVIDE_BY_ZERO);
|
||||
emitErrorPath(&error_stack_low_, SP_ERROR_STACKLOW);
|
||||
emitErrorPath(&error_stack_min_, SP_ERROR_STACKMIN);
|
||||
emitErrorPath(&error_bounds_, SP_ERROR_ARRAY_BOUNDS);
|
||||
emitErrorPath(&error_memaccess_, SP_ERROR_MEMACCESS);
|
||||
emitErrorPath(&error_heap_low_, SP_ERROR_HEAPLOW);
|
||||
emitErrorPath(&error_heap_min_, SP_ERROR_HEAPMIN);
|
||||
emitErrorPath(&error_integer_overflow_, SP_ERROR_INTEGER_OVERFLOW);
|
||||
// For each path that had an error check, bind it to an error routine and
|
||||
// add it to the cip map. What we'll get is something like:
|
||||
//
|
||||
// cmp dividend, 0
|
||||
// jz error_thunk_0
|
||||
//
|
||||
// error_thunk_0:
|
||||
// call integer_overflow
|
||||
//
|
||||
// integer_overflow:
|
||||
// mov eax, SP_ERROR_DIVIDE_BY_ZERO
|
||||
// jmp report_error
|
||||
//
|
||||
// report_error:
|
||||
// create exit frame
|
||||
// push eax
|
||||
// call InvokeReportError(int err)
|
||||
//
|
||||
for (size_t i = 0; i < error_paths_.length(); i++) {
|
||||
ErrorPath &path = error_paths_[i];
|
||||
|
||||
if (extern_error_.used()) {
|
||||
__ bind(&extern_error_);
|
||||
__ movl(eax, intptr_t(rt_->GetBaseContext()));
|
||||
__ movl(eax, Operand(eax, PluginContext::offsetOfNativeError()));
|
||||
// If there's no error code, it should be in eax. Otherwise we'll jump to
|
||||
// a path that sets eax to a hardcoded value.
|
||||
__ bind(&path.label);
|
||||
if (path.err == 0)
|
||||
__ call(&report_error_);
|
||||
else
|
||||
__ call(&throw_error_code_[path.err]);
|
||||
|
||||
emitCipMapping(path.cip);
|
||||
}
|
||||
|
||||
emitThrowPathIfNeeded(SP_ERROR_DIVIDE_BY_ZERO);
|
||||
emitThrowPathIfNeeded(SP_ERROR_STACKLOW);
|
||||
emitThrowPathIfNeeded(SP_ERROR_STACKMIN);
|
||||
emitThrowPathIfNeeded(SP_ERROR_ARRAY_BOUNDS);
|
||||
emitThrowPathIfNeeded(SP_ERROR_MEMACCESS);
|
||||
emitThrowPathIfNeeded(SP_ERROR_HEAPLOW);
|
||||
emitThrowPathIfNeeded(SP_ERROR_HEAPMIN);
|
||||
emitThrowPathIfNeeded(SP_ERROR_INTEGER_OVERFLOW);
|
||||
|
||||
if (report_error_.used()) {
|
||||
__ bind(&report_error_);
|
||||
|
||||
// Create the exit frame. We always get here through a call from the opcode
|
||||
// (and always via an out-of-line thunk).
|
||||
__ movl(ecx, intptr_t(&Environment::get()->exit_frame()));
|
||||
__ movl(Operand(ecx, ExitFrame::offsetOfExitNative()), -1);
|
||||
__ movl(Operand(ecx, ExitFrame::offsetOfExitSp()), esp);
|
||||
|
||||
// Since the return stub wipes out the stack, we don't need to subl after
|
||||
// the call.
|
||||
__ push(eax);
|
||||
__ call(ExternalAddress((void *)InvokeReportError));
|
||||
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||
}
|
||||
|
||||
// We get here if we know an exception is already pending.
|
||||
if (return_reported_error_.used()) {
|
||||
__ bind(&return_reported_error_);
|
||||
__ movl(eax, intptr_t(Environment::get()));
|
||||
__ movl(eax, Operand(eax, Environment::offsetOfExceptionCode()));
|
||||
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||
}
|
||||
|
||||
// The timeout uses a special stub.
|
||||
if (throw_timeout_.used()) {
|
||||
__ bind(&throw_timeout_);
|
||||
|
||||
// Create the exit frame.
|
||||
__ movl(ecx, intptr_t(&Environment::get()->exit_frame()));
|
||||
__ movl(Operand(ecx, ExitFrame::offsetOfExitNative()), -1);
|
||||
__ movl(Operand(ecx, ExitFrame::offsetOfExitSp()), esp);
|
||||
|
||||
// Since the return stub wipes out the stack, we don't need to subl after
|
||||
// the call.
|
||||
__ call(ExternalAddress((void *)InvokeReportTimeout));
|
||||
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Compiler::emitThrowPathIfNeeded(int err)
|
||||
{
|
||||
assert(err < SP_MAX_ERROR_CODES);
|
||||
|
||||
if (!throw_error_code_[err].used())
|
||||
return;
|
||||
|
||||
__ bind(&throw_error_code_[err]);
|
||||
__ movl(eax, err);
|
||||
__ jmp(&report_error_);
|
||||
}
|
||||
|
@ -33,6 +33,36 @@ class LegacyImage;
|
||||
class Environment;
|
||||
class CompiledFunction;
|
||||
|
||||
struct ErrorPath
|
||||
{
|
||||
SilentLabel label;
|
||||
const cell_t *cip;
|
||||
int err;
|
||||
|
||||
ErrorPath(const cell_t *cip, int err)
|
||||
: cip(cip),
|
||||
err(err)
|
||||
{}
|
||||
ErrorPath()
|
||||
{}
|
||||
};
|
||||
|
||||
struct BackwardJump {
|
||||
// The pc at the jump instruction (i.e. after it).
|
||||
uint32_t pc;
|
||||
// The cip of the jump.
|
||||
const cell_t *cip;
|
||||
// The offset of the timeout thunk. This is filled in at the end.
|
||||
uint32_t timeout_offset;
|
||||
|
||||
BackwardJump()
|
||||
{}
|
||||
BackwardJump(uint32_t pc, const cell_t *cip)
|
||||
: pc(pc),
|
||||
cip(cip)
|
||||
{}
|
||||
};
|
||||
|
||||
#define JIT_INLINE_ERRORCHECKS (1<<0)
|
||||
#define JIT_INLINE_NATIVES (1<<1)
|
||||
#define STACK_MARGIN 64 //8 parameters of safety, I guess
|
||||
@ -42,21 +72,9 @@ class CompiledFunction;
|
||||
|
||||
#define sDIMEN_MAX 5 //this must mirror what the compiler has.
|
||||
|
||||
typedef struct funcinfo_s
|
||||
{
|
||||
unsigned int magic;
|
||||
unsigned int index;
|
||||
} funcinfo_t;
|
||||
|
||||
typedef struct functracker_s
|
||||
{
|
||||
unsigned int num_functions;
|
||||
unsigned int code_size;
|
||||
} functracker_t;
|
||||
|
||||
struct CallThunk
|
||||
{
|
||||
Label call;
|
||||
SilentLabel call;
|
||||
cell_t pcode_offset;
|
||||
|
||||
CallThunk(cell_t pcode_offset)
|
||||
@ -89,10 +107,9 @@ class Compiler
|
||||
void emitErrorPath(Label *dest, int code);
|
||||
void emitErrorPaths();
|
||||
void emitFloatCmp(ConditionCode cc);
|
||||
void jumpOnError(ConditionCode cc, int err = 0);
|
||||
void emitThrowPathIfNeeded(int err);
|
||||
|
||||
ExternalAddress cipAddr() {
|
||||
return ExternalAddress(context_->addressOfCip());
|
||||
}
|
||||
ExternalAddress hpAddr() {
|
||||
return ExternalAddress(context_->addressOfHp());
|
||||
}
|
||||
@ -100,6 +117,16 @@ class Compiler
|
||||
return ExternalAddress(context_->addressOfFrm());
|
||||
}
|
||||
|
||||
// Map a return address (i.e. an exit point from a function) to its source
|
||||
// cip. This lets us avoid tracking the cip during runtime. These are
|
||||
// sorted by definition since we assemble and emit in forward order.
|
||||
void emitCipMapping(const cell_t *cip) {
|
||||
CipMapEntry entry;
|
||||
entry.cipoffs = uintptr_t(cip) - uintptr_t(code_start_);
|
||||
entry.pcoffs = masm.pc();
|
||||
cip_map_.append(entry);
|
||||
}
|
||||
|
||||
private:
|
||||
AssemblerX86 masm;
|
||||
Environment *env_;
|
||||
@ -110,20 +137,19 @@ class Compiler
|
||||
uint32_t pcode_start_;
|
||||
const cell_t *code_start_;
|
||||
const cell_t *cip_;
|
||||
const cell_t *op_cip_;
|
||||
const cell_t *code_end_;
|
||||
Label *jump_map_;
|
||||
ke::Vector<size_t> backward_jumps_;
|
||||
ke::Vector<BackwardJump> backward_jumps_;
|
||||
|
||||
// Errors
|
||||
Label error_bounds_;
|
||||
Label error_heap_low_;
|
||||
Label error_heap_min_;
|
||||
Label error_stack_low_;
|
||||
Label error_stack_min_;
|
||||
Label error_divide_by_zero_;
|
||||
Label error_memaccess_;
|
||||
Label error_integer_overflow_;
|
||||
Label extern_error_;
|
||||
ke::Vector<CipMapEntry> cip_map_;
|
||||
|
||||
// Errors.
|
||||
ke::Vector<ErrorPath> error_paths_;
|
||||
Label throw_timeout_;
|
||||
Label throw_error_code_[SP_MAX_ERROR_CODES];
|
||||
Label report_error_;
|
||||
Label return_reported_error_;
|
||||
|
||||
ke::Vector<CallThunk *> thunks_; //:TODO: free
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user