Refactor the public API entrypoint for SourcePawn.

This commit is contained in:
David Anderson 2015-02-23 18:19:33 -08:00
parent e58415f94a
commit 499f7b3929
16 changed files with 650 additions and 410 deletions

View File

@ -54,6 +54,7 @@ ILibrary *g_pJIT = NULL;
SourceHook::String g_BaseDir; SourceHook::String g_BaseDir;
ISourcePawnEngine *g_pSourcePawn = NULL; ISourcePawnEngine *g_pSourcePawn = NULL;
ISourcePawnEngine2 *g_pSourcePawn2 = NULL; ISourcePawnEngine2 *g_pSourcePawn2 = NULL;
ISourcePawnEnvironment *g_pPawnEnv = NULL;
IdentityToken_t *g_pCoreIdent = NULL; IdentityToken_t *g_pCoreIdent = NULL;
IForward *g_pOnMapEnd = NULL; IForward *g_pOnMapEnd = NULL;
IGameConfig *g_pGameConf = NULL; IGameConfig *g_pGameConf = NULL;
@ -61,10 +62,6 @@ bool g_Loaded = false;
bool sm_show_debug_spew = false; bool sm_show_debug_spew = false;
bool sm_disable_jit = false; bool sm_disable_jit = false;
typedef ISourcePawnEngine *(*GET_SP_V1)();
typedef ISourcePawnEngine2 *(*GET_SP_V2)();
typedef void (*NOTIFYSHUTDOWN)();
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
ConVar sm_basepath("sm_basepath", "addons\\sourcemod", 0, "SourceMod base path (set via command line)"); ConVar sm_basepath("sm_basepath", "addons\\sourcemod", 0, "SourceMod base path (set via command line)");
#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE #elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
@ -73,18 +70,17 @@ ConVar sm_basepath("sm_basepath", "addons/sourcemod", 0, "SourceMod base path (s
void ShutdownJIT() void ShutdownJIT()
{ {
NOTIFYSHUTDOWN notify = (NOTIFYSHUTDOWN)g_pJIT->GetSymbolAddress("NotifyShutdown"); if (g_pPawnEnv) {
if (notify) g_pPawnEnv->Shutdown();
{ delete g_pPawnEnv;
notify();
}
if (g_pSourcePawn2 != NULL) g_pPawnEnv = NULL;
{ g_pSourcePawn2 = NULL;
g_pSourcePawn2->Shutdown(); g_pSourcePawn = NULL;
} }
g_pJIT->CloseLibrary(); g_pJIT->CloseLibrary();
g_pJIT = NULL;
} }
SourceModBase::SourceModBase() SourceModBase::SourceModBase()
@ -202,51 +198,35 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
return false; return false;
} }
GET_SP_V1 getv1 = (GET_SP_V1)g_pJIT->GetSymbolAddress("GetSourcePawnEngine1"); GetSourcePawnFactoryFn factoryFn =
GET_SP_V2 getv2 = (GET_SP_V2)g_pJIT->GetSymbolAddress("GetSourcePawnEngine2"); (GetSourcePawnFactoryFn)g_pJIT->GetSymbolAddress("GetSourcePawnFactory");
if (getv1 == NULL) if (!factoryFn) {
{
if (error && maxlength) if (error && maxlength)
{ snprintf(error, maxlength, "SourcePawn library is out of date");
snprintf(error, maxlength, "JIT is too old; upgrade SourceMod");
}
ShutdownJIT();
return false;
}
else if (getv2 == NULL)
{
if (error && maxlength)
{
snprintf(error, maxlength, "JIT is too old; upgrade SourceMod");
}
ShutdownJIT(); ShutdownJIT();
return false; return false;
} }
g_pSourcePawn = getv1(); ISourcePawnFactory *factory = factoryFn(SOURCEPAWN_API_VERSION);
g_pSourcePawn2 = getv2(); if (!factory) {
if (g_pSourcePawn2->GetAPIVersion() < 3)
{
g_pSourcePawn2 = NULL;
if (error && maxlength) if (error && maxlength)
{ snprintf(error, maxlength, "SourcePawn library is out of date");
snprintf(error, maxlength, "JIT version is out of date"); ShutdownJIT();
}
return false; return false;
} }
if (!g_pSourcePawn2->Initialize()) g_pPawnEnv = factory->NewEnvironment();
{ if (!g_pPawnEnv) {
g_pSourcePawn2 = NULL;
if (error && maxlength) if (error && maxlength)
{ snprintf(error, maxlength, "Could not create a SourcePawn environment!");
snprintf(error, maxlength, "JIT could not be initialized"); ShutdownJIT();
}
return false; return false;
} }
g_pSourcePawn = g_pPawnEnv->APIv1();
g_pSourcePawn2 = g_pPawnEnv->APIv2();
g_pSourcePawn2->SetDebugListener(logicore.debugger); g_pSourcePawn2->SetDebugListener(logicore.debugger);
if (sm_disable_jit) if (sm_disable_jit)

View File

@ -1,32 +1,15 @@
/** // 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
* SourcePawn //
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. // 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
* This program is free software; you can redistribute it and/or modify it under // the License, or (at your option) any later version.
* the terms of the GNU General Public License, version 3.0, as published by the //
* Free Software Foundation. // You should have received a copy of the GNU General Public License along with
* // SourcePawn. If not, see http://www.gnu.org/licenses/.
* 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>.
*/
#ifndef _INCLUDE_SOURCEPAWN_VM_API_H_ #ifndef _INCLUDE_SOURCEPAWN_VM_API_H_
#define _INCLUDE_SOURCEPAWN_VM_API_H_ #define _INCLUDE_SOURCEPAWN_VM_API_H_
@ -38,20 +21,13 @@
#include <stdio.h> #include <stdio.h>
#include "sp_vm_types.h" #include "sp_vm_types.h"
/** SourcePawn Engine API Version */ /** SourcePawn Engine API Versions */
#define SOURCEPAWN_ENGINE_API_VERSION 4 #define SOURCEPAWN_ENGINE2_API_VERSION 7
#define SOURCEPAWN_ENGINE2_API_VERSION 6 #define SOURCEPAWN_API_VERSION 0x0207
#if !defined SOURCEMOD_BUILD namespace SourceMod {
#define SOURCEMOD_BUILD
#endif
#if defined SOURCEMOD_BUILD
namespace SourceMod
{
struct IdentityToken_t; struct IdentityToken_t;
}; };
#endif
struct sp_context_s; struct sp_context_s;
typedef struct sp_context_s sp_context_t; typedef struct sp_context_s sp_context_t;
@ -69,7 +45,6 @@ namespace SourcePawn
#define SM_PARAM_STRING_COPY (1<<1) /**< String should be copied into the plugin */ #define SM_PARAM_STRING_COPY (1<<1) /**< String should be copied into the plugin */
#define SM_PARAM_STRING_BINARY (1<<2) /**< String should be handled as binary data */ #define SM_PARAM_STRING_BINARY (1<<2) /**< String should be handled as binary data */
#if defined SOURCEMOD_BUILD
/** /**
* @brief Pseudo-NULL reference types. * @brief Pseudo-NULL reference types.
*/ */
@ -78,7 +53,6 @@ namespace SourcePawn
SP_NULL_VECTOR = 0, /**< Float[3] reference */ SP_NULL_VECTOR = 0, /**< Float[3] reference */
SP_NULL_STRING = 1, /**< const String[1] reference */ SP_NULL_STRING = 1, /**< const String[1] reference */
}; };
#endif
/** /**
* @brief Represents what a function needs to implement in order to be callable. * @brief Represents what a function needs to implement in order to be callable.
@ -1095,30 +1069,18 @@ namespace SourcePawn
{ {
public: public:
/** /**
* @brief Deprecated, do not use. * @brief Deprecated. Does nothing.
*
* @param fp Unused.
* @param err Unused.
* @return NULL.
*/ */
virtual sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) =0; virtual sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) =0;
/** /**
* @brief Deprecated, do not use, * @brief Deprecated. Does nothing.
* */
* @param base Unused.
* @param plugin Unused.
* @param err Unused.
* @return NULL.
*/
virtual sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) =0; virtual sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) =0;
/** /**
* @brief Deprecated, do not use. * @brief Deprecated. Does nothing.
* */
* @param plugin Unused.
* @return SP_ERROR_ABORTED.
*/
virtual int FreeFromMemory(sp_plugin_t *plugin) =0; virtual int FreeFromMemory(sp_plugin_t *plugin) =0;
/** /**
@ -1165,10 +1127,8 @@ namespace SourcePawn
virtual IDebugListener *SetDebugListener(IDebugListener *listener) =0; virtual IDebugListener *SetDebugListener(IDebugListener *listener) =0;
/** /**
* @brief Deprecated, do not use. * @brief Deprecated. Does nothing.
* */
* @return 0.
*/
virtual unsigned int GetContextCallCount() =0; virtual unsigned int GetContextCallCount() =0;
/** /**
@ -1296,16 +1256,13 @@ namespace SourcePawn
virtual const char *GetErrorString(int err) =0; virtual const char *GetErrorString(int err) =0;
/** /**
* @brief Initializes the SourcePawn engine. * @brief Deprecated. Does nothing.
* */
* @return True on success, false if failed.
*/
virtual bool Initialize() =0; virtual bool Initialize() =0;
/** /**
* @brief Shuts down the SourcePawn engine. Only needs to be called if * @brief Deprecated. Does nothing.
* Initialize() succeeded. */
*/
virtual void Shutdown() =0; virtual void Shutdown() =0;
/** /**
@ -1362,6 +1319,50 @@ namespace SourcePawn
*/ */
virtual void SetProfilingTool(IProfilingTool *tool) =0; virtual void SetProfilingTool(IProfilingTool *tool) =0;
}; };
// @brief This class is the v3 API for SourcePawn. It provides access to
// the original v1 and v2 APIs as well.
class ISourcePawnEnvironment
{
public:
// The Environment must be freed with the delete keyword. This
// automatically calls Shutdown().
virtual ~ISourcePawnEnvironment()
{}
// @brief Return the API version.
virtual int ApiVersion() = 0;
// @brief Return a pointer to the v1 API.
virtual ISourcePawnEngine *APIv1() = 0;
// @brief Return a pointer to the v2 API.
virtual ISourcePawnEngine2 *APIv2() = 0;
// @brief Destroy the environment, releasing all resources and freeing
// all plugin memory. This should not be called while plugins have
// active code running on the stack.
virtual void Shutdown() = 0;
};
// @brief This class is the entry-point to using SourcePawn from a DLL.
class ISourcePawnFactory
{
public:
// @brief Return the API version.
virtual int ApiVersion() = 0;
// @brief Initializes a new environment on the current thread.
// Currently, at most one environment may be created in a process.
virtual ISourcePawnEnvironment *NewEnvironment() = 0;
// @brief Returns the environment for the calling thread.
virtual ISourcePawnEnvironment *CurrentEnvironment() = 0;
};
// @brief A function named "GetSourcePawnFactory" is exported from the
// SourcePawn DLL, conforming to the following signature:
typedef ISourcePawnFactory *(*GetSourcePawnFactoryFn)(int apiVersion);
}; };
#endif //_INCLUDE_SOURCEPAWN_VM_API_H_ #endif //_INCLUDE_SOURCEPAWN_VM_API_H_

View File

@ -33,7 +33,9 @@ library = setup(builder.compiler.StaticLibrary('sourcepawn'))
library.sources += [ library.sources += [
'plugin-runtime.cpp', 'plugin-runtime.cpp',
'compiled-function.cpp', 'compiled-function.cpp',
'debug-trace.cpp',
'engine2.cpp', 'engine2.cpp',
'environment.cpp',
'sp_vm_basecontext.cpp', 'sp_vm_basecontext.cpp',
'sp_vm_engine.cpp', 'sp_vm_engine.cpp',
'scripted-invoker.cpp', 'scripted-invoker.cpp',

View File

@ -11,6 +11,7 @@ MMSOURCE17 = ../../../mmsource-1.7
PROJECT = sourcepawn.jit.x86 PROJECT = sourcepawn.jit.x86
OBJECTS = dll_exports.cpp \ OBJECTS = dll_exports.cpp \
environment.cpp \
x86/jit_x86.cpp \ x86/jit_x86.cpp \
sp_vm_basecontext.cpp \ sp_vm_basecontext.cpp \
sp_vm_engine.cpp \ sp_vm_engine.cpp \

View File

@ -11,6 +11,7 @@ MMSOURCE17 = ../../../mmsource-1.7
PROJECT = spshell PROJECT = spshell
OBJECTS = dll_exports.cpp \ OBJECTS = dll_exports.cpp \
environment.cpp \
x86/jit_x86.cpp \ x86/jit_x86.cpp \
sp_vm_basecontext.cpp \ sp_vm_basecontext.cpp \
sp_vm_engine.cpp \ sp_vm_engine.cpp \

View File

@ -0,0 +1,120 @@
// 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 "sp_vm_basecontext.h"
#include "environment.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),
m_Error(err),
m_pMsg(errstr),
m_StartRp(start_rp),
m_Level(0)
{
m_ctx = pRuntime->m_pCtx->GetCtx();
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 = m_ctx->cip;
} else if (m_ctx->rp > 0) {
/* Entries go from ctx.rp - 1 to m_StartRp */
cell_t offs, start, end;
offs = m_Level - 1;
start = m_ctx->rp - 1;
end = m_StartRp;
if (start - offs < end)
{
return false;
}
cip = m_ctx->rstk_cips[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 (m_ctx->n_err == SP_ERROR_NONE)
return NULL;
sp_native_t *native;
if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
return NULL;
if (index)
*index = m_ctx->n_idx;
return native->name;
}

View File

@ -0,0 +1,50 @@
// 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>
class PluginRuntime;
namespace sp {
using namespace SourcePawn;
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;
sp_context_t *m_ctx;
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_

View File

@ -32,14 +32,32 @@
#include <sp_vm_api.h> #include <sp_vm_api.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <am-utility.h> // Replace with am-cxx later.
#include "dll_exports.h" #include "dll_exports.h"
#include "sp_vm_engine.h" #include "sp_vm_engine.h"
#include "engine2.h" #include "engine2.h"
#include "environment.h"
using namespace ke;
using namespace sp;
using namespace SourcePawn; using namespace SourcePawn;
SourcePawnEngine2 g_engine2; SourcePawnEngine2 g_engine2;
class SourcePawnFactory : public ISourcePawnFactory
{
public:
int ApiVersion() KE_OVERRIDE {
return SOURCEPAWN_API_VERSION;
}
ISourcePawnEnvironment *NewEnvironment() KE_OVERRIDE {
return Environment::New();
}
ISourcePawnEnvironment *CurrentEnvironment() KE_OVERRIDE {
return Environment::get();
}
} sFactory;
#ifdef SPSHELL #ifdef SPSHELL
template <typename T> class AutoT template <typename T> class AutoT
{ {
@ -66,6 +84,8 @@ private:
T *t_; T *t_;
}; };
Environment *sEnv;
class ShellDebugListener : public IDebugListener class ShellDebugListener : public IDebugListener
{ {
public: public:
@ -181,7 +201,7 @@ static int Execute(const char *file)
int err; int err;
AutoT<IPluginRuntime> rt(g_engine2.LoadPlugin(co, file, &err)); AutoT<IPluginRuntime> rt(g_engine2.LoadPlugin(co, file, &err));
if (!rt) { if (!rt) {
fprintf(stderr, "Could not load plugin: %s\n", g_engine1.GetErrorString(err)); fprintf(stderr, "Could not load plugin: %s\n", sEnv->GetErrorString(err));
return 1; return 1;
} }
@ -199,7 +219,7 @@ static int Execute(const char *file)
int result = fun->Execute2(cx, &err); int result = fun->Execute2(cx, &err);
if (err != SP_ERROR_NONE) { if (err != SP_ERROR_NONE) {
fprintf(stderr, "Error executing main(): %s\n", g_engine1.GetErrorString(err)); fprintf(stderr, "Error executing main(): %s\n", sEnv->GetErrorString(err));
return 1; return 1;
} }
@ -213,34 +233,37 @@ int main(int argc, char **argv)
return 1; return 1;
} }
if (!g_engine2.Initialize()) { if ((sEnv = Environment::New()) == nullptr) {
fprintf(stderr, "Could not initialize ISourcePawnEngine2\n"); fprintf(stderr, "Could not initialize ISourcePawnEngine2\n");
return 1; return 1;
} }
if (getenv("DISABLE_JIT")) if (getenv("DISABLE_JIT"))
g_engine2.SetJitEnabled(false); sEnv->SetJitEnabled(false);
ShellDebugListener debug; ShellDebugListener debug;
g_engine1.SetDebugListener(&debug); sEnv->SetDebugger(&debug);
g_engine2.InstallWatchdogTimer(5000); sEnv->InstallWatchdogTimer(5000);
int errcode = Execute(argv[1]); int errcode = Execute(argv[1]);
g_engine1.SetDebugListener(NULL); sEnv->SetDebugger(NULL);
g_engine2.Shutdown(); sEnv->Shutdown();
delete sEnv;
return errcode; return errcode;
} }
#else #else
EXPORTFUNC ISourcePawnEngine *GetSourcePawnEngine1()
{
return &g_engine1;
}
EXPORTFUNC ISourcePawnEngine2 *GetSourcePawnEngine2() #define MIN_API_VERSION 0x0207
EXPORTFUNC ISourcePawnFactory *
GetSourcePawnFactory(int apiVersion)
{ {
return &g_engine2; if (apiVersion < MIN_API_VERSION || apiVersion > SOURCEPAWN_API_VERSION)
return nullptr;
return &sFactory;
} }
#endif #endif

View File

@ -20,13 +20,12 @@
#include "sp_vm_engine.h" #include "sp_vm_engine.h"
#include "watchdog_timer.h" #include "watchdog_timer.h"
#include <sourcemod_version.h> #include <sourcemod_version.h>
#include "environment.h"
using namespace SourcePawn; using namespace SourcePawn;
SourcePawnEngine2::SourcePawnEngine2() SourcePawnEngine2::SourcePawnEngine2()
{ {
profiler_ = NULL;
jit_enabled_ = true;
} }
IPluginRuntime * IPluginRuntime *
@ -172,7 +171,9 @@ SourcePawnEngine2::GetVersionString()
IDebugListener * IDebugListener *
SourcePawnEngine2::SetDebugListener(IDebugListener *listener) SourcePawnEngine2::SetDebugListener(IDebugListener *listener)
{ {
return g_engine1.SetDebugListener(listener); IDebugListener *old = Environment::get()->debugger();
Environment::get()->SetDebugger(listener);
return old;
} }
unsigned int unsigned int
@ -190,20 +191,18 @@ SourcePawnEngine2::StartCompilation()
const char * const char *
SourcePawnEngine2::GetErrorString(int err) SourcePawnEngine2::GetErrorString(int err)
{ {
return g_engine1.GetErrorString(err); return Environment::get()->GetErrorString(err);
} }
bool bool
SourcePawnEngine2::Initialize() SourcePawnEngine2::Initialize()
{ {
return g_Jit.InitializeJIT(); return true;
} }
void void
SourcePawnEngine2::Shutdown() SourcePawnEngine2::Shutdown()
{ {
g_WatchdogTimer.Shutdown();
g_Jit.ShutdownJIT();
} }
IPluginRuntime * IPluginRuntime *
@ -227,6 +226,42 @@ SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory)
bool bool
SourcePawnEngine2::InstallWatchdogTimer(size_t timeout_ms) SourcePawnEngine2::InstallWatchdogTimer(size_t timeout_ms)
{ {
return g_WatchdogTimer.Initialize(timeout_ms); return Environment::get()->InstallWatchdogTimer(timeout_ms);
} }
bool
SourcePawnEngine2::SetJitEnabled(bool enabled)
{
Environment::get()->SetJitEnabled(enabled);
return Environment::get()->IsJitEnabled() == enabled;
}
bool
SourcePawnEngine2::IsJitEnabled()
{
return Environment::get()->IsJitEnabled();
}
void
SourcePawnEngine2::SetProfiler(IProfiler *profiler)
{
// Deprecated.
}
void
SourcePawnEngine2::EnableProfiling()
{
Environment::get()->EnableProfiling();
}
void
SourcePawnEngine2::DisableProfiling()
{
Environment::get()->DisableProfiling();
}
void
SourcePawnEngine2::SetProfilingTool(IProfilingTool *tool)
{
Environment::get()->SetProfiler(tool);
}

View File

@ -14,6 +14,7 @@
#define _INCLUDE_SOURCEPAWN_ENGINE_2_H_ #define _INCLUDE_SOURCEPAWN_ENGINE_2_H_
#include <sp_vm_api.h> #include <sp_vm_api.h>
#include <am-utility.h> // Replace with am-cxx later.
namespace SourcePawn { namespace SourcePawn {
@ -40,61 +41,14 @@ class SourcePawnEngine2 : public ISourcePawnEngine2
IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory); IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory);
bool InstallWatchdogTimer(size_t timeout_ms); bool InstallWatchdogTimer(size_t timeout_ms);
bool SetJitEnabled(bool enabled) { bool SetJitEnabled(bool enabled) KE_OVERRIDE;
jit_enabled_ = enabled; bool IsJitEnabled() KE_OVERRIDE;
return true; void SetProfiler(IProfiler *profiler) KE_OVERRIDE;
} void EnableProfiling() KE_OVERRIDE;
void DisableProfiling() KE_OVERRIDE;
bool IsJitEnabled() { void SetProfilingTool(IProfilingTool *tool) KE_OVERRIDE;
return jit_enabled_;
}
void SetProfiler(IProfiler *profiler) {
// Deprecated.
}
void EnableProfiling() {
profiling_enabled_ = !!profiler_;
}
void DisableProfiling() {
profiling_enabled_ = false;
}
bool IsProfilingEnabled() {
return profiling_enabled_;
}
void SetProfilingTool(IProfilingTool *tool) {
profiler_ = tool;
}
public:
IProfilingTool *GetProfiler() {
return profiler_;
}
private:
IProfilingTool *profiler_;
bool jit_enabled_;
bool profiling_enabled_;
}; };
} // namespace SourcePawn } // namespace SourcePawn
extern SourcePawn::SourcePawnEngine2 g_engine2;
class EnterProfileScope
{
public:
EnterProfileScope(const char *group, const char *name)
{
if (g_engine2.IsProfilingEnabled())
g_engine2.GetProfiler()->EnterScope(group, name);
}
~EnterProfileScope()
{
if (g_engine2.IsProfilingEnabled())
g_engine2.GetProfiler()->LeaveScope();
}
};
#endif //_INCLUDE_SOURCEPAWN_ENGINE_2_H_ #endif //_INCLUDE_SOURCEPAWN_ENGINE_2_H_

View File

@ -0,0 +1,157 @@
// 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 "sp_vm_engine.h"
#include "engine2.h"
#include "x86/jit_x86.h"
#include "watchdog_timer.h"
#include "debug-trace.h"
using namespace sp;
using namespace SourcePawn;
static Environment *sEnvironment = nullptr;
Environment::Environment()
: debugger_(nullptr),
profiler_(nullptr),
jit_enabled_(true),
profiling_enabled_(false)
{
}
Environment *
Environment::New()
{
assert(!sEnvironment);
if (sEnvironment)
return nullptr;
Environment *env = new Environment();
if (!env->Initialize()) {
delete env;
return nullptr;
}
sEnvironment = env;
return env;
}
Environment *
Environment::get()
{
return sEnvironment;
}
bool
Environment::Initialize()
{
api_v1_ = new SourcePawnEngine();
api_v2_ = new SourcePawnEngine2();
if (!g_Jit.InitializeJIT())
return false;
return true;
}
void
Environment::Shutdown()
{
g_WatchdogTimer.Shutdown();
g_Jit.ShutdownJIT();
}
void
Environment::EnableProfiling()
{
profiling_enabled_ = !!profiler_;
}
void
Environment::DisableProfiling()
{
profiling_enabled_ = false;
}
bool
Environment::InstallWatchdogTimer(int timeout_ms)
{
return g_WatchdogTimer.Initialize(timeout_ms);
}
ISourcePawnEngine *
Environment::APIv1()
{
return api_v1_;
}
ISourcePawnEngine2 *
Environment::APIv2()
{
return api_v2_;
}
static const char *sErrorMsgTable[] =
{
NULL,
"Unrecognizable file format",
"Decompressor was not found",
"Not enough space on the heap",
"Invalid parameter or parameter type",
"Invalid plugin address",
"Object or index not found",
"Invalid index or index not found",
"Not enough space on the stack",
"Debug section not found or debug not enabled",
"Invalid instruction",
"Invalid memory access",
"Stack went below stack boundary",
"Heap went below heap boundary",
"Divide by zero",
"Array index is out of bounds",
"Instruction contained invalid parameter",
"Stack memory leaked by native",
"Heap memory leaked by native",
"Dynamic array is too big",
"Tracker stack is out of bounds",
"Native is not bound",
"Maximum number of parameters reached",
"Native detected error",
"Plugin not runnable",
"Call was aborted",
"Plugin format is too old",
"Plugin format is too new",
"Out of memory",
"Integer overflow",
"Script execution timed out"
};
const char *
Environment::GetErrorString(int error)
{
if (error < 1 || error > (sizeof(sErrorMsgTable) / sizeof(sErrorMsgTable[0])))
return NULL;
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);
}

View File

@ -0,0 +1,111 @@
// 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_environment_h_
#define _include_sourcepawn_vm_environment_h_
#include <sp_vm_api.h>
#include <am-utility.h> // Replace with am-cxx later.
class PluginRuntime;
namespace sp {
using namespace SourcePawn;
// An Environment encapsulates everything that's needed to load and run
// instances of plugins on a single thread. There can be at most one
// environment per thread.
//
// Currently, the VM is single threaded in that no more than one
// Environment can be created per process.
class Environment : public ISourcePawnEnvironment
{
public:
Environment();
static Environment *New();
void Shutdown() KE_OVERRIDE;
ISourcePawnEngine *APIv1() KE_OVERRIDE;
ISourcePawnEngine2 *APIv2() KE_OVERRIDE;
int ApiVersion() KE_OVERRIDE {
return SOURCEPAWN_API_VERSION;
}
// Access the current Environment.
static Environment *get();
bool InstallWatchdogTimer(int timeout_ms);
// Runtime functions.
const char *GetErrorString(int err);
void ReportError(PluginRuntime *runtime, int err, const char *errstr, cell_t rp_start);
// Helpers.
void SetProfiler(IProfilingTool *profiler) {
profiler_ = profiler;
}
IProfilingTool *profiler() const {
return profiler_;
}
bool IsProfilingEnabled() const {
return profiling_enabled_;
}
void EnableProfiling();
void DisableProfiling();
void SetJitEnabled(bool enabled) {
jit_enabled_ = enabled;
}
bool IsJitEnabled() const {
return jit_enabled_;
}
void SetDebugger(IDebugListener *debugger) {
debugger_ = debugger;
}
IDebugListener *debugger() const {
return debugger_;
}
private:
bool Initialize();
private:
ke::AutoPtr<ISourcePawnEngine> api_v1_;
ke::AutoPtr<ISourcePawnEngine2> api_v2_;
IDebugListener *debugger_;
IProfilingTool *profiler_;
bool jit_enabled_;
bool profiling_enabled_;
};
class EnterProfileScope
{
public:
EnterProfileScope(const char *group, const char *name)
{
if (Environment::get()->IsProfilingEnabled())
Environment::get()->profiler()->EnterScope(group, name);
}
~EnterProfileScope()
{
if (Environment::get()->IsProfilingEnabled())
Environment::get()->profiler()->LeaveScope();
}
};
}
#endif // _include_sourcepawn_vm_environment_h_

View File

@ -21,6 +21,7 @@
#include "x86/jit_x86.h" #include "x86/jit_x86.h"
#include "engine2.h" #include "engine2.h"
#include "interpreter.h" #include "interpreter.h"
#include "environment.h"
using namespace SourcePawn; using namespace SourcePawn;
@ -555,7 +556,7 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned
EnterProfileScope scriptScope("SourcePawn", cfun->FullName()); EnterProfileScope scriptScope("SourcePawn", cfun->FullName());
/* See if we have to compile the callee. */ /* See if we have to compile the callee. */
if (g_engine2.IsJitEnabled() && if (Environment::get()->IsJitEnabled() &&
(fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL) (fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
{ {
/* We might not have to - check pcode offset. */ /* We might not have to - check pcode offset. */
@ -600,7 +601,7 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned
/* Start the frame tracer */ /* Start the frame tracer */
if (g_engine2.IsJitEnabled()) if (Environment::get()->IsJitEnabled())
ir = g_Jit.InvokeFunction(m_pRuntime, fn, result); ir = g_Jit.InvokeFunction(m_pRuntime, fn, result);
else else
ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result); ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result);
@ -635,7 +636,7 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned
g_WatchdogTimer.NotifyTimeoutReceived(); g_WatchdogTimer.NotifyTimeoutReceived();
if (ir != SP_ERROR_NONE) if (ir != SP_ERROR_NONE)
g_engine1.ReportError(m_pRuntime, ir, m_MsgCache, save_rp); Environment::get()->ReportError(m_pRuntime, ir, m_MsgCache, save_rp);
m_ctx.sp = save_sp; m_ctx.sp = save_sp;
m_ctx.hp = save_hp; m_ctx.hp = save_hp;

View File

@ -16,15 +16,14 @@
#include "sp_vm_types.h" #include "sp_vm_types.h"
#include <KeCodeAllocator.h> #include <KeCodeAllocator.h>
#include "sp_vm_engine.h" #include "sp_vm_engine.h"
#include "jit_x86.h"
#include "zlib/zlib.h" #include "zlib/zlib.h"
#include "sp_vm_basecontext.h" #include "sp_vm_basecontext.h"
#include "jit_x86.h" #include "environment.h"
#if defined __GNUC__ #if defined __GNUC__
#include <unistd.h> #include <unistd.h>
#endif #endif
SourcePawnEngine g_engine1;
#if defined WIN32 #if defined WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
@ -38,53 +37,14 @@ SourcePawnEngine g_engine1;
using namespace SourcePawn; using namespace SourcePawn;
#define ERROR_MESSAGE_MAX 30
static const char *g_ErrorMsgTable[] =
{
NULL,
"Unrecognizable file format",
"Decompressor was not found",
"Not enough space on the heap",
"Invalid parameter or parameter type",
"Invalid plugin address",
"Object or index not found",
"Invalid index or index not found",
"Not enough space on the stack",
"Debug section not found or debug not enabled",
"Invalid instruction",
"Invalid memory access",
"Stack went below stack boundary",
"Heap went below heap boundary",
"Divide by zero",
"Array index is out of bounds",
"Instruction contained invalid parameter",
"Stack memory leaked by native",
"Heap memory leaked by native",
"Dynamic array is too big",
"Tracker stack is out of bounds",
"Native is not bound",
"Maximum number of parameters reached",
"Native detected error",
"Plugin not runnable",
"Call was aborted",
"Plugin format is too old",
"Plugin format is too new",
"Out of memory",
"Integer overflow",
"Script execution timed out"
};
const char * const char *
SourcePawnEngine::GetErrorString(int error) SourcePawnEngine::GetErrorString(int error)
{ {
if (error < 1 || error > ERROR_MESSAGE_MAX) return Environment::get()->GetErrorString(error);
return NULL;
return g_ErrorMsgTable[error];
} }
SourcePawnEngine::SourcePawnEngine() SourcePawnEngine::SourcePawnEngine()
{ {
m_pDebugHook = NULL;
} }
SourcePawnEngine::~SourcePawnEngine() SourcePawnEngine::~SourcePawnEngine()
@ -147,7 +107,7 @@ SourcePawnEngine::ExecFree(void *address)
void void
SourcePawnEngine::SetReadWriteExecute(void *ptr) SourcePawnEngine::SetReadWriteExecute(void *ptr)
{ {
//:TODO: g_ExeMemory.SetRWE(ptr); //:TODO: g_ExeMemory.SetRWE(ptr);
SetReadExecute(ptr); SetReadExecute(ptr);
} }
@ -190,17 +150,15 @@ SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
IDebugListener * IDebugListener *
SourcePawnEngine::SetDebugListener(IDebugListener *pListener) SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
{ {
IDebugListener *old = m_pDebugHook; IDebugListener *old = Environment::get()->debugger();
Environment::get()->SetDebugger(pListener);
m_pDebugHook = pListener;
return old; return old;
} }
unsigned int unsigned int
SourcePawnEngine::GetEngineAPIVersion() SourcePawnEngine::GetEngineAPIVersion()
{ {
return SOURCEPAWN_ENGINE_API_VERSION; return 4;
} }
unsigned int unsigned int
@ -208,124 +166,3 @@ SourcePawnEngine::GetContextCallCount()
{ {
return 0; return 0;
} }
void
SourcePawnEngine::ReportError(PluginRuntime *runtime, int err, const char *errstr, cell_t rp_start)
{
if (m_pDebugHook == NULL)
return;
CContextTrace trace(runtime, err, errstr, rp_start);
m_pDebugHook->OnContextExecuteError(runtime->GetDefaultContext(), &trace);
}
CContextTrace::CContextTrace(PluginRuntime *pRuntime, int err, const char *errstr, cell_t start_rp)
: m_pRuntime(pRuntime),
m_Error(err),
m_pMsg(errstr),
m_StartRp(start_rp),
m_Level(0)
{
m_ctx = pRuntime->m_pCtx->GetCtx();
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()
{
if (m_Error > ERROR_MESSAGE_MAX || m_Error < 1)
return "Invalid error code";
return g_ErrorMsgTable[m_Error];
}
void
CContextTrace::ResetTrace()
{
m_Level = 0;
}
bool
CContextTrace::GetTraceInfo(CallStackInfo *trace)
{
cell_t cip;
if (m_Level == 0) {
cip = m_ctx->cip;
} else if (m_ctx->rp > 0) {
/* Entries go from ctx.rp - 1 to m_StartRp */
cell_t offs, start, end;
offs = m_Level - 1;
start = m_ctx->rp - 1;
end = m_StartRp;
if (start - offs < end)
{
return false;
}
cip = m_ctx->rstk_cips[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 (m_ctx->n_err == SP_ERROR_NONE)
return NULL;
sp_native_t *native;
if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
return NULL;
if (index)
*index = m_ctx->n_idx;
return native->name;
}
IDebugListener *
SourcePawnEngine::GetDebugHook()
{
return m_pDebugHook;
}

View File

@ -13,35 +13,12 @@
#ifndef _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ #ifndef _INCLUDE_SOURCEPAWN_VM_ENGINE_H_
#define _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ #define _INCLUDE_SOURCEPAWN_VM_ENGINE_H_
#include "sp_vm_api.h" #include <sp_vm_api.h>
#include <am-utility.h> // Replace with am-cxx.h later.
#include "scripted-invoker.h" #include "scripted-invoker.h"
class BaseContext; class BaseContext;
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;
sp_context_t *m_ctx;
int m_Error;
const char *m_pMsg;
cell_t m_StartRp;
cell_t m_Level;
IPluginDebugInfo *m_pDebug;
};
class SourcePawnEngine : public ISourcePawnEngine class SourcePawnEngine : public ISourcePawnEngine
{ {
public: public:
@ -49,31 +26,22 @@ class SourcePawnEngine : public ISourcePawnEngine
~SourcePawnEngine(); ~SourcePawnEngine();
public: //ISourcePawnEngine public: //ISourcePawnEngine
sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err); sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) KE_OVERRIDE;
sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err); sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) KE_OVERRIDE;
int FreeFromMemory(sp_plugin_t *plugin); int FreeFromMemory(sp_plugin_t *plugin) KE_OVERRIDE;
void *BaseAlloc(size_t size); void *BaseAlloc(size_t size) KE_OVERRIDE;
void BaseFree(void *memory); void BaseFree(void *memory) KE_OVERRIDE;
void *ExecAlloc(size_t size); void *ExecAlloc(size_t size) KE_OVERRIDE;
void ExecFree(void *address); void ExecFree(void *address) KE_OVERRIDE;
IDebugListener *SetDebugListener(IDebugListener *pListener); IDebugListener *SetDebugListener(IDebugListener *pListener) KE_OVERRIDE;
unsigned int GetContextCallCount(); unsigned int GetContextCallCount() KE_OVERRIDE;
unsigned int GetEngineAPIVersion(); unsigned int GetEngineAPIVersion() KE_OVERRIDE;
void *AllocatePageMemory(size_t size); void *AllocatePageMemory(size_t size) KE_OVERRIDE;
void SetReadWrite(void *ptr); void SetReadWrite(void *ptr) KE_OVERRIDE;
void SetReadExecute(void *ptr); void SetReadExecute(void *ptr) KE_OVERRIDE;
void FreePageMemory(void *ptr) KE_OVERRIDE;
void SetReadWriteExecute(void *ptr); void SetReadWriteExecute(void *ptr);
void FreePageMemory(void *ptr);
const char *GetErrorString(int err); const char *GetErrorString(int err);
void ReportError(PluginRuntime *runtime, int err, const char *errstr, cell_t rp_start);
public: //Plugin function stuff
IDebugListener *GetDebugHook();
private:
IDebugListener *m_pDebugHook;
}; };
extern SourcePawnEngine g_engine1;
#endif //_INCLUDE_SOURCEPAWN_VM_ENGINE_H_ #endif //_INCLUDE_SOURCEPAWN_VM_ENGINE_H_

View File

@ -51,7 +51,6 @@ using namespace Knight;
JITX86 g_Jit; JITX86 g_Jit;
KeCodeCache *g_pCodeCache = NULL; KeCodeCache *g_pCodeCache = NULL;
ISourcePawnEngine *engine = &g_engine1;
static inline uint8_t * static inline uint8_t *
LinkCode(AssemblerX86 &masm) LinkCode(AssemblerX86 &masm)