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 (var 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 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 (_definitions.Count > 1) { 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 = _definitions.First(); var common = GraphHelper.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 (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."); case LabelScopeKind.Try: case LabelScopeKind.Catch: _opCode = OpCodes.Leave; break; 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."); } }