diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index 5d68eb1d..94f289c2 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -54,6 +54,7 @@ ILibrary *g_pJIT = NULL; SourceHook::String g_BaseDir; ISourcePawnEngine *g_pSourcePawn = NULL; ISourcePawnEngine2 *g_pSourcePawn2 = NULL; +ISourcePawnEnvironment *g_pPawnEnv = NULL; IdentityToken_t *g_pCoreIdent = NULL; IForward *g_pOnMapEnd = NULL; IGameConfig *g_pGameConf = NULL; @@ -61,10 +62,6 @@ bool g_Loaded = false; bool sm_show_debug_spew = false; bool sm_disable_jit = false; -typedef ISourcePawnEngine *(*GET_SP_V1)(); -typedef ISourcePawnEngine2 *(*GET_SP_V2)(); -typedef void (*NOTIFYSHUTDOWN)(); - #ifdef PLATFORM_WINDOWS ConVar sm_basepath("sm_basepath", "addons\\sourcemod", 0, "SourceMod base path (set via command line)"); #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() { - NOTIFYSHUTDOWN notify = (NOTIFYSHUTDOWN)g_pJIT->GetSymbolAddress("NotifyShutdown"); - if (notify) - { - notify(); - } + if (g_pPawnEnv) { + g_pPawnEnv->Shutdown(); + delete g_pPawnEnv; - if (g_pSourcePawn2 != NULL) - { - g_pSourcePawn2->Shutdown(); + g_pPawnEnv = NULL; + g_pSourcePawn2 = NULL; + g_pSourcePawn = NULL; } g_pJIT->CloseLibrary(); + g_pJIT = NULL; } SourceModBase::SourceModBase() @@ -202,51 +198,35 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late return false; } - GET_SP_V1 getv1 = (GET_SP_V1)g_pJIT->GetSymbolAddress("GetSourcePawnEngine1"); - GET_SP_V2 getv2 = (GET_SP_V2)g_pJIT->GetSymbolAddress("GetSourcePawnEngine2"); + GetSourcePawnFactoryFn factoryFn = + (GetSourcePawnFactoryFn)g_pJIT->GetSymbolAddress("GetSourcePawnFactory"); - if (getv1 == NULL) - { + if (!factoryFn) { if (error && maxlength) - { - 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"); - } + snprintf(error, maxlength, "SourcePawn library is out of date"); ShutdownJIT(); return false; } - g_pSourcePawn = getv1(); - g_pSourcePawn2 = getv2(); - - if (g_pSourcePawn2->GetAPIVersion() < 3) - { - g_pSourcePawn2 = NULL; + ISourcePawnFactory *factory = factoryFn(SOURCEPAWN_API_VERSION); + if (!factory) { if (error && maxlength) - { - snprintf(error, maxlength, "JIT version is out of date"); - } + snprintf(error, maxlength, "SourcePawn library is out of date"); + ShutdownJIT(); return false; } - if (!g_pSourcePawn2->Initialize()) - { - g_pSourcePawn2 = NULL; + g_pPawnEnv = factory->NewEnvironment(); + if (!g_pPawnEnv) { if (error && maxlength) - { - snprintf(error, maxlength, "JIT could not be initialized"); - } + snprintf(error, maxlength, "Could not create a SourcePawn environment!"); + ShutdownJIT(); return false; } + g_pSourcePawn = g_pPawnEnv->APIv1(); + g_pSourcePawn2 = g_pPawnEnv->APIv2(); + g_pSourcePawn2->SetDebugListener(logicore.debugger); if (sm_disable_jit) diff --git a/public/sourcepawn/sp_vm_api.h b/public/sourcepawn/sp_vm_api.h index 0b9a2aac..9c7f45b8 100644 --- a/public/sourcepawn/sp_vm_api.h +++ b/public/sourcepawn/sp_vm_api.h @@ -1,32 +1,15 @@ -/** - * 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 . - * - * 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 . - */ - +// 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_API_H_ #define _INCLUDE_SOURCEPAWN_VM_API_H_ @@ -38,20 +21,13 @@ #include #include "sp_vm_types.h" -/** SourcePawn Engine API Version */ -#define SOURCEPAWN_ENGINE_API_VERSION 4 -#define SOURCEPAWN_ENGINE2_API_VERSION 6 +/** SourcePawn Engine API Versions */ +#define SOURCEPAWN_ENGINE2_API_VERSION 7 +#define SOURCEPAWN_API_VERSION 0x0207 -#if !defined SOURCEMOD_BUILD -#define SOURCEMOD_BUILD -#endif - -#if defined SOURCEMOD_BUILD -namespace SourceMod -{ +namespace SourceMod { struct IdentityToken_t; }; -#endif struct sp_context_s; 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_BINARY (1<<2) /**< String should be handled as binary data */ -#if defined SOURCEMOD_BUILD /** * @brief Pseudo-NULL reference types. */ @@ -78,7 +53,6 @@ namespace SourcePawn SP_NULL_VECTOR = 0, /**< Float[3] reference */ SP_NULL_STRING = 1, /**< const String[1] reference */ }; -#endif /** * @brief Represents what a function needs to implement in order to be callable. @@ -1095,30 +1069,18 @@ namespace SourcePawn { public: /** - * @brief Deprecated, do not use. - * - * @param fp Unused. - * @param err Unused. - * @return NULL. + * @brief Deprecated. Does nothing. */ virtual sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) =0; /** - * @brief Deprecated, do not use, - * - * @param base Unused. - * @param plugin Unused. - * @param err Unused. - * @return NULL. - */ + * @brief Deprecated. Does nothing. + */ virtual sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) =0; /** - * @brief Deprecated, do not use. - * - * @param plugin Unused. - * @return SP_ERROR_ABORTED. - */ + * @brief Deprecated. Does nothing. + */ virtual int FreeFromMemory(sp_plugin_t *plugin) =0; /** @@ -1165,10 +1127,8 @@ namespace SourcePawn virtual IDebugListener *SetDebugListener(IDebugListener *listener) =0; /** - * @brief Deprecated, do not use. - * - * @return 0. - */ + * @brief Deprecated. Does nothing. + */ virtual unsigned int GetContextCallCount() =0; /** @@ -1296,16 +1256,13 @@ namespace SourcePawn virtual const char *GetErrorString(int err) =0; /** - * @brief Initializes the SourcePawn engine. - * - * @return True on success, false if failed. - */ + * @brief Deprecated. Does nothing. + */ virtual bool Initialize() =0; /** - * @brief Shuts down the SourcePawn engine. Only needs to be called if - * Initialize() succeeded. - */ + * @brief Deprecated. Does nothing. + */ virtual void Shutdown() =0; /** @@ -1362,6 +1319,50 @@ namespace SourcePawn */ 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_ diff --git a/sourcepawn/jit/AMBuilder b/sourcepawn/jit/AMBuilder index 1a5b2721..e06bab24 100644 --- a/sourcepawn/jit/AMBuilder +++ b/sourcepawn/jit/AMBuilder @@ -33,7 +33,9 @@ library = setup(builder.compiler.StaticLibrary('sourcepawn')) library.sources += [ 'plugin-runtime.cpp', 'compiled-function.cpp', + 'debug-trace.cpp', 'engine2.cpp', + 'environment.cpp', 'sp_vm_basecontext.cpp', 'sp_vm_engine.cpp', 'scripted-invoker.cpp', diff --git a/sourcepawn/jit/Makefile b/sourcepawn/jit/Makefile index c235ffbe..edc3368c 100644 --- a/sourcepawn/jit/Makefile +++ b/sourcepawn/jit/Makefile @@ -11,6 +11,7 @@ MMSOURCE17 = ../../../mmsource-1.7 PROJECT = sourcepawn.jit.x86 OBJECTS = dll_exports.cpp \ + environment.cpp \ x86/jit_x86.cpp \ sp_vm_basecontext.cpp \ sp_vm_engine.cpp \ diff --git a/sourcepawn/jit/Makefile.shell b/sourcepawn/jit/Makefile.shell index 01564fde..b1509d0c 100644 --- a/sourcepawn/jit/Makefile.shell +++ b/sourcepawn/jit/Makefile.shell @@ -11,6 +11,7 @@ MMSOURCE17 = ../../../mmsource-1.7 PROJECT = spshell OBJECTS = dll_exports.cpp \ + environment.cpp \ x86/jit_x86.cpp \ sp_vm_basecontext.cpp \ sp_vm_engine.cpp \ diff --git a/sourcepawn/jit/debug-trace.cpp b/sourcepawn/jit/debug-trace.cpp new file mode 100644 index 00000000..d6a0a104 --- /dev/null +++ b/sourcepawn/jit/debug-trace.cpp @@ -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; +} diff --git a/sourcepawn/jit/debug-trace.h b/sourcepawn/jit/debug-trace.h new file mode 100644 index 00000000..e6d3f3f2 --- /dev/null +++ b/sourcepawn/jit/debug-trace.h @@ -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 + +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_ diff --git a/sourcepawn/jit/dll_exports.cpp b/sourcepawn/jit/dll_exports.cpp index 55495425..6e410914 100644 --- a/sourcepawn/jit/dll_exports.cpp +++ b/sourcepawn/jit/dll_exports.cpp @@ -32,14 +32,32 @@ #include #include #include +#include // Replace with am-cxx later. #include "dll_exports.h" #include "sp_vm_engine.h" #include "engine2.h" +#include "environment.h" +using namespace ke; +using namespace sp; using namespace SourcePawn; 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 template class AutoT { @@ -66,6 +84,8 @@ private: T *t_; }; +Environment *sEnv; + class ShellDebugListener : public IDebugListener { public: @@ -181,7 +201,7 @@ static int Execute(const char *file) int err; AutoT rt(g_engine2.LoadPlugin(co, file, &err)); 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; } @@ -199,7 +219,7 @@ static int Execute(const char *file) int result = fun->Execute2(cx, &err); 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; } @@ -213,34 +233,37 @@ int main(int argc, char **argv) return 1; } - if (!g_engine2.Initialize()) { + if ((sEnv = Environment::New()) == nullptr) { fprintf(stderr, "Could not initialize ISourcePawnEngine2\n"); return 1; } if (getenv("DISABLE_JIT")) - g_engine2.SetJitEnabled(false); + sEnv->SetJitEnabled(false); ShellDebugListener debug; - g_engine1.SetDebugListener(&debug); - g_engine2.InstallWatchdogTimer(5000); + sEnv->SetDebugger(&debug); + sEnv->InstallWatchdogTimer(5000); int errcode = Execute(argv[1]); - g_engine1.SetDebugListener(NULL); - g_engine2.Shutdown(); + sEnv->SetDebugger(NULL); + sEnv->Shutdown(); + delete sEnv; + return errcode; } #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 diff --git a/sourcepawn/jit/engine2.cpp b/sourcepawn/jit/engine2.cpp index 068ea0ef..c11e7d3e 100644 --- a/sourcepawn/jit/engine2.cpp +++ b/sourcepawn/jit/engine2.cpp @@ -20,13 +20,12 @@ #include "sp_vm_engine.h" #include "watchdog_timer.h" #include +#include "environment.h" using namespace SourcePawn; SourcePawnEngine2::SourcePawnEngine2() { - profiler_ = NULL; - jit_enabled_ = true; } IPluginRuntime * @@ -172,7 +171,9 @@ SourcePawnEngine2::GetVersionString() IDebugListener * SourcePawnEngine2::SetDebugListener(IDebugListener *listener) { - return g_engine1.SetDebugListener(listener); + IDebugListener *old = Environment::get()->debugger(); + Environment::get()->SetDebugger(listener); + return old; } unsigned int @@ -190,20 +191,18 @@ SourcePawnEngine2::StartCompilation() const char * SourcePawnEngine2::GetErrorString(int err) { - return g_engine1.GetErrorString(err); + return Environment::get()->GetErrorString(err); } bool SourcePawnEngine2::Initialize() { - return g_Jit.InitializeJIT(); + return true; } void SourcePawnEngine2::Shutdown() { - g_WatchdogTimer.Shutdown(); - g_Jit.ShutdownJIT(); } IPluginRuntime * @@ -227,6 +226,42 @@ SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory) bool 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); +} diff --git a/sourcepawn/jit/engine2.h b/sourcepawn/jit/engine2.h index ab9db988..4a436711 100644 --- a/sourcepawn/jit/engine2.h +++ b/sourcepawn/jit/engine2.h @@ -14,6 +14,7 @@ #define _INCLUDE_SOURCEPAWN_ENGINE_2_H_ #include +#include // Replace with am-cxx later. namespace SourcePawn { @@ -40,61 +41,14 @@ class SourcePawnEngine2 : public ISourcePawnEngine2 IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory); bool InstallWatchdogTimer(size_t timeout_ms); - bool SetJitEnabled(bool enabled) { - jit_enabled_ = enabled; - return true; - } - - bool IsJitEnabled() { - 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_; + bool SetJitEnabled(bool enabled) KE_OVERRIDE; + bool IsJitEnabled() KE_OVERRIDE; + void SetProfiler(IProfiler *profiler) KE_OVERRIDE; + void EnableProfiling() KE_OVERRIDE; + void DisableProfiling() KE_OVERRIDE; + void SetProfilingTool(IProfilingTool *tool) KE_OVERRIDE; }; } // 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_ diff --git a/sourcepawn/jit/environment.cpp b/sourcepawn/jit/environment.cpp new file mode 100644 index 00000000..bc4c78d0 --- /dev/null +++ b/sourcepawn/jit/environment.cpp @@ -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); +} diff --git a/sourcepawn/jit/environment.h b/sourcepawn/jit/environment.h new file mode 100644 index 00000000..5195778b --- /dev/null +++ b/sourcepawn/jit/environment.h @@ -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 +#include // 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 api_v1_; + ke::AutoPtr 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_ diff --git a/sourcepawn/jit/sp_vm_basecontext.cpp b/sourcepawn/jit/sp_vm_basecontext.cpp index cd2c996d..8dbfb902 100644 --- a/sourcepawn/jit/sp_vm_basecontext.cpp +++ b/sourcepawn/jit/sp_vm_basecontext.cpp @@ -21,6 +21,7 @@ #include "x86/jit_x86.h" #include "engine2.h" #include "interpreter.h" +#include "environment.h" using namespace SourcePawn; @@ -555,7 +556,7 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned EnterProfileScope scriptScope("SourcePawn", cfun->FullName()); /* See if we have to compile the callee. */ - if (g_engine2.IsJitEnabled() && + if (Environment::get()->IsJitEnabled() && (fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL) { /* 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 */ - if (g_engine2.IsJitEnabled()) + if (Environment::get()->IsJitEnabled()) ir = g_Jit.InvokeFunction(m_pRuntime, fn, result); else 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(); 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.hp = save_hp; diff --git a/sourcepawn/jit/sp_vm_engine.cpp b/sourcepawn/jit/sp_vm_engine.cpp index bd653a3d..5efda010 100644 --- a/sourcepawn/jit/sp_vm_engine.cpp +++ b/sourcepawn/jit/sp_vm_engine.cpp @@ -16,15 +16,14 @@ #include "sp_vm_types.h" #include #include "sp_vm_engine.h" +#include "jit_x86.h" #include "zlib/zlib.h" #include "sp_vm_basecontext.h" -#include "jit_x86.h" +#include "environment.h" #if defined __GNUC__ #include #endif -SourcePawnEngine g_engine1; - #if defined WIN32 #define WIN32_LEAN_AND_MEAN #include @@ -38,53 +37,14 @@ SourcePawnEngine g_engine1; 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 * SourcePawnEngine::GetErrorString(int error) { - if (error < 1 || error > ERROR_MESSAGE_MAX) - return NULL; - return g_ErrorMsgTable[error]; + return Environment::get()->GetErrorString(error); } SourcePawnEngine::SourcePawnEngine() { - m_pDebugHook = NULL; } SourcePawnEngine::~SourcePawnEngine() @@ -147,7 +107,7 @@ SourcePawnEngine::ExecFree(void *address) void SourcePawnEngine::SetReadWriteExecute(void *ptr) { -//:TODO: g_ExeMemory.SetRWE(ptr); + //:TODO: g_ExeMemory.SetRWE(ptr); SetReadExecute(ptr); } @@ -190,17 +150,15 @@ SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin) IDebugListener * SourcePawnEngine::SetDebugListener(IDebugListener *pListener) { - IDebugListener *old = m_pDebugHook; - - m_pDebugHook = pListener; - + IDebugListener *old = Environment::get()->debugger(); + Environment::get()->SetDebugger(pListener); return old; } unsigned int SourcePawnEngine::GetEngineAPIVersion() { - return SOURCEPAWN_ENGINE_API_VERSION; + return 4; } unsigned int @@ -208,124 +166,3 @@ SourcePawnEngine::GetContextCallCount() { 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; -} - diff --git a/sourcepawn/jit/sp_vm_engine.h b/sourcepawn/jit/sp_vm_engine.h index 202f9212..7f35f4e3 100644 --- a/sourcepawn/jit/sp_vm_engine.h +++ b/sourcepawn/jit/sp_vm_engine.h @@ -13,35 +13,12 @@ #ifndef _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ #define _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ -#include "sp_vm_api.h" +#include +#include // Replace with am-cxx.h later. #include "scripted-invoker.h" 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 { public: @@ -49,31 +26,22 @@ class SourcePawnEngine : public ISourcePawnEngine ~SourcePawnEngine(); public: //ISourcePawnEngine - sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err); - sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err); - int FreeFromMemory(sp_plugin_t *plugin); - void *BaseAlloc(size_t size); - void BaseFree(void *memory); - void *ExecAlloc(size_t size); - void ExecFree(void *address); - IDebugListener *SetDebugListener(IDebugListener *pListener); - unsigned int GetContextCallCount(); - unsigned int GetEngineAPIVersion(); - void *AllocatePageMemory(size_t size); - void SetReadWrite(void *ptr); - void SetReadExecute(void *ptr); + sp_plugin_t *LoadFromFilePointer(FILE *fp, int *err) KE_OVERRIDE; + sp_plugin_t *LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) KE_OVERRIDE; + int FreeFromMemory(sp_plugin_t *plugin) KE_OVERRIDE; + void *BaseAlloc(size_t size) KE_OVERRIDE; + void BaseFree(void *memory) KE_OVERRIDE; + void *ExecAlloc(size_t size) KE_OVERRIDE; + void ExecFree(void *address) KE_OVERRIDE; + IDebugListener *SetDebugListener(IDebugListener *pListener) KE_OVERRIDE; + unsigned int GetContextCallCount() KE_OVERRIDE; + unsigned int GetEngineAPIVersion() KE_OVERRIDE; + void *AllocatePageMemory(size_t size) KE_OVERRIDE; + void SetReadWrite(void *ptr) KE_OVERRIDE; + void SetReadExecute(void *ptr) KE_OVERRIDE; + void FreePageMemory(void *ptr) KE_OVERRIDE; void SetReadWriteExecute(void *ptr); - void FreePageMemory(void *ptr); 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_ diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 3fb4ff1d..d1ad5886 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -51,7 +51,6 @@ using namespace Knight; JITX86 g_Jit; KeCodeCache *g_pCodeCache = NULL; -ISourcePawnEngine *engine = &g_engine1; static inline uint8_t * LinkCode(AssemblerX86 &masm)