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..903f6195 100644 --- a/sourcepawn/jit/AMBuilder +++ b/sourcepawn/jit/AMBuilder @@ -4,7 +4,6 @@ import os Includes = [ os.path.join(SM.mms_root, 'core', 'sourcehook'), os.path.join(builder.sourcePath, 'sourcepawn', 'jit'), - os.path.join(builder.sourcePath, 'sourcepawn', 'jit', 'x86'), os.path.join(builder.sourcePath, 'public'), os.path.join(builder.sourcePath, 'public', 'sourcepawn'), os.path.join(builder.sourcePath, 'public', 'amtl'), @@ -31,11 +30,12 @@ def setup(binary): # Build the static library. library = setup(builder.compiler.StaticLibrary('sourcepawn')) library.sources += [ + 'api.cpp', 'plugin-runtime.cpp', 'compiled-function.cpp', - 'engine2.cpp', + 'debug-trace.cpp', + 'environment.cpp', 'sp_vm_basecontext.cpp', - 'sp_vm_engine.cpp', 'scripted-invoker.cpp', 'opcodes.cpp', 'interpreter.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/engine2.cpp b/sourcepawn/jit/api.cpp similarity index 57% rename from sourcepawn/jit/engine2.cpp rename to sourcepawn/jit/api.cpp index 068ea0ef..cfee47ea 100644 --- a/sourcepawn/jit/engine2.cpp +++ b/sourcepawn/jit/api.cpp @@ -1,232 +1,415 @@ -// 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 -#include -#include -#include "engine2.h" -#include "x86/jit_x86.h" -#include "zlib/zlib.h" -#include "plugin-runtime.h" -#include "sp_vm_engine.h" -#include "watchdog_timer.h" -#include - -using namespace SourcePawn; - -SourcePawnEngine2::SourcePawnEngine2() -{ - profiler_ = NULL; - jit_enabled_ = true; -} - -IPluginRuntime * -SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err) -{ - sp_file_hdr_t hdr; - uint8_t *base; - int z_result; - int error; - size_t ignore; - PluginRuntime *pRuntime; - - FILE *fp = fopen(file, "rb"); - - if (!fp) { - error = SP_ERROR_NOT_FOUND; - goto return_error; - } - - /* Rewind for safety */ - ignore = fread(&hdr, sizeof(sp_file_hdr_t), 1, fp); - - if (hdr.magic != SmxConsts::FILE_MAGIC) { - error = SP_ERROR_FILE_FORMAT; - goto return_error; - } - - switch (hdr.compression) - { - case SmxConsts::FILE_COMPRESSION_GZ: - { - uint32_t uncompsize = hdr.imagesize - hdr.dataoffs; - uint32_t compsize = hdr.disksize - hdr.dataoffs; - uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t); - uLongf destlen = uncompsize; - - char *tempbuf = (char *)malloc(compsize); - void *uncompdata = malloc(uncompsize); - void *sectheader = malloc(sectsize); - - ignore = fread(sectheader, sectsize, 1, fp); - ignore = fread(tempbuf, compsize, 1, fp); - - z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize); - free(tempbuf); - if (z_result != Z_OK) - { - free(sectheader); - free(uncompdata); - error = SP_ERROR_DECOMPRESSOR; - goto return_error; - } - - base = (uint8_t *)malloc(hdr.imagesize); - memcpy(base, &hdr, sizeof(sp_file_hdr_t)); - memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize); - free(sectheader); - memcpy(base + hdr.dataoffs, uncompdata, uncompsize); - free(uncompdata); - break; - } - case SmxConsts::FILE_COMPRESSION_NONE: - { - base = (uint8_t *)malloc(hdr.imagesize); - rewind(fp); - ignore = fread(base, hdr.imagesize, 1, fp); - break; - } - default: - { - error = SP_ERROR_DECOMPRESSOR; - goto return_error; - } - } - - pRuntime = new PluginRuntime(); - if ((error = pRuntime->CreateFromMemory(&hdr, base)) != SP_ERROR_NONE) { - delete pRuntime; - goto return_error; - } - - size_t len; - - len = strlen(file); - for (size_t i = len - 1; i < len; i--) - { - if (file[i] == '/' - #if defined WIN32 - || file[i] == '\\' - #endif - ) - { - pRuntime->SetName(&file[i+1]); - break; - } - } - - (void)ignore; - - if (!pRuntime->plugin()->name) - pRuntime->SetName(file); - - pRuntime->ApplyCompilationOptions(co); - - fclose(fp); - - return pRuntime; - -return_error: - *err = error; - if (fp != NULL) - { - fclose(fp); - } - - return NULL; -} - -SPVM_NATIVE_FUNC -SourcePawnEngine2::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) -{ - return g_Jit.CreateFakeNative(callback, pData); -} - -void -SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func) -{ - g_Jit.DestroyFakeNative(func); -} - -const char * -SourcePawnEngine2::GetEngineName() -{ - return "SourcePawn 1.7, jit-x86"; -} - -const char * -SourcePawnEngine2::GetVersionString() -{ - return SOURCEMOD_VERSION; -} - -IDebugListener * -SourcePawnEngine2::SetDebugListener(IDebugListener *listener) -{ - return g_engine1.SetDebugListener(listener); -} - -unsigned int -SourcePawnEngine2::GetAPIVersion() -{ - return SOURCEPAWN_ENGINE2_API_VERSION; -} - -ICompilation * -SourcePawnEngine2::StartCompilation() -{ - return g_Jit.StartCompilation(); -} - -const char * -SourcePawnEngine2::GetErrorString(int err) -{ - return g_engine1.GetErrorString(err); -} - -bool -SourcePawnEngine2::Initialize() -{ - return g_Jit.InitializeJIT(); -} - -void -SourcePawnEngine2::Shutdown() -{ - g_WatchdogTimer.Shutdown(); - g_Jit.ShutdownJIT(); -} - -IPluginRuntime * -SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory) -{ - int err; - - PluginRuntime *rt = new PluginRuntime(); - if ((err = rt->CreateBlank(memory)) != SP_ERROR_NONE) { - delete rt; - return NULL; - } - - rt->SetName(name != NULL ? name : ""); - - rt->ApplyCompilationOptions(NULL); - - return rt; -} - -bool -SourcePawnEngine2::InstallWatchdogTimer(size_t timeout_ms) -{ - return g_WatchdogTimer.Initialize(timeout_ms); -} - +// 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 +#include +#include +#include +#include "x86/jit_x86.h" +#include "environment.h" +#include "api.h" +#include "zlib/zlib.h" +#if defined __GNUC__ +#include +#endif + +#if defined WIN32 + #define WIN32_LEAN_AND_MEAN + #include +#elif defined __GNUC__ + #include +#endif + +#if defined __linux__ +#include +#endif + +#include + +using namespace sp; +using namespace SourcePawn; + +// ////// // +// API v1 +// ////// // + +SourcePawnEngine::SourcePawnEngine() +{ +} + +const char * +SourcePawnEngine::GetErrorString(int error) +{ + return Environment::get()->GetErrorString(error); +} + +void * +SourcePawnEngine::ExecAlloc(size_t size) +{ +#if defined WIN32 + return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#elif defined __GNUC__ +# if defined __APPLE__ + void *base = valloc(size); +# else + void *base = memalign(sysconf(_SC_PAGESIZE), size); +# endif + if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { + free(base); + return NULL; + } + return base; +#endif +} + +void * +SourcePawnEngine::AllocatePageMemory(size_t size) +{ + return g_Jit.AllocCode(size); +} + +void +SourcePawnEngine::SetReadExecute(void *ptr) +{ + /* already re */ +} + +void +SourcePawnEngine::SetReadWrite(void *ptr) +{ + /* already rw */ +} + +void +SourcePawnEngine::FreePageMemory(void *ptr) +{ + g_Jit.FreeCode(ptr); +} + +void +SourcePawnEngine::ExecFree(void *address) +{ +#if defined WIN32 + VirtualFree(address, 0, MEM_RELEASE); +#elif defined __GNUC__ + free(address); +#endif +} + +void +SourcePawnEngine::SetReadWriteExecute(void *ptr) +{ + //:TODO: g_ExeMemory.SetRWE(ptr); + SetReadExecute(ptr); +} + +void * +SourcePawnEngine::BaseAlloc(size_t size) +{ + return malloc(size); +} + +void +SourcePawnEngine::BaseFree(void *memory) +{ + free(memory); +} + +sp_plugin_t * +SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err) +{ + if (err != NULL) + *err = SP_ERROR_ABORTED; + + return NULL; +} + +sp_plugin_t * +SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) +{ + if (err != NULL) + *err = SP_ERROR_ABORTED; + + return NULL; +} + +int +SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin) +{ + return SP_ERROR_ABORTED; +} + +IDebugListener * +SourcePawnEngine::SetDebugListener(IDebugListener *pListener) +{ + IDebugListener *old = Environment::get()->debugger(); + Environment::get()->SetDebugger(pListener); + return old; +} + +unsigned int +SourcePawnEngine::GetEngineAPIVersion() +{ + return 4; +} + +unsigned int +SourcePawnEngine::GetContextCallCount() +{ + return 0; +} + +// ////// // +// API v2 +// ////// // + +SourcePawnEngine2::SourcePawnEngine2() +{ +} + +IPluginRuntime * +SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err) +{ + sp_file_hdr_t hdr; + uint8_t *base; + int z_result; + int error; + size_t ignore; + PluginRuntime *pRuntime; + + FILE *fp = fopen(file, "rb"); + + if (!fp) { + error = SP_ERROR_NOT_FOUND; + goto return_error; + } + + /* Rewind for safety */ + ignore = fread(&hdr, sizeof(sp_file_hdr_t), 1, fp); + + if (hdr.magic != SmxConsts::FILE_MAGIC) { + error = SP_ERROR_FILE_FORMAT; + goto return_error; + } + + switch (hdr.compression) + { + case SmxConsts::FILE_COMPRESSION_GZ: + { + uint32_t uncompsize = hdr.imagesize - hdr.dataoffs; + uint32_t compsize = hdr.disksize - hdr.dataoffs; + uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t); + uLongf destlen = uncompsize; + + char *tempbuf = (char *)malloc(compsize); + void *uncompdata = malloc(uncompsize); + void *sectheader = malloc(sectsize); + + ignore = fread(sectheader, sectsize, 1, fp); + ignore = fread(tempbuf, compsize, 1, fp); + + z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize); + free(tempbuf); + if (z_result != Z_OK) + { + free(sectheader); + free(uncompdata); + error = SP_ERROR_DECOMPRESSOR; + goto return_error; + } + + base = (uint8_t *)malloc(hdr.imagesize); + memcpy(base, &hdr, sizeof(sp_file_hdr_t)); + memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize); + free(sectheader); + memcpy(base + hdr.dataoffs, uncompdata, uncompsize); + free(uncompdata); + break; + } + case SmxConsts::FILE_COMPRESSION_NONE: + { + base = (uint8_t *)malloc(hdr.imagesize); + rewind(fp); + ignore = fread(base, hdr.imagesize, 1, fp); + break; + } + default: + { + error = SP_ERROR_DECOMPRESSOR; + goto return_error; + } + } + + pRuntime = new PluginRuntime(); + if ((error = pRuntime->CreateFromMemory(&hdr, base)) != SP_ERROR_NONE) { + delete pRuntime; + goto return_error; + } + + size_t len; + + len = strlen(file); + for (size_t i = len - 1; i < len; i--) + { + if (file[i] == '/' + #if defined WIN32 + || file[i] == '\\' + #endif + ) + { + pRuntime->SetName(&file[i+1]); + break; + } + } + + (void)ignore; + + if (!pRuntime->plugin()->name) + pRuntime->SetName(file); + + pRuntime->ApplyCompilationOptions(co); + + fclose(fp); + + return pRuntime; + +return_error: + *err = error; + if (fp != NULL) + { + fclose(fp); + } + + return NULL; +} + +SPVM_NATIVE_FUNC +SourcePawnEngine2::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) +{ + return g_Jit.CreateFakeNative(callback, pData); +} + +void +SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func) +{ + g_Jit.DestroyFakeNative(func); +} + +const char * +SourcePawnEngine2::GetEngineName() +{ + return "SourcePawn 1.7, jit-x86"; +} + +const char * +SourcePawnEngine2::GetVersionString() +{ + return SOURCEMOD_VERSION; +} + +IDebugListener * +SourcePawnEngine2::SetDebugListener(IDebugListener *listener) +{ + IDebugListener *old = Environment::get()->debugger(); + Environment::get()->SetDebugger(listener); + return old; +} + +unsigned int +SourcePawnEngine2::GetAPIVersion() +{ + return SOURCEPAWN_ENGINE2_API_VERSION; +} + +ICompilation * +SourcePawnEngine2::StartCompilation() +{ + return g_Jit.StartCompilation(); +} + +const char * +SourcePawnEngine2::GetErrorString(int err) +{ + return Environment::get()->GetErrorString(err); +} + +bool +SourcePawnEngine2::Initialize() +{ + return true; +} + +void +SourcePawnEngine2::Shutdown() +{ +} + +IPluginRuntime * +SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory) +{ + int err; + + PluginRuntime *rt = new PluginRuntime(); + if ((err = rt->CreateBlank(memory)) != SP_ERROR_NONE) { + delete rt; + return NULL; + } + + rt->SetName(name != NULL ? name : ""); + + rt->ApplyCompilationOptions(NULL); + + return rt; +} + +bool +SourcePawnEngine2::InstallWatchdogTimer(size_t 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/api.h b/sourcepawn/jit/api.h new file mode 100644 index 00000000..88fa9688 --- /dev/null +++ b/sourcepawn/jit/api.h @@ -0,0 +1,75 @@ +// 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_ + +#include +#include // Replace with am-cxx later. + +namespace sp { + +using namespace SourcePawn; + +class SourcePawnEngine : public ISourcePawnEngine +{ + public: + SourcePawnEngine(); + + 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); + const char *GetErrorString(int err); +}; + +class SourcePawnEngine2 : public ISourcePawnEngine2 +{ + public: + SourcePawnEngine2(); + + unsigned int GetAPIVersion() KE_OVERRIDE; + const char *GetEngineName() KE_OVERRIDE; + const char *GetVersionString() KE_OVERRIDE; + IPluginRuntime *LoadPlugin(ICompilation *co, const char *file, int *err) KE_OVERRIDE; + SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) KE_OVERRIDE; + void DestroyFakeNative(SPVM_NATIVE_FUNC func) KE_OVERRIDE; + IDebugListener *SetDebugListener(IDebugListener *listener) KE_OVERRIDE; + ICompilation *StartCompilation() KE_OVERRIDE; + const char *GetErrorString(int err) KE_OVERRIDE; + bool Initialize() KE_OVERRIDE; + void Shutdown() KE_OVERRIDE; + IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory) KE_OVERRIDE; + bool InstallWatchdogTimer(size_t timeout_ms) KE_OVERRIDE; + + 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 + +#endif // _include_sourcepawn_vm_api_h_ diff --git a/sourcepawn/jit/compiled-function.cpp b/sourcepawn/jit/compiled-function.cpp index 2cfcfbbc..11944a7f 100644 --- a/sourcepawn/jit/compiled-function.cpp +++ b/sourcepawn/jit/compiled-function.cpp @@ -11,8 +11,7 @@ // SourcePawn. If not, see http://www.gnu.org/licenses/. // #include "compiled-function.h" -#include "sp_vm_engine.h" -#include "jit_x86.h" +#include "x86/jit_x86.h" CompiledFunction::CompiledFunction(void *entry_addr, cell_t pcode_offs, FixedArray *edges) : entry_(entry_addr), 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..e7fa3c42 100644 --- a/sourcepawn/jit/dll_exports.cpp +++ b/sourcepawn/jit/dll_exports.cpp @@ -32,13 +32,27 @@ #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 +80,8 @@ private: T *t_; }; +Environment *sEnv; + class ShellDebugListener : public IDebugListener { public: @@ -172,16 +188,16 @@ static cell_t PrintFloat(IPluginContext *cx, const cell_t *params) static int Execute(const char *file) { - ICompilation *co = g_engine2.StartCompilation(); + ICompilation *co = sEnv->APIv2()->StartCompilation(); if (!co) { fprintf(stderr, "Could not create a compilation context\n"); return 1; } int err; - AutoT rt(g_engine2.LoadPlugin(co, file, &err)); + AutoT rt(sEnv->APIv2()->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 +215,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 +229,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.h b/sourcepawn/jit/engine2.h deleted file mode 100644 index ab9db988..00000000 --- a/sourcepawn/jit/engine2.h +++ /dev/null @@ -1,100 +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_ENGINE_2_H_ -#define _INCLUDE_SOURCEPAWN_ENGINE_2_H_ - -#include - -namespace SourcePawn { - -/** - * @brief Outlines the interface a Virtual Machine (JIT) must expose - */ -class SourcePawnEngine2 : public ISourcePawnEngine2 -{ - public: - SourcePawnEngine2(); - - public: - unsigned int GetAPIVersion(); - const char *GetEngineName(); - const char *GetVersionString(); - IPluginRuntime *LoadPlugin(ICompilation *co, const char *file, int *err); - SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData); - void DestroyFakeNative(SPVM_NATIVE_FUNC func); - IDebugListener *SetDebugListener(IDebugListener *listener); - ICompilation *StartCompilation(); - const char *GetErrorString(int err); - bool Initialize(); - void Shutdown(); - 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_; -}; - -} // 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..5f5c8d6c --- /dev/null +++ b/sourcepawn/jit/environment.cpp @@ -0,0 +1,156 @@ +// 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 "x86/jit_x86.h" +#include "watchdog_timer.h" +#include "debug-trace.h" +#include "api.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/plugin-runtime.cpp b/sourcepawn/jit/plugin-runtime.cpp index b460651d..1aa0962b 100644 --- a/sourcepawn/jit/plugin-runtime.cpp +++ b/sourcepawn/jit/plugin-runtime.cpp @@ -15,10 +15,8 @@ #include #include #include "plugin-runtime.h" -#include "sp_vm_engine.h" #include "x86/jit_x86.h" #include "sp_vm_basecontext.h" -#include "engine2.h" #include "md5/md5.h" diff --git a/sourcepawn/jit/scripted-invoker.h b/sourcepawn/jit/scripted-invoker.h index aa2bc257..653a5c10 100644 --- a/sourcepawn/jit/scripted-invoker.h +++ b/sourcepawn/jit/scripted-invoker.h @@ -37,8 +37,6 @@ class CPlugin; class ScriptedInvoker : public IPluginFunction { - friend class SourcePawnEngine; - public: ScriptedInvoker(PluginRuntime *pRuntime, funcid_t fnid, uint32_t pub_id); ~ScriptedInvoker(); diff --git a/sourcepawn/jit/sp_vm_basecontext.cpp b/sourcepawn/jit/sp_vm_basecontext.cpp index cd2c996d..a7e39195 100644 --- a/sourcepawn/jit/sp_vm_basecontext.cpp +++ b/sourcepawn/jit/sp_vm_basecontext.cpp @@ -16,11 +16,10 @@ #include #include "sp_vm_api.h" #include "sp_vm_basecontext.h" -#include "sp_vm_engine.h" #include "watchdog_timer.h" #include "x86/jit_x86.h" -#include "engine2.h" #include "interpreter.h" +#include "environment.h" using namespace SourcePawn; @@ -555,7 +554,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 +599,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 +634,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..5c6718f0 100644 --- a/sourcepawn/jit/sp_vm_engine.cpp +++ b/sourcepawn/jit/sp_vm_engine.cpp @@ -10,322 +10,4 @@ // You should have received a copy of the GNU General Public License along with // SourcePawn. If not, see http://www.gnu.org/licenses/. // -#include -#include -#include -#include "sp_vm_types.h" -#include -#include "sp_vm_engine.h" -#include "zlib/zlib.h" -#include "sp_vm_basecontext.h" -#include "jit_x86.h" -#if defined __GNUC__ -#include -#endif - -SourcePawnEngine g_engine1; - -#if defined WIN32 - #define WIN32_LEAN_AND_MEAN - #include -#elif defined __GNUC__ - #include -#endif - -#if defined __linux__ -#include -#endif - -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]; -} - -SourcePawnEngine::SourcePawnEngine() -{ - m_pDebugHook = NULL; -} - -SourcePawnEngine::~SourcePawnEngine() -{ -} - -void * -SourcePawnEngine::ExecAlloc(size_t size) -{ -#if defined WIN32 - return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); -#elif defined __GNUC__ -# if defined __APPLE__ - void *base = valloc(size); -# else - void *base = memalign(sysconf(_SC_PAGESIZE), size); -# endif - if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { - free(base); - return NULL; - } - return base; -#endif -} - -void * -SourcePawnEngine::AllocatePageMemory(size_t size) -{ - return g_Jit.AllocCode(size); -} - -void -SourcePawnEngine::SetReadExecute(void *ptr) -{ - /* already re */ -} - -void -SourcePawnEngine::SetReadWrite(void *ptr) -{ - /* already rw */ -} - -void -SourcePawnEngine::FreePageMemory(void *ptr) -{ - g_Jit.FreeCode(ptr); -} - -void -SourcePawnEngine::ExecFree(void *address) -{ -#if defined WIN32 - VirtualFree(address, 0, MEM_RELEASE); -#elif defined __GNUC__ - free(address); -#endif -} - -void -SourcePawnEngine::SetReadWriteExecute(void *ptr) -{ -//:TODO: g_ExeMemory.SetRWE(ptr); - SetReadExecute(ptr); -} - -void * -SourcePawnEngine::BaseAlloc(size_t size) -{ - return malloc(size); -} - -void -SourcePawnEngine::BaseFree(void *memory) -{ - free(memory); -} - -sp_plugin_t * -SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err) -{ - if (err != NULL) - *err = SP_ERROR_ABORTED; - - return NULL; -} - -sp_plugin_t * -SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err) -{ - if (err != NULL) - *err = SP_ERROR_ABORTED; - - return NULL; -} - -int -SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin) -{ - return SP_ERROR_ABORTED; -} - -IDebugListener * -SourcePawnEngine::SetDebugListener(IDebugListener *pListener) -{ - IDebugListener *old = m_pDebugHook; - - m_pDebugHook = pListener; - - return old; -} - -unsigned int -SourcePawnEngine::GetEngineAPIVersion() -{ - return SOURCEPAWN_ENGINE_API_VERSION; -} - -unsigned int -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 deleted file mode 100644 index 202f9212..00000000 --- a/sourcepawn/jit/sp_vm_engine.h +++ /dev/null @@ -1,79 +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_ENGINE_H_ -#define _INCLUDE_SOURCEPAWN_VM_ENGINE_H_ - -#include "sp_vm_api.h" -#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: - SourcePawnEngine(); - ~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); - 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..99c6d4e9 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -33,8 +33,6 @@ #include #include #include "jit_x86.h" -#include "../sp_vm_engine.h" -#include "../engine2.h" #include "../plugin-runtime.h" #include "../sp_vm_basecontext.h" #include "watchdog_timer.h" @@ -51,7 +49,6 @@ using namespace Knight; JITX86 g_Jit; KeCodeCache *g_pCodeCache = NULL; -ISourcePawnEngine *engine = &g_engine1; static inline uint8_t * LinkCode(AssemblerX86 &masm)