Пример #1
0
        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);
        }
Пример #2
0
 /// <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();
 }
Пример #4
0
 protected override Expression VisitSwitch(SwitchExpression node)
 {
     throw new NotSupportedException();
 }
Пример #5
0
        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);
Пример #9
0
        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));
        }
Пример #10
0
 /// <nodoc />
 public virtual void Visit(SwitchExpression switchExpression)
 {
 }
Пример #11
0
 public virtual void Visit(SwitchExpression expression, TranslationContext context, bool negated = false)
 {
 }
Пример #12
0
 protected override Expression MakeSwitch(SwitchExpression node, Expression switchValue, Expression defaultBody, ReadOnlyCollection <SwitchCase> cases)
 {
     return(Expression.Switch(switchValue, defaultBody, cases.Reverse().ToArray()));
 }
Пример #13
0
 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;
        }
Пример #15
0
        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);
        }
Пример #16
0
        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;
 }
Пример #18
0
 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);
        }
Пример #20
0
        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;
        }
Пример #21
0
 /// <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));
 }
Пример #22
0
 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);
Пример #24
0
 protected override Expression VisitSwitch(SwitchExpression node)
 {
     this.ThrowUnsupportedNodeException(node);
     return(base.VisitSwitch(node));
 }
 protected override Expression VisitSwitch(SwitchExpression node)
 {
     return(base.VisitSwitch(node));
 }
Пример #26
0
 protected override Expression VisitSwitch(SwitchExpression node)
 {
     return(this.GiveUp <SwitchExpression>(node));
 }
Пример #27
0
        // 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));
        }
Пример #28
0
 protected override bool VisitSwitch(SwitchExpression node) => false;
 protected override Expression VisitSwitch(SwitchExpression node)
 {
     throw new NotImplementedException();
 }
Пример #30
0
 public SwitchExpressionProxy(SwitchExpression node)
 {
     _node = node;
 }
Пример #31
0
 protected override Expression VisitSwitch(SwitchExpression node)
 {
     return(GiveUp(node));
 }
 protected override Expression VisitSwitch(SwitchExpression node) =>
 GiveUp(node);
Пример #33
0
        // 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;
        }
Пример #36
0
        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;
        }
Пример #38
0
 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;
        }
Пример #40
0
 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");
     }
 }