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

198 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
using AspClassic.Scripting.Utils;
namespace AspClassic.Scripting.Runtime;
public sealed class Scope : IDynamicMetaObjectProvider
{
internal sealed class MetaScope : DynamicMetaObject
{
private DynamicMetaObject StorageMetaObject => DynamicMetaObject.Create(Value._storage, StorageExpression);
private MemberExpression StorageExpression => Expression.Property(Expression.Convert(base.Expression, typeof(Scope)), typeof(Scope).GetProperty("Storage"));
public new Scope Value => (Scope)base.Value;
public MetaScope(Expression parameter, Scope scope)
: base(parameter, BindingRestrictions.Empty, scope)
{
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
return Restrict(binder.Bind(StorageMetaObject, DynamicMetaObject.EmptyMetaObjects));
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return Restrict(binder.Bind(StorageMetaObject, args));
}
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
return Restrict(binder.Bind(StorageMetaObject, new DynamicMetaObject[1] { value }));
}
public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
{
return Restrict(binder.Bind(StorageMetaObject, DynamicMetaObject.EmptyMetaObjects));
}
private DynamicMetaObject Restrict(DynamicMetaObject result)
{
if (base.Expression.Type == typeof(Scope))
{
return result;
}
return new DynamicMetaObject(result.Expression, BindingRestrictions.GetTypeRestriction(base.Expression, typeof(Scope)).Merge(result.Restrictions));
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return StorageMetaObject.GetDynamicMemberNames();
}
}
internal sealed class AttributesAdapter : IDynamicMetaObjectProvider
{
internal sealed class Meta : DynamicMetaObject
{
public new AttributesAdapter Value => (AttributesAdapter)base.Value;
public Meta(Expression parameter, AttributesAdapter storage)
: base(parameter, BindingRestrictions.Empty, storage)
{
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
return DynamicTryGetMember(binder.Name, binder.FallbackGetMember(this).Expression, (Expression tmp) => tmp);
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return DynamicTryGetMember(binder.Name, binder.FallbackInvokeMember(this, args).Expression, (Expression tmp) => binder.FallbackInvoke(new DynamicMetaObject(tmp, BindingRestrictions.Empty), args, null).Expression);
}
private DynamicMetaObject DynamicTryGetMember(string name, Expression fallback, Func<Expression, Expression> resultOp)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(object));
return new DynamicMetaObject(Expression.Block(new ParameterExpression[1] { parameterExpression }, Expression.Condition(Expression.NotEqual(Expression.Assign(parameterExpression, Expression.Invoke(Expression.Constant(new Func<object, SymbolId, object>(TryGetMember)), base.Expression, Expression.Constant(SymbolTable.StringToId(name)))), Expression.Constant(_getFailed)), ExpressionUtils.Convert(resultOp(parameterExpression), typeof(object)), ExpressionUtils.Convert(fallback, typeof(object)))), GetRestrictions());
}
private BindingRestrictions GetRestrictions()
{
return BindingRestrictions.GetTypeRestriction(base.Expression, typeof(AttributesAdapter));
}
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
return new DynamicMetaObject(Expression.Block(Expression.Invoke(Expression.Constant(new Action<object, SymbolId, object>(TrySetMember)), base.Expression, Expression.Constant(SymbolTable.StringToId(binder.Name)), Expression.Convert(value.Expression, typeof(object))), value.Expression), GetRestrictions());
}
public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
{
return new DynamicMetaObject(Expression.Condition(Expression.Invoke(Expression.Constant(new Func<object, SymbolId, bool>(TryDeleteMember)), base.Expression, Expression.Constant(SymbolTable.StringToId(binder.Name))), Expression.Default(binder.ReturnType), binder.FallbackDeleteMember(this).Expression), GetRestrictions());
}
public override IEnumerable<string> GetDynamicMemberNames()
{
foreach (object o in Value._data.Keys)
{
if (o is string)
{
yield return (string)o;
}
}
}
}
private readonly IAttributesCollection _data;
public AttributesAdapter(IAttributesCollection data)
{
_data = data;
}
private static object TryGetMember(object adapter, SymbolId name)
{
if (((AttributesAdapter)adapter)._data.TryGetValue(name, out var value))
{
return value;
}
return _getFailed;
}
private static void TrySetMember(object adapter, SymbolId name, object value)
{
((AttributesAdapter)adapter)._data[name] = value;
}
private static bool TryDeleteMember(object adapter, SymbolId name)
{
return ((AttributesAdapter)adapter)._data.Remove(name);
}
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
{
return new Meta(parameter, this);
}
}
private ScopeExtension[] _extensions;
private readonly IDynamicMetaObjectProvider _storage;
private static readonly object _getFailed = new object();
public dynamic Storage => _storage;
public Scope()
{
_extensions = ScopeExtension.EmptyArray;
_storage = new ScopeStorage();
}
[Obsolete("IAttributesCollection is obsolete, use Scope(IDynamicMetaObjectProvider) overload instead")]
public Scope(IAttributesCollection dictionary)
{
_extensions = ScopeExtension.EmptyArray;
_storage = new AttributesAdapter(dictionary);
}
public Scope(IDynamicMetaObjectProvider storage)
{
_extensions = ScopeExtension.EmptyArray;
_storage = storage;
}
public ScopeExtension GetExtension(ContextId languageContextId)
{
if (languageContextId.Id >= _extensions.Length)
{
return null;
}
return _extensions[languageContextId.Id];
}
public ScopeExtension SetExtension(ContextId languageContextId, ScopeExtension extension)
{
ContractUtils.RequiresNotNull(extension, "extension");
lock (_extensions)
{
if (languageContextId.Id >= _extensions.Length)
{
Array.Resize(ref _extensions, languageContextId.Id + 1);
}
return _extensions[languageContextId.Id] ?? (_extensions[languageContextId.Id] = extension);
}
}
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
{
return new MetaScope(parameter, this);
}
}