Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1461 lines
44 KiB

/*===================================================================
Microsoft Denali
Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Executor
Owner: DGottner
File: executor.cpp
This file contains the executor, whose job is to co-ordinate the
execution of Denali scripts.
===================================================================*/
#include "denpre.h"
#pragma hdrstop
#include "exec.h"
#include "response.h"
#include "request.h"
#include "perfdata.h"
#include "memchk.h"
// Local declarations
HRESULT ExecuteGlobal(CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo);
HRESULT ExecuteRequest(CTemplate *pTemplate, CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo);
HRESULT ReInitIntrinsics(CHitObj *pHitObj, const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo, BOOL fPostGlobal);
HRESULT AllocAndLoadEngines(CHitObj *pHitObj, CTemplate *pTemplate, ActiveEngineInfo *pEngineInfo,
CScriptingNamespace *pScriptingNamespace, BOOL fGlobalAsa);
VOID DeAllocAndFreeEngines(ActiveEngineInfo *pEngineInfo, CAppln *pAppln, IASPObjectContextCustom *);
CScriptEngine *GetScriptEngine(int iScriptEngine, void *pvData);
HRESULT CallScriptFunctionOfEngine(ActiveEngineInfo &engineInfo, short iScriptBlock, wchar_t *strFunction,
IASPObjectContextCustom *pTxnScriptContextCustom, BOOLB *pfAborted);
HRESULT CallScriptFunction(ActiveEngineInfo &engineInfo, wchar_t *strFunction,
IASPObjectContextCustom *pTxnScriptContextCustom, BOOLB *pfAborted);
HRESULT TestScriptFunction(ActiveEngineInfo &engineInfo, wchar_t *strFunction);
/*===================================================================
Execute
Execute a request:
First determine if Global needs to be called
then invoke actual requested template
Parameters:
pTemplate - pointer to loaded template (could be NULL)
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
fChild - flag: TRUE when child request (Server.Execute())
Returns:
S_OK on success
===================================================================*/
HRESULT Execute
(
CTemplate *pTemplate,
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
BOOL fChild
)
{
HRESULT hr = S_OK;
ActiveEngineInfo engineInfo;
BOOL fRanGlobal = FALSE;
// The hit obj must be valid
Assert(pHitObj != NULL);
pHitObj->AssertValid();
// Check for valid Session codepage. We do it here, rather than in CSession::Init in
// order to avoid generic "New Session Failed" message.
if (pHitObj->GetCodePage() != CP_ACP && !IsValidCodePage(pHitObj->GetCodePage()))
{
HandleErrorMissingFilename(IDE_BAD_CODEPAGE_IN_MB, pHitObj);
return E_FAIL;
}
// Give the engine list to the hitobject
pHitObj->SetActiveEngineInfo(&engineInfo);
/*
* If there is a Global.ASA, call it
*/
if (pHitObj->GlobalAspPath() && !fChild)
{
// Clear out the engine info
engineInfo.cEngines = 0;
engineInfo.cActiveEngines = 0;
engineInfo.rgActiveEngines = NULL;
// Init the intrinsics
hr = ReInitIntrinsics(pHitObj, intrinsics, &engineInfo, /* fPostGlobal*/ FALSE);
if (FAILED(hr))
return(hr);
hr = ExecuteGlobal(pHitObj, intrinsics, &engineInfo);
if (intrinsics.PResponse() && intrinsics.PResponse()->FResponseAborted())
{
hr = S_OK;
goto LExit;
}
if (E_SOURCE_FILE_IS_EMPTY == hr)
// bug 977: silently ignore empty global.asa file
hr = S_OK;
else if (FAILED(hr))
{
// Bug 481: If global.asa fails due to Response.End (or Response.Redirect),
// then halt execution of the calling script. If the
// script fails due to Response.End, then return OK status
//
if (hr == DISP_E_EXCEPTION)
hr = S_OK;
// In any case, blow out of here
goto LExit;
}
// Running Global.asa added the scripting namespace to the hitobj. This will cause us problems
// later, remove it.
pHitObj->RemoveScriptingNamespace();
fRanGlobal = TRUE;
}
/*
* If this is not a browser request, then we are done
* For non-browser requests, we do want to run Global.asa (if any), but there is no real template to run.
*/
if (!pHitObj->FIsBrowserRequest())
{
hr = S_OK;
goto LExit;
}
// Clear out (or re-clear out) the engine info
engineInfo.cEngines = 0;
engineInfo.cActiveEngines = 0;
engineInfo.rgActiveEngines = NULL;
// Init or Re-Init the intrinsics
ReInitIntrinsics(pHitObj, intrinsics, &engineInfo, fRanGlobal || fChild);
if (!fChild)
{
// For non-child requests hand new Template to Response object
// (for child requests already done)
intrinsics.PResponse()->ReInitTemplate(pTemplate, pHitObj->PSzNewSessionCookie());
}
else
{
// For child requests hand new engine info to the response object
intrinsics.PResponse()->SwapScriptEngineInfo(&engineInfo);
}
// Run the main template
if (pTemplate->FScriptless() && !pHitObj->PAppln()->FDebuggable())
{
// special case scriptless pages
hr = intrinsics.PResponse()->WriteBlock(0);
}
else
{
hr = ExecuteRequest(pTemplate, pHitObj, intrinsics, &engineInfo);
}
LExit:
intrinsics.PResponse()->SwapScriptEngineInfo(NULL);
pHitObj->SetActiveEngineInfo(NULL);
return hr;
}
/*===================================================================
ExecRequest
Execute a request for an actual template (not Global.asa)
execute a request by
- getting the script name
- loading the script into memory
- interpreting the opcodes
Parameters:
pTemplate - pointer to loaded template
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
pEngineInfo - pointers to engine info
Returns:
S_OK on success
===================================================================*/
HRESULT ExecuteRequest
(
CTemplate *pTemplate,
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo
)
{
HRESULT hr = S_OK;
IASPObjectContextCustom *pTxnScriptContextCustom = NULL;
IASPObjectContext *pTxnScriptContext = NULL;
BOOLB fAborted = FALSE;
BOOLB fDebuggerNotifiedOnStart = FALSE;
#ifndef PERF_DISABLE
BOOLB fPerfTransPending = FALSE;
#endif
// The template must be valid
Assert(pTemplate);
// The hit obj must be valid
Assert(pHitObj != NULL);
pHitObj->AssertValid();
// This function should never be called on a non-browser request
Assert(pHitObj->FIsBrowserRequest());
// Remember template's type library wrapper with the HitObj
if (pTemplate->PTypeLibWrapper())
pHitObj->SetTypeLibWrapper(pTemplate->PTypeLibWrapper());
/*
* Bug 86404: If this script is transacted, then create a new "real" ASP ObjectContext object
* and add it to the objects list. If the page is not transacted, then
* CHitObj::UseObjectContext will use pre-instantiated "zombie" ObjectContext object.
*/
if (pTemplate->FTransacted())
{
IObjectContext *pContext;
if (FAILED(hr = GetObjectContext(&pContext)))
goto LExit;
/*
* Incredibly obscure bug in OLE32. If OLE needs to spin up an MTA thread in order to
* create the class factory for the ASPObjectContext object, then the thread needs to be
* created by the unimpersonated user.
* NOTE: This can be removed after the new OLE32 ships in NT4 SP4
*/
RevertToSelf();
hr = pContext->CreateInstance
(
CLSIDObjectContextFromTransType(pTemplate->GetTransType()),
IID_IASPObjectContextCustom,
reinterpret_cast<void **>(&pTxnScriptContextCustom)
);
// Restore Impersonation
HANDLE hThread = GetCurrentThread();
SetThreadToken(&hThread, pHitObj->HImpersonate());
pContext->Release();
if (FAILED(hr))
goto LExit;
hr = pTxnScriptContextCustom->QueryInterface(IID_IASPObjectContext,
reinterpret_cast<void **>(&pTxnScriptContext));
if (FAILED(hr))
goto LExit;
pHitObj->AddObjectContext(pTxnScriptContext);
pTxnScriptContext->Release();
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSTOTAL();
g_PerfData.Incr_TRANSPENDING();
fPerfTransPending = TRUE;
#endif
}
// load script engines
hr = AllocAndLoadEngines(pHitObj, pTemplate, pEngineInfo, intrinsics.PScriptingNamespace(), /* fGlobalAsa */FALSE);
if (FAILED(hr))
{
pHitObj->SetCompilationFailed();
goto LExit;
}
// If debugging, notify debugger ONPAGESTART
// BUG 138773: Notify debugger AFTER scripts load
// (script must be in running state when AttachTo() is called because debugger may want a code context)
//
if (pHitObj->PAppln()->FDebuggable())
{
pTemplate->AttachTo(pHitObj->PAppln());
if (SUCCEEDED(pTemplate->NotifyDebuggerOnPageEvent(TRUE)))
fDebuggerNotifiedOnStart = TRUE;
}
// bug 1009: if no script engines, do not attempt to do anything
if(0 == pTemplate->CountScriptEngines())
goto LExit;
// run the script by calling primary script engine's global code
hr = CallScriptFunctionOfEngine(
*pEngineInfo, // engine-info
0, // primary script engine
NULL, // call the engine's global code
pTxnScriptContextCustom,
&fAborted
);
if (FAILED(hr) && hr != CONTEXT_E_ABORTED)
{
/*
* The cryptically named CONTEXT_E_OLDREF error in this case means that
* we are trying to run a transacted web page, but DTC isnt running.
* CONTEXT_E_TMNOTAVAILABLE means the same thing. God knows why
*/
if (hr == CONTEXT_E_OLDREF || hr == CONTEXT_E_TMNOTAVAILABLE)
{
HandleErrorMissingFilename(IDE_EXECUTOR_DTC_NOT_RUNNING, pHitObj);
}
// Regardless of the error, exit
goto LExit;
}
/*
* If this is a transacted web page, then run either the OnTransactionCommit
* or OnTransactionAbort method in the script, if any.
*
* If the script writer did an explicit SetAbort, or a component run by the script
* did a SetAbort, then we run OnTransactionAbort, otherwise run OnTransactionCommit
*/
if (pTemplate->FTransacted())
{
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSPERSEC();
#endif
if (hr == CONTEXT_E_ABORTED || fAborted)
{
// Reset the error... dont communicate this back to the caller as an actual error
hr = S_OK;
hr = CallScriptFunction(*pEngineInfo, L"OnTransactionAbort", NULL, &fAborted);
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSABORTED();
#endif
}
else
{
hr = CallScriptFunction(*pEngineInfo, L"OnTransactionCommit", NULL, &fAborted);
#ifndef PERF_DISABLE
g_PerfData.Incr_TRANSCOMMIT();
#endif
}
// Ignore UNKNOWNNAME -- this means the author didnt write the method, which is fine
if (hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND)
hr = S_OK;
if (FAILED(hr))
goto LExit;
}
LExit:
#ifndef PERF_DISABLE
if (fPerfTransPending)
g_PerfData.Decr_TRANSPENDING();
#endif
// Uninit the scripting namespace
(VOID)intrinsics.PScriptingNamespace()->UnInit();
// Return the engine(s) to cache
DeAllocAndFreeEngines(pEngineInfo, pHitObj->PAppln(), pTxnScriptContextCustom);
// If debugging, notify debugger ONPAGEDONE
if (fDebuggerNotifiedOnStart)
{
Assert(pHitObj->PAppln()->FDebuggable());
pTemplate->NotifyDebuggerOnPageEvent(FALSE);
}
// Remove the ASP ObjectContext object from the namespace, and dealloc it if we allocated one
pHitObj->RemoveObjectContext();
if (pTxnScriptContextCustom)
{
pTxnScriptContextCustom->Release();
}
return hr;
}
/*===================================================================
ExecuteGlobal
UNDONE: handle script engine the same manner as mainline script engines
with respect to debugging.
Execute code in Global.ASA as part of application or session start or end.
Parameters:
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
pEngineInfo - pointers to engine info
pfDeleteSession - true if global.asa failed, and therefore the caller should
Returns:
S_OK on success
===================================================================*/
HRESULT ExecuteGlobal
(
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo
)
{
HRESULT hr = S_OK;
CTemplate *pTemplate = NULL;
IASPObjectContextCustom *pTxnScriptContextCustom = NULL;
IASPObjectContext *pTxnScriptContext = NULL;
WORD iEng;
BOOLB fAborted = FALSE;
BOOLB fDebuggerNotifiedOnStart = FALSE;
BOOL fUnHideRequestAndResponse = FALSE;
BOOL fOnStartAppln = FALSE;
BOOL fOnEndAppln = FALSE;
BOOL fOnEndSession = FALSE;
BOOL fGlobalAsaInCache;
BOOL fApplnStarted = FALSE;
// The hit obj must be there, be valid, & have a global.asa name
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(pHitObj->GlobalAspPath() != NULL && *(pHitObj->GlobalAspPath()) != '\0');
// Other arg's must be right
Assert(pEngineInfo != NULL);
// Load the script - cache will AddRef
// bug 1051: load template before possibly removing response object (in switch block, below),
// so error reporting to browser will work
hr = LoadTemplate(pHitObj->GlobalAspPath(), pHitObj, &pTemplate, intrinsics, /* fGlobalAsa */ TRUE, &fGlobalAsaInCache);
if (FAILED(hr))
goto LExit;
Assert(pTemplate != NULL);
// Remember GLOBAL.ASA's type library wrapper with the application
// on the first request
if (pHitObj->FStartApplication() && pTemplate->PTypeLibWrapper())
{
pHitObj->PAppln()->SetGlobTypeLibWrapper(pTemplate->PTypeLibWrapper());
}
/*
* Bug 86404: If this script is transacted, then create a new "real" ASP ObjectContext object
* and add it to the objects list. If the page is not transacted, then
* CHitObj::UseObjectContext will use pre-instantiated "zombie" ObjectContext.
*/
if (pTemplate->FTransacted())
{
IObjectContext *pContext;
if (FAILED(hr = GetObjectContext(&pContext)))
goto LExit;
/*
* Incredibly obscure bug in OLE32. If OLE needs to spin up an MTA thread in order to
* create the class factory for the ASPObjectContext object, then the thread needs to be
* created by the unimpersonated user.
* NOTE: This can be removed after the new OLE32 ships in NT4 SP4
*/
RevertToSelf();
hr = pContext->CreateInstance
(
CLSIDObjectContextFromTransType(pTemplate->GetTransType()),
IID_IASPObjectContextCustom,
reinterpret_cast<void **>(&pTxnScriptContextCustom)
);
// Restore Impersonation
HANDLE hThread = GetCurrentThread();
SetThreadToken(&hThread, pHitObj->HImpersonate());
pContext->Release();
if (FAILED(hr))
goto LExit;
hr = pTxnScriptContextCustom->QueryInterface(IID_IASPObjectContext, reinterpret_cast<void **>(&pTxnScriptContext));
if (FAILED(hr))
goto LExit;
// add to namespace
pHitObj->AddObjectContext(pTxnScriptContext);
pTxnScriptContext->Release();
}
Assert(pHitObj->FIsValidRequestType());
// Figure out which events to trigger
if (pHitObj->FIsBrowserRequest())
{
fOnStartAppln = pHitObj->FStartApplication();
if (fOnStartAppln)
{
// Hide response and request intrinsics from namespace
pHitObj->HideRequestAndResponseIntrinsics();
// Flag that intrinsics need to be un-hidden back in.
fUnHideRequestAndResponse = TRUE;
}
}
else if (pHitObj->FIsSessionCleanupRequest())
{
fOnEndSession = TRUE;
}
else if (pHitObj->FIsApplnCleanupRequest())
{
fOnEndAppln = TRUE;
}
// If debugging, notify debugger ONPAGESTART
if (pHitObj->PAppln()->FDebuggable())
{
if (SUCCEEDED(pTemplate->NotifyDebuggerOnPageEvent(TRUE)))
fDebuggerNotifiedOnStart = TRUE;
}
hr = AllocAndLoadEngines(pHitObj, pTemplate, pEngineInfo, intrinsics.PScriptingNamespace(), /* fGlobalAsa */TRUE);
if (FAILED(hr))
goto LExit;
// BUG 93991: Defer registration of new document with debugger until after script engines have
// been loaded
//
if (!fGlobalAsaInCache && pHitObj->PAppln()->FDebuggable())
pTemplate->AttachTo(pHitObj->PAppln());
// bug 975: if no script engines, do not attempt to call event functions
if(0 == pTemplate->CountScriptEngines())
goto LExit;
/*
* Call event functions as required
* bug 459: event functions may be in any script engine
*/
// First run Application_OnStart
if (fOnStartAppln)
{
pHitObj->SetEventState(eEventAppOnStart);
hr = CallScriptFunction(*pEngineInfo, L"Application_OnStart", pTxnScriptContextCustom, &fAborted);
if (SUCCEEDED(hr) || hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND ||
intrinsics.PResponse()->FResponseAborted())
{
if (fUnHideRequestAndResponse)
{
pHitObj->UnHideRequestAndResponseIntrinsics();
fUnHideRequestAndResponse = FALSE;
}
fApplnStarted = TRUE;
hr = S_OK;
}
else
{
goto LExit;
}
}
if (pHitObj->FStartSession())
{
// If application on start was run, add Response and Request names to script engines
if (fOnStartAppln)
{
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
{
if (FAILED(hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddAdditionalObject(WSZ_OBJ_RESPONSE, FALSE)))
goto LExit;
if (FAILED(hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddAdditionalObject(WSZ_OBJ_REQUEST, FALSE)))
goto LExit;
}
}
pHitObj->SetEventState(eEventSesOnStart);
hr = CallScriptFunction(*pEngineInfo, L"Session_OnStart", pTxnScriptContextCustom, &fAborted);
if (FAILED(hr) && hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND &&
!intrinsics.PResponse()->FResponseAborted())
{
// Mark session as on-start-failed - to be deleted soon
pHitObj->SessionOnStartFailed();
}
else
{
if (SUCCEEDED(hr))
{
// Mark as on-start-invoked -- need to wait for timeout
pHitObj->SessionOnStartInvoked();
}
// Check if Session_OnEnd Present
if (SUCCEEDED(TestScriptFunction(*pEngineInfo, L"Session_OnEnd")))
{
// Mark as on-end-present -- need to execute OnEnd later
pHitObj->SessionOnEndPresent();
}
hr = S_OK;
}
goto LExit;
}
if (fOnEndSession)
{
pHitObj->SetEventState(eEventSesOnEnd);
hr = CallScriptFunction(*pEngineInfo, L"Session_OnEnd", pTxnScriptContextCustom, &fAborted);
// We are failing silently here, since there is no corrective action we could take
}
if (fOnEndAppln)
{
pHitObj->SetEventState(eEventAppOnEnd);
hr = CallScriptFunction(*pEngineInfo, L"Application_OnEnd", pTxnScriptContextCustom, &fAborted);
// We are failing silently here, since there is no corrective action we could take
}
LExit:
if (fUnHideRequestAndResponse)
{
pHitObj->UnHideRequestAndResponseIntrinsics();
}
if (FAILED(hr) && (hr != E_SOURCE_FILE_IS_EMPTY) && pHitObj->FStartApplication() && !fApplnStarted)
{
pHitObj->ApplnOnStartFailed();
}
pHitObj->SetEventState(eEventNone);
// Uninit the scripting namespace
(VOID)intrinsics.PScriptingNamespace()->UnInit();
// Release the template
if (pTemplate)
{
// bug 975: if no script engines, do not do this
if(pTemplate->CountScriptEngines() > 0)
// Return the engine(s) to cache
DeAllocAndFreeEngines(pEngineInfo, pHitObj->PAppln(), pTxnScriptContextCustom);
// If debugging, notify debugger ONPAGEDONE
if (fDebuggerNotifiedOnStart)
{
Assert(pHitObj->PAppln()->FDebuggable());
pTemplate->NotifyDebuggerOnPageEvent(FALSE);
}
pTemplate->Release();
}
// Remove the ASP ObjectContext object from the namespace, and dealloc it if we allocated one
pHitObj->RemoveObjectContext();
if (pTxnScriptContextCustom)
{
pTxnScriptContextCustom->Release();
}
// It is OK if the event function was not found in global.asa
if (hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND)
{
hr = S_OK;
}
return hr;
}
/*===================================================================
CIntrinsicObjects::Prepare
Prepare intrinsics for the request processing
Parameters:
pSession session holding the instrinsics (can be NULL)
Returns:
HRESULT
===================================================================*/
HRESULT CIntrinsicObjects::Prepare
(
CSession *pSession
)
{
HRESULT hr = S_OK;
if (pSession)
{
// get request, response, server from session
if (SUCCEEDED(hr))
{
m_pRequest = pSession->PRequest();
if (m_pRequest)
m_pRequest->AddRef();
else
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
m_pResponse = pSession->PResponse();
if (m_pResponse)
m_pResponse->AddRef();
else
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
m_pServer = pSession->PServer();
if (m_pServer)
m_pServer->AddRef();
else
hr = E_FAIL;
}
}
else
{
// create new request, response, server
if (SUCCEEDED(hr))
{
m_pRequest = new CRequest;
if (!m_pRequest)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
m_pResponse = new CResponse;
if (!m_pResponse)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
m_pServer = new CServer;
if (!m_pServer)
hr = E_OUTOFMEMORY;
}
}
// init request, response, server
if (SUCCEEDED(hr))
{
Assert(m_pRequest);
hr = m_pRequest->Init();
}
if (SUCCEEDED(hr))
{
Assert(m_pResponse);
hr = m_pResponse->Init();
}
if (SUCCEEDED(hr))
{
Assert(m_pServer);
hr = m_pServer->Init();
}
// create the scripting namespace
if (SUCCEEDED(hr))
{
m_pScriptingNamespace = new CScriptingNamespace;
if (!m_pScriptingNamespace)
hr = E_OUTOFMEMORY;
}
// cleanup on error
if (FAILED(hr))
Cleanup();
m_fIsChild = FALSE;
return hr;
}
/*===================================================================
CIntrinsicObjects::PrepareChild
Prepare intrinsics structure for a child request
Parameters:
pResponse parent intrinsic
pRequest parent intrinsic
pServer parent intrinsic
Returns:
HRESULT
===================================================================*/
HRESULT CIntrinsicObjects::PrepareChild
(
CResponse *pResponse,
CRequest *pRequest,
CServer *pServer
)
{
HRESULT hr = S_OK;
if (!pResponse || !pRequest || !pServer)
{
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
m_pResponse = pResponse;
m_pResponse->AddRef();
m_pRequest = pRequest;
m_pRequest->AddRef();
m_pServer = pServer;
m_pServer->AddRef();
m_fIsChild = TRUE;
}
if (SUCCEEDED(hr))
{
m_pScriptingNamespace = new CScriptingNamespace;
if (!m_pScriptingNamespace)
hr = E_OUTOFMEMORY;
}
if (FAILED(hr))
Cleanup();
return hr;
}
/*===================================================================
CIntrinsicObjects::Cleanup
Cleanup the intrinsics after the request processing
Parameters:
Returns:
S_OK
===================================================================*/
HRESULT CIntrinsicObjects::Cleanup()
{
if (m_pRequest)
{
if (!m_fIsChild)
m_pRequest->UnInit();
m_pRequest->Release();
m_pRequest = NULL;
}
if (m_pResponse)
{
if (!m_fIsChild)
m_pResponse->UnInit();
m_pResponse->Release();
m_pResponse = NULL;
}
if (m_pServer)
{
if (!m_fIsChild)
m_pServer->UnInit();
m_pServer->Release();
m_pServer = NULL;
}
if (m_pScriptingNamespace)
{
m_pScriptingNamespace->Release();
m_pScriptingNamespace = NULL;
}
return S_OK;
}
/*===================================================================
ReInitIntrinsics
Call re-init on each of the intrinsics that require it
to run a new page.
Parameters:
pHitObj - pointer to the hit object
intrinsics - pointers to the intrinsic IUnknown pointers.
pEngineInfo - some engine info
fPostGlobal - Is this a reinit after running global.asa?
Returns:
S_OK on success
===================================================================*/
HRESULT ReInitIntrinsics
(
CHitObj *pHitObj,
const CIntrinsicObjects &intrinsics,
ActiveEngineInfo *pEngineInfo,
BOOL fPostGlobal
)
{
HRESULT hr;
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(pEngineInfo != NULL);
// Hand the new CIsapiReqInfo to the Server object
// Note on bug 682: We do always need to re-init CServer because it takes the phitobj
if (FAILED(hr = intrinsics.PServer()->ReInit(pHitObj->PIReq(), pHitObj)))
goto LExit;
if (FAILED(hr = intrinsics.PScriptingNamespace()->Init()))
goto LExit;
/*
* Bug 682 & 671 (better fix to 452 & 512)
* Dont re-init the Request & response objects after running a Global.Asa
* because, the running of global.asa may have set cookies into request (bug 671), that reinit
* would wipe, and the global.asa may have output headers (or other stuff) which impacts the response
* object (bug 512) that we dont want to reset.
*/
if (!fPostGlobal)
{
if (FAILED(hr = intrinsics.PRequest()->ReInit(pHitObj->PIReq(), pHitObj)))
goto LExit;
if (FAILED(hr = intrinsics.PResponse()->ReInit(
pHitObj->PIReq(),
pHitObj->PSzNewSessionCookie(),
intrinsics.PRequest(),
GetScriptEngine,
pEngineInfo,
pHitObj
)))
goto LExit;
}
LExit:
return(hr);
}
/*===================================================================
LoadTemplate
Load a template, cleanup and give appropriate errors on failure.
Parameters:
szFile - the file to load a template for
pHitObj - pointer to the hit object
ppTemplate - The returned loaded template
fGlobalAsa - is this for Global.asa?
Returns:
S_OK on success
===================================================================*/
HRESULT LoadTemplate
(
const TCHAR *szFile,
CHitObj *pHitObj,
CTemplate **ppTemplate,
const CIntrinsicObjects &intrinsics,
BOOL fGlobalAsa,
BOOL *pfTemplateInCache)
{
HRESULT hr;
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(ppTemplate != NULL);
// Load the script - cache will AddRef
if (FAILED(hr = g_TemplateCache.Load(
fGlobalAsa,
szFile,
pHitObj->DWInstanceID(),
pHitObj,
ppTemplate,
pfTemplateInCache)))
{
// CONSIDER moving this cleanup into Template.Load
if (hr == E_COULDNT_OPEN_SOURCE_FILE || hr == E_SOURCE_FILE_IS_EMPTY)
{
// Load error string from string table
// BUG 731: added if to retrieve the correct header
WCHAR szwErr[128];
if (hr == E_COULDNT_OPEN_SOURCE_FILE)
{
CwchLoadStringOfId(IDH_404_OBJECT_NOT_FOUND, szwErr, 128);
intrinsics.PResponse()->put_Status( szwErr );
HandleSysError(404, 0, IDE_404_OBJECT_NOT_FOUND, NULL, NULL, pHitObj);
#ifndef PERF_DISABLE
g_PerfData.Incr_REQNOTFOUND();
#endif
}
// bug 977: silently ignore empty global.asa file
else if ((E_SOURCE_FILE_IS_EMPTY == hr) && !fGlobalAsa)
{
CwchLoadStringOfId(IDH_204_NO_CONTENT, szwErr, 128);
intrinsics.PResponse()->put_Status( szwErr );
HandleSysError(204, 0, IDE_204_NO_CONTENT, NULL, NULL, pHitObj);
}
}
// fix for bug 371
if (*ppTemplate)
{
(*ppTemplate)->Release();
*ppTemplate = NULL;
}
if (hr == E_OUTOFMEMORY)
{
DBGPRINTF((DBG_CONTEXT, "Loading template returned E_OUTOFMEMORY. Flushing template & Script Cache.\n"));
g_TemplateCache.FlushAll();
g_ScriptManager.FlushAll();
}
}
return(hr);
}
/*===================================================================
AllocAndLoadEngines
Allocate and load all the engines we need
Parameters:
pHitObj - The hit object
pTemplate - The template we're gonna run
pEngineInfo - Engine info to fill in
pScriptingNamespace - scripting namespace
fGlobalAsa - Are we loading engines to run global.asa?
Returns:
S_OK on success
===================================================================*/
HRESULT AllocAndLoadEngines
(
CHitObj *pHitObj,
CTemplate *pTemplate,
ActiveEngineInfo *pEngineInfo,
CScriptingNamespace *pScriptingNamespace,
BOOL fGlobalAsa
)
{
HRESULT hr = S_OK;
int iObj;
WORD iEng;
WORD iScriptBlock;
WORD cEngines = pTemplate->CountScriptEngines();
Assert(pHitObj != NULL);
pHitObj->AssertValid();
Assert(pTemplate != NULL);
Assert(pEngineInfo != NULL);
Assert(pScriptingNamespace != NULL);
// Load objects from template into hit object
for (iObj = pTemplate->Count(tcompObjectInfo) - 1; iObj >= 0; --iObj)
{
CHAR *szObjectName = NULL;
CLSID clsid;
CompScope scope;
CompModel model;
CMBCSToWChar convStr;
// get object-info from template and add to hitobj's list of objects
hr = pTemplate->GetObjectInfo(iObj, &szObjectName, &clsid, &scope, &model);
if(FAILED(hr))
goto LExit;
hr = convStr.Init(szObjectName);
if (FAILED(hr))
goto LExit;
// ignore error ?
pHitObj->AddComponent(ctTagged, clsid, scope, model, convStr.GetString());
}
// bug 975: if no script engines, exit now
if(cEngines == 0)
goto LExit;
// Allocate space for script engines
//
// NOTE: There is a timing problem here in that the response object needs to
// be instantiated before we instantiate the script engines, but the
// response object needs to be able to access the list of active script
// engines, because it may need to halt execution. To accomplish this,
// the response object is passed a pointer to the "EngineInfo" structure
// as a pointer, and then we modify the contents of the pointer right under
// its nose. We pass an accessor function via pointer so that response just
// sees a void pointer.
//
if (cEngines == 1)
{
// don't do allocations in case of one engine
pEngineInfo->rgActiveEngines = & (pEngineInfo->siOneActiveEngine);
}
else
{
pEngineInfo->rgActiveEngines = new ScriptingInfo[cEngines];
if (pEngineInfo->rgActiveEngines == NULL)
{
hr = E_OUTOFMEMORY;
goto LExit;
}
}
pEngineInfo->cEngines = cEngines;
pEngineInfo->cActiveEngines = 0; // number of SUCCESSFULLY instantiated engines
// Load all of the script engines in advance.
for (iScriptBlock = 0; iScriptBlock < cEngines; ++iScriptBlock)
{
LPCOLESTR wstrScript;
SCRIPTSTATE nScriptState;
ScriptingInfo *pScriptInfo = &pEngineInfo->rgActiveEngines[iScriptBlock];
pTemplate->GetScriptBlock(
iScriptBlock,
&pScriptInfo->szScriptEngine,
&pScriptInfo->pProgLangId,
&wstrScript);
// Populate information required for the line mapping callback.
//
pScriptInfo->LineMapInfo.iScriptBlock = iScriptBlock;
pScriptInfo->LineMapInfo.pTemplate = pTemplate;
// acquire a script engine by:
//
// getting an engine from the template object (if it has one)
// else from the script manager.
//
// If we are in debug mode, the templates tend to be greedy and hold
// onto script engines. (See notes in scrptmgr.h)
//
pScriptInfo->pScriptEngine = NULL;
if (pHitObj->PAppln()->FDebuggable())
{
pScriptInfo->pScriptEngine = pTemplate->GetActiveScript(iScriptBlock);
if (pScriptInfo->pScriptEngine)
{
// If we got one, we don't need to re-init the engine
nScriptState = SCRIPTSTATE_INITIALIZED;
hr = static_cast<CActiveScriptEngine *>(pScriptInfo->pScriptEngine)->ReuseEngine(pHitObj, NULL, iScriptBlock, pHitObj->DWInstanceID());
}
}
if (pScriptInfo->pScriptEngine == NULL)
{
hr = g_ScriptManager.GetEngine(LOCALE_SYSTEM_DEFAULT,
*(pScriptInfo->pProgLangId),
pTemplate->GetSourceFileName(),
pHitObj,
&pScriptInfo->pScriptEngine,
&nScriptState,
pTemplate,
iScriptBlock);
}
if (FAILED(hr))
goto LExit;
// BUG 252: Keep track of how many engines we actually instantiate
++pEngineInfo->cActiveEngines;
if (nScriptState == SCRIPTSTATE_UNINITIALIZED || fGlobalAsa)
{
if (FAILED(hr = pScriptInfo->pScriptEngine->AddObjects(!fGlobalAsa)))
goto LExit;
}
if (nScriptState == SCRIPTSTATE_UNINITIALIZED)
{
if (FAILED(hr = pScriptInfo->pScriptEngine->AddScriptlet(wstrScript)))
goto LExit;
}
// Add the engine to the scripting namespace
if (FAILED(hr = pScriptingNamespace->AddEngineToNamespace(
(CActiveScriptEngine *)pScriptInfo->pScriptEngine)))
goto LExit;
// Update locale & code page (in case they are different om this page)
pScriptInfo->pScriptEngine->UpdateLocaleInfo(hostinfoLocale);
pScriptInfo->pScriptEngine->UpdateLocaleInfo(hostinfoCodePage);
}
// Add the scripting namespace to each script engine. Because all engines might not
// implement "lazy instantiation", this code requires all
// engines are pre-instantiated (which means we can't do it in the above loop.)
// Add the scripting namespace to the hitobj first
pHitObj->AddScriptingNamespace(pScriptingNamespace);
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddScriptingNamespace();
/*
* Bug 735:
* Bring all engines except the "primary engine" (engine 0) to runnable state
* in the order in which script for the given language was found in the script file
*/
for (iEng = 1; iEng < pEngineInfo->cActiveEngines; ++iEng)
{
hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->MakeEngineRunnable();
if (FAILED(hr))
goto LExit;
}
LExit:
return(hr);
}
/*===================================================================
DeAllocAndFreeEngines
Deallocate and free any loaded engines
Parameters:
pEngineInfo - Engine info to release
Returns:
Nothing
===================================================================*/
VOID DeAllocAndFreeEngines
(
ActiveEngineInfo *pEngineInfo,
CAppln *pAppln,
IASPObjectContextCustom *pTxnScriptContextCustom
)
{
WORD iEng;
Assert(pEngineInfo != NULL);
if (pEngineInfo->cActiveEngines > 0) {
if (pEngineInfo->rgActiveEngines == NULL) {
Assert(pEngineInfo->rgActiveEngines);
}
else {
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
g_ScriptManager.ReturnEngineToCache(&pEngineInfo->rgActiveEngines[iEng].pScriptEngine, pAppln, pTxnScriptContextCustom);
pEngineInfo->cActiveEngines = 0;
}
}
if (pEngineInfo->cEngines > 1)
{
delete pEngineInfo->rgActiveEngines;
}
pEngineInfo->cEngines = 0;
pEngineInfo->rgActiveEngines = NULL;
}
/*===================================================================
GetScriptEngine
Get a script engine based on index. Return NULL if the index
is not in range. (this is a callback)
The AllocAndLoadEngines function will create an array of ScriptingInfo
structures that are defined here. It contains all the infomation
needed to 1. set up the MapScript2SourceLine callback,
2. merge namespaces, 3. set up this callback.
Parameters:
iScriptEngine - the script engine to retrieve
pvData - instance data for the function
Returns:
The requested script engine or NULL if not such engine
Side effects:
None
===================================================================*/
CScriptEngine *GetScriptEngine
(
INT iScriptEngine,
VOID *pvData
)
{
ActiveEngineInfo *pInfo = static_cast<ActiveEngineInfo *>(pvData);
if (unsigned(iScriptEngine) >= unsigned(pInfo->cActiveEngines))
{
// Note: the caller has no idea how many script engines there are.
// if the caller asks for an engine out of range, return NULL so they
// know they have asked for more than there are
return NULL;
}
return(pInfo->rgActiveEngines[iScriptEngine].pScriptEngine);
}
/*===================================================================
CallScriptFunctionOfEngine
Calls a script engine to execute one of its functions
Returns:
S_OK on success
Side effects:
None
===================================================================*/
HRESULT CallScriptFunctionOfEngine
(
ActiveEngineInfo &engineInfo,
short iScriptBlock,
wchar_t *strFunction,
IASPObjectContextCustom *pTxnScriptContextCustom,
BOOLB *pfAborted
)
{
HRESULT hr;
Assert(engineInfo.rgActiveEngines != NULL);
Assert(engineInfo.rgActiveEngines[iScriptBlock].pScriptEngine != NULL);
Assert(pfAborted != NULL);
*pfAborted = FALSE;
/*
* If they want a transaction, then we create a TransactedScript object
* and tell it to call the engine
*/
if (pTxnScriptContextCustom)
{
hr = pTxnScriptContextCustom->Call(
#ifdef _WIN64
// Win64 fix -- use UINT64 instead of LONG_PTR since LONG_PTR is broken for Win64 1/21/2000
(UINT64)engineInfo.rgActiveEngines[iScriptBlock].pScriptEngine,strFunction, pfAborted);
#else
(LONG_PTR)engineInfo.rgActiveEngines[iScriptBlock].pScriptEngine,strFunction, pfAborted);
#endif
}
else
{
hr = engineInfo.rgActiveEngines[iScriptBlock].pScriptEngine->Call(strFunction);
}
return hr;
}
/*===================================================================
CallScriptFunction
Calls each script engine in turn to execute a script function;
exits when an engine succeeds or we run out of engines.
Returns:
S_OK on success
Side effects:
None
===================================================================*/
HRESULT CallScriptFunction
(
ActiveEngineInfo &engineInfo,
wchar_t *strFunction,
IASPObjectContextCustom *pTxnScriptContextCustom,
BOOLB *pfAborted
)
{
HRESULT hr = E_FAIL;
int i;
for (i = 0; i < engineInfo.cActiveEngines; i++)
{
// if execution succeeds, bail
if (SUCCEEDED(hr = CallScriptFunctionOfEngine(engineInfo, (SHORT)i, strFunction,
pTxnScriptContextCustom, pfAborted)))
goto LExit;
// if execution fails with exception other then unknown name, bail
if (hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND)
goto LExit;
}
LExit:
return hr;
}
/*===================================================================
TestScriptFunction
Tests each script engine in turn to test [the existance of] a script
function; exits when an engine succeeds or we run out of engines.
Parameters
ActiveEngineInfo &engineInfo
wchar_t *strFunction functions name
Returns:
S_OK if exists
Side effects:
None
===================================================================*/
HRESULT TestScriptFunction
(
ActiveEngineInfo &engineInfo,
wchar_t *strFunction
)
{
HRESULT hr = E_FAIL;
for (int i = 0; i < engineInfo.cActiveEngines; i++)
{
hr = engineInfo.rgActiveEngines[i].pScriptEngine->
CheckEntryPoint(strFunction);
// if execution succeeds, bail
if (SUCCEEDED(hr))
break;
// if fails with result other then unknown name, bail
if (hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND)
break;
}
return hr;
}