core/systems/PluginSys.h
author David Anderson <dvander@alliedmods.net>
Fri Aug 15 05:22:26 2008 +0000 (2008-08-15)
changeset 2226 d10b38524be3
parent 2193 db7c6362f3f2
child 2289 44045ead61cd
permissions -rw-r--r--
Landed sourcepawn-1.2. The big changes:

1) JIT compilation/optimization now occurs per-function, and only when functions are first used. We're now officially a whole-method JIT rather than an AOT compiler (albiet, still a simple JIT). This has two implications: Functions are now much better abstracted internally, and loading a plugin is now much less expensive. If a function contains calls to other functions, THOSE functions are only compiled when they're invoked as well.

2) I've removed debug mode. We always show full backtraces now, as there was a very cheap way to implement this which really cleaned up everything. This is great for a number of reasons -- there's less code, the JIT is better designed, we don't need to relocate debug tables, and best of all we no longer have to tell users to enable debug mode at their own expense.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
    33 #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
    34 
    35 #include <time.h>
    36 #include <errno.h>
    37 #include <sys/stat.h>
    38 #include <IPluginSys.h>
    39 #include <IHandleSys.h>
    40 #include <IForwardSys.h>
    41 #include <sh_list.h>
    42 #include <sh_stack.h>
    43 #include <sh_vector.h>
    44 #include <sh_string.h>
    45 #include "sm_globals.h"
    46 #include "PluginInfoDatabase.h"
    47 #include "sm_trie.h"
    48 #include "sourcemod.h"
    49 #include <IRootConsoleMenu.h>
    50 #if defined ORANGEBOX_BUILD
    51 #include "convar_sm_ob.h"
    52 #else
    53 #include "convar_sm.h"
    54 #endif
    55 #include "ITranslator.h"
    56 #include "NativeOwner.h"
    57 #include "ShareSys.h"
    58 
    59 using namespace SourceHook;
    60 
    61 /**
    62  * NOTES:
    63  *
    64  * UPDATE 2008-03-11: These comments are horribly out of date.  They paint a good overall 
    65  * picture of how PluginSys works, but things like dependencies and fake natives have 
    66  * complicated things quite a bit.
    67  *
    68  *  Currently this system needs a lot of work but it's good skeletally.  Plugin creation 
    69  * is done without actually compiling anything.  This is done by Load functions in the 
    70  * manager.  This will need a rewrite when we add context switching.
    71  *
    72  *  The plugin object itself has a few things to note.  The most important is that it stores
    73  * a table of function objects.  The manager marshals allocation and freeing of these objects.
    74  * The plugin object can be in erroneous states, they are:
    75  *   Plugin_Error   --> Some error occurred any time during or after compilation.
    76  *						This error can be cleared since the plugin itself is valid.
    77  *						However, the state itself being set prevents any runtime action.
    78  *   Plugin_BadLoad	--> The plugin failed to load entirely and nothing can be done to save it.
    79  *
    80  *  If a plugin fails to load externally, it is never added to the internal tracker.  However, 
    81  * plugins that failed to load from the internal loading mechanism are always tracked.  This 
    82  * allows users to see which automatically loaded plugins failed, and makes the interface a bit
    83  * more flexible.
    84  *
    85  *  Once a plugin is compiled, it sets its own state to Plugin_Created.  This state is still invalid
    86  * for execution.  SourceMod is a two pass system, and even though the second pass is not implemented
    87  * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass.  When
    88  * implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
    89  *
    90  *  The two pass loading mechanism is described below.  Modules/natives are not implemented yet.
    91  * PASS ONE: All loadable plugins are found and have the following steps performed:
    92  *			 1.  Loading and compilation is attempted.
    93  *			 2.  If successful, all natives from Core are added.
    94  *			 3.  OnPluginLoad() is called.
    95  *			 4.  If failed, any user natives are scrapped and the process halts here.
    96  *			 5.  If successful, the plugin is ready for Pass 2.
    97  * INTERMEDIATE:
    98  *			 1.  All forced modules are loaded.
    99  * PASS TWO: All loaded plugins are found and have these steps performed:
   100  *			 1. Any modules referenced in the plugin that are not already loaded, are loaded.
   101  *			 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
   102  *			 3. If any natives are unresolved, check if they are found in the user-natives pool.
   103  *			 4. If yes, load succeeds.  If not, natives are passed through a native acceptance filter.
   104  *			 5. If the filter fails, the plugin is marked as failed.
   105  *			 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
   106  *			    Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
   107  * PASS THREE (not a real pass):
   108  *			 7. Once all plugins are deemed to be loaded, OnPluginStart() is called
   109  */
   110 
   111 enum LoadRes
   112 {
   113 	LoadRes_Successful,
   114 	LoadRes_AlreadyLoaded,
   115 	LoadRes_Failure,
   116 	LoadRes_NeverLoad
   117 };
   118 
   119 struct AutoConfig
   120 {
   121 	String autocfg;
   122 	String folder;
   123 	bool create;
   124 };
   125 
   126 class CPlugin;
   127 
   128 class CPlugin : 
   129 	public IPlugin,
   130 	public CNativeOwner
   131 {
   132 	friend class CPluginManager;
   133 	friend class CFunction;
   134 public:
   135 	CPlugin(const char *file);
   136 	~CPlugin();
   137 public:
   138 	PluginType GetType();
   139 	SourcePawn::IPluginContext *GetBaseContext();
   140 	sp_context_t *GetContext();
   141 	void *GetPluginStructure();
   142 	const char *GetFilename();
   143 	bool IsDebugging();
   144 	PluginStatus GetStatus();
   145 	const sm_plugininfo_t *GetPublicInfo();
   146 	bool SetPauseState(bool paused);
   147 	unsigned int GetSerial();
   148 	IdentityToken_t *GetIdentity();
   149 	unsigned int CalcMemUsage();
   150 	bool SetProperty(const char *prop, void *ptr);
   151 	bool GetProperty(const char *prop, void **ptr, bool remove=false);
   152 	void DropEverything();
   153 	SourcePawn::IPluginRuntime *GetRuntime();
   154 public:
   155 	/**
   156 	 * Creates a plugin object with default values.
   157 	 *   If an error buffer is specified, and an error occurs, the error will be copied to the buffer
   158 	 * and NULL will be returned.
   159 	 *   If an error buffer is not specified, the error will be copied to an internal buffer and 
   160 	 * a valid (but error-stated) CPlugin will be returned.
   161 	 */
   162 	static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
   163 public:
   164 
   165 	/**
   166 	 * Sets an error state on the plugin
   167 	 */
   168 	void SetErrorState(PluginStatus status, const char *error_fmt, ...);
   169 
   170 	/**
   171 	 * Initializes the plugin's identity information
   172 	 */
   173 	void InitIdentity();
   174 
   175 	/**
   176 	 * Calls the OnPluginLoad function, and sets any failed states if necessary.
   177 	 * NOTE: Valid pre-states are: Plugin_Created
   178 	 * NOTE: If validated, plugin state is changed to Plugin_Loaded
   179 	 *
   180 	 * If the error buffer is NULL, the error message is cached locally.
   181 	 */
   182 	bool Call_AskPluginLoad(char *error, size_t maxlength);
   183 
   184 	/**
   185 	 * Calls the OnPluginStart function.
   186 	 * NOTE: Valid pre-states are: Plugin_Created
   187 	 * NOTE: Post-state will be Plugin_Running
   188 	 */
   189 	void Call_OnPluginStart();
   190 
   191 	/**
   192 	 * Calls the OnPluginEnd function.
   193 	 */
   194 	void Call_OnPluginEnd();
   195 
   196 	/**
   197 	 * Calls the OnAllPluginsLoaded function.
   198 	 */
   199 	void Call_OnAllPluginsLoaded();
   200 
   201 	/**
   202 	 * Returns true if a plugin is usable.
   203 	 */
   204 	bool IsRunnable();
   205 
   206 	/**
   207 	 * Get languages info.
   208 	 */
   209 	IPhraseCollection *GetPhrases();
   210 public:
   211 	/**
   212 	 * Returns the modification time during last plugin load.
   213 	 */
   214 	time_t GetTimeStamp();
   215 
   216 	/**
   217 	 * Returns the current modification time of the plugin file.
   218 	 */
   219 	time_t GetFileTimeStamp();
   220 
   221 	/** 
   222 	 * Returns true if the plugin was running, but is now invalid.
   223 	 */
   224 	bool WasRunning();
   225 
   226 	Handle_t GetMyHandle();
   227 
   228 	bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
   229 	void AddConfig(bool autoCreate, const char *cfg, const char *folder);
   230 	unsigned int GetConfigCount();
   231 	AutoConfig *GetConfig(unsigned int i);
   232 	inline void AddLibrary(const char *name)
   233 	{
   234 		m_Libraries.push_back(name);
   235 	}
   236 	void LibraryActions(bool dropping);
   237 protected:
   238 	void UpdateInfo();
   239 	void SetTimeStamp(time_t t);
   240 	void DependencyDropped(CPlugin *pOwner);
   241 private:
   242 	PluginType m_type;
   243 	char m_filename[PLATFORM_MAX_PATH];
   244 	PluginStatus m_status;
   245 	unsigned int m_serial;
   246 	sm_plugininfo_t m_info;
   247 	char m_errormsg[256];
   248 	time_t m_LastAccess;
   249 	IdentityToken_t *m_ident;
   250 	Handle_t m_handle;
   251 	bool m_WasRunning;
   252 	IPhraseCollection *m_pPhrases;
   253 	List<String> m_RequiredLibs;
   254 	List<String> m_Libraries;
   255 	Trie *m_pProps;
   256 	bool m_FakeNativesMissing;
   257 	bool m_LibraryMissing;
   258 	CVector<AutoConfig *> m_configs;
   259 	bool m_bGotAllLoaded;
   260 	int m_FileVersion;
   261 	char m_DateTime[256];
   262 	IPluginRuntime *m_pRuntime;
   263 	IPluginContext *m_pContext;
   264 };
   265 
   266 class CPluginManager : 
   267 	public IPluginManager,
   268 	public SMGlobalClass,
   269 	public IHandleTypeDispatch,
   270 	public IRootConsoleCommand
   271 {
   272 	friend class CPlugin;
   273 public:
   274 	CPluginManager();
   275 	~CPluginManager();
   276 public:
   277 	/* Implements iterator class */
   278 	class CPluginIterator : public IPluginIterator
   279 	{
   280 	public:
   281 		CPluginIterator(List<CPlugin *> *mylist);
   282 		virtual ~CPluginIterator();
   283 		virtual bool MorePlugins();
   284 		virtual IPlugin *GetPlugin();
   285 		virtual void NextPlugin();
   286 		void Release();
   287 	public:
   288 		void Reset();
   289 	private:
   290 		List<CPlugin *> *mylist;
   291 		List<CPlugin *>::iterator current;
   292 	};
   293 	friend class CPluginManager::CPluginIterator;
   294 public: //IPluginManager
   295 	IPlugin *LoadPlugin(const char *path, 
   296 								bool debug,
   297 								PluginType type,
   298 								char error[],
   299 								size_t maxlength,
   300 								bool *wasloaded);
   301 	bool UnloadPlugin(IPlugin *plugin);
   302 	IPlugin *FindPluginByContext(const sp_context_t *ctx);
   303 	unsigned int GetPluginCount();
   304 	IPluginIterator *GetPluginIterator();
   305 	void AddPluginsListener(IPluginsListener *listener);
   306 	void RemovePluginsListener(IPluginsListener *listener);
   307 public: //SMGlobalClass
   308 	void OnSourceModAllInitialized();
   309 	void OnSourceModShutdown();
   310 public: //IHandleTypeDispatch
   311 	void OnHandleDestroy(HandleType_t type, void *object);
   312 	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
   313 public: //IRootConsoleCommand
   314 	void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
   315 public:
   316 	/**
   317 	 * Loads all plugins not yet loaded
   318 	 */
   319 	void LoadAll_FirstPass(const char *config, const char *basedir);
   320 
   321 	/**
   322 	 * Runs the second loading pass for all plugins
   323 	 */
   324 	void LoadAll_SecondPass();
   325 
   326 	/**
   327 	 * Tests a plugin file mask against a local folder.
   328 	 * The alias is searched backwards from localdir - i.e., given this input:
   329 	 *   csdm/ban        csdm/ban
   330 	 *   ban             csdm/ban
   331 	 *   csdm/ban        optional/csdm/ban
   332 	 * All of these will return true for an alias match.  
   333 	 * Wildcards are allowed in the filename.
   334 	 */
   335 	bool TestAliasMatch(const char *alias, const char *localdir);
   336 
   337 	/** 
   338 	 * Returns whether anything loaded will be a late load.
   339 	 */
   340 	bool IsLateLoadTime() const;
   341 
   342 	/**
   343 	 * Converts a Handle to an IPlugin if possible.
   344 	 */
   345 	IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
   346 
   347 	/**
   348 	 * Finds a plugin based on its index. (starts on index 1)
   349 	 */
   350 	CPlugin *GetPluginByOrder(int num);
   351 
   352 	int GetOrderOfPlugin(IPlugin *pl);
   353 
   354 	/** 
   355 	 * Internal version of FindPluginByContext()
   356 	 */
   357 	CPlugin *GetPluginByCtx(const sp_context_t *ctx);
   358 
   359 	/**
   360 	 * Gets status text for a status code 
   361 	 */
   362 	const char *GetStatusText(PluginStatus status);
   363 
   364 	/**
   365 	* Reload or update plugins on level shutdown.
   366 	*/
   367 	void ReloadOrUnloadPlugins();
   368 
   369 	/**
   370 	 * Add public functions from all running or paused
   371 	 * plugins to the specified forward if the names match.
   372 	 */
   373 	void AddFunctionsToForward(const char *name, IChangeableForward *pForward);
   374 
   375 	/**
   376 	 * Iterates through plugins to call OnAllPluginsLoaded.
   377 	 */
   378 	void AllPluginsLoaded();
   379 
   380 	CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken);
   381 
   382 	void Shutdown();
   383 
   384 	void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop);
   385 
   386 	bool LibraryExists(const char *lib);
   387 
   388 	bool ReloadPlugin(CPlugin *pl);
   389 
   390 	void UnloadAll();
   391 
   392 	CPlugin *FindPluginByConsoleArg(const char *arg);
   393 private:
   394 	LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength);
   395 
   396 	void LoadAutoPlugin(const char *plugin);
   397 
   398 	/**
   399 	 * Recursively loads all plugins in the given directory.
   400 	 */
   401 	void LoadPluginsFromDir(const char *basedir, const char *localdir);
   402 
   403 	/**
   404 	 * Adds a plugin object.  This is wrapped by LoadPlugin functions.
   405 	 */
   406 	void AddPlugin(CPlugin *pPlugin);
   407 
   408 	/**
   409 	 * Runs the second loading pass on a plugin.
   410 	 */
   411 	bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
   412 
   413 	/**
   414 	 * Runs an extension pass on a plugin.
   415 	 */
   416 	bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
   417 
   418 	/**
   419 	* Manages required natives.
   420 	*/
   421 	bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength);
   422 
   423 	void _SetPauseState(CPlugin *pPlugin, bool pause);
   424 protected:
   425 	/**
   426 	 * Caching internal objects
   427 	 */
   428 	void ReleaseIterator(CPluginIterator *iter);
   429 public:
   430 	inline IdentityToken_t *GetIdentity()
   431 	{
   432 		return m_MyIdent;
   433 	}
   434 private:
   435 	void TryRefreshDependencies(CPlugin *pOther);
   436 private:
   437 	List<IPluginsListener *> m_listeners;
   438 	List<CPlugin *> m_plugins;
   439 	CStack<CPluginManager::CPluginIterator *> m_iters;
   440 	CPluginInfoDatabase m_PluginInfo;
   441 	Trie *m_LoadLookup;
   442 	bool m_AllPluginsLoaded;
   443 	IdentityToken_t *m_MyIdent;
   444 
   445 	/* Dynamic native stuff */
   446 	List<FakeNative *> m_Natives;
   447 
   448 	bool m_LoadingLocked;
   449 };
   450 
   451 extern CPluginManager g_PluginSys;
   452 
   453 #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_