You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
17 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. namespace Net3_Proxy
  8. {
  9. public static class ExpressionEx
  10. {
  11. internal static ExpressionType ExprT(this ExpressionTypeEx ex)
  12. => (ExpressionType)ex;
  13. internal static ExpressionTypeEx ExprTEx(this ExpressionType ex)
  14. => (ExpressionTypeEx)ex;
  15. private static void Validate(Type type, bool allowByRef)
  16. {
  17. if (type == null)
  18. throw new ArgumentNullException(nameof(type));
  19. TypeUtils.ValidateType(type, nameof(type), allowByRef, false);
  20. if (type == typeof(void))
  21. throw new ArgumentException("Argument cannot be of type void", nameof(type));
  22. }
  23. private static void RequiresCanWrite(Expression expression, string paramName)
  24. {
  25. if (expression == null)
  26. {
  27. throw new ArgumentNullException(paramName);
  28. }
  29. ExpressionType nodeType = expression.NodeType;
  30. if (nodeType != ExpressionType.MemberAccess)
  31. {
  32. if (nodeType == ExpressionType.Parameter)
  33. return;
  34. }
  35. else
  36. {
  37. MemberInfo member = ((MemberExpression)expression).Member;
  38. if (member is PropertyInfo propertyInfo)
  39. {
  40. if (propertyInfo.CanWrite)
  41. {
  42. return;
  43. }
  44. }
  45. else
  46. {
  47. FieldInfo fieldInfo = (FieldInfo)member;
  48. if (!fieldInfo.IsInitOnly && !fieldInfo.IsLiteral)
  49. {
  50. return;
  51. }
  52. }
  53. }
  54. throw new ArgumentException("Expression must be writeable", paramName);
  55. }
  56. public static void RequiresCanRead(Expression expression, string paramName, int idx = -1)
  57. {
  58. if (expression == null)
  59. throw new ArgumentNullException(TypeUtils.GetParamName(paramName, idx));
  60. ExpressionType nodeType = expression.NodeType;
  61. if (nodeType == ExpressionType.MemberAccess)
  62. {
  63. if (((MemberExpression)expression).Member is PropertyInfo propertyInfo && !propertyInfo.CanRead)
  64. {
  65. throw new ArgumentException("Expression must be readable", TypeUtils.GetParamName(paramName, idx));
  66. }
  67. }
  68. }
  69. public static VariableExpression Variable(Type type, string name = null)
  70. {
  71. Validate(type, false);
  72. return new VariableExpression(type, name);
  73. }
  74. public static AssignExpression Assign(Expression left, Expression right)
  75. {
  76. RequiresCanWrite(left, nameof(left));
  77. RequiresCanRead(right, nameof(right));
  78. TypeUtils.ValidateType(left.Type, nameof(left), true, true);
  79. TypeUtils.ValidateType(right.Type, nameof(right), true, true);
  80. if (!TypeUtils.AreReferenceAssignable(left.Type, right.Type))
  81. throw new ArgumentException($"Expression of type '{left.Type}' cannot be used for assignment to type '{right.Type}'");
  82. if (left.NodeType.ExprTEx() != ExpressionTypeEx.Variable)
  83. throw new NotSupportedException("Non-variable left hand operands to assignment is currently not supported");
  84. return new AssignExpression(left, right);
  85. }
  86. public static Expression Block(IEnumerable<VariableExpression> vars, params Expression[] body) => Block(vars, body);
  87. public static Expression Block(IEnumerable<VariableExpression> vars, IEnumerable<Expression> body)
  88. { // this is probably terrible performance-wise when executing, but i'm not giving up BlockExpression damnit!
  89. var varSet = new HashSet<VariableExpression>(vars);
  90. var bodyArr = body.ToArray();
  91. var finalExprList = new Stack<(List<Expression> list, BlockParseStackInfo info)>();
  92. var remaining = bodyArr.Length;
  93. var varParams = new Dictionary<VariableExpression, ParameterExpression>(varSet.Count);
  94. finalExprList.Push((new List<Expression>(remaining), default(BlockParseStackInfo)));
  95. while (remaining > 0)
  96. {
  97. var (list, info) = finalExprList.Pop();
  98. var targetExpr = bodyArr[bodyArr.Length - remaining--];
  99. if (targetExpr.NodeType.ExprTEx() == ExpressionTypeEx.Assign)
  100. {
  101. var assign = (AssignExpression)targetExpr;
  102. var left = (VariableExpression)assign.Left;
  103. var right = assign.Right;
  104. var param = Expression.Parameter(left.Type, left.Name);
  105. finalExprList.Push((list, info));
  106. finalExprList.Push((new List<Expression>(remaining), new BlockParseStackInfo
  107. {
  108. ValueExpr = BlockVisitReplaceVariables(right, varParams), Param = param
  109. }));
  110. varParams.Add(left, param);
  111. continue;
  112. }
  113. list.Add(BlockVisitReplaceVariables(targetExpr, varParams));
  114. finalExprList.Push((list, info));
  115. }
  116. var funcType = typeof(Func<>);
  117. Expression topExpr = null;
  118. while (finalExprList.Count > 0)
  119. {
  120. var (list, info) = finalExprList.Pop();
  121. // there is an optimization opportunity for consecutive assignments, but it needs to make sure they don't depend on each other
  122. if (topExpr != null) list.Add(topExpr);
  123. Expression last = null;
  124. Type lastType = null;
  125. var rest = new List<Expression<Action>>(list.Count);
  126. if (list.Count == 0)
  127. list.Add(info.Param);
  128. for (int i = 0; i < list.Count; i++)
  129. {
  130. var expr = list[i];
  131. if (i + 1 == list.Count)
  132. {
  133. var ty = expr.Type;
  134. if (ty == typeof(void))
  135. rest.Add(Expression.Lambda<Action>(expr));
  136. else
  137. {
  138. lastType = ty;
  139. var func = funcType.MakeGenericType(ty);
  140. last = Expression.Lambda(func, expr);
  141. }
  142. }
  143. else
  144. rest.Add(Expression.Lambda<Action>(expr));
  145. }
  146. Expression topBody;
  147. if (rest.Count == 0)
  148. topBody = info.Param;
  149. else if (lastType != null)
  150. {
  151. var execSeq = ExecuteSequenceTyped.MakeGenericMethod(lastType);
  152. topBody = Expression.Call(null, execSeq, last, Expression.NewArrayInit(typeof(Action), rest.Cast<Expression>()));
  153. }
  154. else
  155. topBody = Expression.Call(null, ExecuteSequenceVoid, Expression.NewArrayInit(typeof(Action), rest.Cast<Expression>()));
  156. if (info.Param != null && info.ValueExpr != null)
  157. topExpr = Expression.Invoke(Expression.Lambda(topBody, info.Param), info.ValueExpr);
  158. else
  159. topExpr = topBody;
  160. }
  161. return topExpr;
  162. }
  163. /*
  164. * {
  165. * Console.WriteLine("ho");
  166. * int i = 3;
  167. * Console.WriteLine(i);
  168. * i // return last
  169. * }
  170. *
  171. * ExecuteSequence<int>(
  172. * () => (i =>
  173. * ExecuteSequence<int>(
  174. * () => i,
  175. * () => Console.WriteLine(i)
  176. * )
  177. * )(3),
  178. * () => Console.WriteLine("ho")
  179. * )
  180. */
  181. private struct BlockParseStackInfo
  182. {
  183. public Expression ValueExpr;
  184. public ParameterExpression Param;
  185. }
  186. private static Expression BlockVisitReplaceVariables(Expression expr, Dictionary<VariableExpression, ParameterExpression> mapping)
  187. {
  188. var binary = expr as BinaryExpression;
  189. var unary = expr as UnaryExpression;
  190. switch (expr.NodeType)
  191. {
  192. case ExpressionType.Add:
  193. return Expression.Add(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  194. case ExpressionType.AddChecked:
  195. return Expression.AddChecked(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  196. case ExpressionType.And:
  197. return Expression.And(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  198. case ExpressionType.AndAlso:
  199. return Expression.AndAlso(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  200. case ExpressionType.ArrayIndex:
  201. return Expression.ArrayIndex(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  202. case ExpressionType.Coalesce:
  203. return Expression.Coalesce(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  204. case ExpressionType.Divide:
  205. return Expression.Divide(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  206. case ExpressionType.Equal:
  207. return Expression.Equal(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  208. case ExpressionType.ExclusiveOr:
  209. return Expression.ExclusiveOr(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  210. case ExpressionType.GreaterThan:
  211. return Expression.GreaterThan(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  212. case ExpressionType.GreaterThanOrEqual:
  213. return Expression.GreaterThanOrEqual(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  214. case ExpressionType.LeftShift:
  215. return Expression.LeftShift(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  216. case ExpressionType.LessThan:
  217. return Expression.LessThan(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  218. case ExpressionType.LessThanOrEqual:
  219. return Expression.LessThanOrEqual(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  220. case ExpressionType.Modulo:
  221. return Expression.Modulo(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  222. case ExpressionType.Multiply:
  223. return Expression.Multiply(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  224. case ExpressionType.MultiplyChecked:
  225. return Expression.MultiplyChecked(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  226. case ExpressionType.NotEqual:
  227. return Expression.NotEqual(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  228. case ExpressionType.Or:
  229. return Expression.Or(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  230. case ExpressionType.OrElse:
  231. return Expression.OrElse(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  232. case ExpressionType.Power:
  233. return Expression.Power(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  234. case ExpressionType.RightShift:
  235. return Expression.RightShift(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  236. case ExpressionType.Subtract:
  237. return Expression.Subtract(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  238. case ExpressionType.SubtractChecked:
  239. return Expression.SubtractChecked(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
  240. case ExpressionType.ArrayLength:
  241. return Expression.ArrayLength(BlockVisitReplaceVariables(unary.Operand, mapping));
  242. case ExpressionType.Negate:
  243. return Expression.Negate(BlockVisitReplaceVariables(unary.Operand, mapping));
  244. case ExpressionType.UnaryPlus:
  245. return Expression.UnaryPlus(BlockVisitReplaceVariables(unary.Operand, mapping));
  246. case ExpressionType.NegateChecked:
  247. return Expression.NegateChecked(BlockVisitReplaceVariables(unary.Operand, mapping));
  248. case ExpressionType.Not:
  249. return Expression.Not(BlockVisitReplaceVariables(unary.Operand, mapping));
  250. case ExpressionType.TypeAs:
  251. return Expression.TypeAs(BlockVisitReplaceVariables(unary.Operand, mapping), unary.Type);
  252. case ExpressionType.Call:
  253. var callExpr = expr as MethodCallExpression;
  254. return Expression.Call(BlockVisitReplaceVariables(callExpr.Object, mapping),
  255. callExpr.Method, callExpr.Arguments.Select(a => BlockVisitReplaceVariables(a, mapping)));
  256. case ExpressionType.Conditional:
  257. var condExpr = expr as ConditionalExpression;
  258. return Expression.Condition(BlockVisitReplaceVariables(condExpr.Test, mapping),
  259. BlockVisitReplaceVariables(condExpr.IfTrue, mapping), BlockVisitReplaceVariables(condExpr.IfFalse, mapping));
  260. case ExpressionType.Constant: return expr; // constants should be unchanged
  261. case ExpressionType.Convert:
  262. return Expression.Convert(BlockVisitReplaceVariables(unary.Operand, mapping), unary.Type, unary.Method);
  263. case ExpressionType.ConvertChecked:
  264. return Expression.ConvertChecked(BlockVisitReplaceVariables(unary.Operand, mapping), unary.Type, unary.Method);
  265. case ExpressionType.Invoke:
  266. var invokeExpr = expr as InvocationExpression;
  267. return Expression.Invoke(BlockVisitReplaceVariables(invokeExpr.Expression, mapping),
  268. invokeExpr.Arguments.Select(e => BlockVisitReplaceVariables(e, mapping)));
  269. case ExpressionType.Lambda:
  270. var lambdaExpr = expr as LambdaExpression;
  271. return Expression.Lambda(lambdaExpr.Type, BlockVisitReplaceVariables(lambdaExpr.Body, mapping), lambdaExpr.Parameters);
  272. case ExpressionType.ListInit:
  273. var listInitExpr = expr as ListInitExpression;
  274. return Expression.ListInit((NewExpression)BlockVisitReplaceVariables(listInitExpr.NewExpression, mapping),
  275. listInitExpr.Initializers.Select(i => i.AddMethod).First(),
  276. listInitExpr.Initializers.SelectMany(i => i.Arguments)
  277. .Select(e => BlockVisitReplaceVariables(e, mapping)));
  278. case ExpressionType.MemberAccess:
  279. var memberExpr = expr as MemberExpression;
  280. return Expression.MakeMemberAccess(BlockVisitReplaceVariables(memberExpr.Expression, mapping), memberExpr.Member);
  281. case ExpressionType.MemberInit:
  282. var memberInitExpr = expr as MemberInitExpression;
  283. return Expression.MemberInit((NewExpression)BlockVisitReplaceVariables(memberInitExpr.NewExpression, mapping),
  284. memberInitExpr.Bindings);
  285. case ExpressionType.New:
  286. var newExpr = expr as NewExpression;
  287. return Expression.New(newExpr.Constructor,
  288. newExpr.Arguments.Select(e => BlockVisitReplaceVariables(e, mapping)),
  289. newExpr.Members);
  290. case ExpressionType.NewArrayInit:
  291. var newArrayInitExpr = expr as NewArrayExpression;
  292. return Expression.NewArrayInit(newArrayInitExpr.Type,
  293. newArrayInitExpr.Expressions.Select(e => BlockVisitReplaceVariables(e, mapping)));
  294. case ExpressionType.NewArrayBounds:
  295. var newArrayBoundsExpr = expr as NewArrayExpression;
  296. return Expression.NewArrayBounds(newArrayBoundsExpr.Type,
  297. newArrayBoundsExpr.Expressions.Select(e => BlockVisitReplaceVariables(e, mapping)));
  298. case ExpressionType.Parameter: return expr; // like constant
  299. case ExpressionType.Quote:
  300. return Expression.Quote(BlockVisitReplaceVariables(unary.Operand, mapping));
  301. case ExpressionType.TypeIs:
  302. var typeIsExpr = expr as TypeBinaryExpression;
  303. return Expression.TypeIs(BlockVisitReplaceVariables(typeIsExpr.Expression, mapping), typeIsExpr.TypeOperand);
  304. default:
  305. switch (expr.NodeType.ExprTEx())
  306. {
  307. case ExpressionTypeEx.Variable:
  308. var varExpr = expr as VariableExpression;
  309. if (mapping.TryGetValue(varExpr, out var paramExpr))
  310. return paramExpr;
  311. else
  312. return varExpr; // not in scope in the current context, might be later
  313. case ExpressionTypeEx.Assign:
  314. throw new InvalidOperationException("Assign expression must appear directly inside a block expression");
  315. default:
  316. throw new ArgumentException($"Unhandled expression type '{expr.NodeType}'");
  317. }
  318. }
  319. }
  320. private static readonly MethodInfo ExecuteSequenceVoid = typeof(ExpressionEx).GetMethod(nameof(ExecuteSequence),
  321. BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Action[]) }, null);
  322. private static void ExecuteSequence(params Action[] exec)
  323. {
  324. foreach (var act in exec) act();
  325. }
  326. private static readonly MethodInfo ExecuteSequenceTyped = typeof(ExpressionEx).GetMethod(nameof(ExecuteSequence),
  327. BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Func<>), typeof(Action[]) }, null);
  328. private static T ExecuteSequence<T>(Func<T> last, params Action[] first)
  329. {
  330. ExecuteSequence(first);
  331. return last();
  332. }
  333. }
  334. internal enum ExpressionTypeEx
  335. {
  336. Variable = 46,
  337. Assign = 47
  338. }
  339. public class VariableExpression : Expression, IEquatable<VariableExpression>
  340. {
  341. public string Name { get; }
  342. internal VariableExpression(Type varType, string name) : base(ExpressionTypeEx.Variable.ExprT(), varType)
  343. => Name = name;
  344. public bool Equals(VariableExpression other)
  345. => Name == other.Name && Type == other.Type;
  346. }
  347. public class AssignExpression : Expression
  348. {
  349. public Expression Left { get; }
  350. public Expression Right { get; }
  351. internal AssignExpression(Expression left, Expression right) : base(ExpressionTypeEx.Assign.ExprT(), left.Type)
  352. {
  353. Left = left;
  354. Right = right;
  355. }
  356. }
  357. }