465 lines
14 KiB
C#
465 lines
14 KiB
C#
using System;
|
|
using System.CodeDom;
|
|
using System.Collections.Generic;
|
|
using System.Dynamic;
|
|
using System.IO;
|
|
using System.Linq.Expressions;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using AspClassic.Scripting.Utils;
|
|
|
|
namespace AspClassic.Scripting.Runtime;
|
|
|
|
public abstract class LanguageContext
|
|
{
|
|
private sealed class DefaultUnaryOperationBinder : UnaryOperationBinder
|
|
{
|
|
internal DefaultUnaryOperationBinder(ExpressionType operation)
|
|
: base(operation)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackUnaryOperation(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, target, new DynamicMetaObject[1] { target }, errorSuggestion);
|
|
}
|
|
}
|
|
|
|
private sealed class DefaultBinaryOperationBinder : BinaryOperationBinder
|
|
{
|
|
internal DefaultBinaryOperationBinder(ExpressionType operation)
|
|
: base(operation)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, target, new DynamicMetaObject[2] { target, arg }, errorSuggestion);
|
|
}
|
|
}
|
|
|
|
private class DefaultConvertAction : ConvertBinder
|
|
{
|
|
internal DefaultConvertAction(Type type, bool @explicit)
|
|
: base(type, @explicit)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackConvert(DynamicMetaObject self, DynamicMetaObject errorSuggestion)
|
|
{
|
|
if (base.Type.IsAssignableFrom(self.LimitType))
|
|
{
|
|
return new DynamicMetaObject(Expression.Convert(self.Expression, base.Type), BindingRestrictions.GetTypeRestriction(self.Expression, self.LimitType));
|
|
}
|
|
if (errorSuggestion != null)
|
|
{
|
|
return errorSuggestion;
|
|
}
|
|
return new DynamicMetaObject(Expression.Throw(Expression.Constant(new ArgumentTypeException($"Expected {base.Type.FullName}, got {self.LimitType.FullName}")), ReturnType), BindingRestrictions.GetTypeRestriction(self.Expression, self.LimitType));
|
|
}
|
|
}
|
|
|
|
private class DefaultGetMemberAction : GetMemberBinder
|
|
{
|
|
internal DefaultGetMemberAction(string name, bool ignoreCase)
|
|
: base(name, ignoreCase)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject self, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return errorSuggestion ?? new DynamicMetaObject(Expression.Throw(Expression.New(typeof(MissingMemberException).GetConstructor(new Type[1] { typeof(string) }), Expression.Constant($"unknown member: {base.Name}")), typeof(object)), (self.Value == null) ? BindingRestrictions.GetExpressionRestriction(Expression.Equal(self.Expression, Expression.Constant(null))) : BindingRestrictions.GetTypeRestriction(self.Expression, self.Value.GetType()));
|
|
}
|
|
}
|
|
|
|
private class DefaultSetMemberAction : SetMemberBinder
|
|
{
|
|
internal DefaultSetMemberAction(string name, bool ignoreCase)
|
|
: base(name, ignoreCase)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackSetMember(DynamicMetaObject self, DynamicMetaObject value, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, self, new DynamicMetaObject[1] { value }, errorSuggestion);
|
|
}
|
|
}
|
|
|
|
private class DefaultDeleteMemberAction : DeleteMemberBinder
|
|
{
|
|
internal DefaultDeleteMemberAction(string name, bool ignoreCase)
|
|
: base(name, ignoreCase)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackDeleteMember(DynamicMetaObject self, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, self, DynamicMetaObject.EmptyMetaObjects, errorSuggestion);
|
|
}
|
|
}
|
|
|
|
private class DefaultCallAction : InvokeMemberBinder
|
|
{
|
|
private LanguageContext _context;
|
|
|
|
internal DefaultCallAction(LanguageContext context, string name, bool ignoreCase, CallInfo callInfo)
|
|
: base(name, ignoreCase, callInfo)
|
|
{
|
|
_context = context;
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, target, args.AddFirst(target), errorSuggestion);
|
|
}
|
|
|
|
private static Expression[] GetArgs(DynamicMetaObject target, DynamicMetaObject[] args)
|
|
{
|
|
Expression[] array = new Expression[args.Length + 1];
|
|
array[0] = target.Expression;
|
|
for (int i = 0; i < args.Length; i++)
|
|
{
|
|
array[1 + i] = args[i].Expression;
|
|
}
|
|
return array;
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return new DynamicMetaObject(Expression.Dynamic(_context.CreateInvokeBinder(base.CallInfo), typeof(object), GetArgs(target, args)), target.Restrictions.Merge(BindingRestrictions.Combine(args)));
|
|
}
|
|
}
|
|
|
|
private class DefaultInvokeAction : InvokeBinder
|
|
{
|
|
internal DefaultInvokeAction(CallInfo callInfo)
|
|
: base(callInfo)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, target, args, errorSuggestion);
|
|
}
|
|
}
|
|
|
|
private class DefaultCreateAction : CreateInstanceBinder
|
|
{
|
|
internal DefaultCreateAction(CallInfo callInfo)
|
|
: base(callInfo)
|
|
{
|
|
}
|
|
|
|
public override DynamicMetaObject FallbackCreateInstance(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return ErrorMetaObject(ReturnType, target, args, errorSuggestion);
|
|
}
|
|
}
|
|
|
|
private readonly ScriptDomainManager _domainManager;
|
|
|
|
private readonly ContextId _id;
|
|
|
|
private DynamicOperations _operations;
|
|
|
|
public ContextId ContextId => _id;
|
|
|
|
public ScriptDomainManager DomainManager => _domainManager;
|
|
|
|
public virtual bool CanCreateSourceCode => true;
|
|
|
|
public virtual Version LanguageVersion => new Version(0, 0);
|
|
|
|
public virtual Guid LanguageGuid => Guid.Empty;
|
|
|
|
public virtual Guid VendorGuid => Guid.Empty;
|
|
|
|
public virtual LanguageOptions Options => new LanguageOptions();
|
|
|
|
public DynamicOperations Operations
|
|
{
|
|
get
|
|
{
|
|
if (_operations == null)
|
|
{
|
|
Interlocked.CompareExchange(ref _operations, new DynamicOperations(this), null);
|
|
}
|
|
return _operations;
|
|
}
|
|
}
|
|
|
|
protected LanguageContext(ScriptDomainManager domainManager)
|
|
{
|
|
ContractUtils.RequiresNotNull(domainManager, "domainManager");
|
|
_domainManager = domainManager;
|
|
_id = domainManager.GenerateContextId();
|
|
}
|
|
|
|
public virtual Scope GetScope(string path)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public ScopeExtension EnsureScopeExtension(Scope scope)
|
|
{
|
|
ContractUtils.RequiresNotNull(scope, "scope");
|
|
ScopeExtension extension = scope.GetExtension(ContextId);
|
|
if (extension == null)
|
|
{
|
|
extension = CreateScopeExtension(scope);
|
|
if (extension == null)
|
|
{
|
|
throw Error.MustReturnScopeExtension();
|
|
}
|
|
return scope.SetExtension(ContextId, extension);
|
|
}
|
|
return extension;
|
|
}
|
|
|
|
public virtual ScopeExtension CreateScopeExtension(Scope scope)
|
|
{
|
|
return new ScopeExtension(scope);
|
|
}
|
|
|
|
public virtual void ScopeSetVariable(Scope scope, string name, object value)
|
|
{
|
|
Operations.SetMember(scope, name, value);
|
|
}
|
|
|
|
public virtual bool ScopeTryGetVariable(Scope scope, string name, out dynamic value)
|
|
{
|
|
return Operations.TryGetMember(scope, name, out value);
|
|
}
|
|
|
|
public virtual T ScopeGetVariable<T>(Scope scope, string name)
|
|
{
|
|
return Operations.GetMember<T>(scope, name);
|
|
}
|
|
|
|
public virtual dynamic ScopeGetVariable(Scope scope, string name)
|
|
{
|
|
return Operations.GetMember(scope, name);
|
|
}
|
|
|
|
public virtual SourceCodeReader GetSourceReader(Stream stream, Encoding defaultEncoding, string path)
|
|
{
|
|
ContractUtils.RequiresNotNull(stream, "stream");
|
|
ContractUtils.RequiresNotNull(defaultEncoding, "defaultEncoding");
|
|
ContractUtils.Requires(stream.CanRead && stream.CanSeek, "stream", "The stream must support reading and seeking");
|
|
StreamReader streamReader = new StreamReader(stream, defaultEncoding, detectEncodingFromByteOrderMarks: true);
|
|
streamReader.Peek();
|
|
return new SourceCodeReader(streamReader, streamReader.CurrentEncoding);
|
|
}
|
|
|
|
public virtual CompilerOptions GetCompilerOptions()
|
|
{
|
|
return new CompilerOptions();
|
|
}
|
|
|
|
public virtual CompilerOptions GetCompilerOptions(Scope scope)
|
|
{
|
|
return GetCompilerOptions();
|
|
}
|
|
|
|
public abstract ScriptCode CompileSourceCode(SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink);
|
|
|
|
public virtual ScriptCode LoadCompiledCode(Delegate method, string path, string customData)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public virtual int ExecuteProgram(SourceUnit program)
|
|
{
|
|
ContractUtils.RequiresNotNull(program, "program");
|
|
object obj = program.Execute();
|
|
if (obj == null)
|
|
{
|
|
return 0;
|
|
}
|
|
CallSite<Func<CallSite, object, int>> callSite = CallSite<Func<CallSite, object, int>>.Create(CreateConvertBinder(typeof(int), true));
|
|
return callSite.Target(callSite, obj);
|
|
}
|
|
|
|
public virtual void SetSearchPaths(ICollection<string> paths)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public virtual ICollection<string> GetSearchPaths()
|
|
{
|
|
return Options.SearchPaths;
|
|
}
|
|
|
|
public virtual SourceUnit GenerateSourceCode(CodeObject codeDom, string path, SourceCodeKind kind)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public virtual TService GetService<TService>(params object[] args) where TService : class
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public virtual void Shutdown()
|
|
{
|
|
}
|
|
|
|
public virtual string FormatException(Exception exception)
|
|
{
|
|
return exception.ToString();
|
|
}
|
|
|
|
public SourceUnit CreateSnippet(string code, SourceCodeKind kind)
|
|
{
|
|
return CreateSnippet(code, null, kind);
|
|
}
|
|
|
|
public SourceUnit CreateSnippet(string code, string id, SourceCodeKind kind)
|
|
{
|
|
ContractUtils.RequiresNotNull(code, "code");
|
|
return CreateSourceUnit(new SourceStringContentProvider(code), id, kind);
|
|
}
|
|
|
|
public SourceUnit CreateFileUnit(string path)
|
|
{
|
|
return CreateFileUnit(path, StringUtils.DefaultEncoding);
|
|
}
|
|
|
|
public SourceUnit CreateFileUnit(string path, Encoding encoding)
|
|
{
|
|
return CreateFileUnit(path, encoding, SourceCodeKind.File);
|
|
}
|
|
|
|
public SourceUnit CreateFileUnit(string path, Encoding encoding, SourceCodeKind kind)
|
|
{
|
|
ContractUtils.RequiresNotNull(path, "path");
|
|
ContractUtils.RequiresNotNull(encoding, "encoding");
|
|
TextContentProvider contentProvider = new LanguageBoundTextContentProvider(this, new FileStreamContentProvider(DomainManager.Platform, path), encoding, path);
|
|
return CreateSourceUnit(contentProvider, path, kind);
|
|
}
|
|
|
|
public SourceUnit CreateFileUnit(string path, string content)
|
|
{
|
|
ContractUtils.RequiresNotNull(path, "path");
|
|
ContractUtils.RequiresNotNull(content, "content");
|
|
TextContentProvider contentProvider = new SourceStringContentProvider(content);
|
|
return CreateSourceUnit(contentProvider, path, SourceCodeKind.File);
|
|
}
|
|
|
|
public SourceUnit CreateSourceUnit(StreamContentProvider contentProvider, string path, Encoding encoding, SourceCodeKind kind)
|
|
{
|
|
ContractUtils.RequiresNotNull(contentProvider, "contentProvider");
|
|
ContractUtils.RequiresNotNull(encoding, "encoding");
|
|
ContractUtils.Requires(kind.IsValid(), "kind");
|
|
ContractUtils.Requires(CanCreateSourceCode);
|
|
return new SourceUnit(this, new LanguageBoundTextContentProvider(this, contentProvider, encoding, path), path, kind);
|
|
}
|
|
|
|
public SourceUnit CreateSourceUnit(TextContentProvider contentProvider, string path, SourceCodeKind kind)
|
|
{
|
|
ContractUtils.RequiresNotNull(contentProvider, "contentProvider");
|
|
ContractUtils.Requires(kind.IsValid(), "kind");
|
|
ContractUtils.Requires(CanCreateSourceCode);
|
|
return new SourceUnit(this, contentProvider, path, kind);
|
|
}
|
|
|
|
public virtual ErrorSink GetCompilerErrorSink()
|
|
{
|
|
return ErrorSink.Null;
|
|
}
|
|
|
|
internal static DynamicMetaObject ErrorMetaObject(Type resultType, DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
|
|
{
|
|
return errorSuggestion ?? new DynamicMetaObject(Expression.Throw(Expression.New(typeof(NotImplementedException)), resultType), target.Restrictions.Merge(BindingRestrictions.Combine(args)));
|
|
}
|
|
|
|
public virtual UnaryOperationBinder CreateUnaryOperationBinder(ExpressionType operation)
|
|
{
|
|
return new DefaultUnaryOperationBinder(operation);
|
|
}
|
|
|
|
public virtual BinaryOperationBinder CreateBinaryOperationBinder(ExpressionType operation)
|
|
{
|
|
return new DefaultBinaryOperationBinder(operation);
|
|
}
|
|
|
|
public virtual ConvertBinder CreateConvertBinder(Type toType, bool? explicitCast)
|
|
{
|
|
return new DefaultConvertAction(toType, explicitCast ?? false);
|
|
}
|
|
|
|
public virtual GetMemberBinder CreateGetMemberBinder(string name, bool ignoreCase)
|
|
{
|
|
return new DefaultGetMemberAction(name, ignoreCase);
|
|
}
|
|
|
|
public virtual SetMemberBinder CreateSetMemberBinder(string name, bool ignoreCase)
|
|
{
|
|
return new DefaultSetMemberAction(name, ignoreCase);
|
|
}
|
|
|
|
public virtual DeleteMemberBinder CreateDeleteMemberBinder(string name, bool ignoreCase)
|
|
{
|
|
return new DefaultDeleteMemberAction(name, ignoreCase);
|
|
}
|
|
|
|
public virtual InvokeMemberBinder CreateCallBinder(string name, bool ignoreCase, CallInfo callInfo)
|
|
{
|
|
return new DefaultCallAction(this, name, ignoreCase, callInfo);
|
|
}
|
|
|
|
public virtual InvokeBinder CreateInvokeBinder(CallInfo callInfo)
|
|
{
|
|
return new DefaultInvokeAction(callInfo);
|
|
}
|
|
|
|
public virtual CreateInstanceBinder CreateCreateBinder(CallInfo callInfo)
|
|
{
|
|
return new DefaultCreateAction(callInfo);
|
|
}
|
|
|
|
public virtual IList<string> GetMemberNames(object obj)
|
|
{
|
|
if (obj is IDynamicMetaObjectProvider dynamicMetaObjectProvider)
|
|
{
|
|
DynamicMetaObject metaObject = dynamicMetaObjectProvider.GetMetaObject(Expression.Parameter(typeof(object), null));
|
|
return metaObject.GetDynamicMemberNames().ToReadOnly();
|
|
}
|
|
return AspClassic.Scripting.Utils.EmptyArray<string>.Instance;
|
|
}
|
|
|
|
public virtual string GetDocumentation(object obj)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
public virtual IList<string> GetCallSignatures(object obj)
|
|
{
|
|
return new string[0];
|
|
}
|
|
|
|
public virtual bool IsCallable(object obj)
|
|
{
|
|
if (obj == null)
|
|
{
|
|
return false;
|
|
}
|
|
return typeof(Delegate).IsAssignableFrom(obj.GetType());
|
|
}
|
|
|
|
public virtual string FormatObject(DynamicOperations operations, object obj)
|
|
{
|
|
if (obj != null)
|
|
{
|
|
return obj.ToString();
|
|
}
|
|
return "null";
|
|
}
|
|
|
|
public virtual void GetExceptionMessage(Exception exception, out string message, out string errorTypeName)
|
|
{
|
|
message = exception.Message;
|
|
errorTypeName = exception.GetType().Name;
|
|
}
|
|
}
|