/System.Xronos/Scripting/MetaObjects/FunctionMetaObject.cs
C# | 134 lines | 111 code | 22 blank | 1 comment | 27 complexity | 80f3447490ffbe6c65c8d1f9a06cb549 MD5 | raw file
- 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);
- }
- }
- }