MegaBites-AI/Windows-powershell
0372
1/* ****************************************************************************2 *3 * Copyright (c) Microsoft Corporation.4 *5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A6 * copy of the license can be found in the License.html file at the root of this distribution. If7 * you cannot locate the Apache License, Version 2.0, please send an email to8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound9 * by the terms of the Apache License, Version 2.0.10 *11 * You must not remove this notice, or any other, from this software.12 *13 *14 * ***************************************************************************/15 16#if !CLR217using System.Globalization;18using System.Linq;19using System.Linq.Expressions;20#endif21 22using System.Collections.Generic;23using System.Diagnostics;24using System.Reflection;25using AstUtils = System.Management.Automation.Interpreter.Utils;26using System.Runtime.CompilerServices;27 28namespace System.Management.Automation.Interpreter29{30 internal sealed class ExceptionHandler31 {32 public readonly Type ExceptionType;33 public readonly int StartIndex;34 public readonly int EndIndex;35 public readonly int LabelIndex;36 public readonly int HandlerStartIndex;37 public readonly int HandlerEndIndex;38 39 internal TryCatchFinallyHandler Parent = null;40 41 public bool IsFault { get { return ExceptionType == null; } }42 43 internal ExceptionHandler(int start, int end, int labelIndex, int handlerStartIndex, int handlerEndIndex, Type exceptionType)44 {45 StartIndex = start;46 EndIndex = end;47 LabelIndex = labelIndex;48 ExceptionType = exceptionType;49 HandlerStartIndex = handlerStartIndex;50 HandlerEndIndex = handlerEndIndex;51 }52 53 internal void SetParent(TryCatchFinallyHandler tryHandler)54 {55 Debug.Assert(Parent == null);56 Parent = tryHandler;57 }58 59 public bool Matches(Type exceptionType)60 {61 if (ExceptionType == null || ExceptionType.IsAssignableFrom(exceptionType))62 {63 return true;64 }65 66 return false;67 }68 69 public bool IsBetterThan(ExceptionHandler other)70 {71 if (other == null)72 {73 return true;74 }75 76 Debug.Assert(StartIndex == other.StartIndex && EndIndex == other.EndIndex, "we only need to compare handlers for the same try block");77 return HandlerStartIndex < other.HandlerStartIndex;78 }79 80 internal bool IsInsideTryBlock(int index)81 {82 return index >= StartIndex && index < EndIndex;83 }84 85 internal bool IsInsideCatchBlock(int index)86 {87 return index >= HandlerStartIndex && index < HandlerEndIndex;88 }89 90 internal bool IsInsideFinallyBlock(int index)91 {92 Debug.Assert(Parent != null);93 return Parent.IsFinallyBlockExist && index >= Parent.FinallyStartIndex && index < Parent.FinallyEndIndex;94 }95 96 public override string ToString()97 {98 return string.Format(99 CultureInfo.InvariantCulture,100 "{0} [{1}-{2}] [{3}->{4}]",101 IsFault ? "fault" : "catch(" + ExceptionType.Name + ")",102 StartIndex,103 EndIndex,104 HandlerStartIndex,105 HandlerEndIndex);106 }107 }108 109 internal sealed class TryCatchFinallyHandler110 {111 internal readonly int TryStartIndex = Instruction.UnknownInstrIndex;112 internal readonly int TryEndIndex = Instruction.UnknownInstrIndex;113 internal readonly int FinallyStartIndex = Instruction.UnknownInstrIndex;114 internal readonly int FinallyEndIndex = Instruction.UnknownInstrIndex;115 internal readonly int GotoEndTargetIndex = Instruction.UnknownInstrIndex;116 117 private readonly ExceptionHandler[] _handlers;118 119 internal bool IsFinallyBlockExist120 {121 get { return (FinallyStartIndex != Instruction.UnknownInstrIndex && FinallyEndIndex != Instruction.UnknownInstrIndex); }122 }123 124 internal bool IsCatchBlockExist125 {126 get { return (_handlers != null); }127 }128 129 /// <summary>130 /// No finally block.131 /// </summary>132 internal TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndTargetIndex, ExceptionHandler[] handlers)133 : this(tryStart, tryEnd, gotoEndTargetIndex, Instruction.UnknownInstrIndex, Instruction.UnknownInstrIndex, handlers)134 {135 Debug.Assert(handlers != null, "catch blocks should exist");136 }137 138 /// <summary>139 /// No catch blocks.140 /// </summary>141 internal TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndTargetIndex, int finallyStart, int finallyEnd)142 : this(tryStart, tryEnd, gotoEndTargetIndex, finallyStart, finallyEnd, null)143 {144 Debug.Assert(finallyStart != Instruction.UnknownInstrIndex && finallyEnd != Instruction.UnknownInstrIndex, "finally block should exist");145 }146 147 /// <summary>148 /// Generic constructor.149 /// </summary>150 internal TryCatchFinallyHandler(int tryStart, int tryEnd, int gotoEndLabelIndex, int finallyStart, int finallyEnd, ExceptionHandler[] handlers)151 {152 TryStartIndex = tryStart;153 TryEndIndex = tryEnd;154 FinallyStartIndex = finallyStart;155 FinallyEndIndex = finallyEnd;156 GotoEndTargetIndex = gotoEndLabelIndex;157 158 _handlers = handlers;159 160 if (_handlers != null)161 {162 for (int index = 0; index < _handlers.Length; index++)163 {164 var handler = _handlers[index];165 handler.SetParent(this);166 }167 }168 }169 170 /// <summary>171 /// Goto the index of the first instruction of the suitable catch block.172 /// </summary>173 internal int GotoHandler(InterpretedFrame frame, object exception, out ExceptionHandler handler)174 {175 Debug.Assert(_handlers != null, "we should have at least one handler if the method gets called");176 handler = Array.Find(_handlers, t => t.Matches(exception.GetType()));177 if (handler == null) { return 0; }178 179 return frame.Goto(handler.LabelIndex, exception, gotoExceptionHandler: true);180 }181 }182 183 /// <summary>184 /// The re-throw instruction will throw this exception.185 /// </summary>186 internal sealed class RethrowException : SystemException187 {188 }189 190 internal class DebugInfo191 {192 // TODO: readonly193 194 public int StartLine, EndLine;195 public int Index;196 public string FileName;197 public bool IsClear;198 private static readonly DebugInfoComparer s_debugComparer = new DebugInfoComparer();199 200 private sealed class DebugInfoComparer : IComparer<DebugInfo>201 {202 // We allow comparison between int and DebugInfo here203 int IComparer<DebugInfo>.Compare(DebugInfo d1, DebugInfo d2)204 {205 if (d1.Index > d2.Index) return 1;206 else if (d1.Index == d2.Index) return 0;207 else return -1;208 }209 }210 211 public static DebugInfo GetMatchingDebugInfo(DebugInfo[] debugInfos, int index)212 {213 // Create a faked DebugInfo to do the search214 DebugInfo d = new DebugInfo { Index = index };215 216 // to find the closest debug info before the current index217 218 int i = Array.BinarySearch<DebugInfo>(debugInfos, d, s_debugComparer);219 if (i < 0)220 {221 // ~i is the index for the first bigger element222 // if there is no bigger element, ~i is the length of the array223 i = ~i;224 if (i == 0)225 {226 return null;227 }228 // return the last one that is smaller229 i -= 1;230 }231 232 return debugInfos[i];233 }234 235 public override string ToString()236 {237 if (IsClear)238 {239 return string.Format(CultureInfo.InvariantCulture, "{0}: clear", Index);240 }241 else242 {243 return string.Format(CultureInfo.InvariantCulture, "{0}: [{1}-{2}] '{3}'", Index, StartLine, EndLine, FileName);244 }245 }246 }247 248 // TODO:249 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")]250 internal readonly struct InterpretedFrameInfo251 {252 public readonly string MethodName;253 254 // TODO:255 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]256 public readonly DebugInfo DebugInfo;257 258 public InterpretedFrameInfo(string methodName, DebugInfo info)259 {260 MethodName = methodName;261 DebugInfo = info;262 }263 264 public override string ToString()265 {266 return MethodName + (DebugInfo != null ? ": " + DebugInfo : null);267 }268 }269 270 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]271 internal sealed class LightCompiler272 {273 internal const int DefaultCompilationThreshold = 32;274 275 // zero: sync compilation276 private readonly int _compilationThreshold;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 // Set to true to force compilation of this lambda.288 // This disables the interpreter for this lambda. We still need to289 // walk it, however, to resolve variables closed over from the parent290 // lambdas (because they may be interpreted).291 private bool _forceCompile;292 293 private readonly LightCompiler _parent;294 295 private static readonly LocalDefinition[] s_emptyLocals = Array.Empty<LocalDefinition>();296 297 public LightCompiler(int compilationThreshold)298 {299 _instructions = new InstructionList();300 _compilationThreshold = compilationThreshold < 0 ? DefaultCompilationThreshold : compilationThreshold;301 }302 303 private LightCompiler(LightCompiler parent)304 : this(parent._compilationThreshold)305 {306 _parent = parent;307 }308 309 public InstructionList Instructions310 {311 get { return _instructions; }312 }313 314 public LocalVariables Locals315 {316 get { return _locals; }317 }318 319 internal static Expression Unbox(Expression strongBoxExpression)320 {321 return Expression.Field(strongBoxExpression, typeof(StrongBox<object>).GetField("Value"));322 }323 324 public LightDelegateCreator CompileTop(LambdaExpression node)325 {326 for (int index = 0; index < node.Parameters.Count; index++)327 {328 var p = node.Parameters[index];329 var local = _locals.DefineLocal(p, 0);330 _instructions.EmitInitializeParameter(local.Index);331 }332 333 Compile(node.Body);334 335 // pop the result of the last expression:336 if (node.Body.Type != typeof(void) && node.ReturnType == typeof(void))337 {338 _instructions.EmitPop();339 }340 341 Debug.Assert(_instructions.CurrentStackDepth == (node.ReturnType != typeof(void) ? 1 : 0));342 343 return new LightDelegateCreator(MakeInterpreter(node.Name), node);344 }345 346 // internal LightDelegateCreator CompileTop(LightLambdaExpression node) {347 // foreach (var p in node.Parameters) {348 // var local = _locals.DefineLocal(p, 0);349 // _instructions.EmitInitializeParameter(local.Index);350 // }351 //352 // Compile(node.Body);353 //354 // // pop the result of the last expression:355 // if (node.Body.Type != typeof(void) && node.ReturnType == typeof(void)) {356 // _instructions.EmitPop();357 // }358 //359 // Debug.Assert(_instructions.CurrentStackDepth == (node.ReturnType != typeof(void) ? 1 : 0));360 //361 // return new LightDelegateCreator(MakeInterpreter(node.Name), node);362 // }363 364 private Interpreter MakeInterpreter(string lambdaName)365 {366 if (_forceCompile)367 {368 return null;369 }370 371 var debugInfos = _debugInfos.ToArray();372 return new Interpreter(lambdaName, _locals, GetBranchMapping(), _instructions.ToArray(), debugInfos, _compilationThreshold);373 }374 375 private void CompileConstantExpression(Expression expr)376 {377 var node = (ConstantExpression)expr;378 _instructions.EmitLoad(node.Value, node.Type);379 }380 381 private void CompileDefaultExpression(Expression expr)382 {383 CompileDefaultExpression(expr.Type);384 }385 386 private void CompileDefaultExpression(Type type)387 {388 if (type != typeof(void))389 {390 if (type.IsValueType)391 {392 object value = ScriptingRuntimeHelpers.GetPrimitiveDefaultValue(type);393 if (value != null)394 {395 _instructions.EmitLoad(value);396 }397 else398 {399 _instructions.EmitDefaultValue(type);400 }401 }402 else403 {404 _instructions.EmitLoad(null);405 }406 }407 }408 409 private LocalVariable EnsureAvailableForClosure(ParameterExpression expr)410 {411 LocalVariable local;412 if (_locals.TryGetLocalOrClosure(expr, out local))413 {414 if (!local.InClosure && !local.IsBoxed)415 {416 _locals.Box(expr, _instructions);417 }418 419 return local;420 }421 else if (_parent != null)422 {423 _parent.EnsureAvailableForClosure(expr);424 return _locals.AddClosureVariable(expr);425 }426 else427 {428 throw new InvalidOperationException("unbound variable: " + expr);429 }430 }431 432 // private void EnsureVariable(ParameterExpression variable) {433 // if (!_locals.ContainsVariable(variable)) {434 // EnsureAvailableForClosure(variable);435 // }436 // }437 438 private LocalVariable ResolveLocal(ParameterExpression variable)439 {440 LocalVariable local;441 if (!_locals.TryGetLocalOrClosure(variable, out local))442 {443 local = EnsureAvailableForClosure(variable);444 }445 446 return local;447 }448 449 public void CompileGetVariable(ParameterExpression variable)450 {451 LocalVariable local = ResolveLocal(variable);452 453 if (local.InClosure)454 {455 _instructions.EmitLoadLocalFromClosure(local.Index);456 }457 else if (local.IsBoxed)458 {459 _instructions.EmitLoadLocalBoxed(local.Index);460 }461 else462 {463 _instructions.EmitLoadLocal(local.Index);464 }465 466 _instructions.SetDebugCookie(variable.Name);467 }468 469 public void CompileGetBoxedVariable(ParameterExpression variable)470 {471 LocalVariable local = ResolveLocal(variable);472 473 if (local.InClosure)474 {475 _instructions.EmitLoadLocalFromClosureBoxed(local.Index);476 }477 else478 {479 Debug.Assert(local.IsBoxed);480 _instructions.EmitLoadLocal(local.Index);481 }482 483 _instructions.SetDebugCookie(variable.Name);484 }485 486 public void CompileSetVariable(ParameterExpression variable, bool isVoid)487 {488 LocalVariable local = ResolveLocal(variable);489 490 if (local.InClosure)491 {492 if (isVoid)493 {494 _instructions.EmitStoreLocalToClosure(local.Index);495 }496 else497 {498 _instructions.EmitAssignLocalToClosure(local.Index);499 }500 }501 else if (local.IsBoxed)502 {503 if (isVoid)504 {505 _instructions.EmitStoreLocalBoxed(local.Index);506 }507 else508 {509 _instructions.EmitAssignLocalBoxed(local.Index);510 }511 }512 else513 {514 if (isVoid)515 {516 _instructions.EmitStoreLocal(local.Index);517 }518 else519 {520 _instructions.EmitAssignLocal(local.Index);521 }522 }523 524 _instructions.SetDebugCookie(variable.Name);525 }526 527 public void CompileParameterExpression(Expression expr)528 {529 var node = (ParameterExpression)expr;530 CompileGetVariable(node);531 }532 533 private void CompileBlockExpression(Expression expr, bool asVoid)534 {535 var node = (BlockExpression)expr;536 var end = CompileBlockStart(node);537 538 var lastExpression = node.Expressions[node.Expressions.Count - 1];539 Compile(lastExpression, asVoid);540 CompileBlockEnd(end);541 }542 543 private LocalDefinition[] CompileBlockStart(BlockExpression node)544 {545 var start = _instructions.Count;546 547 LocalDefinition[] locals;548 var variables = node.Variables;549 if (variables.Count != 0)550 {551 // TODO: basic flow analysis so we don't have to initialize all552 // variables.553 locals = new LocalDefinition[variables.Count];554 int localCnt = 0;555 for (int index = 0; index < variables.Count; index++)556 {557 var variable = variables[index];558 var local = _locals.DefineLocal(variable, start);559 locals[localCnt++] = local;560 561 _instructions.EmitInitializeLocal(local.Index, variable.Type);562 _instructions.SetDebugCookie(variable.Name);563 }564 }565 else566 {567 locals = s_emptyLocals;568 }569 570 for (int i = 0; i < node.Expressions.Count - 1; i++)571 {572 CompileAsVoid(node.Expressions[i]);573 }574 575 return locals;576 }577 578 private void CompileBlockEnd(LocalDefinition[] locals)579 {580 for (int index = 0; index < locals.Length; index++)581 {582 var local = locals[index];583 _locals.UndefineLocal(local, _instructions.Count);584 }585 }586 587 private void CompileIndexExpression(Expression expr)588 {589 var index = (IndexExpression)expr;590 591 // instance:592 if (index.Object != null)593 {594 Compile(index.Object);595 }596 597 // indexes, byref args not allowed.598 for (int i = 0; i < index.Arguments.Count; i++)599 {600 var arg = index.Arguments[i];601 Compile(arg);602 }603 604 if (index.Indexer != null)605 {606 _instructions.EmitCall(index.Indexer.GetMethod);607 }608 else if (index.Arguments.Count != 1)609 {610 _instructions.EmitCall(index.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance));611 }612 else613 {614 _instructions.EmitGetArrayItem(index.Object.Type);615 }616 }617 618 private void CompileIndexAssignment(BinaryExpression node, bool asVoid)619 {620 var index = (IndexExpression)node.Left;621 622 if (!asVoid)623 {624 throw new NotImplementedException();625 }626 627 // instance:628 if (index.Object != null)629 {630 Compile(index.Object);631 }632 633 // indexes, byref args not allowed.634 for (int i = 0; i < index.Arguments.Count; i++)635 {636 var arg = index.Arguments[i];637 Compile(arg);638 }639 640 // value:641 Compile(node.Right);642 643 if (index.Indexer != null)644 {645 _instructions.EmitCall(index.Indexer.SetMethod);646 }647 else if (index.Arguments.Count != 1)648 {649 _instructions.EmitCall(index.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance));650 }651 else652 {653 _instructions.EmitSetArrayItem(index.Object.Type);654 }655 }656 657 private void CompileMemberAssignment(BinaryExpression node, bool asVoid)658 {659 var member = (MemberExpression)node.Left;660 661 PropertyInfo pi = member.Member as PropertyInfo;662 if (pi != null)663 {664 var method = pi.SetMethod;665 if (member.Expression != null)666 {667 Compile(member.Expression);668 }669 670 Compile(node.Right);671 672 int start = _instructions.Count;673 if (!asVoid)674 {675 LocalDefinition local = _locals.DefineLocal(Expression.Parameter(node.Right.Type), start);676 _instructions.EmitAssignLocal(local.Index);677 _instructions.EmitCall(method);678 _instructions.EmitLoadLocal(local.Index);679 _locals.UndefineLocal(local, _instructions.Count);680 }681 else682 {683 _instructions.EmitCall(method);684 }685 686 return;687 }688 689 FieldInfo fi = member.Member as FieldInfo;690 if (fi != null)691 {692 if (member.Expression != null)693 {694 Compile(member.Expression);695 }696 697 Compile(node.Right);698 699 int start = _instructions.Count;700 if (!asVoid)701 {702 LocalDefinition local = _locals.DefineLocal(Expression.Parameter(node.Right.Type), start);703 _instructions.EmitAssignLocal(local.Index);704 _instructions.EmitStoreField(fi);705 _instructions.EmitLoadLocal(local.Index);706 _locals.UndefineLocal(local, _instructions.Count);707 }708 else709 {710 _instructions.EmitStoreField(fi);711 }712 713 return;714 }715 716 throw new NotImplementedException();717 }718 719 private void CompileVariableAssignment(BinaryExpression node, bool asVoid)720 {721 this.Compile(node.Right);722 723 var target = (ParameterExpression)node.Left;724 CompileSetVariable(target, asVoid);725 }726 727 private void CompileAssignBinaryExpression(Expression expr, bool asVoid)728 {729 var node = (BinaryExpression)expr;730 731 switch (node.Left.NodeType)732 {733 case ExpressionType.Index:734 CompileIndexAssignment(node, asVoid);735 break;736 737 case ExpressionType.MemberAccess:738 CompileMemberAssignment(node, asVoid);739 break;740 741 case ExpressionType.Parameter:742 case ExpressionType.Extension:743 CompileVariableAssignment(node, asVoid);744 break;745 746 default:747 throw new InvalidOperationException("Invalid lvalue for assignment: " + node.Left.NodeType);748 }749 }750 751 private void CompileBinaryExpression(Expression expr)752 {753 var node = (BinaryExpression)expr;754 755 if (node.Method != null)756 {757 Compile(node.Left);758 Compile(node.Right);759 _instructions.EmitCall(node.Method);760 }761 else762 {763 switch (node.NodeType)764 {765 case ExpressionType.ArrayIndex:766 Debug.Assert(node.Right.Type == typeof(int));767 Compile(node.Left);768 Compile(node.Right);769 _instructions.EmitGetArrayItem(node.Left.Type);770 return;771 772 case ExpressionType.Add:773 case ExpressionType.AddChecked:774 case ExpressionType.Subtract:775 case ExpressionType.SubtractChecked:776 case ExpressionType.Multiply:777 case ExpressionType.MultiplyChecked:778 case ExpressionType.Divide:779 CompileArithmetic(node.NodeType, node.Left, node.Right);780 return;781 782 case ExpressionType.Equal:783 CompileEqual(node.Left, node.Right);784 return;785 786 case ExpressionType.NotEqual:787 CompileNotEqual(node.Left, node.Right);788 return;789 790 case ExpressionType.LessThan:791 case ExpressionType.LessThanOrEqual:792 case ExpressionType.GreaterThan:793 case ExpressionType.GreaterThanOrEqual:794 CompileComparison(node.NodeType, node.Left, node.Right);795 return;796 797 default:798 throw new NotImplementedException(node.NodeType.ToString());799 }800 }801 }802 803 private void CompileEqual(Expression left, Expression right)804 {805 Debug.Assert(left.Type == right.Type ||806 !left.Type.IsValueType && !right.Type.IsValueType);807 Compile(left);808 Compile(right);809 _instructions.EmitEqual(left.Type);810 }811 812 private void CompileNotEqual(Expression left, Expression right)813 {814 Debug.Assert(left.Type == right.Type ||815 !left.Type.IsValueType && !right.Type.IsValueType);816 Compile(left);817 Compile(right);818 _instructions.EmitNotEqual(left.Type);819 }820 821 private void CompileComparison(ExpressionType nodeType, Expression left, Expression right)822 {823 Debug.Assert(left.Type == right.Type && TypeUtils.IsNumeric(left.Type));824 825 // TODO:826 // if (TypeUtils.IsNullableType(left.Type) && liftToNull) ...827 828 Compile(left);829 Compile(right);830 831 switch (nodeType)832 {833 case ExpressionType.LessThan: _instructions.EmitLessThan(left.Type); break;834 case ExpressionType.LessThanOrEqual: _instructions.EmitLessThanOrEqual(left.Type); break;835 case ExpressionType.GreaterThan: _instructions.EmitGreaterThan(left.Type); break;836 case ExpressionType.GreaterThanOrEqual: _instructions.EmitGreaterThanOrEqual(left.Type); break;837 default: throw Assert.Unreachable;838 }839 }840 841 private void CompileArithmetic(ExpressionType nodeType, Expression left, Expression right)842 {843 Debug.Assert(left.Type == right.Type && TypeUtils.IsArithmetic(left.Type));844 Compile(left);845 Compile(right);846 switch (nodeType)847 {848 case ExpressionType.Add: _instructions.EmitAdd(left.Type, false); break;849 case ExpressionType.AddChecked: _instructions.EmitAdd(left.Type, true); break;850 case ExpressionType.Subtract: _instructions.EmitSub(left.Type, false); break;851 case ExpressionType.SubtractChecked: _instructions.EmitSub(left.Type, true); break;852 case ExpressionType.Multiply: _instructions.EmitMul(left.Type, false); break;853 case ExpressionType.MultiplyChecked: _instructions.EmitMul(left.Type, true); break;854 case ExpressionType.Divide: _instructions.EmitDiv(left.Type); break;855 default: throw Assert.Unreachable;856 }857 }858 859 private void CompileConvertUnaryExpression(Expression expr)860 {861 var node = (UnaryExpression)expr;862 if (node.Method != null)863 {864 Compile(node.Operand);865 866 // We should be able to ignore Int32ToObject867 if (node.Method != ScriptingRuntimeHelpers.Int32ToObjectMethod)868 {869 _instructions.EmitCall(node.Method);870 }871 }872 else if (node.Type == typeof(void))873 {874 CompileAsVoid(node.Operand);875 }876 else877 {878 Compile(node.Operand);879 CompileConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);880 }881 }882 883 private void CompileConvertToType(Type typeFrom, Type typeTo, bool isChecked)884 {885 Debug.Assert(typeFrom != typeof(void) && typeTo != typeof(void));886 887 if (typeTo == typeFrom)888 {889 return;890 }891 892 TypeCode from = typeFrom.GetTypeCode();893 TypeCode to = typeTo.GetTypeCode();894 if (TypeUtils.IsNumeric(from) && TypeUtils.IsNumeric(to))895 {896 if (isChecked)897 {898 _instructions.EmitNumericConvertChecked(from, to);899 }900 else901 {902 _instructions.EmitNumericConvertUnchecked(from, to);903 }904 905 return;906 }907 908 // TODO: Conversions to a super-class or implemented interfaces are no-op.909 // A conversion to a non-implemented interface or an unrelated class, etc. should fail.910 return;911 }912 913 private void CompileNotExpression(UnaryExpression node)914 {915 if (node.Operand.Type == typeof(bool))916 {917 Compile(node.Operand);918 _instructions.EmitNot();919 }920 else921 {922 throw new NotImplementedException();923 }924 }925 926 private void CompileUnaryExpression(Expression expr)927 {928 var node = (UnaryExpression)expr;929 930 if (node.Method != null)931 {932 Compile(node.Operand);933 _instructions.EmitCall(node.Method);934 }935 else936 {937 switch (node.NodeType)938 {939 case ExpressionType.Not:940 CompileNotExpression(node);941 return;942 case ExpressionType.TypeAs:943 CompileTypeAsExpression(node);944 return;945 default:946 throw new NotImplementedException(node.NodeType.ToString());947 }948 }949 }950 951 private void CompileAndAlsoBinaryExpression(Expression expr)952 {953 CompileLogicalBinaryExpression(expr, true);954 }955 956 private void CompileOrElseBinaryExpression(Expression expr)957 {958 CompileLogicalBinaryExpression(expr, false);959 }960 961 private void CompileLogicalBinaryExpression(Expression expr, bool andAlso)962 {963 var node = (BinaryExpression)expr;964 if (node.Method != null)965 {966 throw new NotImplementedException();967 }968 969 Debug.Assert(node.Left.Type == node.Right.Type);970 971 if (node.Left.Type == typeof(bool))972 {973 var elseLabel = _instructions.MakeLabel();974 var endLabel = _instructions.MakeLabel();975 Compile(node.Left);976 if (andAlso)977 {978 _instructions.EmitBranchFalse(elseLabel);979 }980 else981 {982 _instructions.EmitBranchTrue(elseLabel);983 }984 985 Compile(node.Right);986 _instructions.EmitBranch(endLabel, false, true);987 _instructions.MarkLabel(elseLabel);988 _instructions.EmitLoad(!andAlso);989 _instructions.MarkLabel(endLabel);990 return;991 }992 993 Debug.Assert(node.Left.Type == typeof(bool?));994 throw new NotImplementedException();995 }996 997 private void CompileConditionalExpression(Expression expr, bool asVoid)998 {999 var node = (ConditionalExpression)expr;1000 Compile(node.Test);1001 1002 if (node.IfTrue == AstUtils.Empty())1003 {1004 var endOfFalse = _instructions.MakeLabel();1005 _instructions.EmitBranchTrue(endOfFalse);1006 Compile(node.IfFalse, asVoid);1007 _instructions.MarkLabel(endOfFalse);1008 }1009 else1010 {1011 var endOfTrue = _instructions.MakeLabel();1012 _instructions.EmitBranchFalse(endOfTrue);1013 Compile(node.IfTrue, asVoid);1014 1015 if (node.IfFalse != AstUtils.Empty())1016 {1017 var endOfFalse = _instructions.MakeLabel();1018 _instructions.EmitBranch(endOfFalse, false, !asVoid);1019 _instructions.MarkLabel(endOfTrue);1020 Compile(node.IfFalse, asVoid);1021 _instructions.MarkLabel(endOfFalse);1022 }1023 else1024 {1025 _instructions.MarkLabel(endOfTrue);1026 }1027 }1028 }1029 1030 #region Loops1031 1032 private static void CompileLoopExpression(Expression expr)1033 {1034 // var node = (LoopExpression)expr;1035 // var enterLoop = new EnterLoopInstruction(node, _locals, _compilationThreshold, _instructions.Count);1036 //1037 // PushLabelBlock(LabelScopeKind.Statement);1038 // LabelInfo breakLabel = DefineLabel(node.BreakLabel);1039 // LabelInfo continueLabel = DefineLabel(node.ContinueLabel);1040 //1041 // _instructions.MarkLabel(continueLabel.GetLabel(this));1042 //1043 // // emit loop body:1044 // _instructions.Emit(enterLoop);1045 // CompileAsVoid(node.Body);1046 //1047 // // emit loop branch:1048 // _instructions.EmitBranch(continueLabel.GetLabel(this), expr.Type != typeof(void), false);1049 //1050 // _instructions.MarkLabel(breakLabel.GetLabel(this));1051 //1052 // PopLabelBlock(LabelScopeKind.Statement);1053 //1054 // enterLoop.FinishLoop(_instructions.Count);1055 }1056 1057 #endregion1058 1059 private void CompileSwitchExpression(Expression expr)1060 {1061 var node = (SwitchExpression)expr;1062 1063 // Currently only supports int test values, with no method1064 if (node.SwitchValue.Type != typeof(int) || node.Comparison != null)1065 {1066 throw new NotImplementedException();1067 }1068 1069 // Test values must be constant1070 if (!node.Cases.All(static c => c.TestValues.All(t => t is ConstantExpression)))1071 {1072 throw new NotImplementedException();1073 }1074 1075 LabelInfo end = DefineLabel(null);1076 bool hasValue = node.Type != typeof(void);1077 1078 Compile(node.SwitchValue);1079 var caseDict = new Dictionary<int, int>();1080 int switchIndex = _instructions.Count;1081 _instructions.EmitSwitch(caseDict);1082 1083 if (node.DefaultBody != null)1084 {1085 Compile(node.DefaultBody);1086 }1087 else1088 {1089 Debug.Assert(!hasValue);1090 }1091 1092 _instructions.EmitBranch(end.GetLabel(this), false, hasValue);1093 1094 for (int i = 0; i < node.Cases.Count; i++)1095 {1096 var switchCase = node.Cases[i];1097 1098 int caseOffset = _instructions.Count - switchIndex;1099 for (int index = 0; index < switchCase.TestValues.Count; index++)1100 {1101 var testValue = (ConstantExpression)switchCase.TestValues[index];1102 caseDict[(int)testValue.Value] = caseOffset;1103 }1104 1105 Compile(switchCase.Body);1106 1107 if (i < node.Cases.Count - 1)1108 {1109 _instructions.EmitBranch(end.GetLabel(this), false, hasValue);1110 }1111 }1112 1113 _instructions.MarkLabel(end.GetLabel(this));1114 }1115 1116 private void CompileLabelExpression(Expression expr)1117 {1118 var node = (LabelExpression)expr;1119 1120 // If we're an immediate child of a block, our label will already1121 // be defined. If not, we need to define our own block so this1122 // label isn't exposed except to its own child expression.1123 LabelInfo label = null;1124 1125 if (_labelBlock.Kind == LabelScopeKind.Block)1126 {1127 _labelBlock.TryGetLabelInfo(node.Target, out label);1128 1129 // We're in a block but didn't find our label, try switch1130 if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch)1131 {1132 _labelBlock.Parent.TryGetLabelInfo(node.Target, out label);1133 }1134 1135 // if we're in a switch or block, we should've found the label1136 Debug.Assert(label != null);1137 }1138 1139 label ??= DefineLabel(node.Target);1140 1141 if (node.DefaultValue != null)1142 {1143 if (node.Target.Type == typeof(void))1144 {1145 CompileAsVoid(node.DefaultValue);1146 }1147 else1148 {1149 Compile(node.DefaultValue);1150 }1151 }1152 1153 _instructions.MarkLabel(label.GetLabel(this));1154 }1155 1156 private void CompileGotoExpression(Expression expr)1157 {1158 var node = (GotoExpression)expr;1159 var labelInfo = ReferenceLabel(node.Target);1160 1161 if (node.Value != null)1162 {1163 Compile(node.Value);1164 }1165 1166 _instructions.EmitGoto(labelInfo.GetLabel(this), node.Type != typeof(void), node.Value != null && node.Value.Type != typeof(void));1167 }1168 1169 public BranchLabel GetBranchLabel(LabelTarget target)1170 {1171 return ReferenceLabel(target).GetLabel(this);1172 }1173 1174 public void PushLabelBlock(LabelScopeKind type)1175 {1176 _labelBlock = new LabelScopeInfo(_labelBlock, type);1177 }1178 1179 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")]1180 public void PopLabelBlock(LabelScopeKind kind)1181 {1182 Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind);1183 _labelBlock = _labelBlock.Parent;1184 }1185 1186 private LabelInfo EnsureLabel(LabelTarget node)1187 {1188 LabelInfo result;1189 if (!_treeLabels.TryGetValue(node, out result))1190 {1191 _treeLabels[node] = result = new LabelInfo(node);1192 }1193 1194 return result;1195 }1196 1197 private LabelInfo ReferenceLabel(LabelTarget node)1198 {1199 LabelInfo result = EnsureLabel(node);1200 result.Reference(_labelBlock);