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