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(); } } } }
// Returns true if the label was successfully defined // or false if the label is now ambiguous 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 (LabelScopeInfo j = block; j != null; j = j.Parent) { if (j.ContainsTarget(_node)) { throw Error.LabelTargetAlreadyDefined(_node.Name); } } _definitions.Add(block); block.AddLabelInfo(_node, this); // Once defined, validate all jumps if (_definitions.Count == 1) { foreach (LabelScopeInfo r in _references) { ValidateJump(r); } } else { // Was just redefined, if we had any across block jumps, they're // now invalid if (_acrossBlockJump) { throw Error.AmbiguousJump(_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 _labelDefined = false; } }
private void ValidateJump(LabelScopeInfo reference) { // Assume we can do a ret/branch _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br; // look for a simple jump out for (LabelScopeInfo j = reference; j != null; j = j.Parent) { if (_definitions.Contains(j)) { // found it, jump is valid! return; } if (j.Kind == LabelScopeKind.Finally || j.Kind == LabelScopeKind.Filter) { break; } if (j.Kind == LabelScopeKind.Try || j.Kind == LabelScopeKind.Catch) { _opCode = OpCodes.Leave; } } _acrossBlockJump = true; if (_node != null && _node.Type != typeof(void)) { throw Error.NonLocalJumpWithValue(_node.Name); } if (_definitions.Count > 1) { throw Error.AmbiguousJump(_node.Name); } // We didn't find an outward jump. Look for a jump across blocks LabelScopeInfo def = _definitions.First(); LabelScopeInfo common = Helpers.CommonNode(def, reference, b => b.Parent); // Assume we can do a ret/branch _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br; // 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(); } if (j.Kind == LabelScopeKind.Try || j.Kind == LabelScopeKind.Catch) { _opCode = OpCodes.Leave; } } // 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(); } } } }