// SwitchCase private void DefaultWalk(SwitchCase node) { if (Walk(node)) { WalkNode(node.Body); } PostWalk(node); }
// SwitchStatement protected internal override bool Walk(SwitchStatement node) { // The expression is evaluated always. // Then each case clause expression is evaluated until match is found. // Therefore, the effects of the case clause expressions accumulate. // Default clause is evaluated last (so all case clause expressions must // accumulate first) WalkNode(node.TestValue); // Flow all the cases, they all start with the same initial state int count; IList <SwitchCase> cases = node.Cases; if (cases != null && (count = cases.Count) > 0) { SwitchCase @default = null; // Save the initial state BitArray entry = _bits; // The state to progressively accumualte effects of the case clause expressions BitArray values = new BitArray(entry); // State to flow the case clause bodies. BitArray caseFlow = new BitArray(_bits.Length); // The state to accumulate results into BitArray result = new BitArray(_bits.Length, true); PushStatement(result); for (int i = 0; i < count; i++) { if (cases[i].IsDefault) { Debug.Assert(@default == null); // postpone the default case @default = cases[i]; continue; } // Set the state for the walking of the body caseFlow.SetAll(false); caseFlow.Or(values); // Walk the body _bits = caseFlow; WalkNode(cases[i].Body); // Accumulate the result into the overall case statement result. result.And(caseFlow); } // Walk the default at the end. if (@default != null) { // Initialize caseFlow.SetAll(false); caseFlow.Or(values); // Walk the default body _bits = caseFlow; WalkNode(@default.Body); // Accumulate. result.And(caseFlow); // If there's a default clause, exactly one case got executed. // The final state is 'and' across all cases, stored in 'result' entry.SetAll(false); entry.Or(result); } else { // In the absence of default clause, we may have executed case, // but didn't have to, so the result is an 'and' between the cases // and the initial state. entry.And(result); } PopStatement(); // Restore the original state. _bits = entry; } return(false); }
private static bool UniqueCaseValues(SwitchCase[] cases, int min, int max) { int length = cases.Length; // If we have small number of cases, use straightforward N2 algorithm // which doesn't allocate memory if (length < N2Threshold) { for (int i = 0; i < length; i++) { SwitchCase sci = cases[i]; if (sci.IsDefault) { continue; } for (int j = i + 1; j < length; j++) { SwitchCase scj = cases[j]; if (scj.IsDefault) { continue; } if (sci.Value == scj.Value) { // Duplicate value found return false; } } } return true; } // We have at least N2Threshold items so the min and max values // are set to actual values and not the Int32.MaxValue and Int32.MaxValue Debug.Assert(min <= max); long delta = (long)max - (long)min; if (delta < BitArrayThreshold) { BitArray ba = new BitArray((int)delta + 1, false); for (int i = 0; i < length; i++) { SwitchCase sc = cases[i]; if (sc.IsDefault) { continue; } // normalize to 0 .. (max - min) int val = sc.Value - min; if (ba.Get(val)) { // Duplicate value found return false; } ba.Set(val, true); } return true; } // Too many values that are too spread around. Use dictionary // Using Dictionary<int, object> as it is used elsewhere to // minimize the impact of generic instantiation Dictionary<int, object> dict = new Dictionary<int, object>(length); for (int i = 0; i < length; i++) { SwitchCase sc = cases[i]; if (sc.IsDefault) { continue; } int val = sc.Value; if (dict.ContainsKey(val)) { // Duplicate value found return false; } dict[val] = null; } return true; }
internal Expression Reduce() { // Visit body Expression body = Visit(_generator.Body); Debug.Assert(_returnLabels.Count == 1); // Add the switch statement to the body int count = _yields.Count; var cases = new SwitchCase[count + 1]; for (int i = 0; i < count; i++) { cases[i] = Expression.SwitchCase(Expression.Goto(_yields[i].Label), AstUtils.Constant(_yields[i].State)); } cases[count] = Expression.SwitchCase(Expression.Goto(_returnLabels.Peek()), AstUtils.Constant(Finished)); Type generatorNextOfT = typeof(GeneratorNext <>).MakeGenericType(_generator.Target.Type); // Create the lambda for the GeneratorNext<T>, hoisting variables // into a scope outside the lambda var allVars = new List <ParameterExpression>(_vars); allVars.AddRange(_temps); // Collect temps that don't have to be closed over var innerTemps = new ReadOnlyCollectionBuilder <ParameterExpression>(1 + (_labelTemps?.Count ?? 0)); innerTemps.Add(_gotoRouter); if (_labelTemps != null) { foreach (LabelInfo info in _labelTemps.Values) { innerTemps.Add(info.Temp); } } body = Expression.Block( allVars, Expression.Lambda( generatorNextOfT, Expression.Block( innerTemps, Expression.Switch(Expression.Assign(_gotoRouter, _state), cases), body, Expression.Assign(_state, AstUtils.Constant(Finished)), Expression.Label(_returnLabels.Peek()) ), _generator.Name, new ParameterExpression[] { _state, _current } ) ); // Enumerable factory takes Func<GeneratorNext<T>> instead of GeneratorNext<T> if (_generator.IsEnumerable) { body = Expression.Lambda(body); } // We can't create a ConstantExpression of _debugCookies array here because we walk the tree // after constants have already been rewritten. Instead we create a NewArrayExpression node // which initializes the array with contents from _debugCookies Expression debugCookiesArray = null; if (_debugCookies != null) { Expression[] debugCookies = new Expression[_debugCookies.Count]; for (int i = 0; i < _debugCookies.Count; i++) { debugCookies[i] = AstUtils.Constant(_debugCookies[i]); } debugCookiesArray = Expression.NewArrayInit( typeof(int), debugCookies); } // Generate a call to ScriptingRuntimeHelpers.MakeGenerator<T>(args) return(Expression.Call( typeof(ScriptingRuntimeHelpers), "MakeGenerator", new[] { _generator.Target.Type }, (debugCookiesArray != null) ? new[] { body, debugCookiesArray } : new[] { body } )); }