PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Unary.cs

https://github.com/mscottford/ironruby
C# | 320 lines | 234 code | 36 blank | 50 comment | 52 complexity | 02a6581b52c1f2cc8b0fe689e9a2fa0e MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System.Diagnostics;
  16. using System.Reflection;
  17. using System.Reflection.Emit;
  18. using System.Runtime.CompilerServices;
  19. using System.Dynamic.Utils;
  20. namespace System.Linq.Expressions.Compiler {
  21. partial class LambdaCompiler {
  22. private void EmitQuoteUnaryExpression(Expression expr) {
  23. EmitQuote((UnaryExpression)expr);
  24. }
  25. //CONFORMING
  26. private void EmitQuote(UnaryExpression quote) {
  27. // emit the quoted expression as a runtime constant
  28. EmitConstant(quote.Operand, quote.Type);
  29. // Heuristic: only emit the tree rewrite logic if we have hoisted
  30. // locals. TODO: we could use an even smarter logic here by
  31. // detecting if any nodes actually need to be rewritten
  32. if (_scope.NearestHoistedLocals != null) {
  33. // HoistedLocals is internal so emit as System.Object
  34. EmitConstant(_scope.NearestHoistedLocals, typeof(object));
  35. _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
  36. _ilg.EmitCall(typeof(RuntimeOps).GetMethod("Quote"));
  37. if (quote.Type != typeof(Expression)) {
  38. _ilg.Emit(OpCodes.Castclass, quote.Type);
  39. }
  40. }
  41. }
  42. private void EmitThrowUnaryExpression(Expression expr) {
  43. EmitThrow((UnaryExpression)expr, EmitAs.Default);
  44. }
  45. private void EmitThrow(UnaryExpression expr, EmitAs emitAs) {
  46. if (expr.Operand == null) {
  47. CheckRethrow();
  48. _ilg.Emit(OpCodes.Rethrow);
  49. } else {
  50. EmitExpression(expr.Operand);
  51. _ilg.Emit(OpCodes.Throw);
  52. }
  53. if (emitAs != EmitAs.Void && expr.Type != typeof(void)) {
  54. _ilg.EmitDefault(expr.Type);
  55. }
  56. }
  57. private void EmitUnaryExpression(Expression expr) {
  58. EmitUnary((UnaryExpression)expr);
  59. }
  60. //CONFORMING
  61. private void EmitUnary(UnaryExpression node) {
  62. if (node.Method != null) {
  63. EmitUnaryMethod(node);
  64. } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) {
  65. _ilg.EmitInt(0);
  66. _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false);
  67. EmitExpression(node.Operand);
  68. EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false);
  69. } else {
  70. EmitExpression(node.Operand);
  71. EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type);
  72. }
  73. }
  74. //CONFORMING
  75. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
  76. private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) {
  77. bool operandIsNullable = TypeUtils.IsNullableType(operandType);
  78. if (op == ExpressionType.ArrayLength) {
  79. _ilg.Emit(OpCodes.Ldlen);
  80. return;
  81. }
  82. if (operandIsNullable) {
  83. switch (op) {
  84. case ExpressionType.Not: {
  85. if (operandType != typeof(bool?))
  86. goto case ExpressionType.Negate;
  87. Label labIfNull = _ilg.DefineLabel();
  88. Label labEnd = _ilg.DefineLabel();
  89. LocalBuilder loc = _ilg.GetLocal(operandType);
  90. // store values (reverse order since they are already on the stack)
  91. _ilg.Emit(OpCodes.Stloc, loc);
  92. // test for null
  93. _ilg.Emit(OpCodes.Ldloca, loc);
  94. _ilg.EmitHasValue(operandType);
  95. _ilg.Emit(OpCodes.Brfalse_S, labEnd);
  96. // do op on non-null value
  97. _ilg.Emit(OpCodes.Ldloca, loc);
  98. _ilg.EmitGetValueOrDefault(operandType);
  99. Type nnOperandType = TypeUtils.GetNonNullableType(operandType);
  100. EmitUnaryOperator(op, nnOperandType, typeof(bool));
  101. // construct result
  102. ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
  103. _ilg.Emit(OpCodes.Newobj, ci);
  104. _ilg.Emit(OpCodes.Stloc, loc);
  105. _ilg.MarkLabel(labEnd);
  106. _ilg.Emit(OpCodes.Ldloc, loc);
  107. _ilg.FreeLocal(loc);
  108. return;
  109. }
  110. case ExpressionType.UnaryPlus:
  111. case ExpressionType.NegateChecked:
  112. case ExpressionType.Negate:
  113. case ExpressionType.Increment:
  114. case ExpressionType.Decrement:
  115. {
  116. Debug.Assert(operandType == resultType);
  117. Label labIfNull = _ilg.DefineLabel();
  118. Label labEnd = _ilg.DefineLabel();
  119. LocalBuilder loc = _ilg.GetLocal(operandType);
  120. // check for null
  121. _ilg.Emit(OpCodes.Stloc, loc);
  122. _ilg.Emit(OpCodes.Ldloca, loc);
  123. _ilg.EmitHasValue(operandType);
  124. _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
  125. // apply operator to non-null value
  126. _ilg.Emit(OpCodes.Ldloca, loc);
  127. _ilg.EmitGetValueOrDefault(operandType);
  128. Type nnOperandType = TypeUtils.GetNonNullableType(resultType);
  129. EmitUnaryOperator(op, nnOperandType, nnOperandType);
  130. // construct result
  131. ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType });
  132. _ilg.Emit(OpCodes.Newobj, ci);
  133. _ilg.Emit(OpCodes.Stloc, loc);
  134. _ilg.Emit(OpCodes.Br_S, labEnd);
  135. // if null then create a default one
  136. _ilg.MarkLabel(labIfNull);
  137. _ilg.Emit(OpCodes.Ldloca, loc);
  138. _ilg.Emit(OpCodes.Initobj, resultType);
  139. _ilg.MarkLabel(labEnd);
  140. _ilg.Emit(OpCodes.Ldloc, loc);
  141. _ilg.FreeLocal(loc);
  142. return;
  143. }
  144. case ExpressionType.TypeAs:
  145. _ilg.Emit(OpCodes.Box, operandType);
  146. _ilg.Emit(OpCodes.Isinst, resultType);
  147. if (TypeUtils.IsNullableType(resultType)) {
  148. _ilg.Emit(OpCodes.Unbox_Any, resultType);
  149. }
  150. return;
  151. default:
  152. throw Error.UnhandledUnary(op);
  153. }
  154. } else {
  155. switch (op) {
  156. case ExpressionType.Not:
  157. if (operandType == typeof(bool)) {
  158. _ilg.Emit(OpCodes.Ldc_I4_0);
  159. _ilg.Emit(OpCodes.Ceq);
  160. } else {
  161. _ilg.Emit(OpCodes.Not);
  162. }
  163. break;
  164. case ExpressionType.UnaryPlus:
  165. _ilg.Emit(OpCodes.Nop);
  166. break;
  167. case ExpressionType.Negate:
  168. case ExpressionType.NegateChecked:
  169. _ilg.Emit(OpCodes.Neg);
  170. break;
  171. case ExpressionType.TypeAs:
  172. if (operandType.IsValueType) {
  173. _ilg.Emit(OpCodes.Box, operandType);
  174. }
  175. _ilg.Emit(OpCodes.Isinst, resultType);
  176. if (TypeUtils.IsNullableType(resultType)) {
  177. _ilg.Emit(OpCodes.Unbox_Any, resultType);
  178. }
  179. break;
  180. case ExpressionType.Increment:
  181. EmitConstantOne(resultType);
  182. _ilg.Emit(OpCodes.Add);
  183. break;
  184. case ExpressionType.Decrement:
  185. EmitConstantOne(resultType);
  186. _ilg.Emit(OpCodes.Sub);
  187. break;
  188. default:
  189. throw Error.UnhandledUnary(op);
  190. }
  191. EmitConvertArithmeticResult(op, resultType);
  192. }
  193. }
  194. private void EmitConstantOne(Type type) {
  195. switch (Type.GetTypeCode(type)) {
  196. case TypeCode.UInt16:
  197. case TypeCode.UInt32:
  198. case TypeCode.Int16:
  199. case TypeCode.Int32:
  200. _ilg.Emit(OpCodes.Ldc_I4_1);
  201. break;
  202. case TypeCode.Int64:
  203. case TypeCode.UInt64:
  204. _ilg.Emit(OpCodes.Ldc_I8, (long)1);
  205. break;
  206. case TypeCode.Single:
  207. _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
  208. break;
  209. case TypeCode.Double:
  210. _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
  211. break;
  212. default:
  213. // we only have to worry about aritmetic types, see
  214. // TypeUtils.IsArithmetic
  215. throw Assert.Unreachable;
  216. }
  217. }
  218. private void EmitUnboxUnaryExpression(Expression expr) {
  219. var node = (UnaryExpression)expr;
  220. Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
  221. // Unbox_Any leaves the value on the stack
  222. EmitExpression(node.Operand);
  223. _ilg.Emit(OpCodes.Unbox_Any, node.Type);
  224. }
  225. private void EmitConvertUnaryExpression(Expression expr) {
  226. EmitConvert((UnaryExpression)expr);
  227. }
  228. //CONFORMING
  229. private void EmitConvert(UnaryExpression node) {
  230. if (node.Method != null) {
  231. // User-defined conversions are only lifted if both source and
  232. // destination types are value types. The C# compiler gets this wrong.
  233. // In C#, if you have an implicit conversion from int->MyClass and you
  234. // "lift" the conversion to int?->MyClass then a null int? goes to a
  235. // null MyClass. This is contrary to the specification, which states
  236. // that the correct behaviour is to unwrap the int?, throw an exception
  237. // if it is null, and then call the conversion.
  238. //
  239. // We cannot fix this in C# but there is no reason why we need to
  240. // propagate this bug into the expression tree API. Unfortunately
  241. // this means that when the C# compiler generates the lambda
  242. // (int? i)=>(MyClass)i, we will get different results for converting
  243. // that lambda to a delegate directly and converting that lambda to
  244. // an expression tree and then compiling it. We can live with this
  245. // discrepancy however.
  246. if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) {
  247. ParameterInfo[] pis = node.Method.GetParametersCached();
  248. Debug.Assert(pis != null && pis.Length == 1);
  249. Type paramType = pis[0].ParameterType;
  250. if (paramType.IsByRef) {
  251. paramType = paramType.GetElementType();
  252. }
  253. UnaryExpression e = Expression.Convert(
  254. Expression.Call(
  255. node.Method,
  256. Expression.Convert(node.Operand, pis[0].ParameterType)
  257. ),
  258. node.Type
  259. );
  260. EmitConvert(e);
  261. } else {
  262. EmitUnaryMethod(node);
  263. }
  264. } else if (node.Type == typeof(void)) {
  265. EmitExpressionAsVoid(node.Operand);
  266. } else {
  267. EmitExpression(node.Operand);
  268. _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);
  269. }
  270. }
  271. //CONFORMING
  272. private void EmitUnaryMethod(UnaryExpression node) {
  273. if (node.IsLifted) {
  274. ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
  275. MethodCallExpression mc = Expression.Call(node.Method, v);
  276. Type resultType = TypeUtils.GetNullableType(mc.Type);
  277. EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand });
  278. _ilg.EmitConvertToType(resultType, node.Type, false);
  279. } else {
  280. EmitMethodCallExpression(Expression.Call(node.Method, node.Operand));
  281. }
  282. }
  283. }
  284. }