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(Scope scope, string name) { return Operations.GetMember(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> callSite = CallSite>.Create(CreateConvertBinder(typeof(int), true)); return callSite.Target(callSite, obj); } public virtual void SetSearchPaths(ICollection paths) { throw new NotSupportedException(); } public virtual ICollection GetSearchPaths() { return Options.SearchPaths; } public virtual SourceUnit GenerateSourceCode(CodeObject codeDom, string path, SourceCodeKind kind) { throw new NotImplementedException(); } public virtual TService GetService(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 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.Instance; } public virtual string GetDocumentation(object obj) { return string.Empty; } public virtual IList 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; } }