1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.Collections.ObjectModel; 7 using System.Diagnostics; 8 using System.Dynamic.Utils; 9 using System.Globalization; 10 using System.Reflection; 11 using System.Runtime.CompilerServices; 12 using static System.Linq.Expressions.CachedReflectionInfo; 13 14 using AstUtils = System.Linq.Expressions.Utils; 15 16 namespace System.Linq.Expressions.Interpreter 17 { 18 internal sealed class ExceptionFilter 19 { 20 public readonly int LabelIndex; 21 public readonly int StartIndex; 22 public readonly int EndIndex; 23 ExceptionFilter(int labelIndex, int start, int end)24 internal ExceptionFilter(int labelIndex, int start, int end) 25 { 26 LabelIndex = labelIndex; 27 StartIndex = start; 28 EndIndex = end; 29 } 30 } 31 32 internal sealed class ExceptionHandler 33 { 34 private readonly Type _exceptionType; 35 public readonly int LabelIndex; 36 public readonly int HandlerStartIndex; 37 public readonly int HandlerEndIndex; 38 public readonly ExceptionFilter Filter; 39 ExceptionHandler(int labelIndex, int handlerStartIndex, int handlerEndIndex, Type exceptionType, ExceptionFilter filter)40 internal ExceptionHandler(int labelIndex, int handlerStartIndex, int handlerEndIndex, Type exceptionType, ExceptionFilter filter) 41 { 42 Debug.Assert(exceptionType != null); 43 LabelIndex = labelIndex; 44 _exceptionType = exceptionType; 45 HandlerStartIndex = handlerStartIndex; 46 HandlerEndIndex = handlerEndIndex; 47 Filter = filter; 48 } 49 50 public bool Matches(Type exceptionType) => _exceptionType.IsAssignableFrom(exceptionType); 51 ToString()52 public override string ToString() => 53 string.Format(CultureInfo.InvariantCulture, "catch({0}) [{1}->{2}]", _exceptionType.Name, HandlerStartIndex, HandlerEndIndex); 54 } 55 56 internal sealed class TryCatchFinallyHandler 57 { 58 internal readonly int TryStartIndex; 59 internal readonly int TryEndIndex; 60 internal readonly int FinallyStartIndex; 61 internal readonly int FinallyEndIndex; 62 internal readonly int GotoEndTargetIndex; 63 64 private readonly ExceptionHandler[] _handlers; 65 66 internal bool IsFinallyBlockExist 67 { 68 get 69 { 70 Debug.Assert((FinallyStartIndex != Instruction.UnknownInstrIndex) == (FinallyEndIndex != Instruction.UnknownInstrIndex)); 71 return FinallyStartIndex != Instruction.UnknownInstrIndex; 72 } 73 } 74 75 internal ExceptionHandler[] Handlers => _handlers; 76 77 internal bool IsCatchBlockExist => _handlers != null; 78 79 /// <summary> 80 /// No finally block 81 /// </summary> TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndTargetIndex, ExceptionHandler[] handlers)82 internal TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndTargetIndex, ExceptionHandler[] handlers) 83 : this(tryStart, tryEnd, gotoEndTargetIndex, Instruction.UnknownInstrIndex, Instruction.UnknownInstrIndex, handlers) 84 { 85 Debug.Assert(handlers != null, "catch blocks should exist"); 86 } 87 88 /// <summary> 89 /// Generic constructor 90 /// </summary> TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndLabelIndex, int finallyStart, int finallyEnd, ExceptionHandler[] handlers)91 internal TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndLabelIndex, int finallyStart, int finallyEnd, ExceptionHandler[] handlers) 92 { 93 TryStartIndex = tryStart; 94 TryEndIndex = tryEnd; 95 FinallyStartIndex = finallyStart; 96 FinallyEndIndex = finallyEnd; 97 GotoEndTargetIndex = gotoEndLabelIndex; 98 _handlers = handlers; 99 } 100 HasHandler(InterpretedFrame frame, Exception exception, out ExceptionHandler handler, out object unwrappedException)101 internal bool HasHandler(InterpretedFrame frame, Exception exception, out ExceptionHandler handler, out object unwrappedException) 102 { 103 #if DEBUG 104 if (exception is RethrowException) 105 { 106 // Unreachable. 107 // Want to assert that this case isn't hit, but an assertion failure here will be eaten because 108 // we are in an exception filter. Therefore return true here and assert in the catch block. 109 handler = null; 110 unwrappedException = exception; 111 return true; 112 } 113 #endif 114 frame.SaveTraceToException(exception); 115 116 if (IsCatchBlockExist) 117 { 118 RuntimeWrappedException rwe = exception as RuntimeWrappedException; 119 unwrappedException = rwe != null ? rwe.WrappedException : exception; 120 Type exceptionType = unwrappedException.GetType(); 121 foreach (ExceptionHandler candidate in _handlers) 122 { 123 if (candidate.Matches(exceptionType) && (candidate.Filter == null || FilterPasses(frame, ref unwrappedException, candidate.Filter))) 124 { 125 handler = candidate; 126 return true; 127 } 128 } 129 } 130 else 131 { 132 unwrappedException = null; 133 } 134 135 handler = null; 136 return false; 137 } 138 FilterPasses(InterpretedFrame frame, ref object exception, ExceptionFilter filter)139 private static bool FilterPasses(InterpretedFrame frame, ref object exception, ExceptionFilter filter) 140 { 141 Interpreter interpreter = frame.Interpreter; 142 Instruction[] instructions = interpreter.Instructions.Instructions; 143 int stackIndex = frame.StackIndex; 144 int frameIndex = frame.InstructionIndex; 145 try 146 { 147 int index = interpreter._labels[filter.LabelIndex].Index; 148 frame.InstructionIndex = index; 149 frame.Push(exception); 150 while (index >= filter.StartIndex && index < filter.EndIndex) 151 { 152 index += instructions[index].Run(frame); 153 frame.InstructionIndex = index; 154 } 155 156 // Exception is stored in a local at start of the filter, and loaded from it at the end, so it is now 157 // on the top of the stack. It may have been assigned to in the course of the filter running. 158 // If this is the handler that will be executed, then if the filter has assigned to the exception variable 159 // that change should be visible to the handler. Otherwise, it should not, so we write it back only on true. 160 object exceptionLocal = frame.Pop(); 161 if ((bool)frame.Pop()) 162 { 163 exception = exceptionLocal; 164 // Stack and instruction indices will be overwritten in the catch block anyway, so no need to restore. 165 return true; 166 } 167 } 168 catch 169 { 170 // Silently eating exceptions and returning false matches the CLR behavior. 171 } 172 173 frame.StackIndex = stackIndex; 174 frame.InstructionIndex = frameIndex; 175 return false; 176 } 177 } 178 179 internal sealed class TryFaultHandler 180 { 181 internal readonly int TryStartIndex; 182 internal readonly int TryEndIndex; 183 internal readonly int FinallyStartIndex; 184 internal readonly int FinallyEndIndex; 185 TryFaultHandler(int tryStart, int tryEnd, int finallyStart, int finallyEnd)186 internal TryFaultHandler(int tryStart, int tryEnd, int finallyStart, int finallyEnd) 187 { 188 TryStartIndex = tryStart; 189 TryEndIndex = tryEnd; 190 FinallyStartIndex = finallyStart; 191 FinallyEndIndex = finallyEnd; 192 } 193 } 194 195 /// <summary> 196 /// The re-throw instruction will throw this exception 197 /// </summary> 198 internal sealed class RethrowException : Exception 199 { 200 } 201 202 internal sealed class DebugInfo 203 { 204 public int StartLine, EndLine; 205 public int Index; 206 public string FileName; 207 public bool IsClear; 208 private static readonly DebugInfoComparer s_debugComparer = new DebugInfoComparer(); 209 210 private class DebugInfoComparer : IComparer<DebugInfo> 211 { 212 //We allow comparison between int and DebugInfo here Compare(DebugInfo d1, DebugInfo d2)213 int IComparer<DebugInfo>.Compare(DebugInfo d1, DebugInfo d2) 214 { 215 if (d1.Index > d2.Index) return 1; 216 else if (d1.Index == d2.Index) return 0; 217 else return -1; 218 } 219 } 220 GetMatchingDebugInfo(DebugInfo[] debugInfos, int index)221 public static DebugInfo GetMatchingDebugInfo(DebugInfo[] debugInfos, int index) 222 { 223 //Create a faked DebugInfo to do the search 224 var d = new DebugInfo { Index = index }; 225 226 //to find the closest debug info before the current index 227 228 int i = Array.BinarySearch<DebugInfo>(debugInfos, d, s_debugComparer); 229 if (i < 0) 230 { 231 //~i is the index for the first bigger element 232 //if there is no bigger element, ~i is the length of the array 233 i = ~i; 234 if (i == 0) 235 { 236 return null; 237 } 238 //return the last one that is smaller 239 i = i - 1; 240 } 241 242 return debugInfos[i]; 243 } 244 ToString()245 public override string ToString() 246 { 247 if (IsClear) 248 { 249 return string.Format(CultureInfo.InvariantCulture, "{0}: clear", Index); 250 } 251 else 252 { 253 return string.Format(CultureInfo.InvariantCulture, "{0}: [{1}-{2}] '{3}'", Index, StartLine, EndLine, FileName); 254 } 255 } 256 } 257 258 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] 259 internal readonly struct InterpretedFrameInfo 260 { 261 private readonly string _methodName; 262 263 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] 264 private readonly DebugInfo _debugInfo; 265 InterpretedFrameInfoSystem.Linq.Expressions.Interpreter.InterpretedFrameInfo266 public InterpretedFrameInfo(string methodName, DebugInfo info) 267 { 268 _methodName = methodName; 269 _debugInfo = info; 270 } 271 ToStringSystem.Linq.Expressions.Interpreter.InterpretedFrameInfo272 public override string ToString() => _debugInfo != null ? _methodName + ": " + _debugInfo : _methodName; 273 } 274 275 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] 276 internal sealed class LightCompiler 277 { 278 private readonly InstructionList _instructions; 279 private readonly LocalVariables _locals = new LocalVariables(); 280 281 private readonly List<DebugInfo> _debugInfos = new List<DebugInfo>(); 282 private readonly HybridReferenceDictionary<LabelTarget, LabelInfo> _treeLabels = new HybridReferenceDictionary<LabelTarget, LabelInfo>(); 283 private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda); 284 285 private readonly Stack<ParameterExpression> _exceptionForRethrowStack = new Stack<ParameterExpression>(); 286 287 private readonly LightCompiler _parent; 288 289 private readonly StackGuard _guard = new StackGuard(); 290 291 private static readonly LocalDefinition[] s_emptyLocals = Array.Empty<LocalDefinition>(); 292 LightCompiler()293 public LightCompiler() 294 { 295 _instructions = new InstructionList(); 296 } 297 LightCompiler(LightCompiler parent)298 private LightCompiler(LightCompiler parent) 299 : this() 300 { 301 _parent = parent; 302 } 303 304 public InstructionList Instructions => _instructions; 305 CompileTop(LambdaExpression node)306 public LightDelegateCreator CompileTop(LambdaExpression node) 307 { 308 node.ValidateArgumentCount(); 309 310 //Console.WriteLine(node.DebugView); 311 for (int i = 0, n = node.ParameterCount; i < n; i++) 312 { 313 ParameterExpression p = node.GetParameter(i); 314 LocalDefinition local = _locals.DefineLocal(p, 0); 315 _instructions.EmitInitializeParameter(local.Index); 316 } 317 318 Compile(node.Body); 319 320 // pop the result of the last expression: 321 if (node.Body.Type != typeof(void) && node.ReturnType == typeof(void)) 322 { 323 _instructions.EmitPop(); 324 } 325 326 Debug.Assert(_instructions.CurrentStackDepth == (node.ReturnType != typeof(void) ? 1 : 0)); 327 328 return new LightDelegateCreator(MakeInterpreter(node.Name), node); 329 } 330 MakeInterpreter(string lambdaName)331 private Interpreter MakeInterpreter(string lambdaName) 332 { 333 DebugInfo[] debugInfos = _debugInfos.ToArray(); 334 foreach (KeyValuePair<LabelTarget, LabelInfo> kvp in _treeLabels) 335 { 336 kvp.Value.ValidateFinish(); 337 } 338 return new Interpreter(lambdaName, _locals, _instructions.ToArray(), debugInfos); 339 } 340 CompileConstantExpression(Expression expr)341 private void CompileConstantExpression(Expression expr) 342 { 343 var node = (ConstantExpression)expr; 344 _instructions.EmitLoad(node.Value, node.Type); 345 } 346 CompileDefaultExpression(Expression expr)347 private void CompileDefaultExpression(Expression expr) 348 { 349 CompileDefaultExpression(expr.Type); 350 } 351 CompileDefaultExpression(Type type)352 private void CompileDefaultExpression(Type type) 353 { 354 if (type != typeof(void)) 355 { 356 if (type.IsNullableOrReferenceType()) 357 { 358 _instructions.EmitLoad(value: null); 359 } 360 else 361 { 362 object value = ScriptingRuntimeHelpers.GetPrimitiveDefaultValue(type); 363 if (value != null) 364 { 365 _instructions.EmitLoad(value); 366 } 367 else 368 { 369 _instructions.EmitDefaultValue(type); 370 } 371 } 372 } 373 } 374 EnsureAvailableForClosure(ParameterExpression expr)375 private LocalVariable EnsureAvailableForClosure(ParameterExpression expr) 376 { 377 LocalVariable local; 378 if (_locals.TryGetLocalOrClosure(expr, out local)) 379 { 380 if (!local.InClosure && !local.IsBoxed) 381 { 382 _locals.Box(expr, _instructions); 383 } 384 return local; 385 } 386 else if (_parent != null) 387 { 388 _parent.EnsureAvailableForClosure(expr); 389 return _locals.AddClosureVariable(expr); 390 } 391 else 392 { 393 throw new InvalidOperationException("unbound variable: " + expr); 394 } 395 } 396 ResolveLocal(ParameterExpression variable)397 private LocalVariable ResolveLocal(ParameterExpression variable) 398 { 399 LocalVariable local; 400 if (!_locals.TryGetLocalOrClosure(variable, out local)) 401 { 402 local = EnsureAvailableForClosure(variable); 403 } 404 return local; 405 } 406 CompileGetVariable(ParameterExpression variable)407 private void CompileGetVariable(ParameterExpression variable) 408 { 409 LoadLocalNoValueTypeCopy(variable); 410 411 _instructions.SetDebugCookie(variable.Name); 412 413 EmitCopyValueType(variable.Type); 414 } 415 EmitCopyValueType(Type valueType)416 private void EmitCopyValueType(Type valueType) 417 { 418 if (MaybeMutableValueType(valueType)) 419 { 420 // loading a value type on the stack has copy semantics unless 421 // we are specifically loading the address of the object, so we 422 // emit a copy here if we don't know the type is immutable. 423 _instructions.Emit(ValueTypeCopyInstruction.Instruction); 424 } 425 } 426 LoadLocalNoValueTypeCopy(ParameterExpression variable)427 private void LoadLocalNoValueTypeCopy(ParameterExpression variable) 428 { 429 LocalVariable local = ResolveLocal(variable); 430 431 if (local.InClosure) 432 { 433 _instructions.EmitLoadLocalFromClosure(local.Index); 434 } 435 else if (local.IsBoxed) 436 { 437 _instructions.EmitLoadLocalBoxed(local.Index); 438 } 439 else 440 { 441 _instructions.EmitLoadLocal(local.Index); 442 } 443 } 444 MaybeMutableValueType(Type type)445 private bool MaybeMutableValueType(Type type) 446 { 447 return type.IsValueType && !type.IsEnum && !type.IsPrimitive; 448 } 449 CompileGetBoxedVariable(ParameterExpression variable)450 private void CompileGetBoxedVariable(ParameterExpression variable) 451 { 452 LocalVariable local = ResolveLocal(variable); 453 454 if (local.InClosure) 455 { 456 _instructions.EmitLoadLocalFromClosureBoxed(local.Index); 457 } 458 else 459 { 460 Debug.Assert(local.IsBoxed); 461 _instructions.EmitLoadLocal(local.Index); 462 } 463 464 _instructions.SetDebugCookie(variable.Name); 465 } 466 CompileSetVariable(ParameterExpression variable, bool isVoid)467 private void CompileSetVariable(ParameterExpression variable, bool isVoid) 468 { 469 LocalVariable local = ResolveLocal(variable); 470 471 if (local.InClosure) 472 { 473 if (isVoid) 474 { 475 _instructions.EmitStoreLocalToClosure(local.Index); 476 } 477 else 478 { 479 _instructions.EmitAssignLocalToClosure(local.Index); 480 } 481 } 482 else if (local.IsBoxed) 483 { 484 if (isVoid) 485 { 486 _instructions.EmitStoreLocalBoxed(local.Index); 487 } 488 else 489 { 490 _instructions.EmitAssignLocalBoxed(local.Index); 491 } 492 } 493 else 494 { 495 if (isVoid) 496 { 497 _instructions.EmitStoreLocal(local.Index); 498 } 499 else 500 { 501 _instructions.EmitAssignLocal(local.Index); 502 } 503 } 504 505 _instructions.SetDebugCookie(variable.Name); 506 } 507 CompileParameterExpression(Expression expr)508 private void CompileParameterExpression(Expression expr) 509 { 510 var node = (ParameterExpression)expr; 511 CompileGetVariable(node); 512 } 513 CompileBlockExpression(Expression expr, bool asVoid)514 private void CompileBlockExpression(Expression expr, bool asVoid) 515 { 516 var node = (BlockExpression)expr; 517 518 if (node.ExpressionCount != 0) 519 { 520 LocalDefinition[] end = CompileBlockStart(node); 521 522 Expression lastExpression = node.Expressions[node.Expressions.Count - 1]; 523 Compile(lastExpression, asVoid); 524 CompileBlockEnd(end); 525 } 526 } 527 CompileBlockStart(BlockExpression node)528 private LocalDefinition[] CompileBlockStart(BlockExpression node) 529 { 530 int start = _instructions.Count; 531 532 LocalDefinition[] locals; 533 ReadOnlyCollection<ParameterExpression> variables = node.Variables; 534 if (variables.Count != 0) 535 { 536 // TODO: basic flow analysis so we don't have to initialize all 537 // variables. 538 locals = new LocalDefinition[variables.Count]; 539 int localCnt = 0; 540 foreach (ParameterExpression variable in variables) 541 { 542 LocalDefinition local = _locals.DefineLocal(variable, start); 543 locals[localCnt++] = local; 544 545 _instructions.EmitInitializeLocal(local.Index, variable.Type); 546 _instructions.SetDebugCookie(variable.Name); 547 } 548 } 549 else 550 { 551 locals = s_emptyLocals; 552 } 553 554 for (int i = 0; i < node.Expressions.Count - 1; i++) 555 { 556 CompileAsVoid(node.Expressions[i]); 557 } 558 return locals; 559 } 560 CompileBlockEnd(LocalDefinition[] locals)561 private void CompileBlockEnd(LocalDefinition[] locals) 562 { 563 foreach (LocalDefinition local in locals) 564 { 565 _locals.UndefineLocal(local, _instructions.Count); 566 } 567 } 568 CompileIndexExpression(Expression expr)569 private void CompileIndexExpression(Expression expr) 570 { 571 var index = (IndexExpression)expr; 572 573 // instance: 574 if (index.Object != null) 575 { 576 EmitThisForMethodCall(index.Object); 577 } 578 579 // indexes, byref args not allowed. 580 for (int i = 0, n = index.ArgumentCount; i < n; i++) 581 { 582 Compile(index.GetArgument(i)); 583 } 584 585 EmitIndexGet(index); 586 } 587 EmitIndexGet(IndexExpression index)588 private void EmitIndexGet(IndexExpression index) 589 { 590 if (index.Indexer != null) 591 { 592 _instructions.EmitCall(index.Indexer.GetGetMethod(nonPublic: true)); 593 } 594 else if (index.ArgumentCount != 1) 595 { 596 _instructions.EmitCall(index.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)); 597 } 598 else 599 { 600 _instructions.EmitGetArrayItem(); 601 } 602 } 603 CompileIndexAssignment(BinaryExpression node, bool asVoid)604 private void CompileIndexAssignment(BinaryExpression node, bool asVoid) 605 { 606 var index = (IndexExpression)node.Left; 607 608 // instance: 609 if (index.Object != null) 610 { 611 EmitThisForMethodCall(index.Object); 612 } 613 614 // indexes, byref args not allowed. 615 for (int i = 0, n = index.ArgumentCount; i < n; i++) 616 { 617 Compile(index.GetArgument(i)); 618 } 619 620 // value: 621 Compile(node.Right); 622 LocalDefinition local = default(LocalDefinition); 623 if (!asVoid) 624 { 625 local = _locals.DefineLocal(Expression.Parameter(node.Right.Type), _instructions.Count); 626 _instructions.EmitAssignLocal(local.Index); 627 } 628 629 if (index.Indexer != null) 630 { 631 _instructions.EmitCall(index.Indexer.GetSetMethod(nonPublic: true)); 632 } 633 else if (index.ArgumentCount != 1) 634 { 635 _instructions.EmitCall(index.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance)); 636 } 637 else 638 { 639 _instructions.EmitSetArrayItem(); 640 } 641 642 if (!asVoid) 643 { 644 _instructions.EmitLoadLocal(local.Index); 645 _locals.UndefineLocal(local, _instructions.Count); 646 } 647 } 648 CompileMemberAssignment(BinaryExpression node, bool asVoid)649 private void CompileMemberAssignment(BinaryExpression node, bool asVoid) 650 { 651 var member = (MemberExpression)node.Left; 652 Expression expr = member.Expression; 653 if (expr != null) 654 { 655 EmitThisForMethodCall(expr); 656 } 657 658 CompileMemberAssignment(asVoid, member.Member, node.Right, forBinding: false); 659 } 660 CompileMemberAssignment(bool asVoid, MemberInfo refMember, Expression value, bool forBinding)661 private void CompileMemberAssignment(bool asVoid, MemberInfo refMember, Expression value, bool forBinding) 662 { 663 var pi = refMember as PropertyInfo; 664 if (pi != null) 665 { 666 MethodInfo method = pi.GetSetMethod(nonPublic: true); 667 if (forBinding && method.IsStatic) 668 { 669 throw Error.InvalidProgram(); 670 } 671 672 EmitThisForMethodCall(value); 673 674 int start = _instructions.Count; 675 if (!asVoid) 676 { 677 LocalDefinition local = _locals.DefineLocal(Expression.Parameter(value.Type), start); 678 _instructions.EmitAssignLocal(local.Index); 679 _instructions.EmitCall(method); 680 _instructions.EmitLoadLocal(local.Index); 681 _locals.UndefineLocal(local, _instructions.Count); 682 } 683 else 684 { 685 _instructions.EmitCall(method); 686 } 687 } 688 else 689 { 690 // other types inherited from MemberInfo (EventInfo\MethodBase\Type) cannot be used in MemberAssignment 691 var fi = (FieldInfo)refMember; 692 Debug.Assert(fi != null); 693 if (fi.IsLiteral) 694 { 695 throw Error.NotSupported(); 696 } 697 698 if (forBinding && fi.IsStatic) 699 { 700 _instructions.UnEmit(); // Undo having pushed the instance to the stack. 701 } 702 703 EmitThisForMethodCall(value); 704 705 int start = _instructions.Count; 706 if (!asVoid) 707 { 708 LocalDefinition local = _locals.DefineLocal(Expression.Parameter(value.Type), start); 709 _instructions.EmitAssignLocal(local.Index); 710 _instructions.EmitStoreField(fi); 711 _instructions.EmitLoadLocal(local.Index); 712 _locals.UndefineLocal(local, _instructions.Count); 713 } 714 else 715 { 716 _instructions.EmitStoreField(fi); 717 } 718 } 719 } 720 CompileVariableAssignment(BinaryExpression node, bool asVoid)721 private void CompileVariableAssignment(BinaryExpression node, bool asVoid) 722 { 723 Compile(node.Right); 724 725 var target = (ParameterExpression)node.Left; 726 CompileSetVariable(target, asVoid); 727 } 728 CompileAssignBinaryExpression(Expression expr, bool asVoid)729 private void CompileAssignBinaryExpression(Expression expr, bool asVoid) 730 { 731 var node = (BinaryExpression)expr; 732 733 switch (node.Left.NodeType) 734 { 735 case ExpressionType.Index: 736 CompileIndexAssignment(node, asVoid); 737 break; 738 739 case ExpressionType.MemberAccess: 740 CompileMemberAssignment(node, asVoid); 741 break; 742 743 case ExpressionType.Parameter: 744 case ExpressionType.Extension: 745 CompileVariableAssignment(node, asVoid); 746 break; 747 748 default: 749 throw Error.InvalidLvalue(node.Left.NodeType); 750 } 751 } 752 CompileBinaryExpression(Expression expr)753 private void CompileBinaryExpression(Expression expr) 754 { 755 var node = (BinaryExpression)expr; 756 757 if (node.Method != null) 758 { 759 if (node.IsLifted) 760 { 761 // lifting: we need to do the null checks for nullable types and reference types. If the value 762 // is null we return null, or false for a comparison unless it's not equal, in which case we return 763 // true. 764 765 // INCOMPAT: The DLR binder short circuits on comparisons other than equal and not equal, 766 // but C# doesn't. 767 BranchLabel end = _instructions.MakeLabel(); 768 769 LocalDefinition leftTemp = _locals.DefineLocal(Expression.Parameter(node.Left.Type), _instructions.Count); 770 Compile(node.Left); 771 _instructions.EmitStoreLocal(leftTemp.Index); 772 773 LocalDefinition rightTemp = _locals.DefineLocal(Expression.Parameter(node.Right.Type), _instructions.Count); 774 Compile(node.Right); 775 _instructions.EmitStoreLocal(rightTemp.Index); 776 777 switch (node.NodeType) 778 { 779 case ExpressionType.Equal: 780 case ExpressionType.NotEqual: 781 /* generating (equal/not equal): 782 * if(left == null) { 783 * right == null/right != null 784 * }else if(right == null) { 785 * False/True 786 * }else{ 787 * op_Equality(left, right)/op_Inequality(left, right) 788 * } 789 */ 790 if (node.IsLiftedToNull) 791 { 792 goto default; 793 } 794 795 BranchLabel testRight = _instructions.MakeLabel(); 796 BranchLabel callMethod = _instructions.MakeLabel(); 797 798 _instructions.EmitLoadLocal(leftTemp.Index); 799 _instructions.EmitLoad(null, typeof(object)); 800 _instructions.EmitEqual(typeof(object)); 801 _instructions.EmitBranchFalse(testRight); 802 803 // left is null 804 _instructions.EmitLoadLocal(rightTemp.Index); 805 _instructions.EmitLoad(null, typeof(object)); 806 if (node.NodeType == ExpressionType.Equal) 807 { 808 _instructions.EmitEqual(typeof(object)); 809 } 810 else 811 { 812 _instructions.EmitNotEqual(typeof(object)); 813 } 814 _instructions.EmitBranch(end, hasResult: false, hasValue: true); 815 816 _instructions.MarkLabel(testRight); 817 818 // left is not null, check right 819 _instructions.EmitLoadLocal(rightTemp.Index); 820 _instructions.EmitLoad(null, typeof(object)); 821 _instructions.EmitEqual(typeof(object)); 822 _instructions.EmitBranchFalse(callMethod); 823 824 // right null, left not, false 825 // right null, left not, true 826 _instructions.EmitLoad( 827 node.NodeType == ExpressionType.Equal ? AstUtils.BoxedFalse : AstUtils.BoxedTrue, 828 typeof(bool)); 829 _instructions.EmitBranch(end, hasResult: false, hasValue: true); 830 831 // both are not null 832 _instructions.MarkLabel(callMethod); 833 _instructions.EmitLoadLocal(leftTemp.Index); 834 _instructions.EmitLoadLocal(rightTemp.Index); 835 _instructions.EmitCall(node.Method); 836 break; 837 default: 838 BranchLabel loadDefault = _instructions.MakeLabel(); 839 840 if (node.Left.Type.IsNullableOrReferenceType()) 841 { 842 _instructions.EmitLoadLocal(leftTemp.Index); 843 _instructions.EmitLoad(null, typeof(object)); 844 _instructions.EmitEqual(typeof(object)); 845 _instructions.EmitBranchTrue(loadDefault); 846 } 847 848 if (node.Right.Type.IsNullableOrReferenceType()) 849 { 850 _instructions.EmitLoadLocal(rightTemp.Index); 851 _instructions.EmitLoad(null, typeof(object)); 852 _instructions.EmitEqual(typeof(object)); 853 _instructions.EmitBranchTrue(loadDefault); 854 } 855 856 _instructions.EmitLoadLocal(leftTemp.Index); 857 _instructions.EmitLoadLocal(rightTemp.Index); 858 _instructions.EmitCall(node.Method); 859 _instructions.EmitBranch(end, hasResult: false, hasValue: true); 860 861 _instructions.MarkLabel(loadDefault); 862 switch (node.NodeType) 863 { 864 case ExpressionType.LessThan: 865 case ExpressionType.LessThanOrEqual: 866 case ExpressionType.GreaterThan: 867 case ExpressionType.GreaterThanOrEqual: 868 if (node.IsLiftedToNull) 869 { 870 goto default; 871 } 872 _instructions.EmitLoad(AstUtils.BoxedFalse, typeof(object)); 873 break; 874 default: 875 _instructions.EmitLoad(null, typeof(object)); 876 break; 877 } 878 break; 879 } 880 881 _instructions.MarkLabel(end); 882 883 _locals.UndefineLocal(leftTemp, _instructions.Count); 884 _locals.UndefineLocal(rightTemp, _instructions.Count); 885 } 886 else 887 { 888 Compile(node.Left); 889 Compile(node.Right); 890 _instructions.EmitCall(node.Method); 891 } 892 } 893 else 894 { 895 switch (node.NodeType) 896 { 897 case ExpressionType.ArrayIndex: 898 Debug.Assert(node.Right.Type == typeof(int)); 899 Compile(node.Left); 900 Compile(node.Right); 901 _instructions.EmitGetArrayItem(); 902 return; 903 904 case ExpressionType.Add: 905 case ExpressionType.AddChecked: 906 case ExpressionType.Subtract: 907 case ExpressionType.SubtractChecked: 908 case ExpressionType.Multiply: 909 case ExpressionType.MultiplyChecked: 910 case ExpressionType.Divide: 911 case ExpressionType.Modulo: 912 CompileArithmetic(node.NodeType, node.Left, node.Right); 913 return; 914 915 case ExpressionType.ExclusiveOr: 916 Compile(node.Left); 917 Compile(node.Right); 918 _instructions.EmitExclusiveOr(node.Left.Type); 919 break; 920 case ExpressionType.Or: 921 Compile(node.Left); 922 Compile(node.Right); 923 _instructions.EmitOr(node.Left.Type); 924 break; 925 case ExpressionType.And: 926 Compile(node.Left); 927 Compile(node.Right); 928 _instructions.EmitAnd(node.Left.Type); 929 break; 930 931 case ExpressionType.Equal: 932 CompileEqual(node.Left, node.Right, node.IsLiftedToNull); 933 return; 934 935 case ExpressionType.NotEqual: 936 CompileNotEqual(node.Left, node.Right, node.IsLiftedToNull); 937 return; 938 939 case ExpressionType.LessThan: 940 case ExpressionType.LessThanOrEqual: 941 case ExpressionType.GreaterThan: 942 case ExpressionType.GreaterThanOrEqual: 943 CompileComparison((BinaryExpression)node); 944 return; 945 946 case ExpressionType.LeftShift: 947 Compile(node.Left); 948 Compile(node.Right); 949 _instructions.EmitLeftShift(node.Left.Type); 950 break; 951 case ExpressionType.RightShift: 952 Compile(node.Left); 953 Compile(node.Right); 954 _instructions.EmitRightShift(node.Left.Type); 955 break; 956 default: 957 throw new PlatformNotSupportedException(SR.Format(SR.UnsupportedExpressionType, node.NodeType)); 958 } 959 } 960 } 961 962 #if DEBUG IsNullComparison(Expression left, Expression right)963 private static bool IsNullComparison(Expression left, Expression right) 964 { 965 return IsNullConstant(left) 966 ? !IsNullConstant(right) && right.Type.IsNullableType() 967 : IsNullConstant(right) && left.Type.IsNullableType(); 968 } 969 IsNullConstant(Expression e)970 private static bool IsNullConstant(Expression e) 971 { 972 var c = e as ConstantExpression; 973 return c != null && c.Value == null; 974 } 975 #endif CompileEqual(Expression left, Expression right, bool liftedToNull)976 private void CompileEqual(Expression left, Expression right, bool liftedToNull) 977 { 978 #if DEBUG 979 Debug.Assert(IsNullComparison(left, right) || left.Type == right.Type || !left.Type.IsValueType && !right.Type.IsValueType); 980 #endif 981 Compile(left); 982 Compile(right); 983 _instructions.EmitEqual(left.Type, liftedToNull); 984 } 985 CompileNotEqual(Expression left, Expression right, bool liftedToNull)986 private void CompileNotEqual(Expression left, Expression right, bool liftedToNull) 987 { 988 #if DEBUG 989 Debug.Assert(IsNullComparison(left, right) || left.Type == right.Type || !left.Type.IsValueType && !right.Type.IsValueType); 990 #endif 991 Compile(left); 992 Compile(right); 993 _instructions.EmitNotEqual(left.Type, liftedToNull); 994 } 995 CompileComparison(BinaryExpression node)996 private void CompileComparison(BinaryExpression node) 997 { 998 Expression left = node.Left; 999 Expression right = node.Right; 1000 Debug.Assert(left.Type == right.Type && left.Type.IsNumeric()); 1001 1002 Compile(left); 1003 Compile(right); 1004 1005 switch (node.NodeType) 1006 { 1007 case ExpressionType.LessThan: _instructions.EmitLessThan(left.Type, node.IsLiftedToNull); break; 1008 case ExpressionType.LessThanOrEqual: _instructions.EmitLessThanOrEqual(left.Type, node.IsLiftedToNull); break; 1009 case ExpressionType.GreaterThan: _instructions.EmitGreaterThan(left.Type, node.IsLiftedToNull); break; 1010 case ExpressionType.GreaterThanOrEqual: _instructions.EmitGreaterThanOrEqual(left.Type, node.IsLiftedToNull); break; 1011 default: throw ContractUtils.Unreachable; 1012 } 1013 } 1014 CompileArithmetic(ExpressionType nodeType, Expression left, Expression right)1015 private void CompileArithmetic(ExpressionType nodeType, Expression left, Expression right) 1016 { 1017 Debug.Assert(left.Type == right.Type && left.Type.IsArithmetic()); 1018 Compile(left); 1019 Compile(right); 1020 switch (nodeType) 1021 { 1022 case ExpressionType.Add: _instructions.EmitAdd(left.Type, @checked: false); break; 1023 case ExpressionType.AddChecked: _instructions.EmitAdd(left.Type, @checked: true); break; 1024 case ExpressionType.Subtract: _instructions.EmitSub(left.Type, @checked: false); break; 1025 case ExpressionType.SubtractChecked: _instructions.EmitSub(left.Type, @checked: true); break; 1026 case ExpressionType.Multiply: _instructions.EmitMul(left.Type, @checked: false); break; 1027 case ExpressionType.MultiplyChecked: _instructions.EmitMul(left.Type, @checked: true); break; 1028 case ExpressionType.Divide: _instructions.EmitDiv(left.Type); break; 1029 case ExpressionType.Modulo: _instructions.EmitModulo(left.Type); break; 1030 default: throw ContractUtils.Unreachable; 1031 } 1032 } 1033 CompileConvertUnaryExpression(Expression expr)1034 private void CompileConvertUnaryExpression(Expression expr) 1035 { 1036 var node = (UnaryExpression)expr; 1037 if (node.Method != null) 1038 { 1039 BranchLabel end = _instructions.MakeLabel(); 1040 BranchLabel loadDefault = _instructions.MakeLabel(); 1041 MethodInfo method = node.Method; 1042 ParameterInfo[] parameters = method.GetParametersCached(); 1043 Debug.Assert(parameters.Length == 1); 1044 ParameterInfo parameter = parameters[0]; 1045 Expression operand = node.Operand; 1046 Type operandType = operand.Type; 1047 LocalDefinition opTemp = _locals.DefineLocal(Expression.Parameter(operandType), _instructions.Count); 1048 ByRefUpdater updater = null; 1049 Type parameterType = parameter.ParameterType; 1050 if (parameterType.IsByRef) 1051 { 1052 if (node.IsLifted) 1053 { 1054 Compile(node.Operand); 1055 } 1056 else 1057 { 1058 updater = CompileAddress(node.Operand, 0); 1059 parameterType = parameterType.GetElementType(); 1060 } 1061 } 1062 else 1063 { 1064 Compile(node.Operand); 1065 } 1066 1067 _instructions.EmitStoreLocal(opTemp.Index); 1068 1069 if (!operandType.IsValueType || operandType.IsNullableType() && node.IsLiftedToNull) 1070 { 1071 _instructions.EmitLoadLocal(opTemp.Index); 1072 _instructions.EmitLoad(null, typeof(object)); 1073 _instructions.EmitEqual(typeof(object)); 1074 _instructions.EmitBranchTrue(loadDefault); 1075 } 1076 1077 _instructions.EmitLoadLocal(opTemp.Index); 1078 if (operandType.IsNullableType() && parameterType.Equals(operandType.GetNonNullableType())) 1079 { 1080 _instructions.Emit(NullableMethodCallInstruction.CreateGetValue()); 1081 } 1082 1083 if (updater == null) 1084 { 1085 _instructions.EmitCall(method); 1086 } 1087 else 1088 { 1089 _instructions.EmitByRefCall(method, parameters, new[] {updater}); 1090 updater.UndefineTemps(_instructions, _locals); 1091 } 1092 1093 _instructions.EmitBranch(end, hasResult: false, hasValue: true); 1094 1095 _instructions.MarkLabel(loadDefault); 1096 _instructions.EmitLoad(null, typeof(object)); 1097 1098 _instructions.MarkLabel(end); 1099 1100 _locals.UndefineLocal(opTemp, _instructions.Count); 1101 } 1102 else if (node.Type == typeof(void)) 1103 { 1104 CompileAsVoid(node.Operand); 1105 } 1106 else 1107 { 1108 Compile(node.Operand); 1109 CompileConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked, node.IsLiftedToNull); 1110 } 1111 } 1112 CompileConvertToType(Type typeFrom, Type typeTo, bool isChecked, bool isLiftedToNull)1113 private void CompileConvertToType(Type typeFrom, Type typeTo, bool isChecked, bool isLiftedToNull) 1114 { 1115 Debug.Assert(typeFrom != typeof(void) && typeTo != typeof(void)); 1116 1117 if (typeTo.Equals(typeFrom)) 1118 { 1119 return; 1120 } 1121 1122 if (typeFrom.IsValueType && 1123 typeTo.IsNullableType() && 1124 typeTo.GetNonNullableType().Equals(typeFrom)) 1125 { 1126 // VT -> vt?, no conversion necessary 1127 return; 1128 } 1129 1130 if (typeTo.IsValueType && 1131 typeFrom.IsNullableType() && 1132 typeFrom.GetNonNullableType().Equals(typeTo)) 1133 { 1134 // VT? -> vt, call get_Value 1135 _instructions.Emit(NullableMethodCallInstruction.CreateGetValue()); 1136 return; 1137 } 1138 1139 Type nonNullableFrom = typeFrom.GetNonNullableType(); 1140 Type nonNullableTo = typeTo.GetNonNullableType(); 1141 1142 // use numeric conversions for both numeric types and enums 1143 if ((nonNullableFrom.IsNumericOrBool() || nonNullableFrom.IsEnum) 1144 && (nonNullableTo.IsNumericOrBool() || nonNullableTo.IsEnum || nonNullableTo == typeof(decimal))) 1145 { 1146 Type enumTypeTo = null; 1147 1148 if (nonNullableFrom.IsEnum) 1149 { 1150 nonNullableFrom = Enum.GetUnderlyingType(nonNullableFrom); 1151 } 1152 if (nonNullableTo.IsEnum) 1153 { 1154 enumTypeTo = nonNullableTo; 1155 nonNullableTo = Enum.GetUnderlyingType(nonNullableTo); 1156 } 1157 1158 TypeCode from = nonNullableFrom.GetTypeCode(); 1159 TypeCode to = nonNullableTo.GetTypeCode(); 1160 1161 if (from == to) 1162 { 1163 if ((object)enumTypeTo != null) 1164 { 1165 // If casting between enums of the same underlying type or to enum from the underlying 1166 // type, there's no need for the numeric conversion, so just include a null-check if 1167 // appropriate. 1168 if (typeFrom.IsNullableType() && !typeTo.IsNullableType()) 1169 { 1170 _instructions.Emit(NullableMethodCallInstruction.CreateGetValue()); 1171 } 1172 } 1173 else 1174 { 1175 // Casting to the underlying check still needs a numeric conversion to force the type 1176 // change that EmitCastToEnum provides for enums, but needs only one cast. Checked can 1177 // also never throw, so always be unchecked. 1178 _instructions.EmitConvertToUnderlying(to, isLiftedToNull); 1179 } 1180 } 1181 else 1182 { 1183 if (isChecked) 1184 { 1185 _instructions.EmitNumericConvertChecked(from, to, isLiftedToNull); 1186 } 1187 else 1188 { 1189 _instructions.EmitNumericConvertUnchecked(from, to, isLiftedToNull); 1190 } 1191 } 1192 1193 if ((object)enumTypeTo != null) 1194 { 1195 // Convert from underlying to the enum 1196 _instructions.EmitCastToEnum(enumTypeTo); 1197 } 1198 1199 return; 1200 } 1201 1202 if (typeTo.IsEnum) 1203 { 1204 _instructions.Emit(NullCheckInstruction.Instance); 1205 _instructions.EmitCastReferenceToEnum(typeTo); 1206 return; 1207 } 1208 1209 if (typeTo == typeof(object) || typeTo.IsAssignableFrom(typeFrom)) 1210 { 1211 // Conversions to a super-class or implemented interfaces are no-op. 1212 return; 1213 } 1214 1215 // A conversion to a non-implemented interface or an unrelated class, etc. should fail. 1216 _instructions.EmitCast(typeTo); 1217 } 1218 CompileNotExpression(UnaryExpression node)1219 private void CompileNotExpression(UnaryExpression node) 1220 { 1221 Compile(node.Operand); 1222 _instructions.EmitNot(node.Operand.Type); 1223 } 1224 CompileUnaryExpression(Expression expr)1225 private void CompileUnaryExpression(Expression expr) 1226 { 1227 var node = (UnaryExpression)expr; 1228 1229 if (node.Method != null) 1230 { 1231 EmitUnaryMethodCall(node); 1232 } 1233 else 1234 { 1235 switch (node.NodeType) 1236 { 1237 case ExpressionType.Not: 1238 case ExpressionType.OnesComplement: 1239 CompileNotExpression(node); 1240 break; 1241 case ExpressionType.TypeAs: 1242 CompileTypeAsExpression(node); 1243 break; 1244 case ExpressionType.ArrayLength: 1245 Compile(node.Operand); 1246 _instructions.EmitArrayLength(); 1247 break; 1248 case ExpressionType.NegateChecked: 1249 Compile(node.Operand); 1250 _instructions.EmitNegateChecked(node.Type); 1251 break; 1252 case ExpressionType.Negate: 1253 Compile(node.Operand); 1254 _instructions.EmitNegate(node.Type); 1255 break; 1256 case ExpressionType.Increment: 1257 Compile(node.Operand); 1258 _instructions.EmitIncrement(node.Type); 1259 break; 1260 case ExpressionType.Decrement: 1261 Compile(node.Operand); 1262 _instructions.EmitDecrement(node.Type); 1263 break; 1264 case ExpressionType.UnaryPlus: 1265 Compile(node.Operand); 1266 break; 1267 case ExpressionType.IsTrue: 1268 case ExpressionType.IsFalse: 1269 EmitUnaryBoolCheck(node); 1270 break; 1271 default: 1272 throw new PlatformNotSupportedException(SR.Format(SR.UnsupportedExpressionType, node.NodeType)); 1273 } 1274 } 1275 } 1276 EmitUnaryMethodCall(UnaryExpression node)1277 private void EmitUnaryMethodCall(UnaryExpression node) 1278 { 1279 Compile(node.Operand); 1280 if (node.IsLifted) 1281 { 1282 BranchLabel notNull = _instructions.MakeLabel(); 1283 BranchLabel computed = _instructions.MakeLabel(); 1284 1285 _instructions.EmitCoalescingBranch(notNull); 1286 _instructions.EmitBranch(computed); 1287 1288 _instructions.MarkLabel(notNull); 1289 _instructions.EmitCall(node.Method); 1290 1291 _instructions.MarkLabel(computed); 1292 } 1293 else 1294 { 1295 _instructions.EmitCall(node.Method); 1296 } 1297 } 1298 EmitUnaryBoolCheck(UnaryExpression node)1299 private void EmitUnaryBoolCheck(UnaryExpression node) 1300 { 1301 Compile(node.Operand); 1302 if (node.IsLifted) 1303 { 1304 BranchLabel notNull = _instructions.MakeLabel(); 1305 BranchLabel computed = _instructions.MakeLabel(); 1306 1307 _instructions.EmitCoalescingBranch(notNull); 1308 _instructions.EmitBranch(computed); 1309 1310 _instructions.MarkLabel(notNull); 1311 _instructions.EmitLoad(node.NodeType == ExpressionType.IsTrue); 1312 _instructions.EmitEqual(typeof(bool)); 1313 1314 _instructions.MarkLabel(computed); 1315 } 1316 else 1317 { 1318 _instructions.EmitLoad(node.NodeType == ExpressionType.IsTrue); 1319 _instructions.EmitEqual(typeof(bool)); 1320 } 1321 } 1322 CompileAndAlsoBinaryExpression(Expression expr)1323 private void CompileAndAlsoBinaryExpression(Expression expr) 1324 { 1325 CompileLogicalBinaryExpression((BinaryExpression)expr, andAlso: true); 1326 } 1327 CompileOrElseBinaryExpression(Expression expr)1328 private void CompileOrElseBinaryExpression(Expression expr) 1329 { 1330 CompileLogicalBinaryExpression((BinaryExpression)expr, andAlso: false); 1331 } 1332 CompileLogicalBinaryExpression(BinaryExpression b, bool andAlso)1333 private void CompileLogicalBinaryExpression(BinaryExpression b, bool andAlso) 1334 { 1335 if (b.Method != null && !b.IsLiftedLogical) 1336 { 1337 CompileMethodLogicalBinaryExpression(b, andAlso); 1338 } 1339 else if (b.Left.Type == typeof(bool?)) 1340 { 1341 CompileLiftedLogicalBinaryExpression(b, andAlso); 1342 } 1343 else if (b.IsLiftedLogical) 1344 { 1345 Compile(b.ReduceUserdefinedLifted()); 1346 } 1347 else 1348 { 1349 CompileUnliftedLogicalBinaryExpression(b, andAlso); 1350 } 1351 } 1352 CompileMethodLogicalBinaryExpression(BinaryExpression expr, bool andAlso)1353 private void CompileMethodLogicalBinaryExpression(BinaryExpression expr, bool andAlso) 1354 { 1355 BranchLabel labEnd = _instructions.MakeLabel(); 1356 Compile(expr.Left); 1357 _instructions.EmitDup(); 1358 1359 MethodInfo opTrue = TypeUtils.GetBooleanOperator(expr.Method.DeclaringType, andAlso ? "op_False" : "op_True"); 1360 Debug.Assert(opTrue != null, "factory should check that the method exists"); 1361 _instructions.EmitCall(opTrue); 1362 _instructions.EmitBranchTrue(labEnd); 1363 1364 Compile(expr.Right); 1365 1366 Debug.Assert(expr.Method.IsStatic); 1367 _instructions.EmitCall(expr.Method); 1368 1369 _instructions.MarkLabel(labEnd); 1370 } 1371 CompileLiftedLogicalBinaryExpression(BinaryExpression node, bool andAlso)1372 private void CompileLiftedLogicalBinaryExpression(BinaryExpression node, bool andAlso) 1373 { 1374 BranchLabel computeRight = _instructions.MakeLabel(); 1375 BranchLabel returnFalse = _instructions.MakeLabel(); 1376 BranchLabel returnNull = _instructions.MakeLabel(); 1377 BranchLabel returnValue = _instructions.MakeLabel(); 1378 LocalDefinition result = _locals.DefineLocal(Expression.Parameter(node.Left.Type), _instructions.Count); 1379 LocalDefinition leftTemp = _locals.DefineLocal(Expression.Parameter(node.Left.Type), _instructions.Count); 1380 1381 Compile(node.Left); 1382 _instructions.EmitStoreLocal(leftTemp.Index); 1383 1384 _instructions.EmitLoadLocal(leftTemp.Index); 1385 _instructions.EmitLoad(null, typeof(object)); 1386 _instructions.EmitEqual(typeof(object)); 1387 1388 _instructions.EmitBranchTrue(computeRight); 1389 1390 _instructions.EmitLoadLocal(leftTemp.Index); 1391 1392 if (andAlso) 1393 { 1394 _instructions.EmitBranchFalse(returnFalse); 1395 } 1396 else 1397 { 1398 _instructions.EmitBranchTrue(returnFalse); 1399 } 1400 1401 // compute right 1402 _instructions.MarkLabel(computeRight); 1403 LocalDefinition rightTemp = _locals.DefineLocal(Expression.Parameter(node.Right.Type), _instructions.Count); 1404 Compile(node.Right); 1405 _instructions.EmitStoreLocal(rightTemp.Index); 1406 1407 _instructions.EmitLoadLocal(rightTemp.Index); 1408 _instructions.EmitLoad(null, typeof(object)); 1409 _instructions.EmitEqual(typeof(object)); 1410 _instructions.EmitBranchTrue(returnNull); 1411 1412 _instructions.EmitLoadLocal(rightTemp.Index); 1413 if (andAlso) 1414 { 1415 _instructions.EmitBranchFalse(returnFalse); 1416 } 1417 else 1418 { 1419 _instructions.EmitBranchTrue(returnFalse); 1420 } 1421 1422 // check left for null again 1423 _instructions.EmitLoadLocal(leftTemp.Index); 1424 _instructions.EmitLoad(null, typeof(object)); 1425 _instructions.EmitEqual(typeof(object)); 1426 _instructions.EmitBranchTrue(returnNull); 1427 1428 // return true 1429 _instructions.EmitLoad(andAlso ? AstUtils.BoxedTrue : AstUtils.BoxedFalse, typeof(object)); 1430 _instructions.EmitStoreLocal(result.Index); 1431 _instructions.EmitBranch(returnValue); 1432 1433 // return false 1434 _instructions.MarkLabel(returnFalse); 1435 _instructions.EmitLoad(andAlso ? AstUtils.BoxedFalse : AstUtils.BoxedTrue, typeof(object)); 1436 _instructions.EmitStoreLocal(result.Index); 1437 _instructions.EmitBranch(returnValue); 1438 1439 // return null 1440 _instructions.MarkLabel(returnNull); 1441 _instructions.EmitLoad(null, typeof(object)); 1442 _instructions.EmitStoreLocal(result.Index); 1443 1444 _instructions.MarkLabel(returnValue); 1445 _instructions.EmitLoadLocal(result.Index); 1446 1447 _locals.UndefineLocal(leftTemp, _instructions.Count); 1448 _locals.UndefineLocal(rightTemp, _instructions.Count); 1449 _locals.UndefineLocal(result, _instructions.Count); 1450 } 1451 CompileUnliftedLogicalBinaryExpression(BinaryExpression expr, bool andAlso)1452 private void CompileUnliftedLogicalBinaryExpression(BinaryExpression expr, bool andAlso) 1453 { 1454 BranchLabel elseLabel = _instructions.MakeLabel(); 1455 BranchLabel endLabel = _instructions.MakeLabel(); 1456 Compile(expr.Left); 1457 1458 if (andAlso) 1459 { 1460 _instructions.EmitBranchFalse(elseLabel); 1461 } 1462 else 1463 { 1464 _instructions.EmitBranchTrue(elseLabel); 1465 } 1466 Compile(expr.Right); 1467 _instructions.EmitBranch(endLabel, hasResult: false, hasValue: true); 1468 _instructions.MarkLabel(elseLabel); 1469 _instructions.EmitLoad(!andAlso); 1470 _instructions.MarkLabel(endLabel); 1471 } 1472 CompileConditionalExpression(Expression expr, bool asVoid)1473 private void CompileConditionalExpression(Expression expr, bool asVoid) 1474 { 1475 var node = (ConditionalExpression)expr; 1476 Compile(node.Test); 1477 1478 if (node.IfTrue == AstUtils.Empty) 1479 { 1480 BranchLabel endOfFalse = _instructions.MakeLabel(); 1481 _instructions.EmitBranchTrue(endOfFalse); 1482 Compile(node.IfFalse, asVoid); 1483 _instructions.MarkLabel(endOfFalse); 1484 } 1485 else 1486 { 1487 BranchLabel endOfTrue = _instructions.MakeLabel(); 1488 _instructions.EmitBranchFalse(endOfTrue); 1489 Compile(node.IfTrue, asVoid); 1490 1491 if (node.IfFalse != AstUtils.Empty) 1492 { 1493 BranchLabel endOfFalse = _instructions.MakeLabel(); 1494 _instructions.EmitBranch(endOfFalse, false, !asVoid); 1495 _instructions.MarkLabel(endOfTrue); 1496 Compile(node.IfFalse, asVoid); 1497 _instructions.MarkLabel(endOfFalse); 1498 } 1499 else 1500 { 1501 _instructions.MarkLabel(endOfTrue); 1502 } 1503 } 1504 } 1505 CompileLoopExpression(Expression expr)1506 private void CompileLoopExpression(Expression expr) 1507 { 1508 var node = (LoopExpression)expr; 1509 1510 PushLabelBlock(LabelScopeKind.Statement); 1511 LabelInfo breakLabel = DefineLabel(node.BreakLabel); 1512 LabelInfo continueLabel = DefineLabel(node.ContinueLabel); 1513 1514 _instructions.MarkLabel(continueLabel.GetLabel(this)); 1515 1516 // emit loop body: 1517 CompileAsVoid(node.Body); 1518 1519 // emit loop branch: 1520 _instructions.EmitBranch(continueLabel.GetLabel(this), node.Type != typeof(void), hasValue: false); 1521 1522 _instructions.MarkLabel(breakLabel.GetLabel(this)); 1523 1524 PopLabelBlock(LabelScopeKind.Statement); 1525 } 1526 CompileSwitchExpression(Expression expr)1527 private void CompileSwitchExpression(Expression expr) 1528 { 1529 var node = (SwitchExpression)expr; 1530 1531 if (node.Cases.All(c => c.TestValues.All(t => t is ConstantExpression))) 1532 { 1533 if (node.Cases.Count == 0) 1534 { 1535 // Emit the switch value in case it has side-effects, but as void 1536 // since the value is ignored. 1537 CompileAsVoid(node.SwitchValue); 1538 1539 // Now if there is a default body, it happens unconditionally. 1540 if (node.DefaultBody != null) 1541 { 1542 Compile(node.DefaultBody); 1543 } 1544 else 1545 { 1546 // If there are no cases and no default then the type must be void. 1547 // Assert that earlier validation caught any exceptions to that. 1548 Debug.Assert(node.Type == typeof(void)); 1549 } 1550 return; 1551 } 1552 1553 TypeCode switchType = node.SwitchValue.Type.GetTypeCode(); 1554 1555 if (node.Comparison == null) 1556 { 1557 switch (switchType) 1558 { 1559 case TypeCode.Int32: 1560 CompileIntSwitchExpression<int>(node); 1561 return; 1562 1563 // the following cases are uncommon, 1564 // so to avoid numerous unnecessary generic 1565 // instantiations of Dictionary<K, V> and related types 1566 // in AOT scenarios, we will just use "object" as the key 1567 // NOTE: this does not actually result in any 1568 // extra boxing since both keys and values 1569 // are already boxed when we get them 1570 case TypeCode.Byte: 1571 case TypeCode.SByte: 1572 case TypeCode.UInt16: 1573 case TypeCode.Int16: 1574 case TypeCode.UInt32: 1575 case TypeCode.UInt64: 1576 case TypeCode.Int64: 1577 CompileIntSwitchExpression<object>(node); 1578 return; 1579 } 1580 } 1581 1582 if (switchType == TypeCode.String) 1583 { 1584 // If we have a comparison other than string equality, bail 1585 MethodInfo equality = String_op_Equality_String_String; 1586 if (equality != null && !equality.IsStatic) 1587 { 1588 equality = null; 1589 } 1590 1591 if (object.Equals(node.Comparison, equality)) 1592 { 1593 CompileStringSwitchExpression(node); 1594 return; 1595 } 1596 } 1597 } 1598 1599 LocalDefinition temp = _locals.DefineLocal(Expression.Parameter(node.SwitchValue.Type), _instructions.Count); 1600 Compile(node.SwitchValue); 1601 _instructions.EmitStoreLocal(temp.Index); 1602 1603 LabelTarget doneLabel = Expression.Label(node.Type, "done"); 1604 1605 foreach (SwitchCase @case in node.Cases) 1606 { 1607 foreach (Expression val in @case.TestValues) 1608 { 1609 // temp == val ? 1610 // goto(Body) doneLabel: 1611 // {}; 1612 CompileConditionalExpression( 1613 Expression.Condition( 1614 Expression.Equal(temp.Parameter, val, false, node.Comparison), 1615 Expression.Goto(doneLabel, @case.Body), 1616 AstUtils.Empty 1617 ), 1618 asVoid: true); 1619 } 1620 } 1621 1622 // doneLabel(DefaultBody): 1623 CompileLabelExpression(Expression.Label(doneLabel, node.DefaultBody)); 1624 1625 _locals.UndefineLocal(temp, _instructions.Count); 1626 } 1627 CompileIntSwitchExpression(SwitchExpression node)1628 private void CompileIntSwitchExpression<T>(SwitchExpression node) 1629 { 1630 LabelInfo end = DefineLabel(node: null); 1631 bool hasValue = node.Type != typeof(void); 1632 1633 Compile(node.SwitchValue); 1634 var caseDict = new Dictionary<T, int>(); 1635 int switchIndex = _instructions.Count; 1636 _instructions.EmitIntSwitch(caseDict); 1637 1638 if (node.DefaultBody != null) 1639 { 1640 Compile(node.DefaultBody, !hasValue); 1641 } 1642 else 1643 { 1644 Debug.Assert(!hasValue); 1645 } 1646 _instructions.EmitBranch(end.GetLabel(this), false, hasValue); 1647 1648 for (int i = 0; i < node.Cases.Count; i++) 1649 { 1650 SwitchCase switchCase = node.Cases[i]; 1651 1652 int caseOffset = _instructions.Count - switchIndex; 1653 foreach (ConstantExpression testValue in switchCase.TestValues) 1654 { 1655 var key = (T)testValue.Value; 1656 caseDict.TryAdd(key, caseOffset); 1657 } 1658 1659 Compile(switchCase.Body, !hasValue); 1660 1661 if (i < node.Cases.Count - 1) 1662 { 1663 _instructions.EmitBranch(end.GetLabel(this), false, hasValue); 1664 } 1665 } 1666 1667 _instructions.MarkLabel(end.GetLabel(this)); 1668 } 1669 CompileStringSwitchExpression(SwitchExpression node)1670 private void CompileStringSwitchExpression(SwitchExpression node) 1671 { 1672 LabelInfo end = DefineLabel(node: null); 1673 bool hasValue = node.Type != typeof(void); 1674 1675 Compile(node.SwitchValue); 1676 var caseDict = new Dictionary<string, int>(); 1677 int switchIndex = _instructions.Count; 1678 // by default same as default 1679 var nullCase = new StrongBox<int>(1); 1680 _instructions.EmitStringSwitch(caseDict, nullCase); 1681 1682 if (node.DefaultBody != null) 1683 { 1684 Compile(node.DefaultBody, !hasValue); 1685 } 1686 else 1687 { 1688 Debug.Assert(!hasValue); 1689 } 1690 _instructions.EmitBranch(end.GetLabel(this), false, hasValue); 1691 1692 for (int i = 0; i < node.Cases.Count; i++) 1693 { 1694 SwitchCase switchCase = node.Cases[i]; 1695 1696 int caseOffset = _instructions.Count - switchIndex; 1697 foreach (ConstantExpression testValue in switchCase.TestValues) 1698 { 1699 var key = (string)testValue.Value; 1700 if (key == null) 1701 { 1702 if (nullCase.Value == 1) 1703 { 1704 nullCase.Value = caseOffset; 1705 } 1706 } 1707 else 1708 { 1709 caseDict.TryAdd(key, caseOffset); 1710 } 1711 } 1712 1713 Compile(switchCase.Body, !hasValue); 1714 1715 if (i < node.Cases.Count - 1) 1716 { 1717 _instructions.EmitBranch(end.GetLabel(this), false, hasValue); 1718 } 1719 } 1720 1721 _instructions.MarkLabel(end.GetLabel(this)); 1722 } 1723 CompileLabelExpression(Expression expr)1724 private void CompileLabelExpression(Expression expr) 1725 { 1726 var node = (LabelExpression)expr; 1727 1728 // If we're an immediate child of a block, our label will already 1729 // be defined. If not, we need to define our own block so this 1730 // label isn't exposed except to its own child expression. 1731 LabelInfo label = null; 1732 1733 if (_labelBlock.Kind == LabelScopeKind.Block) 1734 { 1735 _labelBlock.TryGetLabelInfo(node.Target, out label); 1736 1737 // We're in a block but didn't find our label, try switch 1738 if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch) 1739 { 1740 _labelBlock.Parent.TryGetLabelInfo(node.Target, out label); 1741 } 1742 1743 // if we're in a switch or block, we should've found the label 1744 Debug.Assert(label != null); 1745 } 1746 1747 if (label == null) 1748 { 1749 label = DefineLabel(node.Target); 1750 } 1751 1752 if (node.DefaultValue != null) 1753 { 1754 if (node.Target.Type == typeof(void)) 1755 { 1756 CompileAsVoid(node.DefaultValue); 1757 } 1758 else 1759 { 1760 Compile(node.DefaultValue); 1761 } 1762 } 1763 1764 _instructions.MarkLabel(label.GetLabel(this)); 1765 } 1766 CompileGotoExpression(Expression expr)1767 private void CompileGotoExpression(Expression expr) 1768 { 1769 var node = (GotoExpression)expr; 1770 LabelInfo labelInfo = ReferenceLabel(node.Target); 1771 1772 if (node.Value != null) 1773 { 1774 Compile(node.Value); 1775 } 1776 1777 _instructions.EmitGoto(labelInfo.GetLabel(this), 1778 node.Type != typeof(void), 1779 node.Value != null && node.Value.Type != typeof(void), 1780 node.Target.Type != typeof(void)); 1781 } 1782 PushLabelBlock(LabelScopeKind type)1783 private void PushLabelBlock(LabelScopeKind type) 1784 { 1785 _labelBlock = new LabelScopeInfo(_labelBlock, type); 1786 } 1787 1788 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")] PopLabelBlock(LabelScopeKind kind)1789 private void PopLabelBlock(LabelScopeKind kind) 1790 { 1791 Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind); 1792 _labelBlock = _labelBlock.Parent; 1793 } 1794 EnsureLabel(LabelTarget node)1795 private LabelInfo EnsureLabel(LabelTarget node) 1796 { 1797 LabelInfo result; 1798 if (!_treeLabels.TryGetValue(node, out result)) 1799 { 1800 _treeLabels[node] = result = new LabelInfo(node); 1801 } 1802 return result; 1803 } 1804 ReferenceLabel(LabelTarget node)1805 private LabelInfo ReferenceLabel(LabelTarget node) 1806 { 1807 LabelInfo result = EnsureLabel(node); 1808 result.Reference(_labelBlock); 1809 return result; 1810 } 1811 DefineLabel(LabelTarget node)1812 private LabelInfo DefineLabel(LabelTarget node) 1813 { 1814 if (node == null) 1815 { 1816 return new LabelInfo(null); 1817 } 1818 LabelInfo result = EnsureLabel(node); 1819 result.Define(_labelBlock); 1820 return result; 1821 } 1822 TryPushLabelBlock(Expression node)1823 private bool TryPushLabelBlock(Expression node) 1824 { 1825 // Anything that is "statement-like" -- e.g. has no associated 1826 // stack state can be jumped into, with the exception of try-blocks 1827 // We indicate this by a "Block" 1828 // 1829 // Otherwise, we push an "Expression" to indicate that it can't be 1830 // jumped into 1831 switch (node.NodeType) 1832 { 1833 default: 1834 if (_labelBlock.Kind != LabelScopeKind.Expression) 1835 { 1836 PushLabelBlock(LabelScopeKind.Expression); 1837 return true; 1838 } 1839 return false; 1840 case ExpressionType.Label: 1841 // LabelExpression is a bit special, if it's directly in a 1842 // block it becomes associate with the block's scope. Same 1843 // thing if it's in a switch case body. 1844 if (_labelBlock.Kind == LabelScopeKind.Block) 1845 { 1846 LabelTarget label = ((LabelExpression)node).Target; 1847 if (_labelBlock.ContainsTarget(label)) 1848 { 1849 return false; 1850 } 1851 if (_labelBlock.Parent.Kind == LabelScopeKind.Switch && 1852 _labelBlock.Parent.ContainsTarget(label)) 1853 { 1854 return false; 1855 } 1856 } 1857 PushLabelBlock(LabelScopeKind.Statement); 1858 return true; 1859 case ExpressionType.Block: 1860 PushLabelBlock(LabelScopeKind.Block); 1861 // Labels defined immediately in the block are valid for 1862 // the whole block. 1863 if (_labelBlock.Parent.Kind != LabelScopeKind.Switch) 1864 { 1865 DefineBlockLabels(node); 1866 } 1867 return true; 1868 case ExpressionType.Switch: 1869 PushLabelBlock(LabelScopeKind.Switch); 1870 // Define labels inside of the switch cases so they are in 1871 // scope for the whole switch. This allows "goto case" and 1872 // "goto default" to be considered as local jumps. 1873 var @switch = (SwitchExpression)node; 1874 foreach (SwitchCase c in @switch.Cases) 1875 { 1876 DefineBlockLabels(c.Body); 1877 } 1878 DefineBlockLabels(@switch.DefaultBody); 1879 return true; 1880 1881 // Remove this when Convert(Void) goes away. 1882 case ExpressionType.Convert: 1883 if (node.Type != typeof(void)) 1884 { 1885 // treat it as an expression 1886 goto default; 1887 } 1888 PushLabelBlock(LabelScopeKind.Statement); 1889 return true; 1890 1891 case ExpressionType.Conditional: 1892 case ExpressionType.Loop: 1893 case ExpressionType.Goto: 1894 PushLabelBlock(LabelScopeKind.Statement); 1895 return true; 1896 } 1897 } 1898 DefineBlockLabels(Expression node)1899 private void DefineBlockLabels(Expression node) 1900 { 1901 var block = node as BlockExpression; 1902 if (block == null) 1903 { 1904 return; 1905 } 1906 1907 for (int i = 0, n = block.Expressions.Count; i < n; i++) 1908 { 1909 Expression e = block.Expressions[i]; 1910 1911 var label = e as LabelExpression; 1912 if (label != null) 1913 { 1914 DefineLabel(label.Target); 1915 } 1916 } 1917 } 1918 CheckRethrow()1919 private void CheckRethrow() 1920 { 1921 // Rethrow is only valid inside a catch. 1922 for (LabelScopeInfo j = _labelBlock; j != null; j = j.Parent) 1923 { 1924 if (j.Kind == LabelScopeKind.Catch) 1925 { 1926 return; 1927 } 1928 else if (j.Kind == LabelScopeKind.Finally) 1929 { 1930 // Rethrow from inside finally is not verifiable 1931 break; 1932 } 1933 } 1934 throw Error.RethrowRequiresCatch(); 1935 } 1936 CompileThrowUnaryExpression(Expression expr, bool asVoid)1937 private void CompileThrowUnaryExpression(Expression expr, bool asVoid) 1938 { 1939 var node = (UnaryExpression)expr; 1940 1941 if (node.Operand == null) 1942 { 1943 CheckRethrow(); 1944 1945 CompileParameterExpression(_exceptionForRethrowStack.Peek()); 1946 if (asVoid) 1947 { 1948 _instructions.EmitRethrowVoid(); 1949 } 1950 else 1951 { 1952 _instructions.EmitRethrow(); 1953 } 1954 } 1955 else 1956 { 1957 Compile(node.Operand); 1958 if (asVoid) 1959 { 1960 _instructions.EmitThrowVoid(); 1961 } 1962 else 1963 { 1964 _instructions.EmitThrow(); 1965 } 1966 } 1967 } 1968 CompileTryExpression(Expression expr)1969 private void CompileTryExpression(Expression expr) 1970 { 1971 var node = (TryExpression)expr; 1972 if (node.Fault != null) 1973 { 1974 CompileTryFaultExpression(node); 1975 } 1976 else 1977 { 1978 BranchLabel end = _instructions.MakeLabel(); 1979 BranchLabel gotoEnd = _instructions.MakeLabel(); 1980 int tryStart = _instructions.Count; 1981 1982 BranchLabel startOfFinally = null; 1983 if (node.Finally != null) 1984 { 1985 startOfFinally = _instructions.MakeLabel(); 1986 _instructions.EmitEnterTryFinally(startOfFinally); 1987 } 1988 else 1989 { 1990 _instructions.EmitEnterTryCatch(); 1991 } 1992 1993 List<ExceptionHandler> exHandlers = null; 1994 var enterTryInstr = _instructions.GetInstruction(tryStart) as EnterTryCatchFinallyInstruction; 1995 Debug.Assert(enterTryInstr != null); 1996 1997 PushLabelBlock(LabelScopeKind.Try); 1998 bool hasValue = node.Type != typeof(void); 1999 2000 Compile(node.Body, !hasValue); 2001 2002 int tryEnd = _instructions.Count; 2003 2004 // handlers jump here: 2005 _instructions.MarkLabel(gotoEnd); 2006 _instructions.EmitGoto(end, hasValue, hasValue, hasValue); 2007 2008 // keep the result on the stack: 2009 if (node.Handlers.Count > 0) 2010 { 2011 exHandlers = new List<ExceptionHandler>(); 2012 foreach (CatchBlock handler in node.Handlers) 2013 { 2014 ParameterExpression parameter = handler.Variable ?? Expression.Parameter(handler.Test); 2015 2016 LocalDefinition local = _locals.DefineLocal(parameter, _instructions.Count); 2017 _exceptionForRethrowStack.Push(parameter); 2018 2019 ExceptionFilter filter = null; 2020 2021 if (handler.Filter != null) 2022 { 2023 PushLabelBlock(LabelScopeKind.Filter); 2024 2025 _instructions.EmitEnterExceptionFilter(); 2026 2027 // at this point the stack balance is prepared for the hidden exception variable: 2028 int filterLabel = _instructions.MarkRuntimeLabel(); 2029 int filterStart = _instructions.Count; 2030 2031 CompileSetVariable(parameter, isVoid: true); 2032 Compile(handler.Filter); 2033 CompileGetVariable(parameter); 2034 2035 filter = new ExceptionFilter(filterLabel, filterStart, _instructions.Count); 2036 2037 // keep the value of the body on the stack: 2038 _instructions.EmitLeaveExceptionFilter(); 2039 2040 PopLabelBlock(LabelScopeKind.Filter); 2041 } 2042 2043 PushLabelBlock(LabelScopeKind.Catch); 2044 2045 // add a stack balancing nop instruction (exception handling pushes the current exception): 2046 if (hasValue) 2047 { 2048 _instructions.EmitEnterExceptionHandlerNonVoid(); 2049 } 2050 else 2051 { 2052 _instructions.EmitEnterExceptionHandlerVoid(); 2053 } 2054 2055 // at this point the stack balance is prepared for the hidden exception variable: 2056 int handlerLabel = _instructions.MarkRuntimeLabel(); 2057 int handlerStart = _instructions.Count; 2058 2059 CompileSetVariable(parameter, isVoid: true); 2060 Compile(handler.Body, !hasValue); 2061 2062 _exceptionForRethrowStack.Pop(); 2063 2064 // keep the value of the body on the stack: 2065 _instructions.EmitLeaveExceptionHandler(hasValue, gotoEnd); 2066 2067 exHandlers.Add(new ExceptionHandler(handlerLabel, handlerStart, _instructions.Count, handler.Test, filter)); 2068 PopLabelBlock(LabelScopeKind.Catch); 2069 2070 _locals.UndefineLocal(local, _instructions.Count); 2071 } 2072 } 2073 2074 if (node.Finally != null) 2075 { 2076 Debug.Assert(startOfFinally != null); 2077 PushLabelBlock(LabelScopeKind.Finally); 2078 2079 _instructions.MarkLabel(startOfFinally); 2080 _instructions.EmitEnterFinally(startOfFinally); 2081 CompileAsVoid(node.Finally); 2082 _instructions.EmitLeaveFinally(); 2083 2084 enterTryInstr.SetTryHandler( 2085 new TryCatchFinallyHandler(tryStart, tryEnd, gotoEnd.TargetIndex, 2086 startOfFinally.TargetIndex, _instructions.Count, 2087 exHandlers?.ToArray())); 2088 PopLabelBlock(LabelScopeKind.Finally); 2089 } 2090 else 2091 { 2092 Debug.Assert(exHandlers != null); 2093 enterTryInstr.SetTryHandler( 2094 new TryCatchFinallyHandler(tryStart, tryEnd, gotoEnd.TargetIndex, exHandlers.ToArray())); 2095 } 2096 2097 _instructions.MarkLabel(end); 2098 2099 PopLabelBlock(LabelScopeKind.Try); 2100 } 2101 } 2102 CompileTryFaultExpression(TryExpression expr)2103 private void CompileTryFaultExpression(TryExpression expr) 2104 { 2105 Debug.Assert(expr.Finally == null); 2106 Debug.Assert(expr.Handlers.Count == 0); 2107 2108 // Mark where we begin. 2109 int tryStart = _instructions.Count; 2110 BranchLabel end = _instructions.MakeLabel(); 2111 EnterTryFaultInstruction enterTryInstr = _instructions.EmitEnterTryFault(end); 2112 Debug.Assert(enterTryInstr == _instructions.GetInstruction(tryStart)); 2113 2114 // Emit the try block. 2115 PushLabelBlock(LabelScopeKind.Try); 2116 bool hasValue = expr.Type != typeof(void); 2117 Compile(expr.Body, !hasValue); 2118 int tryEnd = _instructions.Count; 2119 2120 // Jump out of the try block to the end of the finally. If we got 2121 // This far, then the fault block shouldn't be run. 2122 _instructions.EmitGoto(end, hasValue, hasValue, hasValue); 2123 2124 // Emit the fault block. The scope kind used is the same as for finally 2125 // blocks, which matches the Compiler.LambdaCompiler.EmitTryExpression approach. 2126 PushLabelBlock(LabelScopeKind.Finally); 2127 BranchLabel startOfFault = _instructions.MakeLabel(); 2128 _instructions.MarkLabel(startOfFault); 2129 _instructions.EmitEnterFault(startOfFault); 2130 CompileAsVoid(expr.Fault); 2131 _instructions.EmitLeaveFault(); 2132 enterTryInstr.SetTryHandler(new TryFaultHandler(tryStart, tryEnd, startOfFault.TargetIndex, _instructions.Count)); 2133 PopLabelBlock(LabelScopeKind.Finally); 2134 PopLabelBlock(LabelScopeKind.Try); 2135 _instructions.MarkLabel(end); 2136 } 2137 CompileMethodCallExpression(Expression expr)2138 private void CompileMethodCallExpression(Expression expr) 2139 { 2140 var node = (MethodCallExpression)expr; 2141 CompileMethodCallExpression(node.Object, node.Method, node); 2142 } 2143 2144 private void CompileMethodCallExpression(Expression @object, MethodInfo method, IArgumentProvider arguments) 2145 { 2146 ParameterInfo[] parameters = method.GetParametersCached(); 2147 2148 // TODO: Support pass by reference. 2149 List<ByRefUpdater> updaters = null; 2150 if (!method.IsStatic) 2151 { 2152 ByRefUpdater updater = CompileAddress(@object, -1); 2153 if (updater != null) 2154 { 2155 updaters = new List<ByRefUpdater>() { updater }; 2156 } 2157 } 2158 2159 Debug.Assert(parameters.Length == arguments.ArgumentCount); 2160 2161 for (int i = 0, n = arguments.ArgumentCount; i < n; i++) 2162 { 2163 Expression arg = arguments.GetArgument(i); 2164 2165 // byref calls leave out values on the stack, we use a callback 2166 // to emit the code which processes each value left on the stack. 2167 if (parameters[i].ParameterType.IsByRef) 2168 { 2169 ByRefUpdater updater = CompileAddress(arg, i); 2170 if (updater != null) 2171 { 2172 if (updaters == null) 2173 { 2174 updaters = new List<ByRefUpdater>(); 2175 } 2176 2177 updaters.Add(updater); 2178 } 2179 } 2180 else 2181 { 2182 Compile(arg); 2183 } 2184 } 2185 2186 if (!method.IsStatic && 2187 @object.Type.IsNullableType()) 2188 { 2189 // reflection doesn't let us call methods on Nullable<T> when the value 2190 // is null... so we get to special case those methods! 2191 _instructions.EmitNullableCall(method, parameters); 2192 } 2193 else 2194 { 2195 if (updaters == null) 2196 { 2197 _instructions.EmitCall(method, parameters); 2198 } 2199 else 2200 { 2201 _instructions.EmitByRefCall(method, parameters, updaters.ToArray()); 2202 2203 foreach (ByRefUpdater updater in updaters) 2204 { 2205 updater.UndefineTemps(_instructions, _locals); 2206 } 2207 } 2208 } 2209 } 2210 CompileArrayIndexAddress(Expression array, Expression index, int argumentIndex)2211 private ByRefUpdater CompileArrayIndexAddress(Expression array, Expression index, int argumentIndex) 2212 { 2213 LocalDefinition left = _locals.DefineLocal(Expression.Parameter(array.Type, nameof(array)), _instructions.Count); 2214 LocalDefinition right = _locals.DefineLocal(Expression.Parameter(index.Type, nameof(index)), _instructions.Count); 2215 Compile(array); 2216 _instructions.EmitStoreLocal(left.Index); 2217 Compile(index); 2218 _instructions.EmitStoreLocal(right.Index); 2219 2220 _instructions.EmitLoadLocal(left.Index); 2221 _instructions.EmitLoadLocal(right.Index); 2222 _instructions.EmitGetArrayItem(); 2223 2224 return new ArrayByRefUpdater(left, right, argumentIndex); 2225 } 2226 EmitThisForMethodCall(Expression node)2227 private void EmitThisForMethodCall(Expression node) 2228 { 2229 CompileAddress(node, -1); 2230 } 2231 ShouldWritebackNode(Expression node)2232 private static bool ShouldWritebackNode(Expression node) 2233 { 2234 if (node.Type.IsValueType) 2235 { 2236 switch (node.NodeType) 2237 { 2238 case ExpressionType.Parameter: 2239 case ExpressionType.Call: 2240 case ExpressionType.ArrayIndex: 2241 return true; 2242 case ExpressionType.Index: 2243 return ((IndexExpression)node).Object.Type.IsArray; 2244 case ExpressionType.MemberAccess: 2245 return ((MemberExpression)node).Member is FieldInfo; 2246 // ExpressionType.Unbox does have the behaviour write-back is used to simulate, but 2247 // it doesn't need explicit write-back to produce it, so include it in the default 2248 // false cases. 2249 } 2250 } 2251 return false; 2252 } 2253 2254 /// <summary> 2255 /// Emits the address of the specified node. 2256 /// </summary> CompileAddress(Expression node, int index)2257 private ByRefUpdater CompileAddress(Expression node, int index) 2258 { 2259 if (index != -1 || ShouldWritebackNode(node)) 2260 { 2261 switch (node.NodeType) 2262 { 2263 case ExpressionType.Parameter: 2264 LoadLocalNoValueTypeCopy((ParameterExpression)node); 2265 2266 return new ParameterByRefUpdater(ResolveLocal((ParameterExpression)node), index); 2267 case ExpressionType.ArrayIndex: 2268 var array = (BinaryExpression)node; 2269 2270 return CompileArrayIndexAddress(array.Left, array.Right, index); 2271 case ExpressionType.Index: 2272 var indexNode = (IndexExpression)node; 2273 if (/*!TypeUtils.AreEquivalent(type, node.Type) || */indexNode.Indexer != null) 2274 { 2275 LocalDefinition? objTmp = null; 2276 if (indexNode.Object != null) 2277 { 2278 objTmp = _locals.DefineLocal(Expression.Parameter(indexNode.Object.Type), _instructions.Count); 2279 EmitThisForMethodCall(indexNode.Object); 2280 _instructions.EmitDup(); 2281 _instructions.EmitStoreLocal(objTmp.GetValueOrDefault().Index); 2282 } 2283 2284 int count = indexNode.ArgumentCount; 2285 var indexLocals = new LocalDefinition[count]; 2286 for (int i = 0; i < count; i++) 2287 { 2288 Expression arg = indexNode.GetArgument(i); 2289 Compile(arg); 2290 2291 LocalDefinition argTmp = _locals.DefineLocal(Expression.Parameter(arg.Type), _instructions.Count); 2292 _instructions.EmitDup(); 2293 _instructions.EmitStoreLocal(argTmp.Index); 2294 2295 indexLocals[i] = argTmp; 2296 } 2297 2298 EmitIndexGet(indexNode); 2299 2300 return new IndexMethodByRefUpdater(objTmp, indexLocals, indexNode.Indexer.GetSetMethod(), index); 2301 } 2302 else if (indexNode.ArgumentCount == 1) 2303 { 2304 return CompileArrayIndexAddress(indexNode.Object, indexNode.GetArgument(0), index); 2305 } 2306 else 2307 { 2308 return CompileMultiDimArrayAccess(indexNode.Object, indexNode, index); 2309 } 2310 case ExpressionType.MemberAccess: 2311 var member = (MemberExpression)node; 2312 2313 LocalDefinition? memberTemp = null; 2314 if (member.Expression != null) 2315 { 2316 memberTemp = _locals.DefineLocal(Expression.Parameter(member.Expression.Type, "member"), _instructions.Count); 2317 EmitThisForMethodCall(member.Expression); 2318 _instructions.EmitDup(); 2319 _instructions.EmitStoreLocal(memberTemp.GetValueOrDefault().Index); 2320 } 2321 2322 var field = member.Member as FieldInfo; 2323 if (field != null) 2324 { 2325 _instructions.EmitLoadField(field); 2326 if (!field.IsLiteral && !field.IsInitOnly) 2327 { 2328 return new FieldByRefUpdater(memberTemp, field, index); 2329 } 2330 return null; 2331 } 2332 Debug.Assert(member.Member is PropertyInfo); 2333 var property = (PropertyInfo)member.Member; 2334 _instructions.EmitCall(property.GetGetMethod(nonPublic: true)); 2335 if (property.CanWrite) 2336 { 2337 return new PropertyByRefUpdater(memberTemp, property, index); 2338 } 2339 return null; 2340 case ExpressionType.Call: 2341 // An array index of a multi-dimensional array is represented by a call to Array.Get, 2342 // rather than having its own array-access node. This means that when we are trying to 2343 // get the address of a member of a multi-dimensional array, we'll be trying to 2344 // get the address of a Get method, and it will fail to do so. Instead, detect 2345 // this situation and replace it with a call to the Address method. 2346 var call = (MethodCallExpression)node; 2347 if (!call.Method.IsStatic && 2348 call.Object.Type.IsArray && 2349 call.Method == call.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)) 2350 { 2351 return CompileMultiDimArrayAccess( 2352 call.Object, 2353 call, 2354 index 2355 ); 2356 } 2357 break; 2358 } 2359 } 2360 // Includes Unbox case as it doesn't need explicit write-back. 2361 Compile(node); 2362 return null; 2363 } 2364 CompileMultiDimArrayAccess(Expression array, IArgumentProvider arguments, int index)2365 private ByRefUpdater CompileMultiDimArrayAccess(Expression array, IArgumentProvider arguments, int index) 2366 { 2367 Compile(array); 2368 LocalDefinition objTmp = _locals.DefineLocal(Expression.Parameter(array.Type), _instructions.Count); 2369 _instructions.EmitDup(); 2370 _instructions.EmitStoreLocal(objTmp.Index); 2371 2372 int count = arguments.ArgumentCount; 2373 var indexLocals = new LocalDefinition[count]; 2374 for (int i = 0; i < count; i++) 2375 { 2376 Expression arg = arguments.GetArgument(i); 2377 Compile(arg); 2378 2379 LocalDefinition argTmp = _locals.DefineLocal(Expression.Parameter(arg.Type), _instructions.Count); 2380 _instructions.EmitDup(); 2381 _instructions.EmitStoreLocal(argTmp.Index); 2382 2383 indexLocals[i] = argTmp; 2384 } 2385 2386 _instructions.EmitCall(array.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)); 2387 2388 return new IndexMethodByRefUpdater(objTmp, indexLocals, array.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance), index); 2389 } 2390 CompileNewExpression(Expression expr)2391 private void CompileNewExpression(Expression expr) 2392 { 2393 var node = (NewExpression)expr; 2394 2395 if (node.Constructor != null) 2396 { 2397 if (node.Constructor.DeclaringType.IsAbstract) 2398 throw Error.NonAbstractConstructorRequired(); 2399 2400 ParameterInfo[] parameters = node.Constructor.GetParametersCached(); 2401 List<ByRefUpdater> updaters = null; 2402 2403 for (int i = 0; i < parameters.Length; i++) 2404 { 2405 Expression arg = node.GetArgument(i); 2406 2407 if (parameters[i].ParameterType.IsByRef) 2408 { 2409 ByRefUpdater updater = CompileAddress(arg, i); 2410 if (updater != null) 2411 { 2412 if (updaters == null) 2413 { 2414 updaters = new List<ByRefUpdater>(); 2415 } 2416 updaters.Add(updater); 2417 } 2418 } 2419 else 2420 { 2421 Compile(arg); 2422 } 2423 } 2424 2425 if (updaters != null) 2426 { 2427 _instructions.EmitByRefNew(node.Constructor, parameters, updaters.ToArray()); 2428 } 2429 else 2430 { 2431 _instructions.EmitNew(node.Constructor, parameters); 2432 } 2433 } 2434 else 2435 { 2436 Type type = node.Type; 2437 Debug.Assert(type.IsValueType); 2438 if (type.IsNullableType()) 2439 { 2440 _instructions.EmitLoad(value: null); 2441 } 2442 else 2443 { 2444 _instructions.EmitDefaultValue(type); 2445 } 2446 } 2447 } 2448 CompileMemberExpression(Expression expr)2449 private void CompileMemberExpression(Expression expr) 2450 { 2451 var node = (MemberExpression)expr; 2452 2453 CompileMember(node.Expression, node.Member, forBinding: false); 2454 } 2455 CompileMember(Expression from, MemberInfo member, bool forBinding)2456 private void CompileMember(Expression from, MemberInfo member, bool forBinding) 2457 { 2458 var fi = member as FieldInfo; 2459 if (fi != null) 2460 { 2461 if (fi.IsLiteral) 2462 { 2463 Debug.Assert(!forBinding); 2464 _instructions.EmitLoad(fi.GetValue(obj: null), fi.FieldType); 2465 } 2466 else if (fi.IsStatic) 2467 { 2468 if (forBinding) 2469 { 2470 throw Error.InvalidProgram(); 2471 } 2472 2473 if (fi.IsInitOnly) 2474 { 2475 _instructions.EmitLoad(fi.GetValue(obj: null), fi.FieldType); 2476 } 2477 else 2478 { 2479 _instructions.EmitLoadField(fi); 2480 } 2481 } 2482 else 2483 { 2484 if (from != null) 2485 { 2486 EmitThisForMethodCall(from); 2487 } 2488 2489 _instructions.EmitLoadField(fi); 2490 } 2491 } 2492 else 2493 { 2494 // MemberExpression can use either FieldInfo or PropertyInfo - other types derived from MemberInfo are not permitted 2495 var pi = (PropertyInfo)member; 2496 if (pi != null) 2497 { 2498 MethodInfo method = pi.GetGetMethod(nonPublic: true); 2499 if (forBinding && method.IsStatic) 2500 { 2501 throw Error.InvalidProgram(); 2502 } 2503 2504 if (from != null) 2505 { 2506 EmitThisForMethodCall(from); 2507 } 2508 2509 if (!method.IsStatic && 2510 (from != null && from.Type.IsNullableType())) 2511 { 2512 // reflection doesn't let us call methods on Nullable<T> when the value 2513 // is null... so we get to special case those methods! 2514 _instructions.EmitNullableCall(method, Array.Empty<ParameterInfo>()); 2515 } 2516 else 2517 { 2518 _instructions.EmitCall(method); 2519 } 2520 } 2521 } 2522 } 2523 CompileNewArrayExpression(Expression expr)2524 private void CompileNewArrayExpression(Expression expr) 2525 { 2526 var node = (NewArrayExpression)expr; 2527 2528 foreach (Expression arg in node.Expressions) 2529 { 2530 Compile(arg); 2531 } 2532 2533 Type elementType = node.Type.GetElementType(); 2534 int rank = node.Expressions.Count; 2535 2536 if (node.NodeType == ExpressionType.NewArrayInit) 2537 { 2538 _instructions.EmitNewArrayInit(elementType, rank); 2539 } 2540 else 2541 { 2542 Debug.Assert(node.NodeType == ExpressionType.NewArrayBounds); 2543 if (rank == 1) 2544 { 2545 _instructions.EmitNewArray(elementType); 2546 } 2547 else 2548 { 2549 _instructions.EmitNewArrayBounds(elementType, rank); 2550 } 2551 } 2552 } 2553 CompileDebugInfoExpression(Expression expr)2554 private void CompileDebugInfoExpression(Expression expr) 2555 { 2556 var node = (DebugInfoExpression)expr; 2557 int start = _instructions.Count; 2558 var info = new DebugInfo() 2559 { 2560 Index = start, 2561 FileName = node.Document.FileName, 2562 StartLine = node.StartLine, 2563 EndLine = node.EndLine, 2564 IsClear = node.IsClear 2565 }; 2566 _debugInfos.Add(info); 2567 } 2568 CompileRuntimeVariablesExpression(Expression expr)2569 private void CompileRuntimeVariablesExpression(Expression expr) 2570 { 2571 // Generates IRuntimeVariables for all requested variables 2572 var node = (RuntimeVariablesExpression)expr; 2573 foreach (ParameterExpression variable in node.Variables) 2574 { 2575 EnsureAvailableForClosure(variable); 2576 CompileGetBoxedVariable(variable); 2577 } 2578 2579 _instructions.EmitNewRuntimeVariables(node.Variables.Count); 2580 } 2581 CompileLambdaExpression(Expression expr)2582 private void CompileLambdaExpression(Expression expr) 2583 { 2584 var node = (LambdaExpression)expr; 2585 var compiler = new LightCompiler(this); 2586 LightDelegateCreator creator = compiler.CompileTop(node); 2587 2588 if (compiler._locals.ClosureVariables != null) 2589 { 2590 foreach (ParameterExpression variable in compiler._locals.ClosureVariables.Keys) 2591 { 2592 EnsureAvailableForClosure(variable); 2593 CompileGetBoxedVariable(variable); 2594 } 2595 } 2596 _instructions.EmitCreateDelegate(creator); 2597 } 2598 CompileCoalesceBinaryExpression(Expression expr)2599 private void CompileCoalesceBinaryExpression(Expression expr) 2600 { 2601 var node = (BinaryExpression)expr; 2602 2603 bool hasConversion = node.Conversion != null; 2604 bool hasImplicitConversion = false; 2605 if (!hasConversion && node.Left.Type.IsNullableType()) 2606 { 2607 // reference types don't need additional conversions (the interpreter operates on Object 2608 // anyway); non-nullable value types can't occur on the left side; all that's left is 2609 // nullable value types with implicit (numeric) conversions which are allowed by Coalesce 2610 // factory methods 2611 2612 Type typeToCompare = node.Left.Type; 2613 if (!node.Type.IsNullableType()) 2614 { 2615 typeToCompare = typeToCompare.GetNonNullableType(); 2616 } 2617 2618 if (!TypeUtils.AreEquivalent(node.Type, typeToCompare)) 2619 { 2620 hasImplicitConversion = true; 2621 hasConversion = true; 2622 } 2623 } 2624 2625 BranchLabel leftNotNull = _instructions.MakeLabel(); 2626 BranchLabel end = null; 2627 2628 Compile(node.Left); 2629 _instructions.EmitCoalescingBranch(leftNotNull); 2630 _instructions.EmitPop(); 2631 Compile(node.Right); 2632 2633 if (hasConversion) 2634 { 2635 // skip over conversion on RHS 2636 end = _instructions.MakeLabel(); 2637 _instructions.EmitBranch(end); 2638 } 2639 else if (node.Right.Type.IsValueType && !TypeUtils.AreEquivalent(node.Type, node.Right.Type)) 2640 { 2641 // The right hand side may need to be widened to either the left hand side's type 2642 // if the right hand side is nullable, or the left hand side's underlying type otherwise 2643 CompileConvertToType(node.Right.Type, node.Type, isChecked: true, isLiftedToNull: node.Type.IsNullableType()); 2644 } 2645 2646 _instructions.MarkLabel(leftNotNull); 2647 2648 if (node.Conversion != null) 2649 { 2650 ParameterExpression temp = Expression.Parameter(node.Left.Type, "temp"); 2651 LocalDefinition local = _locals.DefineLocal(temp, _instructions.Count); 2652 _instructions.EmitStoreLocal(local.Index); 2653 2654 CompileMethodCallExpression( 2655 Expression.Call(node.Conversion, node.Conversion.Type.GetInvokeMethod(), new[] { temp }) 2656 ); 2657 2658 _locals.UndefineLocal(local, _instructions.Count); 2659 } 2660 else if (hasImplicitConversion) 2661 { 2662 Type nnLeftType = node.Left.Type.GetNonNullableType(); 2663 CompileConvertToType(nnLeftType, node.Type, isChecked: true, isLiftedToNull: false); 2664 } 2665 2666 if (hasConversion) 2667 { 2668 _instructions.MarkLabel(end); 2669 } 2670 } 2671 CompileInvocationExpression(Expression expr)2672 private void CompileInvocationExpression(Expression expr) 2673 { 2674 var node = (InvocationExpression)expr; 2675 2676 if (typeof(LambdaExpression).IsAssignableFrom(node.Expression.Type)) 2677 { 2678 MethodInfo compMethod = node.Expression.Type.GetMethod("Compile", Array.Empty<Type>()); 2679 CompileMethodCallExpression( 2680 Expression.Call( 2681 node.Expression, 2682 compMethod 2683 ), 2684 compMethod.ReturnType.GetInvokeMethod(), 2685 node 2686 ); 2687 } 2688 else 2689 { 2690 CompileMethodCallExpression( 2691 node.Expression, node.Expression.Type.GetInvokeMethod(), node 2692 ); 2693 } 2694 } 2695 CompileListInitExpression(Expression expr)2696 private void CompileListInitExpression(Expression expr) 2697 { 2698 var node = (ListInitExpression)expr; 2699 EmitThisForMethodCall(node.NewExpression); 2700 ReadOnlyCollection<ElementInit> initializers = node.Initializers; 2701 CompileListInit(initializers); 2702 } 2703 CompileListInit(ReadOnlyCollection<ElementInit> initializers)2704 private void CompileListInit(ReadOnlyCollection<ElementInit> initializers) 2705 { 2706 for (int i = 0; i < initializers.Count; i++) 2707 { 2708 ElementInit initializer = initializers[i]; 2709 _instructions.EmitDup(); 2710 foreach (Expression arg in initializer.Arguments) 2711 { 2712 Compile(arg); 2713 } 2714 MethodInfo add = initializer.AddMethod; 2715 _instructions.EmitCall(add); 2716 if (add.ReturnType != typeof(void)) 2717 _instructions.EmitPop(); 2718 } 2719 } 2720 CompileMemberInitExpression(Expression expr)2721 private void CompileMemberInitExpression(Expression expr) 2722 { 2723 var node = (MemberInitExpression)expr; 2724 EmitThisForMethodCall(node.NewExpression); 2725 CompileMemberInit(node.Bindings); 2726 } 2727 CompileMemberInit(ReadOnlyCollection<MemberBinding> bindings)2728 private void CompileMemberInit(ReadOnlyCollection<MemberBinding> bindings) 2729 { 2730 foreach (MemberBinding binding in bindings) 2731 { 2732 switch (binding.BindingType) 2733 { 2734 case MemberBindingType.Assignment: 2735 _instructions.EmitDup(); 2736 CompileMemberAssignment( 2737 true, 2738 ((MemberAssignment)binding).Member, 2739 ((MemberAssignment)binding).Expression, 2740 forBinding: true 2741 ); 2742 break; 2743 case MemberBindingType.ListBinding: 2744 var memberList = (MemberListBinding)binding; 2745 _instructions.EmitDup(); 2746 CompileMember(null, memberList.Member, forBinding: true); 2747 CompileListInit(memberList.Initializers); 2748 _instructions.EmitPop(); 2749 break; 2750 case MemberBindingType.MemberBinding: 2751 var memberMember = (MemberMemberBinding)binding; 2752 _instructions.EmitDup(); 2753 Type type = GetMemberType(memberMember.Member); 2754 if (memberMember.Member is PropertyInfo && type.IsValueType) 2755 { 2756 throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(memberMember.Bindings); 2757 } 2758 2759 CompileMember(null, memberMember.Member, forBinding: true); 2760 CompileMemberInit(memberMember.Bindings); 2761 _instructions.EmitPop(); 2762 break; 2763 } 2764 } 2765 } 2766 GetMemberType(MemberInfo member)2767 private static Type GetMemberType(MemberInfo member) 2768 { 2769 var fi = member as FieldInfo; 2770 if (fi != null) return fi.FieldType; 2771 var pi = member as PropertyInfo; 2772 if (pi != null) return pi.PropertyType; 2773 throw new InvalidOperationException("MemberNotFieldOrProperty"); 2774 } 2775 CompileQuoteUnaryExpression(Expression expr)2776 private void CompileQuoteUnaryExpression(Expression expr) 2777 { 2778 var unary = (UnaryExpression)expr; 2779 2780 var visitor = new QuoteVisitor(); 2781 visitor.Visit(unary.Operand); 2782 2783 var mapping = new Dictionary<ParameterExpression, LocalVariable>(); 2784 2785 foreach (ParameterExpression local in visitor._hoistedParameters) 2786 { 2787 EnsureAvailableForClosure(local); 2788 mapping[local] = ResolveLocal(local); 2789 } 2790 2791 _instructions.Emit(new QuoteInstruction(unary.Operand, mapping.Count > 0 ? mapping : null)); 2792 } 2793 2794 private sealed class QuoteVisitor : ExpressionVisitor 2795 { 2796 private readonly Dictionary<ParameterExpression, int> _definedParameters = new Dictionary<ParameterExpression, int>(); 2797 public readonly HashSet<ParameterExpression> _hoistedParameters = new HashSet<ParameterExpression>(); 2798 VisitParameter(ParameterExpression node)2799 protected internal override Expression VisitParameter(ParameterExpression node) 2800 { 2801 if (!_definedParameters.ContainsKey(node)) 2802 { 2803 _hoistedParameters.Add(node); 2804 } 2805 return node; 2806 } 2807 VisitBlock(BlockExpression node)2808 protected internal override Expression VisitBlock(BlockExpression node) 2809 { 2810 PushParameters(node.Variables); 2811 2812 base.VisitBlock(node); 2813 2814 PopParameters(node.Variables); 2815 2816 return node; 2817 } 2818 VisitCatchBlock(CatchBlock node)2819 protected override CatchBlock VisitCatchBlock(CatchBlock node) 2820 { 2821 if (node.Variable != null) 2822 { 2823 PushParameters(new[] { node.Variable }); 2824 } 2825 Visit(node.Body); 2826 Visit(node.Filter); 2827 if (node.Variable != null) 2828 { 2829 PopParameters(new[] { node.Variable }); 2830 } 2831 return node; 2832 } 2833 VisitLambda(Expression<T> node)2834 protected internal override Expression VisitLambda<T>(Expression<T> node) 2835 { 2836 IEnumerable<ParameterExpression> parameters = Array.Empty<ParameterExpression>(); 2837 2838 int count = node.ParameterCount; 2839 2840 if (count > 0) 2841 { 2842 var parameterList = new List<ParameterExpression>(count); 2843 2844 for (int i = 0; i < count; i++) 2845 { 2846 parameterList.Add(node.GetParameter(i)); 2847 } 2848 2849 parameters = parameterList; 2850 } 2851 2852 PushParameters(parameters); 2853 2854 base.VisitLambda(node); 2855 2856 PopParameters(parameters); 2857 2858 return node; 2859 } 2860 PushParameters(IEnumerable<ParameterExpression> parameters)2861 private void PushParameters(IEnumerable<ParameterExpression> parameters) 2862 { 2863 foreach (ParameterExpression param in parameters) 2864 { 2865 int count; 2866 if (_definedParameters.TryGetValue(param, out count)) 2867 { 2868 _definedParameters[param] = count + 1; 2869 } 2870 else 2871 { 2872 _definedParameters[param] = 1; 2873 } 2874 } 2875 } 2876 PopParameters(IEnumerable<ParameterExpression> parameters)2877 private void PopParameters(IEnumerable<ParameterExpression> parameters) 2878 { 2879 foreach (ParameterExpression param in parameters) 2880 { 2881 int count = _definedParameters[param]; 2882 if (count == 0) 2883 { 2884 _definedParameters.Remove(param); 2885 } 2886 else 2887 { 2888 _definedParameters[param] = count - 1; 2889 } 2890 } 2891 } 2892 } 2893 CompileUnboxUnaryExpression(Expression expr)2894 private void CompileUnboxUnaryExpression(Expression expr) 2895 { 2896 var node = (UnaryExpression)expr; 2897 2898 Compile(node.Operand); 2899 2900 if (node.Type.IsValueType && !node.Type.IsNullableType()) 2901 { 2902 _instructions.Emit(NullCheckInstruction.Instance); 2903 } 2904 } 2905 CompileTypeEqualExpression(Expression expr)2906 private void CompileTypeEqualExpression(Expression expr) 2907 { 2908 Debug.Assert(expr.NodeType == ExpressionType.TypeEqual); 2909 var node = (TypeBinaryExpression)expr; 2910 2911 Compile(node.Expression); 2912 if (node.Expression.Type == typeof(void)) 2913 { 2914 _instructions.EmitLoad(node.TypeOperand == typeof(void), typeof(bool)); 2915 } 2916 else 2917 { 2918 _instructions.EmitLoad(node.TypeOperand.GetNonNullableType()); 2919 _instructions.EmitTypeEquals(); 2920 } 2921 } 2922 CompileTypeAsExpression(UnaryExpression node)2923 private void CompileTypeAsExpression(UnaryExpression node) 2924 { 2925 Compile(node.Operand); 2926 _instructions.EmitTypeAs(node.Type); 2927 } 2928 CompileTypeIsExpression(Expression expr)2929 private void CompileTypeIsExpression(Expression expr) 2930 { 2931 Debug.Assert(expr.NodeType == ExpressionType.TypeIs); 2932 var node = (TypeBinaryExpression)expr; 2933 2934 AnalyzeTypeIsResult result = ConstantCheck.AnalyzeTypeIs(node); 2935 2936 Compile(node.Expression); 2937 2938 switch (result) 2939 { 2940 case AnalyzeTypeIsResult.KnownTrue: 2941 case AnalyzeTypeIsResult.KnownFalse: 2942 2943 // Result is known statically, so just emit the expression for 2944 // its side effects and return the result 2945 if (node.Expression.Type != typeof(void)) 2946 { 2947 _instructions.EmitPop(); 2948 } 2949 2950 _instructions.EmitLoad(result == AnalyzeTypeIsResult.KnownTrue); 2951 break; 2952 case AnalyzeTypeIsResult.KnownAssignable: 2953 2954 // Either the value is of the type or it is null 2955 // so emit test for not-null. 2956 _instructions.EmitLoad(null); 2957 _instructions.EmitNotEqual(typeof(object)); 2958 break; 2959 default: 2960 if (node.TypeOperand.IsValueType) 2961 { 2962 _instructions.EmitLoad(node.TypeOperand.GetNonNullableType()); 2963 _instructions.EmitTypeEquals(); 2964 } 2965 else 2966 { 2967 _instructions.EmitTypeIs(node.TypeOperand); 2968 } 2969 break; 2970 } 2971 } 2972 Compile(Expression expr, bool asVoid)2973 private void Compile(Expression expr, bool asVoid) 2974 { 2975 if (asVoid) 2976 { 2977 CompileAsVoid(expr); 2978 } 2979 else 2980 { 2981 Compile(expr); 2982 } 2983 } 2984 CompileAsVoid(Expression expr)2985 private void CompileAsVoid(Expression expr) 2986 { 2987 bool pushLabelBlock = TryPushLabelBlock(expr); 2988 int startingStackDepth = _instructions.CurrentStackDepth; 2989 switch (expr.NodeType) 2990 { 2991 case ExpressionType.Assign: 2992 CompileAssignBinaryExpression(expr, asVoid: true); 2993 break; 2994 2995 case ExpressionType.Block: 2996 CompileBlockExpression(expr, asVoid: true); 2997 break; 2998 2999 case ExpressionType.Throw: 3000 CompileThrowUnaryExpression(expr, asVoid: true); 3001 break; 3002 3003 case ExpressionType.Constant: 3004 case ExpressionType.Default: 3005 case ExpressionType.Parameter: 3006 // no-op 3007 break; 3008 3009 default: 3010 CompileNoLabelPush(expr); 3011 if (expr.Type != typeof(void)) 3012 { 3013 _instructions.EmitPop(); 3014 } 3015 break; 3016 } 3017 Debug.Assert(_instructions.CurrentStackDepth == startingStackDepth); 3018 if (pushLabelBlock) 3019 { 3020 PopLabelBlock(_labelBlock.Kind); 3021 } 3022 } 3023 3024 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] CompileNoLabelPush(Expression expr)3025 private void CompileNoLabelPush(Expression expr) 3026 { 3027 // When compiling deep trees, we run the risk of triggering a terminating StackOverflowException, 3028 // so we use the StackGuard utility here to probe for sufficient stack and continue the work on 3029 // another thread when we run out of stack space. 3030 if (!_guard.TryEnterOnCurrentStack()) 3031 { 3032 _guard.RunOnEmptyStack((LightCompiler @this, Expression e) => @this.CompileNoLabelPush(e), this, expr); 3033 return; 3034 } 3035 3036 int startingStackDepth = _instructions.CurrentStackDepth; 3037 switch (expr.NodeType) 3038 { 3039 case ExpressionType.Add: 3040 case ExpressionType.AddChecked: 3041 case ExpressionType.And: 3042 case ExpressionType.ArrayIndex: 3043 case ExpressionType.Divide: 3044 case ExpressionType.Equal: 3045 case ExpressionType.ExclusiveOr: 3046 case ExpressionType.GreaterThan: 3047 case ExpressionType.GreaterThanOrEqual: 3048 case ExpressionType.LeftShift: 3049 case ExpressionType.LessThan: 3050 case ExpressionType.LessThanOrEqual: 3051 case ExpressionType.Modulo: 3052 case ExpressionType.Multiply: 3053 case ExpressionType.MultiplyChecked: 3054 case ExpressionType.NotEqual: 3055 case ExpressionType.Or: 3056 case ExpressionType.Power: 3057 case ExpressionType.RightShift: 3058 case ExpressionType.Subtract: 3059 case ExpressionType.SubtractChecked: CompileBinaryExpression(expr); break; 3060 case ExpressionType.AndAlso: CompileAndAlsoBinaryExpression(expr); break; 3061 case ExpressionType.OrElse: CompileOrElseBinaryExpression(expr); break; 3062 case ExpressionType.Coalesce: CompileCoalesceBinaryExpression(expr); break; 3063 case ExpressionType.ArrayLength: 3064 case ExpressionType.Decrement: 3065 case ExpressionType.Increment: 3066 case ExpressionType.IsTrue: 3067 case ExpressionType.IsFalse: 3068 case ExpressionType.Negate: 3069 case ExpressionType.NegateChecked: 3070 case ExpressionType.Not: 3071 case ExpressionType.OnesComplement: 3072 case ExpressionType.TypeAs: 3073 case ExpressionType.UnaryPlus: CompileUnaryExpression(expr); break; 3074 case ExpressionType.Convert: 3075 case ExpressionType.ConvertChecked: CompileConvertUnaryExpression(expr); break; 3076 case ExpressionType.Quote: CompileQuoteUnaryExpression(expr); break; 3077 case ExpressionType.Throw: CompileThrowUnaryExpression(expr, expr.Type == typeof(void)); break; 3078 case ExpressionType.Unbox: CompileUnboxUnaryExpression(expr); break; 3079 case ExpressionType.Call: CompileMethodCallExpression(expr); break; 3080 case ExpressionType.Conditional: CompileConditionalExpression(expr, expr.Type == typeof(void)); break; 3081 case ExpressionType.Constant: CompileConstantExpression(expr); break; 3082 case ExpressionType.Invoke: CompileInvocationExpression(expr); break; 3083 case ExpressionType.Lambda: CompileLambdaExpression(expr); break; 3084 case ExpressionType.ListInit: CompileListInitExpression(expr); break; 3085 case ExpressionType.MemberAccess: CompileMemberExpression(expr); break; 3086 case ExpressionType.MemberInit: CompileMemberInitExpression(expr); break; 3087 case ExpressionType.New: CompileNewExpression(expr); break; 3088 case ExpressionType.NewArrayInit: 3089 case ExpressionType.NewArrayBounds: CompileNewArrayExpression(expr); break; 3090 case ExpressionType.Parameter: CompileParameterExpression(expr); break; 3091 case ExpressionType.TypeIs: CompileTypeIsExpression(expr); break; 3092 case ExpressionType.TypeEqual: CompileTypeEqualExpression(expr); break; 3093 case ExpressionType.Assign: CompileAssignBinaryExpression(expr, expr.Type == typeof(void)); break; 3094 case ExpressionType.Block: CompileBlockExpression(expr, expr.Type == typeof(void)); break; 3095 case ExpressionType.DebugInfo: CompileDebugInfoExpression(expr); break; 3096 case ExpressionType.Default: CompileDefaultExpression(expr); break; 3097 case ExpressionType.Goto: CompileGotoExpression(expr); break; 3098 case ExpressionType.Index: CompileIndexExpression(expr); break; 3099 case ExpressionType.Label: CompileLabelExpression(expr); break; 3100 case ExpressionType.RuntimeVariables: CompileRuntimeVariablesExpression(expr); break; 3101 case ExpressionType.Loop: CompileLoopExpression(expr); break; 3102 case ExpressionType.Switch: CompileSwitchExpression(expr); break; 3103 case ExpressionType.Try: CompileTryExpression(expr); break; 3104 default: 3105 Compile(expr.ReduceAndCheck()); 3106 break; 3107 } 3108 Debug.Assert(_instructions.CurrentStackDepth == startingStackDepth + (expr.Type == typeof(void) ? 0 : 1), 3109 string.Format("{0} vs {1} for {2}", _instructions.CurrentStackDepth, startingStackDepth + (expr.Type == typeof(void) ? 0 : 1), expr.NodeType)); 3110 } 3111 Compile(Expression expr)3112 private void Compile(Expression expr) 3113 { 3114 bool pushLabelBlock = TryPushLabelBlock(expr); 3115 CompileNoLabelPush(expr); 3116 if (pushLabelBlock) 3117 { 3118 PopLabelBlock(_labelBlock.Kind); 3119 } 3120 } 3121 } 3122 3123 internal abstract class ByRefUpdater 3124 { 3125 public readonly int ArgumentIndex; 3126 ByRefUpdater(int argumentIndex)3127 public ByRefUpdater(int argumentIndex) 3128 { 3129 ArgumentIndex = argumentIndex; 3130 } 3131 Update(InterpretedFrame frame, object value)3132 public abstract void Update(InterpretedFrame frame, object value); 3133 UndefineTemps(InstructionList instructions, LocalVariables locals)3134 public virtual void UndefineTemps(InstructionList instructions, LocalVariables locals) 3135 { 3136 } 3137 } 3138 3139 internal sealed class ParameterByRefUpdater : ByRefUpdater 3140 { 3141 private readonly LocalVariable _parameter; 3142 ParameterByRefUpdater(LocalVariable parameter, int argumentIndex)3143 public ParameterByRefUpdater(LocalVariable parameter, int argumentIndex) 3144 : base(argumentIndex) 3145 { 3146 _parameter = parameter; 3147 } 3148 Update(InterpretedFrame frame, object value)3149 public override void Update(InterpretedFrame frame, object value) 3150 { 3151 if (_parameter.InClosure) 3152 { 3153 IStrongBox box = frame.Closure[_parameter.Index]; 3154 box.Value = value; 3155 } 3156 else if (_parameter.IsBoxed) 3157 { 3158 var box = (IStrongBox)frame.Data[_parameter.Index]; 3159 box.Value = value; 3160 } 3161 else 3162 { 3163 frame.Data[_parameter.Index] = value; 3164 } 3165 } 3166 } 3167 3168 internal sealed class ArrayByRefUpdater : ByRefUpdater 3169 { 3170 private readonly LocalDefinition _array, _index; 3171 ArrayByRefUpdater(LocalDefinition array, LocalDefinition index, int argumentIndex)3172 public ArrayByRefUpdater(LocalDefinition array, LocalDefinition index, int argumentIndex) 3173 : base(argumentIndex) 3174 { 3175 _array = array; 3176 _index = index; 3177 } 3178 Update(InterpretedFrame frame, object value)3179 public override void Update(InterpretedFrame frame, object value) 3180 { 3181 object index = frame.Data[_index.Index]; 3182 ((Array)frame.Data[_array.Index]).SetValue(value, (int)index); 3183 } 3184 UndefineTemps(InstructionList instructions, LocalVariables locals)3185 public override void UndefineTemps(InstructionList instructions, LocalVariables locals) 3186 { 3187 locals.UndefineLocal(_array, instructions.Count); 3188 locals.UndefineLocal(_index, instructions.Count); 3189 } 3190 } 3191 3192 internal sealed class FieldByRefUpdater : ByRefUpdater 3193 { 3194 private readonly LocalDefinition? _object; 3195 private readonly FieldInfo _field; 3196 3197 public FieldByRefUpdater(LocalDefinition? obj, FieldInfo field, int argumentIndex) base(argumentIndex)3198 : base(argumentIndex) 3199 { 3200 _object = obj; 3201 _field = field; 3202 } 3203 Update(InterpretedFrame frame, object value)3204 public override void Update(InterpretedFrame frame, object value) 3205 { 3206 object obj = _object == null ? null : frame.Data[_object.GetValueOrDefault().Index]; 3207 _field.SetValue(obj, value); 3208 } 3209 UndefineTemps(InstructionList instructions, LocalVariables locals)3210 public override void UndefineTemps(InstructionList instructions, LocalVariables locals) 3211 { 3212 if (_object != null) 3213 { 3214 locals.UndefineLocal(_object.GetValueOrDefault(), instructions.Count); 3215 } 3216 } 3217 } 3218 3219 internal sealed class PropertyByRefUpdater : ByRefUpdater 3220 { 3221 private readonly LocalDefinition? _object; 3222 private readonly PropertyInfo _property; 3223 3224 public PropertyByRefUpdater(LocalDefinition? obj, PropertyInfo property, int argumentIndex) base(argumentIndex)3225 : base(argumentIndex) 3226 { 3227 _object = obj; 3228 _property = property; 3229 } 3230 Update(InterpretedFrame frame, object value)3231 public override void Update(InterpretedFrame frame, object value) 3232 { 3233 object obj = _object == null ? null : frame.Data[_object.GetValueOrDefault().Index]; 3234 3235 try 3236 { 3237 _property.SetValue(obj, value); 3238 } 3239 catch (TargetInvocationException e) 3240 { 3241 ExceptionHelpers.UnwrapAndRethrow(e); 3242 throw ContractUtils.Unreachable; 3243 } 3244 } 3245 UndefineTemps(InstructionList instructions, LocalVariables locals)3246 public override void UndefineTemps(InstructionList instructions, LocalVariables locals) 3247 { 3248 if (_object != null) 3249 { 3250 locals.UndefineLocal(_object.GetValueOrDefault(), instructions.Count); 3251 } 3252 } 3253 } 3254 3255 internal sealed class IndexMethodByRefUpdater : ByRefUpdater 3256 { 3257 private readonly MethodInfo _indexer; 3258 private readonly LocalDefinition? _obj; 3259 private readonly LocalDefinition[] _args; 3260 3261 public IndexMethodByRefUpdater(LocalDefinition? obj, LocalDefinition[] args, MethodInfo indexer, int argumentIndex) base(argumentIndex)3262 : base(argumentIndex) 3263 { 3264 _obj = obj; 3265 _args = args; 3266 _indexer = indexer; 3267 } 3268 Update(InterpretedFrame frame, object value)3269 public override void Update(InterpretedFrame frame, object value) 3270 { 3271 var args = new object[_args.Length + 1]; 3272 for (int i = 0; i < args.Length - 1; i++) 3273 { 3274 args[i] = frame.Data[_args[i].Index]; 3275 } 3276 args[args.Length - 1] = value; 3277 3278 object instance = _obj == null ? null : frame.Data[_obj.GetValueOrDefault().Index]; 3279 3280 try 3281 { 3282 _indexer.Invoke(instance, args); 3283 } 3284 catch (TargetInvocationException e) 3285 { 3286 ExceptionHelpers.UnwrapAndRethrow(e); 3287 throw ContractUtils.Unreachable; 3288 } 3289 } 3290 UndefineTemps(InstructionList instructions, LocalVariables locals)3291 public override void UndefineTemps(InstructionList instructions, LocalVariables locals) 3292 { 3293 if (_obj != null) 3294 { 3295 locals.UndefineLocal(_obj.GetValueOrDefault(), instructions.Count); 3296 } 3297 3298 for (int i = 0; i < _args.Length; i++) 3299 { 3300 locals.UndefineLocal(_args[i], instructions.Count); 3301 } 3302 } 3303 } 3304 } 3305