Example #1
0
        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.");
            }
        }