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

Language C# Lines 135
MD5 Hash 80f3447490ffbe6c65c8d1f9a06cb549 Estimated Cost $2,687 (why?)
Repository https://bitbucket.org/stefanrusek/xronos View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Scripting;
using Microsoft.Scripting.Ast;
using Microsoft.Linq.Expressions;
using System.Xronos.Language;

namespace System.Xronos.Scripting.MetaObjects
{
    public interface IFunctionMapProvider
    {
        FunctionMapEntry[] Entries { get; }
        string Unique { get; }
    }

    public class FunctionMapEntry
    {
        public int Arity;
        public object Delegate;
        public bool IsRest;

        public static string Unique(FunctionMapEntry[] entries)
        {
            var sb = new StringBuilder();
            foreach (var entry in entries)
            {
                if (entry == null)
                    sb.Append('.');
                else if (!entry.IsRest)
                    sb.Append('F');
                else
                    sb.Append('R');
            }
            return sb.ToString();
        }
    }

    public class FunctionMetaObject : DynamicMetaObject
    {
        IFunctionMapProvider fn;

        public FunctionMetaObject(IFunctionMapProvider fn, Expression ex)
            : base(ex, BindingRestrictions.Empty, fn)
        {
            this.fn = fn;
        }

        public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
        {
            return BindInvoke(args) ?? base.BindInvoke(binder, args);
        }

        private BindingRestrictions RestrictUnique(Expression expr, int ix, char type)
        {
            return BindingRestrictions.GetExpressionRestriction(
                Expression.AndAlso(Expression.AndAlso(
                    Expression.NotEqual(Expression.Constant(null), Expression.TypeAs(expr, typeof(IFunctionMapProvider))),
                    Expression.GreaterThan(Expression.Property(Expression.Property(Expression.Convert(expr, typeof(IFunctionMapProvider)), "Unique"), "Length"), Expression.Constant(ix))),
                    Expression.Equal(Expression.Constant(type),
                        Expression.Call(Expression.Property(Expression.Convert(expr, typeof(IFunctionMapProvider)), "Unique"), typeof(string).GetMethod("get_Chars"), Expression.Constant(ix))
                        )));
        }

        private DynamicMetaObject BindInvoke(DynamicMetaObject[] dynargs)
        {
            var map = fn.Entries;
            var c = dynargs.Length;

            int ix = -1;
            FunctionMapEntry entry = null;

            for (int i = 0; i < map.Length; i++)
            {
                if (map[i] == null)
                    continue;

                ix = i;
                entry = map[i];
                if (c <= map[i].Arity)
                    break;
            }

            if (entry == null || (c != entry.Arity && !entry.IsRest))
                return null;

            var args = new List<Expression>();
            var restArgs = new List<Expression>();

            foreach (var item in dynargs)
            {
                if (entry.IsRest && args.Count + 1 == entry.Arity)
                    restArgs.Add(item.Expression);
                else
                    args.Add(item.Expression);
            }

            if (entry.IsRest)
                args.Add(Expression.Call(typeof(RT).GetMethod("seq"), Expression.NewArrayInit(typeof(object), restArgs)));

            // double check arity, becaues the check above is purposely too simple
            if (entry.Arity != args.Count)
                return null;

            args.Insert(0, this.Expression);
            
            var _entry = Expression.ArrayIndex(Expression.Property(Expression.Convert(this.Expression, typeof(IFunctionMapProvider)), "Entries"), Expression.Constant(ix));
            var _func = Expression.Convert(Expression.Field(_entry, "Delegate"), entry.Delegate.GetType());
            var _call = Expression.Invoke(_func, args);

            var restrict = RestrictUnique(this.Expression, entry.Arity, entry.IsRest ? 'R' : 'F');
            if (entry.IsRest && restArgs.Count == 0 && entry.Arity > 0)
                restrict = restrict.Merge(RestrictUnique(this.Expression, entry.Arity - 1, '.'));

            Statistics.IncrementBindMisses();
            return new DynamicMetaObject(_call, restrict);
        }

        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
        {
            if (string.Compare("Invoke", binder.Name, binder.IgnoreCase) == 0)
                return BindInvoke(args) ?? base.BindInvokeMember(binder, args);
            return base.BindInvokeMember(binder, args);
        }

        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
        {
            if (string.Compare("Invoke", binder.Name, binder.IgnoreCase) == 0)
                return this;
            return base.BindGetMember(binder);
        }
    }
}
Back to Top