|
|
/*===================================================================
Microsoft Denali
Microsoft Confidential. Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Script Manager
File: ScrptMgr.h
Owner: AndrewS
This file contains the declarations for the Script Manager, ie. siting an ActiveX Scripting engine (in our case VBScript) for Denali. ===================================================================*/
#ifndef __ScrptMgr_h
#define __ScrptMgr_h
#include <dispex.h>
#include "activscp.h"
#include "activdbg.h"
#include "hostinfo.h"
#include "util.h"
#include "HitObj.h"
#include "hashing.h"
#include "memcls.h"
#include "scrpteng.h"
typedef SCRIPTSTATE ENGINESTATE; // Uninited, Loaded, etc
typedef CLSID PROGLANG_ID; const CBPROGLANG_ID = sizeof(PROGLANG_ID);
class CActiveScriptSite; class CActiveScriptEngine; class CASEElem; class CScriptingNamespace; class CAppln;
// SMHash depends on stuff in this include file which must be defined first.
#include "SMHash.h"
/*
* * * C S c r i p t M a n a g e r * * * Manages script engines, potentially caching them for future use, * hands script engines to callers for use. * */ class CScriptManager { private: // private data members
BOOLB m_fInited; // Are we initialized?
/*
* Script Engines that are not in use can be reused and * go on the Free Script Queue. It is a queue so we can * discard the oldest if we need to. * * Engines that are in use cant be reused. When an engine * is handed out to be used, it is removed from the FSQ. When a thread * is done using an engine, it calls ReturnEngineToCache to put it back on * the FSQ. If the Queue is at max length, the oldest engine on the queue is * freed at that point. The one returned is put on the front of the queue. * * We also maintain a Running Script List. This is needed so that if we * are told to flush a given script from our cache, we can "zombify" any * running scripts that have that script in them (so they will be discarded * when they are done running.) * * Additional note: Though we cant have multiple users of the *same* runing engine * we can "clone" a running engine. If we get two simulanteous requests for Foo.ASP * we expect that it will be faster to clone the second one from the first one than * to create a second engine for the second request. Thus, the RSL will be searched * for a given engine to clone if no suitable engine is found on the FSQ. * * DEBUGGING NOTE: * Once the debugger asks a script engine for a code context cookie, we cannot * ever let go of the script engine until the debugger detaches. Therefore, we * don't cache scripts in the FSQ if debugging is active. Instead, the scripts * are placed in the template when execution is finished, there to be doled back * out when that engine is needed by the debugging engine. * * CONSIDER: * We could be smarter about this, and cache scripts UNTIL the debugger either * a. Asks for a code context from a document context, or * b. Calls GetDocumentContextFromPosition, in which case, the debugger * got a code context "behind our virtual backs". * * If we don't do this, we could, at the very least, only implement this * debugging behavior when a debugger attaches to our application. * (i.e. stop caching on attach, then on detach, resume caching, and also * free scripts that the template objects are holding onto.) */ CSMHash m_htFSQ; // Free Script Queue
CRITICAL_SECTION m_csFSQ; // Serialize access to FSQ
CSMHash m_htRSL; // Running Script List
CRITICAL_SECTION m_csRSL; // Serialize access to RSL
CHashTable m_hTPLL; // Hash table of language engine classid's
CRITICAL_SECTION m_cSPLL; // Serialize access to PLL
DWORD m_idScriptKiller; // Script killer sched workitem id
DWORD m_msecScriptKillerTimeout;// Current script killer timeout
// private methods
HRESULT UnInitASEElems(); HRESULT UnInitPLL(); HRESULT AddProgLangToPLL(CHAR *szProgLangName, PROGLANG_ID progLangId);
// script killer
static VOID WINAPI ScriptKillerSchedulerCallback(VOID *pv);
public: // public methods
CScriptManager(); ~CScriptManager();
HRESULT Init(); HRESULT UnInit();
// Resolves a language name into a prog lang id, adding to engine list (m_hTPLL) if not already there
HRESULT ProgLangIdOfLangName(LPCSTR szProgLang, PROGLANG_ID *pProgLangId);
// Return an engine, preferably filled with the script for the given template/language
HRESULT GetEngine( LCID lcid, // The system language to use
PROGLANG_ID& progLangId, // prog lang id of the script
LPCTSTR szTemplateName, // Template we want an engine for
CHitObj *pHitObj, // Hit obj to use in this engine
CScriptEngine **ppSE, // Returned script engine
ENGINESTATE *pdwState, // Current state of the engine
CTemplate *pTemplate, // template (debug document)
DWORD dwSourceContext); // script engine index
HRESULT ReturnEngineToCache(CScriptEngine **, CAppln *, IASPObjectContextCustom *);
// Throw out any cached engines containing a given template
// (presumably the script changed on disk so the cache is obsolete.)
HRESULT FlushCache(LPCTSTR szTemplateName); // Template to throw out of the cache
HRESULT FlushAll(); // Clear the entire FSQ
HRESULT KillOldEngines(BOOLB fKillNow = FALSE); // Kill expired scripting engines
// Bug 1140: Called prior to shutting down script manager to make sure RSL is empty
HRESULT EmptyRunningScriptList();
// Adjust (shorten) script killer timeout
HRESULT AdjustScriptKillerTimeout(DWORD msecNewTimeout);
// Find running script that corresponds to a template (in one of its script blocks)
IActiveScriptDebug *GetDebugScript(CTemplate *pTemplate, DWORD dwSourceContext);
private: HRESULT FindEngineInList(LPCTSTR szTemplateName, PROGLANG_ID progLangId, DWORD dwInstanceID, BOOL fFSQ, CASEElem **ppASEElem); HRESULT FindASEElemInList(CActiveScriptEngine *pASE, BOOL fFSQ, CASEElem **ppASEElem);
// For threading a FIFO queue through the hash table
HRESULT AddToFSQ(CASEElem *pASEElem); HRESULT CheckFSQLRU();
#ifdef DBG
virtual void AssertValid() const; #else
virtual void AssertValid() const {} #endif
};
extern CScriptManager g_ScriptManager;
/*
* * * C A c t i v e S c r i p t E n g i n e * * Object defining methods required to host an ActiveXScripting engine & * service requests to that engine. * */ class CActiveScriptEngine : public CScriptEngine, public IActiveScriptSite, public IActiveScriptSiteDebug, public IHostInfoProvider { private: // private data members
UINT m_cRef; // Reference count
IDispatch *m_pDisp; // IDispatch interface on script
CHitObj *m_pHitObj; // The hit object contains a list of objects for this run
LPTSTR m_szTemplateName; // The name of the template this engine has loaded
DWORD m_dwInstanceID; // server instance ID of template this engine has loaded
TCHAR m_szTemplateNameBuf[64]; // Buffer for short templates to fit to avoid allocs
PROGLANG_ID m_proglang_id; // What programming language?
LCID m_lcid; // what system language
IActiveScript *m_pAS; // The script object sited here
IActiveScriptParse *m_pASP; // The script object parser
IHostInfoUpdate *m_pHIUpdate;// Interface for advising the script that we have new host info
time_t m_timeStarted; // Time when the script engine was handed out last.
CTemplate *m_pTemplate; // template that acts as debugging document
DWORD m_dwSourceContext; // "Cookie" value which is really script engine
DWORD m_fInited : 1; // Have we been inited?
DWORD m_fZombie : 1; // Do we need to be deleted on last use
DWORD m_fScriptLoaded : 1; // Have we been called with script to load yet? (Used for clone)
DWORD m_fObjectsLoaded : 1; // Have we been called with a set of objects yet? (Used for clone)
DWORD m_fBeingDebugged : 1; // Is this script being debugged now?
DWORD m_fTemplateNameAllocated : 1; // Is name allocated? (need to free?)
/*
* NOTE: ActiveXScripting: * ActiveXScripting had an undone such that the excepinfo filled in in InteruptScript * was not passed to OnScriptError. We would have liked to use that mechanism to cause * correct error loging (or suppression) if we interrupt a script. However, * since ActiveXScripting wasnt passing the info, we didnt know. We wrote this code to * handle it ourselves. They have now fixed it, but the mechanism we implemented works very * well, so we are not going to change it. */ DWORD m_fScriptAborted : 1; // The script did a Response.End
DWORD m_fScriptTimedOut : 1; // We killed the script on timeout
DWORD m_fScriptHadError : 1; // The script had an error while running. Transacted script should autoabort
/*
* BUG 1225: If there is a GPF running a script, we shouldnt reuse the engine */ DWORD m_fCorrupted : 1; // Might the engine be "unsafe" for reuse?
// handle GetItemInfo() failure
void HandleItemNotFound(LPCOLESTR pcszName);
HRESULT StoreTemplateName(LPCTSTR szTemplateName);
public: CActiveScriptEngine(); ~CActiveScriptEngine();
HRESULT Init( PROGLANG_ID proglang_id, LPCTSTR szTemplateName, LCID lcid, CHitObj *pHitObj, CTemplate *pTemplate, DWORD dwSourceContext);
HRESULT MakeClone( PROGLANG_ID proglang_id, LPCTSTR szTemplateName, LCID lcid, CHitObj *pHitObj, CTemplate *pTemplate, DWORD dwSourceContext, DWORD dwInstanceID, IActiveScript *pAS); // The cloned script engine
HRESULT ReuseEngine( CHitObj *pHitObj, CTemplate *pTemplate, DWORD dwSourceContext, DWORD dwInstanceID );
time_t TimeStarted(); VOID SetTimeStarted(time_t timeStarted);
BOOL FBeingDebugged(); // Is the script being debugged?
VOID IsBeingDebugged(); // Notify script that it is being debugged
HRESULT ResetToUninitialized(IASPObjectContextCustom *); HRESULT GetASP(); HRESULT GetIDisp(); HRESULT GetIHostInfoUpdate(); IActiveScript *GetActiveScript(); LPTSTR SzTemplateName(); BOOL FIsZombie(); BOOL FIsCorrupted(); PROGLANG_ID ProgLang_Id(); DWORD DWInstanceID(); BOOL FFullyLoaded(); long GetTimeout(); BOOL FScriptTimedOut(); BOOL FScriptHadError(); void GetDebugDocument(CTemplate **ppTemplate, DWORD *pdwSourceContext);
/*
* C S c r i p t E n g i n e M e t h o d s */ HRESULT AddScriptlet(LPCOLESTR wstrScript);
HRESULT AddObjects(BOOL fPersistNames = TRUE);
HRESULT AddAdditionalObject(LPWSTR strObjName, BOOL fPersistNames = TRUE);
HRESULT AddScriptingNamespace(); HRESULT Call(LPCOLESTR strEntryPoint);
HRESULT CheckEntryPoint(LPCOLESTR strEntryPoint);
HRESULT MakeEngineRunnable() { return(Call(NULL)); };
HRESULT ResetScript() { return m_pAS? m_pAS->SetScriptState(SCRIPTSTATE_UNINITIALIZED) : E_FAIL; }
VOID Zombify();
HRESULT InterruptScript(BOOL fAbnormal = TRUE);
HRESULT UpdateLocaleInfo(hostinfo hi);
HRESULT TryCall(LPCOLESTR strEntryPoint);
ULONG FinalRelease();
/*
* I U n k n o w n M e t h o d s */ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); STDMETHOD_(ULONG, AddRef)(VOID); STDMETHOD_(ULONG, Release)(VOID);
/*
* C A c t i v e S c r i p t S i t e M e t h o d s */ STDMETHOD(GetLCID)(LCID *plcid);
STDMETHOD(GetItemInfo)(LPCOLESTR pcszName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti);
STDMETHOD(GetDocVersionString)(BSTR *pszVersion);
STDMETHOD(RequestItems)(BOOL fPersistNames = TRUE);
STDMETHOD(RequestTypeLibs)(VOID);
STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo); STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState);
STDMETHOD(OnScriptError)(IActiveScriptError __RPC_FAR *pscripterror);
STDMETHOD(OnEnterScript)(VOID);
STDMETHOD(OnLeaveScript)(VOID);
/*
* C A c t i v e S c r i p t S i t e D e b u g M e t h o d s */ STDMETHOD(GetDocumentContextFromPosition)( /* [in] */ DWORD_PTR dwSourceContext, /* [in] */ ULONG uCharacterOffset, /* [in] */ ULONG uNumChars, /* [out] */ IDebugDocumentContext **ppsc);
STDMETHOD(GetApplication)(/* [out] */ IDebugApplication **ppda);
STDMETHOD(GetRootApplicationNode)(/* [out] */ IDebugApplicationNode **);
STDMETHOD(OnScriptErrorDebug)( /* [in] */ IActiveScriptErrorDebug *pErrorDebug, /* [out] */ BOOL *pfEnterDebugger, /* [out] */ BOOL *pfCallOnScriptErrorWhenContinuing); /*
* IHostInfoProvider methods */
STDMETHOD(GetHostInfo)(hostinfo hostinfoRequest, void **ppvInfo);
public: #ifdef DBG
virtual void AssertValid() const; #else
virtual void AssertValid() const {} #endif
// Cache on per-class basis
ACACHE_INCLASS_DEFINITIONS() };
inline VOID CActiveScriptEngine::Zombify() { m_fZombie = TRUE; } inline BOOL CActiveScriptEngine::FFullyLoaded() { return(m_fScriptLoaded && m_fObjectsLoaded); } inline BOOL CActiveScriptEngine::FIsZombie() { return(m_fZombie); } inline BOOL CActiveScriptEngine::FIsCorrupted() { return(m_fCorrupted); } inline time_t CActiveScriptEngine::TimeStarted() { return(m_timeStarted); } inline VOID CActiveScriptEngine::SetTimeStarted(time_t timeStarted) { m_timeStarted = timeStarted; } inline IActiveScript *CActiveScriptEngine::GetActiveScript() { return(m_pAS); } inline LPTSTR CActiveScriptEngine::SzTemplateName() { return(m_szTemplateName); } inline PROGLANG_ID CActiveScriptEngine::ProgLang_Id() { return(m_proglang_id); } inline DWORD CActiveScriptEngine::DWInstanceID() { return(m_dwInstanceID); } inline BOOL CActiveScriptEngine::FBeingDebugged() { return(m_fBeingDebugged); } // Is the script being debugged?
inline VOID CActiveScriptEngine::IsBeingDebugged() { m_fBeingDebugged = TRUE; } inline BOOL CActiveScriptEngine::FScriptTimedOut() { return m_fScriptTimedOut; } inline BOOL CActiveScriptEngine::FScriptHadError() { return m_fScriptHadError; } inline long CActiveScriptEngine::GetTimeout() { return m_fBeingDebugged? LONG_MAX : m_pHitObj->GetScriptTimeout(); } inline void CActiveScriptEngine::GetDebugDocument(CTemplate **ppTemplate, DWORD *pdwSourceContext) { if (ppTemplate) *ppTemplate = m_pTemplate; if (pdwSourceContext) *pdwSourceContext = m_dwSourceContext; }
/*
* * * C A S E E l e m * * Script element. For keeping lists and queues of script engines * */ class CASEElem : public CLruLinkElem { private: CActiveScriptEngine *m_pASE;
public: CASEElem() : m_pASE(NULL) {} ~CASEElem();
HRESULT Init(CActiveScriptEngine *pASE); CActiveScriptEngine *PASE(); // Cache on per-class basis
ACACHE_INCLASS_DEFINITIONS() };
inline CActiveScriptEngine *CASEElem::PASE() { return(m_pASE); }
/*
* * * C P L L E l e m * * Hash table list element for a Programming Language List. * */ class CPLLElem : public CLinkElem { private: PROGLANG_ID m_ProgLangId; // clsid for the language
public: CPLLElem() : m_ProgLangId(CLSID_NULL) {}; ~CPLLElem();
HRESULT Init(CHAR *szProgLangName, PROGLANG_ID progLangId); PROGLANG_ID ProgLangId(); };
inline PROGLANG_ID CPLLElem::ProgLangId() { return(m_ProgLangId); }
/*
* * * C S c r i p t i n g N a m e s p a c e * * We need to keep track of all of the names which different engines (and typeinfos) * contribute to the namespace. All of these names go into this object * which we give to each engine with the SCRIPTITEM_GLOBALMEMBERS flag. When * ActiveXScripting calls us back on GetIdsOfNames, we will call the engines * we have cached until we find the name. When AXS calls us with Invoke, * we will map the id to the appropriate engine and pass on the invoke * * Data structure note: * We implement the ScriptingNamespace with a linked list of arrays. * This gives reasonable access time and should minimize heap * fragmentation. In debug mode, the number of buckets is small to * excersize the resize code. * * NOTE: "ENGDISPMAX" should be a power of two - this will allow the optimizer * to optimize the integer divide and modulus operations with bit-ands and * shifts. However, the code does not assume that "ENGDISPMAX" is a power * of two. */
#ifdef DBG
#define ENGDISPMAX 2
#else
#define ENGDISPMAX 32
#endif
typedef struct _engdisp { DISPID dispid; // the dispid that the engine really uses
IDispatch *pDisp; // the engine to call for this dispid
IDispatchEx *pDispEx; // the engine to call for this dispid
} ENGDISP;
typedef struct _engdispbucket : CDblLink { ENGDISP rgEngDisp[ENGDISPMAX+1]; } ENGDISPBUCKET;
class CEngineDispElem : public CDblLink { public: IDispatch *m_pDisp; IDispatchEx *m_pDispEx; // Cache on per-class basis
ACACHE_INCLASS_DEFINITIONS() };
class CScriptingNamespace : public IDispatchEx { private: ULONG m_cRef; // Reference count
BOOLB m_fInited; CDblLink m_listSE; // List of scripting engines (list of CSEElem's)
UINT m_cEngDispMac; CDblLink m_listEngDisp;
HRESULT CacheDispID(CEngineDispElem *pEngine, DISPID dispidEngine, DISPID *pdispidCached); HRESULT FetchDispID(DISPID dispid, ENGDISP **ppEngDisp);
public: // public methods
CScriptingNamespace(); ~CScriptingNamespace();
HRESULT Init(); HRESULT UnInit(); HRESULT ReInit(); HRESULT AddEngineToNamespace(CActiveScriptEngine *pASE);
// IUnknown
STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *); STDMETHODIMP GetTypeInfo(UINT, LCID, ITypeInfo **); STDMETHODIMP GetIDsOfNames(REFIID, OLECHAR **, UINT, LCID, DISPID *); STDMETHODIMP Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *); // IDispatchEx
STDMETHODIMP DeleteMemberByDispID(DISPID id); STDMETHODIMP DeleteMemberByName(BSTR bstrName, DWORD grfdex); STDMETHODIMP GetMemberName(DISPID id, BSTR *pbstrName); STDMETHODIMP GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex); STDMETHODIMP GetNameSpaceParent(IUnknown **ppunk); STDMETHODIMP GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid); STDMETHODIMP GetDispID(BSTR bstrName, DWORD grfdex, DISPID *pid); STDMETHODIMP InvokeEx(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pVarRes, EXCEPINFO *pei, IServiceProvider *pspCaller);
public: #ifdef DBG
VOID AssertValid() const; #else
VOID AssertValid() const {} #endif
// Cache on per-class basis
ACACHE_INCLASS_DEFINITIONS() };
/*
* * * U t i l i t i e s * * General utility functions * */ HRESULT WrapTypeLibs(ITypeLib **prgpTypeLib, UINT cTypeLibs, IDispatch **ppDisp);
#endif // __ScrptMgr_h
|