PageRenderTime 43ms CodeModel.GetById 30ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/System.Xronos/Scripting/MetaObjects/FunctionMetaObject.cs

https://bitbucket.org/stefanrusek/xronos
C# | 134 lines | 111 code | 22 blank | 1 comment | 27 complexity | 80f3447490ffbe6c65c8d1f9a06cb549 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Text;
  5using Microsoft.Scripting;
  6using Microsoft.Scripting.Ast;
  7using Microsoft.Linq.Expressions;
  8using System.Xronos.Language;
  9
 10namespace System.Xronos.Scripting.MetaObjects
 11{
 12    public interface IFunctionMapProvider
 13    {
 14        FunctionMapEntry[] Entries { get; }
 15        string Unique { get; }
 16    }
 17
 18    public class FunctionMapEntry
 19    {
 20        public int Arity;
 21        public object Delegate;
 22        public bool IsRest;
 23
 24        public static string Unique(FunctionMapEntry[] entries)
 25        {
 26            var sb = new StringBuilder();
 27            foreach (var entry in entries)
 28            {
 29                if (entry == null)
 30                    sb.Append('.');
 31                else if (!entry.IsRest)
 32                    sb.Append('F');
 33                else
 34                    sb.Append('R');
 35            }
 36            return sb.ToString();
 37        }
 38    }
 39
 40    public class FunctionMetaObject : DynamicMetaObject
 41    {
 42        IFunctionMapProvider fn;
 43
 44        public FunctionMetaObject(IFunctionMapProvider fn, Expression ex)
 45            : base(ex, BindingRestrictions.Empty, fn)
 46        {
 47            this.fn = fn;
 48        }
 49
 50        public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
 51        {
 52            return BindInvoke(args) ?? base.BindInvoke(binder, args);
 53        }
 54
 55        private BindingRestrictions RestrictUnique(Expression expr, int ix, char type)
 56        {
 57            return BindingRestrictions.GetExpressionRestriction(
 58                Expression.AndAlso(Expression.AndAlso(
 59                    Expression.NotEqual(Expression.Constant(null), Expression.TypeAs(expr, typeof(IFunctionMapProvider))),
 60                    Expression.GreaterThan(Expression.Property(Expression.Property(Expression.Convert(expr, typeof(IFunctionMapProvider)), "Unique"), "Length"), Expression.Constant(ix))),
 61                    Expression.Equal(Expression.Constant(type),
 62                        Expression.Call(Expression.Property(Expression.Convert(expr, typeof(IFunctionMapProvider)), "Unique"), typeof(string).GetMethod("get_Chars"), Expression.Constant(ix))
 63                        )));
 64        }
 65
 66        private DynamicMetaObject BindInvoke(DynamicMetaObject[] dynargs)
 67        {
 68            var map = fn.Entries;
 69            var c = dynargs.Length;
 70
 71            int ix = -1;
 72            FunctionMapEntry entry = null;
 73
 74            for (int i = 0; i < map.Length; i++)
 75            {
 76                if (map[i] == null)
 77                    continue;
 78
 79                ix = i;
 80                entry = map[i];
 81                if (c <= map[i].Arity)
 82                    break;
 83            }
 84
 85            if (entry == null || (c != entry.Arity && !entry.IsRest))
 86                return null;
 87
 88            var args = new List<Expression>();
 89            var restArgs = new List<Expression>();
 90
 91            foreach (var item in dynargs)
 92            {
 93                if (entry.IsRest && args.Count + 1 == entry.Arity)
 94                    restArgs.Add(item.Expression);
 95                else
 96                    args.Add(item.Expression);
 97            }
 98
 99            if (entry.IsRest)
100                args.Add(Expression.Call(typeof(RT).GetMethod("seq"), Expression.NewArrayInit(typeof(object), restArgs)));
101
102            // double check arity, becaues the check above is purposely too simple
103            if (entry.Arity != args.Count)
104                return null;
105
106            args.Insert(0, this.Expression);
107            
108            var _entry = Expression.ArrayIndex(Expression.Property(Expression.Convert(this.Expression, typeof(IFunctionMapProvider)), "Entries"), Expression.Constant(ix));
109            var _func = Expression.Convert(Expression.Field(_entry, "Delegate"), entry.Delegate.GetType());
110            var _call = Expression.Invoke(_func, args);
111
112            var restrict = RestrictUnique(this.Expression, entry.Arity, entry.IsRest ? 'R' : 'F');
113            if (entry.IsRest && restArgs.Count == 0 && entry.Arity > 0)
114                restrict = restrict.Merge(RestrictUnique(this.Expression, entry.Arity - 1, '.'));
115
116            Statistics.IncrementBindMisses();
117            return new DynamicMetaObject(_call, restrict);
118        }
119
120        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
121        {
122            if (string.Compare("Invoke", binder.Name, binder.IgnoreCase) == 0)
123                return BindInvoke(args) ?? base.BindInvokeMember(binder, args);
124            return base.BindInvokeMember(binder, args);
125        }
126
127        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
128        {
129            if (string.Compare("Invoke", binder.Name, binder.IgnoreCase) == 0)
130                return this;
131            return base.BindGetMember(binder);
132        }
133    }
134}