Share via


engine.cpp

#include "headers.h"

ScriptEngine::ScriptEngine()
{
DLLAddRef();

    this->m_cref = 1;
this->m_thread = threadNone;
this->m_state = SCRIPTSTATE_UNINITIALIZED;
this->m_psite = NULL;
this->m_pNamedItemList = NULL;
}

ScriptEngine::~ScriptEngine()
{
Assert(this->m_cref == 0);

    if (this->m_state != SCRIPTSTATE_CLOSED && this->m_state != SCRIPTSTATE_UNINITIALIZED)
{
Bug("It's a very bad idea to deallocate an initialized engine.");
}

    this->Close();
if (NULL != this->m_pNamedItemList)
delete this->m_pNamedItemList;

    DLLRelease();
}

HRESULT ScriptEngine::Create(ScriptEngine * * ppEngine)
{
AssertOutPtr(ppEngine);

    HRESULT hr;

    *ppEngine = NULL;
ScriptEngine * pEngine = NULL;

    pEngine = new ScriptEngine();
if (NULL == pEngine)
{
hr = E_OUTOFMEMORY;
goto LError;
}

    hr = NamedItemList::Create(23, &pEngine->m_pNamedItemList);
if (FAILED(hr))
goto LError;

    *ppEngine = pEngine;
pEngine = NULL;

    hr = S_OK;       

LError:

    if (NULL != pEngine)
pEngine->Release();

return hr;
}

void ScriptEngine::ChangeScriptState(SCRIPTSTATE state)
{
if (this->m_state != state)
{
this->m_state = state;
if (this->m_psite != NULL)
this->m_psite->OnStateChange(state);
}
}

BOOL ScriptEngine::IsInitialized(void)
{
switch(this->m_state)
{
case SCRIPTSTATE_CLOSED:
case SCRIPTSTATE_UNINITIALIZED:
Assert(NULL == this->m_psite);
Assert(threadNone == this->m_thread);
return FALSE;
case SCRIPTSTATE_INITIALIZED:
case SCRIPTSTATE_STARTED:
case SCRIPTSTATE_CONNECTED:
case SCRIPTSTATE_DISCONNECTED:
Assert(threadNone != this->m_thread);
Assert(NULL != this->m_psite);
return TRUE;
default:
Bug("The script state is a bad value.");
return FALSE;
}
}

HRESULT ScriptEngine::VerifyThread(void)
{
if (this->IsInitialized() && this->m_thread != GetCurrentThreadId())
{

        Bug("The host is in violation of the script engine threading contract. "
"Hosts are required to treat the engine as apartment threaded when "
"the engine is initialized (with some exceptions) and as rental "
"threaded when it is not initialized.");

        return E_UNEXPECTED;
}
return S_OK;
}

HRESULT ScriptEngine::Uninitialize(void)
{
HRESULT hr;

if (this->m_state == SCRIPTSTATE_CLOSED)
{
Bug("It is illegal to move a script engine from CLOSED to UNINITIALIZED");
// Pointless, too.
return E_UNEXPECTED;
}

    if (this->m_state == SCRIPTSTATE_UNINITIALIZED)
return S_OK;

    hr = this->Initialize();
if (FAILED(hr))
goto LError;

    this->m_pNamedItemList->Reset();

    this->m_psite->Release();
this->m_psite = NULL;
this->m_thread = threadNone;

    hr = S_OK;

LError:

    return hr;
}

HRESULT ScriptEngine::Initialize(void)
{
HRESULT hr;

if (!this->IsInitialized())
{
Bug("To initialize an uninitialized engine, call SetScriptSite.");
return E_UNEXPECTED;
}

    if (this->m_state == SCRIPTSTATE_INITIALIZED)
return S_OK;

    if (this->m_state == SCRIPTSTATE_CONNECTED || this->m_state == SCRIPTSTATE_DISCONNECTED)
{
hr = this->FullyDisconnectEventSinks();
if (FAILED(hr))
goto LError;
}

    hr = this->StopEngine();
if (FAILED(hr))
goto LError;

    hr = S_OK;

LError:

    return hr;
}

HRESULT ScriptEngine::Start(void)
{
HRESULT hr;

    if (!this->IsInitialized())
{
Bug("You cannot start an uninitialized engine.");
return E_UNEXPECTED;
}

    if (this->m_state == SCRIPTSTATE_STARTED)
return S_OK;

    if (this->m_state == SCRIPTSTATE_CONNECTED || this->m_state == SCRIPTSTATE_DISCONNECTED)
{
hr = this->FullyDisconnectEventSinks();
if (FAILED(hr))
goto LError;
}

    hr = this->Execute();

    if (SCRIPT_E_REPORTED == hr)
hr = S_OK;
if (FAILED(hr))
goto LError;

    hr = S_OK;

LError:

    return hr;
}

HRESULT ScriptEngine::Execute(void)
{
// UNDONE
return S_OK;
}

HRESULT ScriptEngine::StopEngine(void)
{
// UNDONE
return S_OK;
}

HRESULT ScriptEngine::Connect(void)
{
HRESULT hr;

    if (!this->IsInitialized())
{
Bug("You cannot connect an uninitialized engine.");
return E_UNEXPECTED;
}

    if (this->m_state == SCRIPTSTATE_CONNECTED)
return S_OK;

    if (this->m_state == SCRIPTSTATE_DISCONNECTED)
{
hr = this->ReconnectEventSinks();
if (FAILED(hr))
goto LError;
}
else
{
hr = this->Start();
if (FAILED(hr))
goto LError;

        hr = this->ConnectEventSinks();
if (FAILED(hr))
goto LError;
}

    hr = S_OK;

LError:

    return hr;
}

HRESULT ScriptEngine::TemporarilyDisconnectEventSinks(void)
{
if (this->m_state == SCRIPTSTATE_DISCONNECTED)
return S_OK;

    if (this->m_state != SCRIPTSTATE_CONNECTED)
{
Bug("You cannot disconnect an engine that is not connected!");
return E_UNEXPECTED;
}

// UNDONE
return S_OK;
}

HRESULT ScriptEngine::FullyDisconnectEventSinks(void)
{
Assert(this->m_state == SCRIPTSTATE_CONNECTED || this->m_state == SCRIPTSTATE_DISCONNECTED);

    // UNDONE
return S_OK;
}

HRESULT ScriptEngine::ConnectEventSinks(void)
{
// UNDONE
return S_OK;
}

HRESULT ScriptEngine::ReconnectEventSinks(void)
{
// UNDONE
return S_OK;
}

// IUnknown

STDMETHODIMP ScriptEngine::QueryInterface(REFIID riid, void * * ppv)
{
// This method can be called on any thread at any time.

if (NULL == ppv)
{
Bug("Null out pointer");
return E_POINTER;
}

AssertOutPtr(ppv);

    *ppv = NULL;

    if (IsEqualIID(riid, IID_IUnknown))
*ppv = (IUnknown *)(IActiveScript *) this;
else if (IsEqualIID(riid, IID_IActiveScript))
*ppv = (IActiveScript *) this;
else if (IsEqualIID(riid, IID_IActiveScriptParse))
*ppv = (IActiveScriptParse *) this;
else if (IsEqualIID(riid, IID_IActiveScriptParseProcedure2))
*ppv = (IActiveScriptParseProcedure2 *) this;
else if (IsEqualIID(riid, IID_IObjectSafety))
*ppv = (IObjectSafety *) this;
else
return E_NOINTERFACE;

    this->AddRef();
return S_OK;
}

STDMETHODIMP_(ULONG) ScriptEngine::AddRef(void)
{
// This method can be called on any thread at any time.

return InterlockedIncrement(&this->m_cref);
}

STDMETHODIMP_(ULONG) ScriptEngine::Release(void)
{
// This method can be called on any thread at any time,
// but if this is the final release, the engine should be
// uninitialized.

long cref = InterlockedDecrement(&this->m_cref);
if (0 == cref)
delete this;
return cref;
}

// IActiveScript

STDMETHODIMP ScriptEngine::SetScriptSite(IActiveScriptSite * psite)
{
if (NULL == psite)
{
Bug("Null site pointer");
return E_POINTER;
}

    AssertReadPtr(psite);

if (this->IsInitialized())
{
Bug("It is a violation of the script engine contract to set "
"the site of an engine which is already initialized");
return E_UNEXPECTED;
}

psite->AddRef();
this->m_psite = psite;
this->m_thread = GetCurrentThreadId();

    this->ChangeScriptState(SCRIPTSTATE_INITIALIZED);

    return S_OK;
}

STDMETHODIMP ScriptEngine::GetScriptSite(REFIID riid, void * * ppv)
{
HRESULT hr;

    if (NULL == ppv)
{
Bug("Null out pointer");
return E_POINTER;
}

    AssertOutPtr(ppv);

    *ppv = NULL;

hr = this->VerifyThread();
if (FAILED(hr))
return hr;

    if (this->m_psite == NULL)
return S_FALSE;

    return this->m_psite->QueryInterface(riid, ppv);
}

STDMETHODIMP ScriptEngine::SetScriptState(SCRIPTSTATE state)
{
HRESULT hr;

hr = this->VerifyThread();
if (FAILED(hr))
goto LError;

switch(state)
{
default:
Bug("The script state is a bad value.");
hr = E_INVALIDARG;
break;
case SCRIPTSTATE_CLOSED:
hr = this->Close();
break;
case SCRIPTSTATE_UNINITIALIZED:
hr = this->Uninitialize();
break;
case SCRIPTSTATE_INITIALIZED:
hr = this->Initialize();
break;
case SCRIPTSTATE_STARTED:
hr = this->Start();
break;
case SCRIPTSTATE_CONNECTED:
hr = this->Connect();
break;
case SCRIPTSTATE_DISCONNECTED:
hr = this->TemporarilyDisconnectEventSinks();
break;
}

    if (FAILED(hr))
goto LError;

    this->ChangeScriptState(state);

    hr = S_OK;

LError:

    return hr;
}

STDMETHODIMP ScriptEngine::Close(void)
{
HRESULT hr;

    if (this->m_state == SCRIPTSTATE_CLOSED)
return S_OK;

    hr = this->VerifyThread();
if (FAILED(hr))
goto LError;

    hr = this->Uninitialize();
if (FAILED(hr))
goto LError;

    this->m_pNamedItemList->Clear();

    this->ChangeScriptState(SCRIPTSTATE_CLOSED);

    hr = S_OK;

LError:   

    return hr;
}

STDMETHODIMP ScriptEngine::AddNamedItem(const WCHAR * pszName, DWORD flags)
{

// There are six possible flags. Suppose the named item is "foo":
//
// SCRIPTITEM_ISVISIBLE : foo is added to the namespace, so you can call
// "foo.bar(123)" from the script
// SCRIPTITEM_ISSOURCE : foo is an object which sources events
// SCRIPTITEM_GLOBALMEMBERS : calling "bar(123)" calls "foo.bar(123)". Think
// "window.alert()". These guys go into the global module.
// SCRIPTITEM_ISPERSISTENT : foo survives when we go back to Uninitialized state.
// SCRIPTITEM_CODEONLY : foo is just the name of a module, not an object
// SCRIPTITEM_NOCODE : foo is just the name of a named item, not a code module

    HRESULT hr;

    if (NULL == pszName)
return E_POINTER;

    AssertReadString(pszName);

if (!this->IsInitialized())
{
Bug("It is a violation of the script engine contract to add "
"a named item to an uninitialized engine.");
return E_UNEXPECTED;
}

    if ((flags & ~SCRIPTITEM_ALL_FLAGS) != 0)
{
Bug("Bad flag passed to AddNamedItem");
return E_INVALIDARG;
}

    hr = this->VerifyThread();
if (FAILED(hr))
return hr;

    hr = this->m_pNamedItemList->Add(pszName, flags);
if (FAILED(hr))
return hr;

    return S_OK;
}

STDMETHODIMP ScriptEngine::AddTypeLib(REFGUID rguid, DWORD major, DWORD minor,
DWORD flags)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::GetScriptDispatch(const WCHAR * pszName,
IDispatch * * ppdisp)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::GetScriptState(SCRIPTSTATE * pstate)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::GetCurrentScriptThreadID(SCRIPTTHREADID * pthread)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::GetScriptThreadID(DWORD thread, SCRIPTTHREADID * pthread)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::GetScriptThreadState(SCRIPTTHREADID thread,
SCRIPTTHREADSTATE * pstate)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::InterruptScriptThread(SCRIPTTHREADID thread,
const EXCEPINFO * pexcepinfo, DWORD flags)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::Clone(IActiveScript * * ppScriptEngine)
{
return E_NOTIMPL;
}

// IActiveScriptParse

STDMETHODIMP ScriptEngine::InitNew(void)
{
return S_OK;
}

STDMETHODIMP ScriptEngine::AddScriptlet(const WCHAR * pszDefaultName,
const WCHAR * pszCode, const WCHAR * pszItemName, const WCHAR * pszSubItemName,
const WCHAR * pszEventName, const WCHAR * pszDelimiter, DWORD sourceContext,
ULONG startingLineNumber, DWORD flags, BSTR * pbstrName, EXCEPINFO * pexcepinfo)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::ParseScriptText(const WCHAR * pszCode,
const WCHAR * pszItemName, IUnknown *punkContext, const WCHAR * pszDelimiter,
DWORD sourceContext, ULONG startingLineNumber, DWORD flags,
VARIANT *pvarResult, EXCEPINFO *pexcepinfo)
{
return E_NOTIMPL;
}

// IActiveScriptParseProcedure2

STDMETHODIMP ScriptEngine::ParseProcedureText(const WCHAR * pstrCode,
const WCHAR * pstrFormalParams, const WCHAR * pstrProcedureName,
const WCHAR * pstrItemName, IUnknown * punkContext, const WCHAR * pstrDelimiter,
DWORD sourceContext, ULONG startingLineNumber, DWORD flags, IDispatch * * ppdisp)
{
return E_NOTIMPL;
}

// IObjectSafety

STDMETHODIMP ScriptEngine::GetInterfaceSafetyOptions(REFIID riid,
DWORD * pSupported, DWORD * pEnabled)
{
return E_NOTIMPL;
}

STDMETHODIMP ScriptEngine::SetInterfaceSafetyOptions(REFIID riid,
DWORD mask, DWORD options)
{
return E_NOTIMPL;
}