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 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 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(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(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(TryDeleteMember)), base.Expression, Expression.Constant(SymbolTable.StringToId(binder.Name))), Expression.Default(binder.ReturnType), binder.FallbackDeleteMember(this).Expression), GetRestrictions()); } public override IEnumerable 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); } }