Click here to Skip to main content
15,885,278 members
Articles / Programming Languages / C#

Accessing Assembly Metadata with Reflection or Mono Cecil (Part 6)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
2 Dec 2012CDDL16 min read 42.2K   936   20  
Loading type metadata from assemblies with Reflection or Mono Cecil.
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using Nova.Parsing;
using Nova.Rendering;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Redirects execution to the specified <see cref="Label"/> or <see cref="SwitchItem"/> (<see cref="Case"/> or <see cref="Default"/>).
    /// </summary>
    public class Goto : Statement
    {
        #region /* FIELDS */

        // Should evaluate to a GotoTargetRef (LabelRef or a SwitchItemRef) or an UnresolvedRef
        protected SymbolicRef _target;

        // The constant expression used by a "goto case ..."
        protected Expression _constantExpression;

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create a <see cref="Goto"/> to a <see cref="Label"/>.
        /// </summary>
        public Goto(Label label)
        {
            Target = new LabelRef(label);
        }

        /// <summary>
        /// Create a <see cref="Goto"/> to a <see cref="SwitchItem"/>.
        /// </summary>
        public Goto(SwitchItem item)
        {
            Target = new SwitchItemRef(item);
        }

        /// <summary>
        /// Create a <see cref="Goto"/> to a string name.
        /// </summary>
        public Goto(string name)
        {
            Target = new UnresolvedRef(name);
        }

        /// <summary>
        /// Create a <see cref="Goto"/> to a constant <see cref="Expression"/>.
        /// </summary>
        public Goto(Expression constantExpression)
        {
            ConstantExpression = constantExpression;
            Target = new UnresolvedRef(Case.ParseToken + " " + _constantExpression.AsString());
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The target <see cref="GotoTargetRef"/> (<see cref="LabelRef"/> or <see cref="SwitchItemRef"/>) or <see cref="UnresolvedRef"/>.
        /// </summary>
        public SymbolicRef Target
        {
            get { return _target; }
            set { SetField(ref _target, value, true); }
        }

        /// <summary>
        /// The constant expression if this is a "goto case ...", otherwise null.
        /// </summary>
        public Expression ConstantExpression
        {
            get { return _constantExpression; }
            set { SetField(ref _constantExpression, value, true); }
        }

        /// <summary>
        /// The hidden GotoTargetRef (or UnresolvedRef) that represents the goto target if we have a "goto case ...".
        /// </summary>
        public override SymbolicRef HiddenRef
        {
            get { return (_constantExpression != null ? _target : null); }
        }

        /// <summary>
        /// True if this is a 'goto case ...'.
        /// </summary>
        public bool IsGotoCase
        {
            get { return (_constantExpression != null); }
        }

        /// <summary>
        /// True if this is a 'goto default'.
        /// </summary>
        public bool IsGotoDefault
        {
            get { return (_target.AsString() == Default.ParseToken); }
        }

        /// <summary>
        /// The keyword associated with the <see cref="Statement"/>.
        /// </summary>
        public override string Keyword
        {
            get { return ParseToken; }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Deep-clone the code object.
        /// </summary>
        public override CodeObject Clone()
        {
            Goto clone = (Goto)base.Clone();
            clone.CloneField(ref clone._target, _target);
            clone.CloneField(ref clone._constantExpression, _constantExpression);
            return clone;
        }

        #endregion

        #region /* PARSING */

        /// <summary>
        /// The token used to parse the code object.
        /// </summary>
        public const string ParseToken = "goto";

        internal static void AddParsePoints()
        {
            Parser.AddParsePoint(ParseToken, Parse, typeof(IBlock));
        }

        /// <summary>
        /// Parse a <see cref="Goto"/>.
        /// </summary>
        public static Goto Parse(Parser parser, CodeObject parent, ParseFlags flags)
        {
            return new Goto(parser, parent);
        }

        protected Goto(Parser parser, CodeObject parent)
            : base(parser, parent)
        {
            parser.NextToken();  // Move past 'goto'
            Token startToken = parser.Token;
            string target = "";

            // Handle "goto case ..."
            if (parser.TokenText == Case.ParseToken)
            {
                parser.NextToken();  // Move past 'case'
                SetField(ref _constantExpression, Expression.Parse(parser, this, true, ParseTokenTerminator), false);
                target = Case.ParseToken + " " + _constantExpression.AsString();
            }
            else  // Handle "goto <label>" or "goto default"
            {
                // Build a symbolic reference from all text up to the ';' (or EOL)
                bool first = true;
                while (parser.TokenText != Terminator && parser.Token != null && !parser.Token.IsFirstOnLine)
                {
                    target += (first ? "" : parser.Token.LeadingWhitespace) + parser.TokenText;
                    parser.NextToken();
                    first = false;
                }
            }
            SetField(ref _target, new UnresolvedRef(target, startToken.LineNumber, startToken.ColumnNumber), false);

            ParseTerminator(parser);
        }

        #endregion

        #region /* FORMATTING */

        /// <summary>
        /// True if the <see cref="Statement"/> has parens around its argument.
        /// </summary>
        public override bool HasArgumentParens
        {
            get { return false; }
        }

        /// <summary>
        /// True if the <see cref="Statement"/> has a terminator character by default.
        /// </summary>
        public override bool HasTerminatorDefault
        {
            get { return true; }
        }

        /// <summary>
        /// Determines if the code object only requires a single line for display.
        /// </summary>
        public override bool IsSingleLine
        {
            get { return (base.IsSingleLine && (_target == null || (!_target.IsFirstOnLine && _target.IsSingleLine))); }
            set
            {
                base.IsSingleLine = value;
                if (value && _target != null)
                {
                    _target.IsFirstOnLine = false;
                    _target.IsSingleLine = true;
                }
            }
        }

        #endregion

        #region /* RENDERING */

        protected override void AsTextArgument(CodeWriter writer, RenderFlags flags)
        {
            // If we have a constant expression ("goto case ..."), always render it instead of
            // the target reference.
            if (_constantExpression != null)
            {
                writer.Write(Case.ParseToken + " ");
                _constantExpression.AsText(writer, flags);
            }
            else
                _target.AsText(writer, flags);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions