aspclassic-core/AspClassic.Scripting/Runtime/LanguageContext.cs
Jelle Luteijn 484dbfc9d9 progress
2022-05-15 11:19:49 +02:00

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;
}
}