309 lines
8.3 KiB
C#
309 lines
8.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Dynamic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Security.Permissions;
|
|
using System.Threading;
|
|
using AspClassic.Scripting.Runtime;
|
|
using AspClassic.Scripting.Utils;
|
|
|
|
namespace AspClassic.Scripting.Hosting;
|
|
|
|
public sealed class ScriptRuntime : MarshalByRefObject
|
|
{
|
|
private readonly Dictionary<LanguageContext, ScriptEngine> _engines;
|
|
|
|
private readonly ScriptDomainManager _manager;
|
|
|
|
private readonly InvariantContext _invariantContext;
|
|
|
|
private readonly ScriptIO _io;
|
|
|
|
private readonly ScriptHost _host;
|
|
|
|
private readonly ScriptRuntimeSetup _setup;
|
|
|
|
private readonly object _lock = new object();
|
|
|
|
private ScriptScope _globals;
|
|
|
|
private Scope _scopeGlobals;
|
|
|
|
private ScriptEngine _invariantEngine;
|
|
|
|
internal ScriptDomainManager Manager => _manager;
|
|
|
|
public ScriptHost Host => _host;
|
|
|
|
public ScriptIO IO => _io;
|
|
|
|
public ScriptRuntimeSetup Setup => _setup;
|
|
|
|
public ScriptScope Globals
|
|
{
|
|
get
|
|
{
|
|
Scope globals = _manager.Globals;
|
|
if (_scopeGlobals == globals)
|
|
{
|
|
return _globals;
|
|
}
|
|
lock (_lock)
|
|
{
|
|
if (_scopeGlobals != globals)
|
|
{
|
|
_globals = new ScriptScope(InvariantEngine, globals);
|
|
_scopeGlobals = globals;
|
|
}
|
|
return _globals;
|
|
}
|
|
}
|
|
set
|
|
{
|
|
ContractUtils.RequiresNotNull(value, "value");
|
|
lock (_lock)
|
|
{
|
|
_globals = value;
|
|
_manager.Globals = value.Scope;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ObjectOperations Operations => InvariantEngine.Operations;
|
|
|
|
internal ScriptEngine InvariantEngine
|
|
{
|
|
get
|
|
{
|
|
if (_invariantEngine == null)
|
|
{
|
|
_invariantEngine = GetEngine(_invariantContext);
|
|
}
|
|
return _invariantEngine;
|
|
}
|
|
}
|
|
|
|
public ScriptRuntime(ScriptRuntimeSetup setup)
|
|
{
|
|
ContractUtils.RequiresNotNull(setup, "setup");
|
|
DlrConfiguration configuration = setup.ToConfiguration();
|
|
_setup = setup;
|
|
try
|
|
{
|
|
_host = (ScriptHost)Activator.CreateInstance(setup.HostType, Utils.CollectionExtensions.ToArray(setup.HostArguments));
|
|
}
|
|
catch (TargetInvocationException ex)
|
|
{
|
|
throw new InvalidImplementationException(Strings.InvalidCtorImplementation(setup.HostType, ex.InnerException.Message), ex.InnerException);
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
throw new InvalidImplementationException(Strings.InvalidCtorImplementation(setup.HostType, ex2.Message), ex2);
|
|
}
|
|
ScriptHostProxy hostingProvider = new ScriptHostProxy(_host);
|
|
_manager = new ScriptDomainManager(hostingProvider, configuration);
|
|
_invariantContext = new InvariantContext(_manager);
|
|
_io = new ScriptIO(_manager.SharedIO);
|
|
_engines = new Dictionary<LanguageContext, ScriptEngine>();
|
|
_globals = new ScriptScope(GetEngineNoLockNoNotification(_invariantContext, out var _), _manager.Globals);
|
|
_host.SetRuntime(this);
|
|
if (!setup.Options.TryGetValue("NoDefaultReferences", out var value) || !Convert.ToBoolean(value))
|
|
{
|
|
LoadAssembly(typeof(string).Assembly);
|
|
LoadAssembly(typeof(Debug).Assembly);
|
|
}
|
|
}
|
|
|
|
public static ScriptRuntime CreateFromConfiguration()
|
|
{
|
|
return new ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration());
|
|
}
|
|
|
|
public static ScriptRuntime CreateRemote(AppDomain domain, ScriptRuntimeSetup setup)
|
|
{
|
|
ContractUtils.RequiresNotNull(domain, "domain");
|
|
return (ScriptRuntime)domain.CreateInstanceAndUnwrap(typeof(ScriptRuntime).Assembly.FullName, typeof(ScriptRuntime).FullName, ignoreCase: false, BindingFlags.Default, null, new object[1] { setup }, null, null);
|
|
}
|
|
|
|
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
|
|
public override object InitializeLifetimeService()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public ScriptEngine GetEngine(string languageName)
|
|
{
|
|
ContractUtils.RequiresNotNull(languageName, "languageName");
|
|
if (!TryGetEngine(languageName, out var engine))
|
|
{
|
|
throw new ArgumentException($"Unknown language name: '{languageName}'");
|
|
}
|
|
return engine;
|
|
}
|
|
|
|
public ScriptEngine GetEngineByTypeName(string assemblyQualifiedTypeName)
|
|
{
|
|
ContractUtils.RequiresNotNull(assemblyQualifiedTypeName, "assemblyQualifiedTypeName");
|
|
return GetEngine(_manager.GetLanguageByTypeName(assemblyQualifiedTypeName));
|
|
}
|
|
|
|
public ScriptEngine GetEngineByFileExtension(string fileExtension)
|
|
{
|
|
ContractUtils.RequiresNotNull(fileExtension, "fileExtension");
|
|
if (!TryGetEngineByFileExtension(fileExtension, out var engine))
|
|
{
|
|
throw new ArgumentException($"Unknown file extension: '{fileExtension}'");
|
|
}
|
|
return engine;
|
|
}
|
|
|
|
public bool TryGetEngine(string languageName, out ScriptEngine engine)
|
|
{
|
|
if (!_manager.TryGetLanguage(languageName, out var language))
|
|
{
|
|
engine = null;
|
|
return false;
|
|
}
|
|
engine = GetEngine(language);
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetEngineByFileExtension(string fileExtension, out ScriptEngine engine)
|
|
{
|
|
if (!_manager.TryGetLanguageByFileExtension(fileExtension, out var language))
|
|
{
|
|
engine = null;
|
|
return false;
|
|
}
|
|
engine = GetEngine(language);
|
|
return true;
|
|
}
|
|
|
|
internal ScriptEngine GetEngine(LanguageContext language)
|
|
{
|
|
ScriptEngine engineNoLockNoNotification;
|
|
bool freshEngineCreated;
|
|
lock (_engines)
|
|
{
|
|
engineNoLockNoNotification = GetEngineNoLockNoNotification(language, out freshEngineCreated);
|
|
}
|
|
if (freshEngineCreated && !object.ReferenceEquals(language, _invariantContext))
|
|
{
|
|
_host.EngineCreated(engineNoLockNoNotification);
|
|
}
|
|
return engineNoLockNoNotification;
|
|
}
|
|
|
|
private ScriptEngine GetEngineNoLockNoNotification(LanguageContext language, out bool freshEngineCreated)
|
|
{
|
|
if (freshEngineCreated = !_engines.TryGetValue(language, out var value))
|
|
{
|
|
value = new ScriptEngine(this, language);
|
|
Thread.MemoryBarrier();
|
|
_engines.Add(language, value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public ScriptScope CreateScope()
|
|
{
|
|
return InvariantEngine.CreateScope();
|
|
}
|
|
|
|
public ScriptScope CreateScope(string languageId)
|
|
{
|
|
return GetEngine(languageId).CreateScope();
|
|
}
|
|
|
|
public ScriptScope CreateScope(IDynamicMetaObjectProvider storage)
|
|
{
|
|
return InvariantEngine.CreateScope(storage);
|
|
}
|
|
|
|
public ScriptScope CreateScope(string languageId, IDynamicMetaObjectProvider storage)
|
|
{
|
|
return GetEngine(languageId).CreateScope(storage);
|
|
}
|
|
|
|
[Obsolete("IAttributesCollection is obsolete, use CreateScope(IDynamicMetaObjectProvider) instead")]
|
|
public ScriptScope CreateScope(IAttributesCollection dictionary)
|
|
{
|
|
return InvariantEngine.CreateScope(dictionary);
|
|
}
|
|
|
|
[Obsolete("IAttributesCollection is obsolete, use CreateScope(string, IDynamicMetaObjectProvider) instead")]
|
|
public ScriptScope CreateScope(string languageId, IAttributesCollection storage)
|
|
{
|
|
return GetEngine(languageId).CreateScope(storage);
|
|
}
|
|
|
|
public ScriptScope ExecuteFile(string path)
|
|
{
|
|
ContractUtils.RequiresNotEmpty(path, "path");
|
|
string extension = Path.GetExtension(path);
|
|
if (!TryGetEngineByFileExtension(extension, out var engine))
|
|
{
|
|
throw new ArgumentException($"File extension '{extension}' is not associated with any language.");
|
|
}
|
|
return engine.ExecuteFile(path);
|
|
}
|
|
|
|
public ScriptScope UseFile(string path)
|
|
{
|
|
ContractUtils.RequiresNotEmpty(path, "path");
|
|
string extension = Path.GetExtension(path);
|
|
if (!TryGetEngineByFileExtension(extension, out var engine))
|
|
{
|
|
throw new ArgumentException($"File extension '{extension}' is not associated with any language.");
|
|
}
|
|
ICollection<string> searchPaths = engine.GetSearchPaths();
|
|
if (searchPaths.Count == 0)
|
|
{
|
|
throw new InvalidOperationException($"No search paths defined for language '{engine.Setup.DisplayName}'");
|
|
}
|
|
foreach (string item in searchPaths)
|
|
{
|
|
string path2 = Path.Combine(item, path);
|
|
ScriptScope scope = engine.GetScope(path2);
|
|
if (scope != null)
|
|
{
|
|
return scope;
|
|
}
|
|
}
|
|
foreach (string item2 in searchPaths)
|
|
{
|
|
string path3 = Path.Combine(item2, path);
|
|
if (_manager.Platform.FileExists(path3))
|
|
{
|
|
return ExecuteFile(path3);
|
|
}
|
|
}
|
|
string arg = Utils.CollectionExtensions.Aggregate(searchPaths ,(string x, string y) => x + ", " + y);
|
|
throw new FileNotFoundException($"File '{path}' not found in language's search path: {arg}");
|
|
}
|
|
|
|
public void LoadAssembly(Assembly assembly)
|
|
{
|
|
_manager.LoadAssembly(assembly);
|
|
}
|
|
|
|
public ObjectOperations CreateOperations()
|
|
{
|
|
return InvariantEngine.CreateOperations();
|
|
}
|
|
|
|
public void Shutdown()
|
|
{
|
|
List<LanguageContext> list;
|
|
lock (_engines)
|
|
{
|
|
list = new List<LanguageContext>(_engines.Keys);
|
|
}
|
|
foreach (LanguageContext item in list)
|
|
{
|
|
item.Shutdown();
|
|
}
|
|
}
|
|
}
|