mirror of
https://github.com/SSCLI/sscli_20021101
synced 2026-06-08 12:28:57 +00:00
9fa3874800
Moved the original file to the archive subfolder.
760 lines
24 KiB
C#
760 lines
24 KiB
C#
// ==++==
|
|
//
|
|
//
|
|
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// The use and distribution terms for this software are contained in the file
|
|
// named license.txt, which can be found in the root of this distribution.
|
|
// By using this software in any fashion, you are agreeing to be bound by the
|
|
// terms of this license.
|
|
//
|
|
// You must not remove this notice, or any other, from this software.
|
|
//
|
|
//
|
|
// ==--==
|
|
namespace Microsoft.Vsa
|
|
{
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using System.Security.Permissions;
|
|
using System.Threading;
|
|
|
|
public abstract class BaseVsaEngine : IVsaEngine{
|
|
|
|
// === Members ===
|
|
|
|
protected string applicationPath;
|
|
protected System.Reflection.Assembly loadedAssembly;
|
|
protected string compiledRootNamespace;
|
|
protected IVsaSite engineSite;
|
|
protected bool genDebugInfo;
|
|
protected bool haveCompiledState; // true if there is compiled state in the engine
|
|
protected bool failedCompilation; // true if IVsaEngine.Compile returned false on the most recent call (false by default)
|
|
protected bool isClosed;
|
|
protected bool isEngineCompiled; // true if current compiled state reflects current source state
|
|
protected bool isDebugInfoSupported;
|
|
protected bool isEngineDirty; // true if current source state does not reflect the persisted source state
|
|
protected bool isEngineInitialized;
|
|
protected bool isEngineRunning;
|
|
protected IVsaItems vsaItems;
|
|
protected string scriptLanguage;
|
|
protected int errorLocale;
|
|
static protected Hashtable nameTable = new Hashtable(10);
|
|
protected string engineName;
|
|
protected string engineMoniker;
|
|
protected string rootNamespace;
|
|
protected Type startupClass;
|
|
protected BaseVsaStartup startupInstance;
|
|
protected string assemblyVersion;
|
|
protected System.Security.Policy.Evidence executionEvidence;
|
|
|
|
// === Constructor ===
|
|
|
|
public BaseVsaEngine(string language, string version, bool supportDebug){
|
|
// Set default property values and initial state
|
|
this.applicationPath = "";
|
|
this.compiledRootNamespace = null;
|
|
this.genDebugInfo = false;
|
|
this.haveCompiledState = false;
|
|
this.failedCompilation = false;
|
|
this.isClosed = false;
|
|
this.isEngineCompiled = false;
|
|
this.isEngineDirty = false;
|
|
this.isEngineInitialized = false;
|
|
this.isEngineRunning = false;
|
|
this.vsaItems = null;
|
|
this.engineSite = null;
|
|
this.errorLocale = CultureInfo.CurrentCulture.LCID;
|
|
this.engineName = "";
|
|
this.rootNamespace = "";
|
|
this.engineMoniker = "";
|
|
|
|
// Set implementation-dependent values
|
|
this.scriptLanguage = language;
|
|
this.assemblyVersion = version;
|
|
this.isDebugInfoSupported = supportDebug;
|
|
this.executionEvidence = null;
|
|
}
|
|
|
|
// === Helper Methods ===
|
|
|
|
protected VsaException Error(VsaError vsaErrorNumber){
|
|
return new VsaException(vsaErrorNumber);
|
|
}
|
|
|
|
internal void TryObtainLock() {
|
|
if (!Monitor.TryEnter(this))
|
|
throw new VsaException(VsaError.EngineBusy);
|
|
}
|
|
|
|
internal void ReleaseLock() {
|
|
Monitor.Exit(this);
|
|
}
|
|
|
|
// Pre is a set of preconditions that must hold in order to perform certain
|
|
// operations on or with the engine
|
|
|
|
protected enum Pre{
|
|
None = 0x0000,
|
|
EngineNotClosed = 0x0001, // optional; tested by default
|
|
SupportForDebug = 0x0002,
|
|
EngineCompiled = 0x0004,
|
|
EngineRunning = 0x0008,
|
|
EngineNotRunning = 0x0010,
|
|
RootMonikerSet = 0x0020,
|
|
RootMonikerNotSet = 0x0040,
|
|
RootNamespaceSet = 0x0080,
|
|
SiteSet = 0x0100,
|
|
SiteNotSet = 0x0200,
|
|
EngineInitialised = 0x0400,
|
|
EngineNotInitialised = 0x0800,
|
|
}
|
|
|
|
private bool IsCondition(Pre flag, Pre test){
|
|
return ((flag & test) != Pre.None);
|
|
}
|
|
|
|
// The Preconditions method tests a set of preconditions and throws the
|
|
// appropriate VsaException if any of them do not hold.
|
|
// Pre.EngineNotClosed is always tested, regardless of whether or not it
|
|
// appears in the flags bitfield (including when only Pre.None is indicated).
|
|
|
|
protected void Preconditions(Pre flags){
|
|
// Every operation on this object requires that the engine not be closed
|
|
if (this.isClosed)
|
|
throw Error(VsaError.EngineClosed);
|
|
if (flags == (Pre.EngineNotClosed | Pre.None))
|
|
return;
|
|
|
|
if (IsCondition(flags, Pre.SupportForDebug) && !this.isDebugInfoSupported)
|
|
throw Error(VsaError.DebugInfoNotSupported);
|
|
if (IsCondition(flags, Pre.EngineCompiled) && !this.haveCompiledState)
|
|
throw Error(VsaError.EngineNotCompiled);
|
|
if (IsCondition(flags, Pre.EngineRunning) && !this.isEngineRunning)
|
|
throw Error(VsaError.EngineNotRunning);
|
|
if (IsCondition(flags, Pre.EngineNotRunning) && this.isEngineRunning)
|
|
throw Error(VsaError.EngineRunning);
|
|
if (IsCondition(flags, Pre.RootMonikerSet) && (this.engineMoniker == ""))
|
|
throw Error(VsaError.RootMonikerNotSet);
|
|
if (IsCondition(flags, Pre.RootMonikerNotSet) && (this.engineMoniker != ""))
|
|
throw Error(VsaError.RootMonikerAlreadySet);
|
|
if (IsCondition(flags, Pre.RootNamespaceSet) && (this.rootNamespace == ""))
|
|
throw Error(VsaError.RootNamespaceNotSet);
|
|
if (IsCondition(flags, Pre.SiteSet) && (this.engineSite == null))
|
|
throw Error(VsaError.SiteNotSet);
|
|
if (IsCondition(flags, Pre.SiteNotSet) && (this.engineSite != null))
|
|
throw Error(VsaError.SiteAlreadySet);
|
|
if (IsCondition(flags, Pre.EngineInitialised) && !this.isEngineInitialized)
|
|
throw Error(VsaError.EngineNotInitialized);
|
|
if (IsCondition(flags, Pre.EngineNotInitialised) && this.isEngineInitialized)
|
|
throw Error(VsaError.EngineInitialized);
|
|
}
|
|
|
|
// === Properties ===
|
|
|
|
// IVsaEngine.AppDomain is not supported by managed engines
|
|
public System._AppDomain AppDomain{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
throw new NotSupportedException();
|
|
}
|
|
set{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
throw new VsaException(VsaError.AppDomainCannotBeSet);
|
|
}
|
|
}
|
|
|
|
public System.Security.Policy.Evidence Evidence{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.executionEvidence;
|
|
}
|
|
set{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotRunning |
|
|
Pre.EngineInitialised);
|
|
this.executionEvidence = value;
|
|
}
|
|
}
|
|
|
|
// IVsaEngine.ApplicationBase is not supported by managed engines
|
|
public string ApplicationBase{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
throw new NotSupportedException();
|
|
}
|
|
set{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
throw new VsaException(VsaError.ApplicationBaseCannotBeSet);
|
|
}
|
|
}
|
|
|
|
public System.Reflection.Assembly Assembly{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineRunning);
|
|
return this.loadedAssembly;
|
|
}
|
|
}
|
|
|
|
public bool GenerateDebugInfo{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.genDebugInfo;
|
|
}
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotRunning |
|
|
Pre.EngineInitialised |
|
|
Pre.SupportForDebug);
|
|
if (this.genDebugInfo != value){
|
|
this.genDebugInfo = value;
|
|
this.isEngineDirty = true;
|
|
this.isEngineCompiled = false;
|
|
}
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsCompiled{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.isEngineCompiled;
|
|
}
|
|
}
|
|
|
|
public bool IsDirty{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.isEngineDirty;
|
|
}
|
|
// IsDirty setter is used by VsaItems to notify the engine when new items have been
|
|
// manipulated or added to/removed from the engine. It is not part of an interface.
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
this.isEngineDirty = value;
|
|
if (this.isEngineDirty)
|
|
this.isEngineCompiled = false;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsRunning{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.isEngineRunning;
|
|
}
|
|
}
|
|
|
|
public IVsaItems Items{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.vsaItems;
|
|
}
|
|
}
|
|
|
|
public string Language{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.scriptLanguage;
|
|
}
|
|
}
|
|
|
|
public int LCID{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.errorLocale;
|
|
}
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised | Pre.EngineNotRunning);
|
|
try{
|
|
CultureInfo errorCultureInfo = new CultureInfo(value);
|
|
}catch(ArgumentException){
|
|
throw Error(VsaError.LCIDNotSupported);
|
|
}
|
|
this.errorLocale = value;
|
|
this.isEngineDirty = true;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Name{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.engineName;
|
|
}
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineNotRunning | Pre.EngineInitialised);
|
|
// if the new name is the same as the old one, do nothing
|
|
if (this.engineName == value) return;
|
|
// put the name into a static table so that we can detect duplicates
|
|
lock(nameTable){
|
|
if (nameTable[value] != null)
|
|
throw Error(VsaError.EngineNameInUse);
|
|
nameTable[value] = new Object();
|
|
if (this.engineName != null && this.engineName.Length > 0)
|
|
nameTable[this.engineName] = null;
|
|
}
|
|
this.engineName = value;
|
|
this.isEngineDirty = true;
|
|
this.isEngineCompiled = false;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string RootMoniker{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
return this.engineMoniker;
|
|
}
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.RootMonikerNotSet);
|
|
this.ValidateRootMoniker(value);
|
|
this.engineMoniker = value;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string RootNamespace{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.rootNamespace;
|
|
}
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineNotRunning | Pre.EngineInitialised);
|
|
if (!IsValidNamespaceName(value))
|
|
throw Error(VsaError.RootNamespaceInvalid);
|
|
this.rootNamespace = value;
|
|
this.isEngineDirty = true;
|
|
this.isEngineCompiled = false;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public IVsaSite Site{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.RootMonikerSet);
|
|
return this.engineSite;
|
|
}
|
|
set{
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.RootMonikerSet | Pre.SiteNotSet);
|
|
if (value == null)
|
|
throw Error(VsaError.SiteInvalid);
|
|
this.engineSite = value;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Version{
|
|
get{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
return this.assemblyVersion;
|
|
}
|
|
}
|
|
|
|
// === Methods ===
|
|
|
|
public virtual void Close(){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed);
|
|
if (this.isEngineRunning)
|
|
this.Reset();
|
|
lock(nameTable){
|
|
if (this.engineName != null && this.engineName.Length > 0)
|
|
nameTable[this.engineName] = null;
|
|
}
|
|
this.DoClose();
|
|
this.isClosed = true;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual bool Compile(){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotRunning |
|
|
Pre.EngineInitialised |
|
|
Pre.RootNamespaceSet);
|
|
// look through items for at least one code item
|
|
bool haveCode = false;
|
|
for (int i = 0, n = this.vsaItems.Count; !haveCode && i < n; ++i){
|
|
IVsaItem item = vsaItems[i];
|
|
haveCode = this.vsaItems[i].ItemType == VsaItemType.Code;
|
|
}
|
|
if (!haveCode)
|
|
throw Error(VsaError.EngineEmpty);
|
|
try{
|
|
this.ResetCompiledState();
|
|
this.isEngineCompiled = DoCompile();
|
|
}catch(VsaException){
|
|
throw;
|
|
}catch(Exception e){
|
|
throw new VsaException(VsaError.InternalCompilerError, e.ToString(), e);
|
|
}
|
|
if (this.isEngineCompiled){
|
|
this.haveCompiledState = true;
|
|
this.failedCompilation = false;
|
|
this.compiledRootNamespace = this.rootNamespace;
|
|
}
|
|
return this.isEngineCompiled;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual object GetOption(string name){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineInitialised);
|
|
object option = GetCustomOption(name);
|
|
return option;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void InitNew(){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotInitialised |
|
|
Pre.RootMonikerSet |
|
|
Pre.SiteSet);
|
|
this.isEngineInitialized = true;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
// Load the local compiled state into the AppDomain and return the loaded assembly
|
|
protected virtual Assembly LoadCompiledState(){
|
|
Debug.Assert(this.haveCompiledState);
|
|
byte[] pe;
|
|
byte[] pdb;
|
|
this.DoSaveCompiledState(out pe, out pdb);
|
|
return Assembly.Load(pe, pdb, this.executionEvidence);
|
|
}
|
|
|
|
public virtual void LoadSourceState(IVsaPersistSite site){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotInitialised |
|
|
Pre.RootMonikerSet |
|
|
Pre.SiteSet);
|
|
this.isEngineInitialized = true;
|
|
try{
|
|
this.DoLoadSourceState(site);
|
|
}catch{
|
|
this.isEngineInitialized = false;
|
|
throw;
|
|
}
|
|
this.isEngineDirty = false;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void Reset(){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineRunning);
|
|
try{
|
|
this.startupInstance.Shutdown();
|
|
}catch(Exception e){
|
|
throw new VsaException(VsaError.EngineCannotReset, e.ToString(), e);
|
|
}
|
|
this.isEngineRunning = false;
|
|
this.loadedAssembly = null;
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void RevokeCache(){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineNotRunning | Pre.RootMonikerSet);
|
|
try{
|
|
System.AppDomain.CurrentDomain.SetData(this.engineMoniker, null);
|
|
}catch(Exception e){
|
|
throw new VsaException(VsaError.RevokeFailed, e.ToString(), e);
|
|
}
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void Run(){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotRunning |
|
|
Pre.RootMonikerSet |
|
|
Pre.SiteSet |
|
|
Pre.RootNamespaceSet);
|
|
// managed engines cannot support the AppDomain property so we always use the current one
|
|
AppDomain appDomain = System.AppDomain.CurrentDomain;
|
|
if (this.haveCompiledState){
|
|
// make sure the RootNamespace hasn't been changed since the compilation
|
|
if (this.rootNamespace != this.compiledRootNamespace)
|
|
throw new VsaException(VsaError.RootNamespaceInvalid);
|
|
this.loadedAssembly = this.LoadCompiledState();
|
|
appDomain.SetData(this.engineMoniker, this.loadedAssembly);
|
|
}else{
|
|
// if Compile returned false last time it was called, quit before cache lookup or callback
|
|
if (this.failedCompilation)
|
|
throw new VsaException(VsaError.EngineNotCompiled);
|
|
this.startupClass = null;
|
|
this.loadedAssembly = appDomain.GetData(this.engineMoniker) as Assembly;
|
|
if (this.loadedAssembly == null){
|
|
// assembly is not in the cache, so callback to the site for compiled state
|
|
// prevent multiple callbacks which could result in duplicate assemblies being loaded into the AppDomain
|
|
string mutexName = this.engineMoniker + "/" + appDomain.GetHashCode().ToString(CultureInfo.InvariantCulture);
|
|
Mutex compiledStateCallbackMutex = new Mutex(false, mutexName);
|
|
if (compiledStateCallbackMutex.WaitOne()){
|
|
try{
|
|
// see if there was another callback before we got the lock
|
|
this.loadedAssembly = appDomain.GetData(this.engineMoniker) as Assembly;
|
|
if (this.loadedAssembly == null){
|
|
byte[] pe;
|
|
byte[] pdb;
|
|
this.engineSite.GetCompiledState(out pe, out pdb);
|
|
if (pe == null)
|
|
throw new VsaException(VsaError.GetCompiledStateFailed);
|
|
this.loadedAssembly = Assembly.Load(pe, pdb, this.executionEvidence);
|
|
appDomain.SetData(this.engineMoniker, this.loadedAssembly);
|
|
}
|
|
}finally{
|
|
compiledStateCallbackMutex.ReleaseMutex();
|
|
compiledStateCallbackMutex.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// update the _Startup class Type object (if needed)
|
|
try{
|
|
if (this.startupClass == null)
|
|
this.startupClass = this.loadedAssembly.GetType(this.rootNamespace + "._Startup", true);
|
|
}catch(Exception e){
|
|
// the _Startup class was not in the RootNamespace
|
|
throw new VsaException(VsaError.BadAssembly, e.ToString(), e);
|
|
}
|
|
// set the engine to running, callback for global and event instances, then run global code
|
|
try{
|
|
this.startupInstance = (BaseVsaStartup)Activator.CreateInstance(this.startupClass);
|
|
this.isEngineRunning = true;
|
|
this.startupInstance.SetSite(this.engineSite);
|
|
this.startupInstance.Startup();
|
|
}catch(Exception e){
|
|
throw new VsaException(VsaError.UnknownError, e.ToString(), e);
|
|
}
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void SetOption(string name, object value){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotRunning |
|
|
Pre.EngineInitialised);
|
|
this.SetCustomOption(name, value);
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void SaveCompiledState(out byte[] pe, out byte[] debugInfo){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed |
|
|
Pre.EngineNotRunning |
|
|
Pre.EngineCompiled |
|
|
Pre.EngineInitialised);
|
|
this.DoSaveCompiledState(out pe, out debugInfo);
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
public virtual void SaveSourceState(IVsaPersistSite site){
|
|
this.TryObtainLock();
|
|
try{
|
|
Preconditions(Pre.EngineNotClosed | Pre.EngineNotRunning | Pre.EngineInitialised);
|
|
if (site == null)
|
|
throw Error(VsaError.SiteInvalid);
|
|
try{
|
|
DoSaveSourceState(site);
|
|
}catch(Exception e){
|
|
throw new VsaException(VsaError.SaveElementFailed, e.ToString(), e);
|
|
}
|
|
}finally{
|
|
this.ReleaseLock();
|
|
}
|
|
}
|
|
|
|
protected virtual void ValidateRootMoniker(string rootMoniker){
|
|
// Refer to RFC 2396 for details: ftp://nis.nsf.net/internet/documents/rfc/rfc2396.txt
|
|
if (rootMoniker == null)
|
|
throw new VsaException(VsaError.RootMonikerInvalid);
|
|
Uri moniker = null;
|
|
try{
|
|
moniker = new Uri(rootMoniker);
|
|
}catch(UriFormatException){
|
|
throw new VsaException(VsaError.RootMonikerInvalid);
|
|
}
|
|
// The overall format is okay, so now we validate the protocol
|
|
string protocol = moniker.Scheme;
|
|
if (protocol.Length == 0)
|
|
throw new VsaException(VsaError.RootMonikerProtocolInvalid);
|
|
string[] knownSchemes = {
|
|
"file",
|
|
"ftp",
|
|
"gopher",
|
|
"http",
|
|
"https",
|
|
"javascript",
|
|
"mailto",
|
|
"microsoft",
|
|
"news",
|
|
"res",
|
|
"smtp",
|
|
"socks",
|
|
"vbscript",
|
|
"xlang",
|
|
"xml",
|
|
"xpath",
|
|
"xsd",
|
|
"xsl",
|
|
};
|
|
foreach (string scheme in knownSchemes){
|
|
// Scheme names are case-insensitive (RFC 2396 section 3.2)
|
|
if (String.Compare(scheme, protocol, true, CultureInfo.InvariantCulture) == 0)
|
|
throw new VsaException(VsaError.RootMonikerProtocolInvalid);
|
|
}
|
|
}
|
|
|
|
// === Abstract Methods ===
|
|
|
|
// Implement this method to clean up data structures
|
|
protected abstract void DoClose();
|
|
|
|
// Return flag for compilation success.
|
|
// Errors should be reported to the engine site via OnCompilerError.
|
|
// Internal compiler errors should throw exceptions.
|
|
protected abstract bool DoCompile();
|
|
|
|
// Implement this method to load source state from site
|
|
protected abstract void DoLoadSourceState(IVsaPersistSite site);
|
|
|
|
protected abstract void DoSaveCompiledState(out byte[] pe, out byte[] debugInfo);
|
|
|
|
// Save source items using IVsaPersistSite.SaveElement
|
|
protected abstract void DoSaveSourceState(IVsaPersistSite site);
|
|
|
|
// Enable custom options
|
|
protected abstract object GetCustomOption(string name);
|
|
|
|
// Verify that the name is a valid namespace name
|
|
protected abstract bool IsValidNamespaceName(string name);
|
|
|
|
// Verify that the identifier is valid for the language
|
|
public abstract bool IsValidIdentifier(string ident);
|
|
|
|
// Discard the existing compiled state so we can compile a new source state
|
|
// Postconditions:
|
|
// this.haveCompiledState == false
|
|
// this.failedCompilation == true
|
|
// this.compiledRootNamespace == null
|
|
// this.startupClass == null
|
|
protected abstract void ResetCompiledState();
|
|
|
|
// Enable custom options
|
|
protected abstract void SetCustomOption(string name, object value);
|
|
}
|
|
|
|
// BaseVsaSite is a default implementation of IVsaSite. Note that GetCompiledState
|
|
// defers to two properties so that languages that don't support declaring out params
|
|
// can provide an implementation without having to change languages.
|
|
|
|
public class BaseVsaSite: IVsaSite{
|
|
public virtual byte[] Assembly{
|
|
get{
|
|
throw new VsaException(VsaError.GetCompiledStateFailed);
|
|
}
|
|
}
|
|
|
|
public virtual byte[] DebugInfo{
|
|
get{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public virtual void GetCompiledState(out byte[] pe, out byte[] debugInfo){
|
|
pe = this.Assembly;
|
|
debugInfo = this.DebugInfo;
|
|
}
|
|
|
|
public virtual object GetEventSourceInstance( string itemName, string eventSourceName){
|
|
throw new VsaException(VsaError.CallbackUnexpected);
|
|
}
|
|
|
|
public virtual object GetGlobalInstance(string name){
|
|
throw new VsaException(VsaError.CallbackUnexpected);
|
|
}
|
|
|
|
public virtual void Notify(string notify, object optional){
|
|
throw new VsaException(VsaError.CallbackUnexpected);
|
|
}
|
|
|
|
public virtual bool OnCompilerError(IVsaError error){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public abstract class BaseVsaStartup{
|
|
protected IVsaSite site;
|
|
public void SetSite(IVsaSite site){
|
|
this.site = site;
|
|
}
|
|
public abstract void Startup();
|
|
public abstract void Shutdown();
|
|
}
|
|
}
|