private bool TryEmitHashtableSwitch(SwitchExpression node, CompilationFlags flags) { // If we have a comparison other than string equality, bail if (node.Comparison != String_op_Equality_String_String && node.Comparison != String_Equals_String_String) { return(false); } // All test values must be constant. int tests = 0; foreach (SwitchCase c in node.Cases) { foreach (Expression t in c.TestValues) { if (!(t is ConstantExpression)) { return(false); } tests++; } } // Must have >= 7 labels for it to be worth it. if (tests < 7) { return(false); } // If we're in a DynamicMethod, we could just build the dictionary // immediately. But that would cause the two code paths to be more // different than they really need to be. var initializers = new List <ElementInit>(tests); var cases = new ArrayBuilder <SwitchCase>(node.Cases.Count); int nullCase = -1; MethodInfo add = DictionaryOfStringInt32_Add_String_Int32; for (int i = 0, n = node.Cases.Count; i < n; i++) { foreach (ConstantExpression t in node.Cases[i].TestValues) { if (t.Value != null) { initializers.Add(Expression.ElementInit(add, new TrueReadOnlyCollection <Expression>(t, Utils.Constant(i)))); } else { nullCase = i; } } cases.UncheckedAdd(Expression.SwitchCase(node.Cases[i].Body, new TrueReadOnlyCollection <Expression>(Utils.Constant(i)))); } // Create the field to hold the lazily initialized dictionary MemberExpression dictField = CreateLazyInitializedField <Dictionary <string, int> >("dictionarySwitch"); // If we happen to initialize it twice (multithreaded case), it's // not the end of the world. The C# compiler does better here by // emitting a volatile access to the field. Expression dictInit = Expression.Condition( Expression.Equal(dictField, Expression.Constant(null, dictField.Type)), Expression.Assign( dictField, Expression.ListInit( Expression.New( DictionaryOfStringInt32_Ctor_Int32, new TrueReadOnlyCollection <Expression>( Utils.Constant(initializers.Count) ) ), initializers ) ), dictField ); // // Create a tree like: // // switchValue = switchValueExpression; // if (switchValue == null) { // switchIndex = nullCase; // } else { // if (_dictField == null) { // _dictField = new Dictionary<string, int>(count) { { ... }, ... }; // } // if (!_dictField.TryGetValue(switchValue, out switchIndex)) { // switchIndex = -1; // } // } // switch (switchIndex) { // case 0: ... // case 1: ... // ... // default: // } // ParameterExpression switchValue = Expression.Variable(typeof(string), "switchValue"); ParameterExpression switchIndex = Expression.Variable(typeof(int), "switchIndex"); BlockExpression reduced = Expression.Block( new TrueReadOnlyCollection <ParameterExpression>(switchIndex, switchValue), new TrueReadOnlyCollection <Expression>( Expression.Assign(switchValue, node.SwitchValue), Expression.IfThenElse( Expression.Equal(switchValue, Expression.Constant(null, typeof(string))), Expression.Assign(switchIndex, Utils.Constant(nullCase)), Expression.IfThenElse( Expression.Call(dictInit, "TryGetValue", null, switchValue, switchIndex), Utils.Empty, Expression.Assign(switchIndex, Utils.Constant(-1)) ) ), Expression.Switch(node.Type, switchIndex, node.DefaultBody, null, cases.ToReadOnly()) ) ); EmitExpression(reduced, flags); return(true); }
/// <summary> /// Visits the children of <see cref="System.Linq.Expressions.SwitchExpression"/>. /// </summary> /// <param name="node">The expression to visit.</param> /// <returns>The modified expression, if it or any subexpression was modified; otherwise, /// returns the original expression.</returns> protected override Expression VisitSwitch(SwitchExpression node) { throw new NotSupportedException(string.Format(Resources.EX_PROCESS_NODE_NOT_SUPPORT, node.GetType().Name)); }
private static Paths BuildPathsSwitch(SwitchExpression node, Context context) { throw new NotImplementedException(); }
protected override Expression VisitSwitch(SwitchExpression node) { throw new NotSupportedException(); }
private bool TryEmitHashtableSwitch(SwitchExpression node, CompilationFlags flags) { // If we have a comparison other than string equality, bail MethodInfo equality = String_op_Equality_String_String; if (equality != null && !equality.IsStatic) { equality = null; } if (node.Comparison != equality) { return false; } // All test values must be constant. int tests = 0; foreach (SwitchCase c in node.Cases) { foreach (Expression t in c.TestValues) { if (!(t is ConstantExpression)) { return false; } tests++; } } // Must have >= 7 labels for it to be worth it. if (tests < 7) { return false; } // If we're in a DynamicMethod, we could just build the dictionary // immediately. But that would cause the two code paths to be more // different than they really need to be. var initializers = new List<ElementInit>(tests); var cases = new ArrayBuilder<SwitchCase>(node.Cases.Count); int nullCase = -1; MethodInfo add = DictionaryOfStringInt32_Add_String_Int32; for (int i = 0, n = node.Cases.Count; i < n; i++) { foreach (ConstantExpression t in node.Cases[i].TestValues) { if (t.Value != null) { initializers.Add(Expression.ElementInit(add, new TrueReadOnlyCollection<Expression>(t, Utils.Constant(i)))); } else { nullCase = i; } } cases.UncheckedAdd(Expression.SwitchCase(node.Cases[i].Body, new TrueReadOnlyCollection<Expression>(Utils.Constant(i)))); } // Create the field to hold the lazily initialized dictionary MemberExpression dictField = CreateLazyInitializedField<Dictionary<string, int>>("dictionarySwitch"); // If we happen to initialize it twice (multithreaded case), it's // not the end of the world. The C# compiler does better here by // emitting a volatile access to the field. Expression dictInit = Expression.Condition( Expression.Equal(dictField, Expression.Constant(null, dictField.Type)), Expression.Assign( dictField, Expression.ListInit( Expression.New( DictionaryOfStringInt32_Ctor_Int32, new TrueReadOnlyCollection<Expression>( Utils.Constant(initializers.Count) ) ), initializers ) ), dictField ); // // Create a tree like: // // switchValue = switchValueExpression; // if (switchValue == null) { // switchIndex = nullCase; // } else { // if (_dictField == null) { // _dictField = new Dictionary<string, int>(count) { { ... }, ... }; // } // if (!_dictField.TryGetValue(switchValue, out switchIndex)) { // switchIndex = -1; // } // } // switch (switchIndex) { // case 0: ... // case 1: ... // ... // default: // } // ParameterExpression switchValue = Expression.Variable(typeof(string), "switchValue"); ParameterExpression switchIndex = Expression.Variable(typeof(int), "switchIndex"); BlockExpression reduced = Expression.Block( new TrueReadOnlyCollection<ParameterExpression>(switchIndex, switchValue), new TrueReadOnlyCollection<Expression>( Expression.Assign(switchValue, node.SwitchValue), Expression.IfThenElse( Expression.Equal(switchValue, Expression.Constant(null, typeof(string))), Expression.Assign(switchIndex, Utils.Constant(nullCase)), Expression.IfThenElse( Expression.Call(dictInit, "TryGetValue", null, switchValue, switchIndex), Utils.Empty, Expression.Assign(switchIndex, Utils.Constant(-1)) ) ), Expression.Switch(node.Type, switchIndex, node.DefaultBody, null, cases.ToReadOnly()) ) ); EmitExpression(reduced, flags); return true; }
internal SwitchInfo(SwitchExpression node, LocalBuilder value, Label @default) { Node = node; Value = value; Default = @default; Type = Node.SwitchValue.Type; IsUnsigned = TypeUtils.IsUnsigned(Type); var code = Type.GetTypeCode(Type); Is64BitSwitch = code == TypeCode.UInt64 || code == TypeCode.Int64; }
// Emits the switch as if stmts private void EmitConditionalBranches(SwitchExpression node, Label[] labels) { LocalBuilder testValueSlot = _ilg.GetLocal(typeof(int)); _ilg.Emit(OpCodes.Stloc, testValueSlot); // For all the "cases" create their conditional branches for (int i = 0; i < node.SwitchCases.Count; i++) { // Not default case emit the condition if (!node.SwitchCases[i].IsDefault) { // Test for equality of case value and the test expression _ilg.EmitInt(node.SwitchCases[i].Value); _ilg.Emit(OpCodes.Ldloc, testValueSlot); _ilg.Emit(OpCodes.Beq, labels[i]); } } _ilg.FreeLocal(testValueSlot); }
private bool CompareSwitch(SwitchExpression a, SwitchExpression b) => Equals(a.Comparison, b.Comparison) && Compare(a.SwitchValue, b.SwitchValue) && Compare(a.DefaultBody, b.DefaultBody) && CompareSwitchCaseList(a.Cases, b.Cases);
private Result RewriteSwitchExpression(Expression expr, Stack stack) { var node = (SwitchExpression)expr; // The switch statement test is emitted on the stack in current state. var switchValue = RewriteExpressionFreeTemps(node.SwitchValue, stack); var action = switchValue.Action; var cases = node.Cases.AsArrayInternal(); SwitchCase[] clone = null; for (var i = 0; i < cases.Length; i++) { var @case = cases[i]; Expression[] cloneTests = null; var testValues = @case.TestValues.AsArrayInternal(); for (var j = 0; j < testValues.Length; j++) { // All tests execute at the same stack state as the switch. // This is guaranteed by the compiler (to simplify spilling). var test = RewriteExpression(testValues[j], stack); action |= test.Action; if (cloneTests == null && test.Action != RewriteAction.None) { cloneTests = Clone(testValues, j); } if (cloneTests != null) { cloneTests[j] = test.Node; } } // And all the cases also run on the same stack level. var body = RewriteExpression(@case.Body, stack); action |= body.Action; if (body.Action != RewriteAction.None || cloneTests != null) { if (cloneTests != null) { testValues = cloneTests; } @case = new SwitchCase(body.Node, testValues); if (clone == null) { clone = Clone(cases, i); } } if (clone != null) { clone[i] = @case; } } // default body also runs on initial stack var defaultBody = RewriteExpression(node.DefaultBody, stack); action |= defaultBody.Action; if (action == RewriteAction.None) { return(new Result(action, expr)); } if (clone != null) { // okay to wrap because we aren't modifying the array cases = clone; } expr = new SwitchExpression(node.Type, switchValue.Node, defaultBody.Node, node.Comparison, cases); return(new Result(action, expr)); }
/// <nodoc /> public virtual void Visit(SwitchExpression switchExpression) { }
public virtual void Visit(SwitchExpression expression, TranslationContext context, bool negated = false) { }
protected override Expression MakeSwitch(SwitchExpression node, Expression switchValue, Expression defaultBody, ReadOnlyCollection <SwitchCase> cases) { return(Expression.Switch(switchValue, defaultBody, cases.Reverse().ToArray())); }
public override void VisitSwitchExpression(SwitchExpression switchExpression) { ParenthesizeIfRequired(switchExpression.Expression, PrecedenceLevel.Switch + 1); base.VisitSwitchExpression(switchExpression); }
// Tries to emit switch as a jmp table private bool TryEmitJumpTable(SwitchExpression node, Label[] labels, Label defaultTarget) { if (node.SwitchCases.Count > MaxJumpTableSize) { return false; } int min = Int32.MaxValue; int max = Int32.MinValue; // Find the min and max of the values for (int i = 0; i < node.SwitchCases.Count; ++i) { // Not the default case. if (!node.SwitchCases[i].IsDefault) { int val = node.SwitchCases[i].Value; if (min > val) min = val; if (max < val) max = val; } } long delta = (long)max - (long)min; if (delta > MaxJumpTableSize) { return false; } // Value distribution is too sparse, don't emit jump table. if (delta > node.SwitchCases.Count + MaxJumpTableSparsity) { return false; } // The actual jmp table of switch int len = (int)delta + 1; Label[] jmpLabels = new Label[len]; // Initialize all labels to the default for (int i = 0; i < len; i++) { jmpLabels[i] = defaultTarget; } // Replace with the actual label target for all cases for (int i = 0; i < node.SwitchCases.Count; i++) { SwitchCase sc = node.SwitchCases[i]; if (!sc.IsDefault) { jmpLabels[sc.Value - min] = labels[i]; } } // Emit the normalized index and then switch based on that if (min != 0) { _ilg.EmitInt(min); _ilg.Emit(OpCodes.Sub); } _ilg.Emit(OpCodes.Switch, jmpLabels); return true; }
private void EmitSwitchExpression(Expression expr, CompilationFlags flags) { SwitchExpression node = (SwitchExpression)expr; if (node.Cases.Count == 0) { // Emit the switch value in case it has side-effects, but as void // since the value is ignored. EmitExpressionAsVoid(node.SwitchValue); // Now if there is a default body, it happens unconditionally. if (node.DefaultBody != null) { EmitExpressionAsType(node.DefaultBody, node.Type, flags); } else { // If there are no cases and no default then the type must be void. // Assert that earlier validation caught any exceptions to that. Debug.Assert(node.Type == typeof(void)); } return; } // Try to emit it as an IL switch. Works for integer types. if (TryEmitSwitchInstruction(node, flags)) { return; } // Try to emit as a hashtable lookup. Works for strings. if (TryEmitHashtableSwitch(node, flags)) { return; } // // Fall back to a series of tests. We need to IL gen instead of // transform the tree to avoid stack overflow on a big switch. // ParameterExpression switchValue = Expression.Parameter(node.SwitchValue.Type, "switchValue"); ParameterExpression testValue = Expression.Parameter(GetTestValueType(node), "testValue"); _scope.AddLocal(this, switchValue); _scope.AddLocal(this, testValue); EmitExpression(node.SwitchValue); _scope.EmitSet(switchValue); // Emit tests var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; for (int i = 0, n = node.Cases.Count; i < n; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (Expression test in node.Cases[i].TestValues) { // Pull the test out into a temp so it runs on the same // stack as the switch. This simplifies spilling. EmitExpression(test); _scope.EmitSet(testValue); Debug.Assert(TypeUtils.AreReferenceAssignable(testValue.Type, test.Type)); EmitExpressionAndBranch(true, Expression.Equal(switchValue, testValue, false, node.Comparison), labels[i]); } } // Define labels Label end = _ilg.DefineLabel(); Label @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the case and default bodies EmitSwitchCases(node, labels, isGoto, @default, end, flags); }
protected override Expression VisitSwitch(SwitchExpression node) { if (node.SwitchValue.Type.GetUnwrappedNullableType().IsIntegerType() || node.SwitchValue.Type.GetUnwrappedNullableType().BaseType == typeof(Enum)) { this.Write("switch"); this.Write(" ("); this.Visit(node.SwitchValue); this.WriteLine(")"); using (this.AcquireIndentationContext(BraceLanguageStyleIndentationOptions.IncludeBracesNewLineAfter)) { foreach (var switchCase in node.Cases) { this.VisitSwitchCase(switchCase); } this.WriteLine("default:"); using (this.AcquireIndentationContext(BraceLanguageStyleIndentationOptions.Default)) { this.Visit(node.DefaultBody); } } return(node); } else { var j = 0; foreach (var switchCase in node.Cases) { if (j++ > 0) { this.Write("else "); } this.Write("if ("); var i = 0; foreach (var testValue in switchCase.TestValues) { if (switchCase.TestValues.Count > 1) { this.Write("("); } this.Visit(node.SwitchValue); this.Write(" == "); this.Visit(testValue); if (switchCase.TestValues.Count > 1) { this.Write(")"); } if (i++ < switchCase.TestValues.Count - 1) { this.Write(" || "); } } this.WriteLine(")"); if (switchCase.Body.NodeType != ExpressionType.Block) { using (this.AcquireIndentationContext(BraceLanguageStyleIndentationOptions.IncludeBraces)) { this.Visit(switchCase.Body); } this.WriteLine(); } else { this.Visit(switchCase.Body); this.WriteLine(); } } if (node.DefaultBody != null) { if (node.Cases.Count > 0) { this.WriteLine("else"); } using (this.AcquireIndentationContext(BraceLanguageStyleIndentationOptions.IncludeBracesNewLineAfter)) { this.Visit(node.DefaultBody); } } return(node); } }
public SwitchExpressionProxy(SwitchExpression node) { _node = node; }
protected override Expression VisitSwitch(SwitchExpression node) { throw new NotSupportedException("Switch not supported by OData Query Filters."); }
private void EmitSwitchCases(SwitchExpression node, Label[] labels, bool[] isGoto, Label @default, Label end, CompilationFlags flags) { // Jump to default (to handle the fallthrough case) _ilg.Emit(OpCodes.Br, @default); // Emit the cases for (int i = 0, n = node.Cases.Count; i < n; i++) { // If the body is a goto, we already emitted an optimized // branch directly to it. No need to emit anything else. if (isGoto[i]) { continue; } _ilg.MarkLabel(labels[i]); EmitExpressionAsType(node.Cases[i].Body, node.Type, flags); // Last case doesn't need branch if (node.DefaultBody != null || i < n - 1) { if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail) { //The switch case is at the tail of the lambda so //it is safe to emit a Ret. _ilg.Emit(OpCodes.Ret); } else { _ilg.Emit(OpCodes.Br, end); } } } // Default value if (node.DefaultBody != null) { _ilg.MarkLabel(@default); EmitExpressionAsType(node.DefaultBody, node.Type, flags); } _ilg.MarkLabel(end); }
public SwitchTranslation(SwitchExpression switchStatement, ITranslationContext context) { _settings = context.Settings; Type = switchStatement.Type; _valueTranslation = context.GetTranslationFor(switchStatement.SwitchValue); var keywordFormattingSize = context.GetKeywordFormattingSize(); var translationSize = _switch.Length + _valueTranslation.TranslationSize + 4; var formattingSize = keywordFormattingSize + // <- for 'switch' _valueTranslation.FormattingSize; _casesCount = switchStatement.Cases.Count; _caseTestValueTranslations = new ITranslation[_casesCount][]; _caseTranslations = new ITranslation[_casesCount]; for (var i = 0; ;) { var @case = switchStatement.Cases[i]; var testValueCount = @case.TestValues.Count; var caseTestValueTranslations = new ITranslation[testValueCount]; for (var j = 0; ;) { var caseTestValueTranslation = context.GetTranslationFor(@case.TestValues[j]); caseTestValueTranslations[j] = caseTestValueTranslation; translationSize += _case.Length + caseTestValueTranslation.TranslationSize + 3; formattingSize += keywordFormattingSize + // <- for 'case' caseTestValueTranslation.FormattingSize; ++j; if (j == testValueCount) { break; } translationSize += 3; } _caseTestValueTranslations[i] = caseTestValueTranslations; var caseTranslation = GetCaseBodyTranslationOrNull(@case.Body, context); _caseTranslations[i] = caseTranslation; translationSize += caseTranslation.TranslationSize; formattingSize += caseTranslation.FormattingSize; if (WriteBreak(caseTranslation)) { translationSize += "break;".Length; formattingSize += keywordFormattingSize; } ++i; if (i == _casesCount) { break; } } _defaultCaseTranslation = GetCaseBodyTranslationOrNull(switchStatement.DefaultBody, context); if (_defaultCaseTranslation != null) { translationSize += _defaultCaseTranslation.TranslationSize; formattingSize += _defaultCaseTranslation.FormattingSize; if (WriteBreak(_defaultCaseTranslation)) { translationSize += "break;".Length; formattingSize += keywordFormattingSize; } } TranslationSize = translationSize; FormattingSize = formattingSize; }
/// <summary>Visits the children of the <see cref="T:System.Linq.Expressions.SwitchExpression"></see>.</summary> /// <param name="node">The expression to visit.</param> /// <returns>The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.</returns> protected override Expression VisitSwitch(SwitchExpression node) { WriteMember(node.Comparison); return(base.VisitSwitch(node)); }
protected override Expression VisitSwitch(SwitchExpression node) { throw new ArgumentException($"Expecxting a path like x.Property.Value was {this.expression}", nameof(node)); }
/// <summary> /// Visits the children of the SwitchExpression. /// </summary> /// <param name="node">Expression to visit.</param> /// <returns>Result of visiting the expression.</returns> protected override TExpression VisitSwitch(SwitchExpression node) => throw NotSupported(node);
protected override Expression VisitSwitch(SwitchExpression node) { this.ThrowUnsupportedNodeException(node); return(base.VisitSwitch(node)); }
protected override Expression VisitSwitch(SwitchExpression node) { return(base.VisitSwitch(node)); }
protected override Expression VisitSwitch(SwitchExpression node) { return(this.GiveUp <SwitchExpression>(node)); }
// SwitchStatement private Result RewriteSwitchExpression(Expression expr, Stack stack) { SwitchExpression node = (SwitchExpression)expr; // The switch statement test is emitted on the stack in current state Result switchValue = RewriteExpressionFreeTemps(node.SwitchValue, stack); RewriteAction action = switchValue.Action; ReadOnlyCollection <SwitchCase> cases = node.Cases; SwitchCase[] clone = null; for (int i = 0; i < cases.Count; i++) { SwitchCase @case = cases[i]; Expression[] cloneTests = null; ReadOnlyCollection <Expression> testValues = @case.TestValues; for (int j = 0; j < testValues.Count; j++) { // All tests execute at the same stack state as the switch. // This is guaranteed by the compiler (to simplify spilling) Result test = RewriteExpression(testValues[j], stack); action |= test.Action; if (cloneTests == null && test.Action != RewriteAction.None) { cloneTests = Clone(testValues, j); } if (cloneTests != null) { cloneTests[j] = test.Node; } } // And all the cases also run on the same stack level. Result body = RewriteExpression(@case.Body, stack); action |= body.Action; if (body.Action != RewriteAction.None || cloneTests != null) { if (cloneTests != null) { testValues = new ReadOnlyCollection <Expression>(cloneTests); } @case = new SwitchCase(body.Node, testValues); if (clone == null) { clone = Clone(cases, i); } } if (clone != null) { clone[i] = @case; } } // default body also runs on initial stack Result defaultBody = RewriteExpression(node.DefaultBody, stack); action |= defaultBody.Action; if (action != RewriteAction.None) { if (clone != null) { // okay to wrap because we aren't modifying the array cases = new ReadOnlyCollection <SwitchCase>(clone); } expr = new SwitchExpression(node.Type, switchValue.Node, defaultBody.Node, node.Comparison, cases); } return(new Result(action, expr)); }
protected override bool VisitSwitch(SwitchExpression node) => false;
protected override Expression VisitSwitch(SwitchExpression node) { throw new NotImplementedException(); }
protected override Expression VisitSwitch(SwitchExpression node) { return(GiveUp(node)); }
protected override Expression VisitSwitch(SwitchExpression node) => GiveUp(node);
// Tries to emit switch as a jmp table private bool TryEmitSwitchInstruction(SwitchExpression node, CompilationFlags flags) { // If we have a comparison, bail if (node.Comparison != null) { return(false); } // Make sure the switch value type and the right side type // are types we can optimize Type type = node.SwitchValue.Type; if (!CanOptimizeSwitchType(type) || !TypeUtils.AreEquivalent(type, node.Cases[0].TestValues[0].Type)) { return(false); } // Make sure all test values are constant, or we can't emit the // jump table. if (!node.Cases.All(c => c.TestValues.All(t => t is ConstantExpression))) { return(false); } // // We can emit the optimized switch, let's do it. // // Build target labels, collect keys. var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; var uniqueKeys = new HashSet <decimal>(); var keys = new List <SwitchLabel>(); for (int i = 0; i < node.Cases.Count; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (ConstantExpression test in node.Cases[i].TestValues) { // Guaranteed to work thanks to CanOptimizeSwitchType. // // Use decimal because it can hold Int64 or UInt64 without // precision loss or signed/unsigned conversions. decimal key = ConvertSwitchValue(test.Value); // Only add each key once. If it appears twice, it's // allowed, but can't be reached. if (uniqueKeys.Add(key)) { keys.Add(new SwitchLabel(key, test.Value, labels[i])); } } } // Sort the keys, and group them into buckets. keys.Sort((x, y) => Math.Sign(x.Key - y.Key)); var buckets = new List <List <SwitchLabel> >(); foreach (var key in keys) { AddToBuckets(buckets, key); } // Emit the switchValue LocalBuilder value = GetLocal(node.SwitchValue.Type); EmitExpression(node.SwitchValue); _ilg.Emit(OpCodes.Stloc, value); // Create end label, and default label if needed Label end = _ilg.DefineLabel(); Label @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the switch var info = new SwitchInfo(node, value, @default); EmitSwitchBuckets(info, buckets, 0, buckets.Count - 1); // Emit the case bodies and default EmitSwitchCases(node, labels, isGoto, @default, end, flags); FreeLocal(value); return(true); }
public DefaultCaseStatementWithLinqExpressions(SwitchExpression statement) { Statement = statement; statements = LinqStatementBuilder.BuildStatements(statement.DefaultBody); }
/// <summary> /// Gets the common test test value type of the SwitchExpression. /// </summary> private static Type GetTestValueType(SwitchExpression node) { if (node.Comparison == null) { // If we have no comparison, all right side types must be the // same. return node.Cases[0].TestValues[0].Type; } // Otherwise, get the type from the method. Type result = node.Comparison.GetParametersCached()[1].ParameterType.GetNonRefType(); if (node.IsLifted) { result = TypeUtils.GetNullableType(result); } return result; }
private void EmitSwitchCases(SwitchExpression node, Label[] labels, bool[] isGoto, Label @default, Label end) { // Jump to default (to handle the fallthrough case) _ilg.Emit(OpCodes.Br, @default); // Emit the cases for (int i = 0, n = node.Cases.Count; i < n; i++) { // If the body is a goto, we already emitted an optimized // branch directly to it. No need to emit anything else. if (isGoto[i]) { continue; } _ilg.MarkLabel(labels[i]); EmitExpressionAsType(node.Cases[i].Body, node.Type); // Last case doesn't need branch if (node.DefaultBody != null || i < n - 1) { _ilg.Emit(OpCodes.Br, end); } } // Default value if (node.DefaultBody != null) { _ilg.MarkLabel(@default); EmitExpressionAsType(node.DefaultBody, node.Type); } _ilg.MarkLabel(end); }
// Tries to emit switch as a jmp table private bool TryEmitSwitchInstruction(SwitchExpression node, CompilationFlags flags) { // If we have a comparison, bail if (node.Comparison != null) { return false; } // Make sure the switch value type and the right side type // are types we can optimize Type type = node.SwitchValue.Type; if (!CanOptimizeSwitchType(type) || !TypeUtils.AreEquivalent(type, node.Cases[0].TestValues[0].Type)) { return false; } // Make sure all test values are constant, or we can't emit the // jump table. if (!node.Cases.All(c => c.TestValues.All(t => t is ConstantExpression))) { return false; } // // We can emit the optimized switch, let's do it. // // Build target labels, collect keys. var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; var uniqueKeys = new Set<decimal>(); var keys = new List<SwitchLabel>(); for (int i = 0; i < node.Cases.Count; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (ConstantExpression test in node.Cases[i].TestValues) { // Guarenteed to work thanks to CanOptimizeSwitchType. // // Use decimal because it can hold Int64 or UInt64 without // precision loss or signed/unsigned conversions. decimal key = ConvertSwitchValue(test.Value); // Only add each key once. If it appears twice, it's // allowed, but can't be reached. if (!uniqueKeys.Contains(key)) { keys.Add(new SwitchLabel(key, test.Value, labels[i])); uniqueKeys.Add(key); } } } // Sort the keys, and group them into buckets. keys.Sort((x, y) => Math.Sign(x.Key - y.Key)); var buckets = new List<List<SwitchLabel>>(); foreach (var key in keys) { AddToBuckets(buckets, key); } // Emit the switchValue LocalBuilder value = GetLocal(node.SwitchValue.Type); EmitExpression(node.SwitchValue); _ilg.Emit(OpCodes.Stloc, value); // Create end label, and default label if needed Label end = _ilg.DefineLabel(); Label @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the switch var info = new SwitchInfo(node, value, @default); EmitSwitchBuckets(info, buckets, 0, buckets.Count - 1); // Emit the case bodies and default EmitSwitchCases(node, labels, isGoto, @default, end, flags); FreeLocal(value); return true; }
protected override Expression MakeSwitch(SwitchExpression node, Expression switchValue, Expression defaultBody, ReadOnlyCollection <SwitchCase> cases) => node;
private bool TryEmitHashtableSwitch(SwitchExpression node, CompilationFlags flags) { // If we have a comparison other than string equality, bail if (node.Comparison != typeof(string).GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static | BindingFlags.ExactBinding, null, new[] { typeof(string), typeof(string) }, null)) { return false; } // All test values must be constant. int tests = 0; foreach (SwitchCase c in node.Cases) { foreach (Expression t in c.TestValues) { if (!(t is ConstantExpression)) { return false; } tests++; } } // Must have >= 7 labels for it to be worth it. if (tests < 7) { return false; } // If we're in a DynamicMethod, we could just build the dictionary // immediately. But that would cause the two code paths to be more // different than they really need to be. var initializers = new List<ElementInit>(tests); var cases = new List<SwitchCase>(node.Cases.Count); int nullCase = -1; MethodInfo add = typeof(Dictionary<string, int>).GetMethod("Add", new[] { typeof(string), typeof(int) }); for (int i = 0, n = node.Cases.Count; i < n; i++) { foreach (ConstantExpression t in node.Cases[i].TestValues) { if (t.Value != null) { initializers.Add(Expression.ElementInit(add, t, Expression.Constant(i))); } else { nullCase = i; } } cases.Add(Expression.SwitchCase(node.Cases[i].Body, Expression.Constant(i))); } // Create the field to hold the lazily initialized dictionary MemberExpression dictField = CreateLazyInitializedField<Dictionary<string, int>>("dictionarySwitch"); // If we happen to initialize it twice (multithreaded case), it's // not the end of the world. The C# compiler does better here by // emitting a volatile access to the field. Expression dictInit = Expression.Condition( Expression.Equal(dictField, Expression.Constant(null, dictField.Type)), Expression.Assign( dictField, Expression.ListInit( Expression.New( typeof(Dictionary<string, int>).GetConstructor(new[] { typeof(int) }), Expression.Constant(initializers.Count) ), initializers ) ), dictField ); // // Create a tree like: // // switchValue = switchValueExpression; // if (switchValue == null) { // switchIndex = nullCase; // } else { // if (_dictField == null) { // _dictField = new Dictionary<string, int>(count) { { ... }, ... }; // } // if (!_dictField.TryGetValue(switchValue, out switchIndex)) { // switchIndex = -1; // } // } // switch (switchIndex) { // case 0: ... // case 1: ... // ... // default: // } // var switchValue = Expression.Variable(typeof(string), "switchValue"); var switchIndex = Expression.Variable(typeof(int), "switchIndex"); var reduced = Expression.Block( new[] { switchIndex, switchValue }, Expression.Assign(switchValue, node.SwitchValue), Expression.IfThenElse( Expression.Equal(switchValue, Expression.Constant(null, typeof(string))), Expression.Assign(switchIndex, Expression.Constant(nullCase)), Expression.IfThenElse( Expression.Call(dictInit, "TryGetValue", null, switchValue, switchIndex), Expression.Empty(), Expression.Assign(switchIndex, Expression.Constant(-1)) ) ), Expression.Switch(node.Type, switchIndex, node.DefaultBody, null, cases) ); EmitExpression(reduced, flags); return true; }
public SerializableExpression Convert(Expression expression) { if (expression == null) { return(null); } else if (serialized.ContainsKey(expression)) { /* Caching is required to maintain object references during serialization. * See the comments on SerializableExpression.ConvertWithCache for more info. */ return(serialized[expression]); } else if ((methodCall = expression as MethodCallExpression) != null) { return(serialized[expression] = new SerializableMethodCallExpression(methodCall, this)); } else if ((lambda = expression as LambdaExpression) != null) { return(serialized[expression] = new SerializableLambdaExpression(lambda, this)); } else if ((constant = expression as ConstantExpression) != null) { return(serialized[expression] = new SerializableConstantExpression(constant, this)); } else if ((member = expression as MemberExpression) != null) { return(serialized[expression] = new SerializableMemberExpression(member, this)); } else if ((binary = expression as BinaryExpression) != null) { return(serialized[expression] = new SerializableBinaryExpression(binary, this)); } else if ((block = expression as BlockExpression) != null) { return(serialized[expression] = new SerializableBlockExpression(block, this)); } else if ((conditional = expression as ConditionalExpression) != null) { return(serialized[expression] = new SerializableConditionalExpression(conditional, this)); } else if ((@default = expression as DefaultExpression) != null) { return(serialized[expression] = new SerializableDefaultExpression(@default, this)); } else if ((@goto = expression as GotoExpression) != null) { return(serialized[expression] = new SerializableGotoExpression(@goto, this)); } else if ((index = expression as IndexExpression) != null) { return(serialized[expression] = new SerializableIndexExpression(index, this)); } else if ((invocation = expression as InvocationExpression) != null) { return(serialized[expression] = new SerializableInvocationExpression(invocation, this)); } else if ((label = expression as LabelExpression) != null) { return(serialized[expression] = new SerializableLabelExpression(label, this)); } else if ((listInit = expression as ListInitExpression) != null) { return(serialized[expression] = new SerializableListInitExpression(listInit, this)); } else if ((loop = expression as LoopExpression) != null) { return(serialized[expression] = new SerializableLoopExpression(loop, this)); } else if ((memberInit = expression as MemberInitExpression) != null) { return(serialized[expression] = new SerializableMemberInitExpression(memberInit, this)); } else if ((newArray = expression as NewArrayExpression) != null) { return(serialized[expression] = new SerializableNewArrayExpression(newArray, this)); } else if ((@new = expression as NewExpression) != null) { return(serialized[expression] = new SerializableNewExpression(@new, this)); } else if ((parameter = expression as ParameterExpression) != null) { return(serialized[expression] = new SerializableParameterExpression(parameter, this)); } else if ((runtimeVariables = expression as RuntimeVariablesExpression) != null) { return(serialized[expression] = new SerializableRuntimeVariablesExpression(runtimeVariables, this)); } else if ((@switch = expression as SwitchExpression) != null) { return(serialized[expression] = new SerializableSwitchExpression(@switch, this)); } else if ((@try = expression as TryExpression) != null) { return(serialized[expression] = new SerializableTryExpression(@try, this)); } else if ((typeBinary = expression as TypeBinaryExpression) != null) { return(serialized[expression] = new SerializableTypeBinaryExpression(typeBinary, this)); } else if ((unary = expression as UnaryExpression) != null) { return(serialized[expression] = new SerializableUnaryExpression(unary, this)); } else { throw new ArgumentOutOfRangeException("expression"); } }