private void ValidateJump(LabelScopeInfo reference) { // look for a simple jump out for (LabelScopeInfo?j = reference; j != null; j = j.Parent) { if (DefinedIn(j)) { // found it, jump is valid! return; } if (j.Kind == LabelScopeKind.Finally || j.Kind == LabelScopeKind.Filter) { break; } } _acrossBlockJump = true; if (_node != null && _node.Type != typeof(void)) { throw Error.NonLocalJumpWithValue(_node.Name); } if (HasMultipleDefinitions) { throw Error.AmbiguousJump(_node !.Name); } // We didn't find an outward jump. Look for a jump across blocks LabelScopeInfo def = FirstDefinition(); LabelScopeInfo?common = CommonNode(def, reference, b => b.Parent !); // Validate that we aren't jumping across a finally for (LabelScopeInfo?j = reference; j != common; j = j.Parent) { if (j !.Kind == LabelScopeKind.Finally) { throw Error.ControlCannotLeaveFinally(); } if (j.Kind == LabelScopeKind.Filter) { throw Error.ControlCannotLeaveFilterTest(); } } // Validate that we aren't jumping into a catch or an expression for (LabelScopeInfo?j = def; j != common; j = j.Parent) { if (!j !.CanJumpInto) { if (j.Kind == LabelScopeKind.Expression) { throw Error.ControlCannotEnterExpression(); } else { throw Error.ControlCannotEnterTry(); } } } }
internal void Reference(LabelScopeInfo block) { _references.Add(block); if (HasDefinitions) { ValidateJump(block); } }
private void ValidateJump(LabelScopeInfo reference) { // look for a simple jump out for (var j = reference; j != null; j = j.Parent) { if (DefinedIn(j)) { // found it, jump is valid! return; } if (j.Kind == LabelScopeKind.Finally || j.Kind == LabelScopeKind.Filter) { break; } } _acrossBlockJump = true; if (HasMultipleDefinitions) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Ambiguous jump {0}", _node.Name)); } // We didn't find an outward jump. Look for a jump across blocks var def = FirstDefinition(); var common = CommonNode(def, reference, b => b.Parent); // Validate that we aren't jumping across a finally for (var j = reference; j != common; j = j.Parent) { if (j.Kind == LabelScopeKind.Finally) { throw new InvalidOperationException("Control cannot leave finally"); } if (j.Kind == LabelScopeKind.Filter) { throw new InvalidOperationException("Control cannot leave filter test"); } } // Valdiate that we aren't jumping into a catch or an expression for (var j = def; j != common; j = j.Parent) { if (!j.CanJumpInto) { if (j.Kind == LabelScopeKind.Expression) { throw new InvalidOperationException("Control cannot enter an expression"); } else { throw new InvalidOperationException("Control cannot enter try"); } } } }
private bool DefinedIn(LabelScopeInfo scope) { if (_definitions == scope) { return(true); } if (_definitions is HashSet <LabelScopeInfo> definitions) { return(definitions.Contains(scope)); } return(false); }
private LabelScopeInfo FirstDefinition() { LabelScopeInfo scope = _definitions as LabelScopeInfo; if (scope != null) { return(scope); } foreach (var x in (HashSet <LabelScopeInfo>)_definitions) { return(x); } throw new InvalidOperationException(); }
private bool DefinedIn(LabelScopeInfo scope) { if (_definitions == scope) { return(true); } var definitions = _definitions as HashSet <LabelScopeInfo>; if (definitions != null) { return(definitions.Contains(scope)); } return(false); }
private void AddDefinition(LabelScopeInfo scope) { if (_definitions == null) { _definitions = scope; } else { if (!(_definitions is HashSet <LabelScopeInfo> set)) { _definitions = set = new HashSet <LabelScopeInfo> { (LabelScopeInfo)_definitions }; } set.Add(scope); } }
private void AddDefinition(LabelScopeInfo scope) { if (_definitions == null) { _definitions = scope; } else { var set = _definitions as HashSet <LabelScopeInfo>; if (set == null) { _definitions = set = new HashSet <LabelScopeInfo> { (LabelScopeInfo)_definitions }; } set.Add(scope); } }
internal void Define(LabelScopeInfo block) { // Prevent the label from being shadowed, which enforces cleaner // trees. Also we depend on this for simplicity (keeping only one // active IL Label per LabelInfo) for (var j = block; j != null; j = j.Parent) { if (j.ContainsTarget(_node)) { throw new InvalidOperationException($"Cannot redefine label '{_node.Name}' in an inner block."); } } AddDefinition(block); block.AddLabelInfo(_node, this); // Once defined, validate all jumps if (HasDefinitions && !HasMultipleDefinitions) { foreach (var r in _references) { ValidateJump(r); } } else { // Was just redefined, if we had any across block jumps, they're // now invalid if (_acrossBlockJump) { throw new InvalidOperationException($"Cannot jump to ambiguous label '{_node.Name}'."); } // For local jumps, we need a new IL label // This is okay because: // 1. no across block jumps have been made or will be made // 2. we don't allow the label to be shadowed _label = null; } }
private HybridReferenceDictionary <LabelTarget, LabelInfo> _labels; // lazily allocated, we typically use this only once every 6th-7th block internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) { Parent = parent; Kind = kind; }
private void ValidateJump(LabelScopeInfo reference) { // look for a simple jump out for (var j = reference; j != null; j = j.Parent) { if (DefinedIn(j)) { // found it, jump is valid! return; } if (j.Kind == LabelScopeKind.Finally || j.Kind == LabelScopeKind.Filter) { break; } } _acrossBlockJump = true; if (_node != null && _node.Type != typeof(void)) { throw new InvalidOperationException($"Cannot jump to non-local label '{_node.Name}' with a value. Only jumps to labels defined in outer blocks can pass values."); } if (HasMultipleDefinitions) { throw new InvalidOperationException($"Cannot jump to ambiguous label '{_node?.Name}'."); } // We didn't find an outward jump. Look for a jump across blocks var def = FirstDefinition(); var common = CommonNode(def, reference, b => b.Parent); // Validate that we aren't jumping across a finally for (var j = reference; j != common; j = j.Parent) { switch (j.Kind) { case LabelScopeKind.Finally: throw new InvalidOperationException("Control cannot leave a finally block."); case LabelScopeKind.Filter: throw new InvalidOperationException("Control cannot leave a filter test."); default: break; } } // Validate that we aren't jumping into a catch or an expression for (var j = def; j != common; j = j.Parent) { if (j.CanJumpInto) { continue; } if (j.Kind == LabelScopeKind.Expression) { throw new InvalidOperationException("Control cannot enter an expression--only statements can be jumped into."); } throw new InvalidOperationException("Control cannot enter a try block."); } }
public LabelScopeChangeInfo(LabelScopeInfo parent, LabelScopeKind kind, IList <Expression>?nodes) { Parent = parent; Kind = kind; Nodes = nodes; }