Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors. The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError. Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin. Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster. These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
This commit is contained in:
parent
04827466b0
commit
a1afa23bc4
@ -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,11 +150,12 @@ 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;
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One byte for null terminator, one for newline */
|
||||
@ -171,11 +172,12 @@ 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;
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer) - 2, pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One byte for null terminator, one for newline */
|
||||
@ -211,11 +213,12 @@ 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;
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
engine->ClientCommand(pPlayer->GetEdict(), buffer);
|
||||
@ -240,11 +243,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
engine->FakeClientCommand(pPlayer->GetEdict(), buffer);
|
||||
@ -258,11 +261,13 @@ 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;
|
||||
{
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/* If we're printing to the server, shortcut out */
|
||||
|
@ -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];
|
||||
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
pPlugin->SetErrorState(Plugin_Failed, "%s", str);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "Formatting error (%s)", str);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException()) {
|
||||
pPlugin->SetErrorState(Plugin_Failed, "%s", str);
|
||||
return 0;
|
||||
}
|
||||
pPlugin->SetErrorState(Plugin_Failed, "%s", buffer);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
|
||||
pContext->ReportFatalError("%s", buffer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,12 +508,12 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
|
||||
|
||||
@ -536,14 +537,15 @@ static cell_t LogToFile(IPluginContext *pContext, const cell_t *params)
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
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];
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException()) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(fp, "%s", buffer);
|
||||
|
||||
@ -648,21 +651,27 @@ static cell_t RequireFeature(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
if (sharesys->TestFeature(pContext->GetRuntime(), type, name) != FeatureStatus_Available)
|
||||
{
|
||||
char buffer[255];
|
||||
char *msg = buffer;
|
||||
char default_message[255];
|
||||
char buffer[255];
|
||||
char *msg = buffer;
|
||||
char default_message[255];
|
||||
SMPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
|
||||
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE || 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);
|
||||
}
|
||||
|
||||
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
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);
|
||||
|
||||
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];
|
||||
smcore.atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
return 0;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
smcore.atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
|
||||
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);
|
||||
|
||||
smcore.atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
return 0;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
smcore.atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g_pSM->BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
|
||||
}
|
||||
@ -808,12 +814,13 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len >= sizeof(buffer)-2)
|
||||
@ -835,11 +842,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
|
||||
@ -853,11 +860,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
|
||||
@ -900,9 +907,12 @@ static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
char buffer[2048];
|
||||
g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
return 0;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
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);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
return 0;
|
||||
{
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
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,11 +1087,12 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s\n", tag, buffer);
|
||||
@ -1102,11 +1103,12 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s\n", tag, buffer);
|
||||
@ -1141,11 +1143,12 @@ static cell_t _ShowActivity(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s: %s", tag, newsign, buffer);
|
||||
@ -1165,11 +1168,12 @@ static cell_t _ShowActivity(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s: %s", tag, newsign, buffer);
|
||||
@ -1210,11 +1214,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't display directly to the console because the chat text
|
||||
@ -1227,11 +1231,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s\n", tag, buffer);
|
||||
@ -1266,11 +1270,12 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s: %s", tag, newsign, buffer);
|
||||
@ -1290,11 +1295,12 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
|
||||
{
|
||||
newsign = name;
|
||||
}
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
|
||||
if (pContext->GetLastNativeError() != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pSM->Format(message, sizeof(message), "%s%s: %s", tag, newsign, buffer);
|
||||
@ -1350,11 +1356,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pPlayer->IsFakeClient())
|
||||
@ -1387,11 +1393,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
pPlayer->Kick(buffer);
|
||||
|
@ -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,11 +909,12 @@ 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;
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One byte for null terminator, one for newline */
|
||||
@ -965,11 +966,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_HL2.AddToFakeCliCmdQueue(params[1], GetPlayerUserId(pPlayer->GetEdict()), buffer);
|
||||
|
@ -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,12 +321,12 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_HL2.TextMsg(client, HUD_PRINTTALK, buffer))
|
||||
@ -355,12 +355,12 @@ 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)
|
||||
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_HL2.TextMsg(client, HUD_PRINTCENTER, buffer))
|
||||
@ -389,12 +389,11 @@ 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)
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_HL2.HintTextMsg(client, buffer))
|
||||
|
@ -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,10 +415,12 @@ 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)
|
||||
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_hud_params.channel = s_HudMsgHelpers.AutoSelectChannel(client, obj);
|
||||
@ -488,10 +490,12 @@ 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)
|
||||
|
||||
{
|
||||
return 0;
|
||||
DetectExceptions eh(pContext);
|
||||
g_SourceMod.FormatString(message_buffer, sizeof(message_buffer), pContext, params, 3);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (params[2] == -1)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -309,7 +323,7 @@ namespace SourcePawn
|
||||
*
|
||||
* @param index Unused.
|
||||
* @param native Unused.
|
||||
* @return Returns SP_ERROR_PARAM.
|
||||
* @return Returns SP_ERROR_PARAM.
|
||||
*/
|
||||
virtual int GetNativeByIndex(uint32_t index, sp_native_t **native) =0;
|
||||
|
||||
@ -409,7 +423,7 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief If |co| is non-NULL, destroys |co|. No other action is taken.
|
||||
*
|
||||
* @return Returns SP_ERROR_NONE.
|
||||
* @return Returns SP_ERROR_NONE.
|
||||
*/
|
||||
virtual int ApplyCompilationOptions(ICompilation *co) =0;
|
||||
|
||||
@ -448,13 +462,13 @@ namespace SourcePawn
|
||||
*/
|
||||
virtual unsigned char *GetDataHash() =0;
|
||||
|
||||
/**
|
||||
* @brief Update the native binding at the given index.
|
||||
*
|
||||
* @param pfn Native function pointer.
|
||||
* @param flags Native flags.
|
||||
* @param user User data pointer.
|
||||
*/
|
||||
/**
|
||||
* @brief Update the native binding at the given index.
|
||||
*
|
||||
* @param pfn Native function pointer.
|
||||
* @param flags Native flags.
|
||||
* @param user User data pointer.
|
||||
*/
|
||||
virtual int UpdateNativeBinding(uint32_t index, SPVM_NATIVE_FUNC pfn, uint32_t flags, void *data) = 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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -559,7 +578,7 @@ namespace SourcePawn
|
||||
*
|
||||
* @param index Unused.
|
||||
* @param native Unused.
|
||||
* @return Returns SP_ERROR_PARAM.
|
||||
* @return Returns SP_ERROR_PARAM.
|
||||
*/
|
||||
virtual int GetNativeByIndex(uint32_t index, sp_native_t **native) =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,
|
||||
@ -832,9 +854,12 @@ 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 Return a pointer to the ISourcePawnEngine2 that is active.
|
||||
* This is a convenience function.
|
||||
*
|
||||
* @return API pointer.
|
||||
*/
|
||||
virtual ISourcePawnEngine2 *APIv2() = 0;
|
||||
|
||||
/**
|
||||
* @brief Information about a position in a call stack.
|
||||
*/
|
||||
struct CallStackInfo
|
||||
{
|
||||
const char *filename; /**< NULL if not found */
|
||||
unsigned int line; /**< 0 if not found */
|
||||
const char *function; /**< NULL if not found */
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -983,21 +1108,21 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Return the name of the profiling tool.
|
||||
*
|
||||
* @return Profiling tool name.
|
||||
* @return Profiling tool name.
|
||||
*/
|
||||
virtual const char *Name() = 0;
|
||||
|
||||
/**
|
||||
* @brief Description of the profiler.
|
||||
*
|
||||
* @return Description.
|
||||
* @return Description.
|
||||
*/
|
||||
virtual const char *Description() = 0;
|
||||
|
||||
/**
|
||||
* @brief Called to render help text.
|
||||
*
|
||||
* @param render Function to render one line of text.
|
||||
* @param render Function to render one line of text.
|
||||
*/
|
||||
virtual void RenderHelp(void (*render)(const char *fmt, ...)) = 0;
|
||||
|
||||
@ -1013,7 +1138,7 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Initiate a stop command.
|
||||
*
|
||||
* @param render Function to render any help messages.
|
||||
* @param render Function to render any help messages.
|
||||
*/
|
||||
virtual void Stop(void (*render)(const char *fmt, ...)) = 0;
|
||||
|
||||
@ -1029,14 +1154,14 @@ namespace SourcePawn
|
||||
/**
|
||||
* @brief Returns whether or not the profiler is currently profiling.
|
||||
*
|
||||
* @return True if active, false otherwise.
|
||||
* @return True if active, false otherwise.
|
||||
*/
|
||||
virtual bool IsActive() = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether the profiler is attached.
|
||||
*
|
||||
* @return True if attached, false otherwise.
|
||||
* @return True if attached, false otherwise.
|
||||
*/
|
||||
virtual bool IsAttached() = 0;
|
||||
|
||||
@ -1045,8 +1170,8 @@ namespace SourcePawn
|
||||
*
|
||||
* LeaveScope() mus be called exactly once for each call to EnterScope().
|
||||
*
|
||||
* @param group A named budget group, or NULL for the default.
|
||||
* @param name Event name.
|
||||
* @param group A named budget group, or NULL for the default.
|
||||
* @param name Event name.
|
||||
*/
|
||||
virtual void EnterScope(const char *group, const char *name) = 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',
|
||||
|
@ -177,14 +177,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';
|
||||
@ -193,6 +189,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)
|
||||
{
|
||||
@ -255,13 +263,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;
|
||||
}
|
||||
@ -340,7 +348,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;
|
||||
}
|
||||
|
||||
@ -386,3 +396,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,10 +201,13 @@ 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));
|
||||
return 1;
|
||||
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);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
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);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
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();
|
||||
ir = env->Invoke(m_pRuntime, fn, result);
|
||||
|
||||
/* Restore some states, stop the frame tracer */
|
||||
|
||||
m_InExec = save_exec;
|
||||
int ir;
|
||||
{
|
||||
InvokeFrame ivkframe(this, fn->GetCodeOffset());
|
||||
Environment *env = env_;
|
||||
ir = env->Invoke(m_pRuntime, fn, result);
|
||||
}
|
||||
|
||||
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());
|
||||
{
|
||||
FunctionMap::Insert p = function_map_.findForAdd(pcode_offset);
|
||||
assert(!p.found());
|
||||
|
||||
function_map_.add(p, pcode_offset, fn);
|
||||
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;
|
||||
return nullptr;
|
||||
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,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr)))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
int err = context_->HeapAlloc(
|
||||
temp_info[i].size,
|
||||
&(temp_info[i].local_addr),
|
||||
&(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,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr)))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
int err = context_->HeapAlloc(
|
||||
cells,
|
||||
&(temp_info[i].local_addr),
|
||||
&(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,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr,
|
||||
NULL))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
context_->StringToLocalUTF8(
|
||||
temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr,
|
||||
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,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
context_->StringToLocal(
|
||||
temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(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