001// ASM: a very small and fast Java bytecode manipulation framework
002// Copyright (c) 2000-2011 INRIA, France Telecom
003// All rights reserved.
004//
005// Redistribution and use in source and binary forms, with or without
006// modification, are permitted provided that the following conditions
007// are met:
008// 1. Redistributions of source code must retain the above copyright
009//    notice, this list of conditions and the following disclaimer.
010// 2. Redistributions in binary form must reproduce the above copyright
011//    notice, this list of conditions and the following disclaimer in the
012//    documentation and/or other materials provided with the distribution.
013// 3. Neither the name of the copyright holders nor the names of its
014//    contributors may be used to endorse or promote products derived from
015//    this software without specific prior written permission.
016//
017// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
027// THE POSSIBILITY OF SUCH DAMAGE.
028
029package org.springframework.asm;
030
031/**
032 * The path to a type argument, wildcard bound, array element type, or static inner type within an
033 * enclosing type.
034 *
035 * @author Eric Bruneton
036 */
037public final class TypePath {
038
039  /** A type path step that steps into the element type of an array type. See {@link #getStep}. */
040  public static final int ARRAY_ELEMENT = 0;
041
042  /** A type path step that steps into the nested type of a class type. See {@link #getStep}. */
043  public static final int INNER_TYPE = 1;
044
045  /** A type path step that steps into the bound of a wildcard type. See {@link #getStep}. */
046  public static final int WILDCARD_BOUND = 2;
047
048  /** A type path step that steps into a type argument of a generic type. See {@link #getStep}. */
049  public static final int TYPE_ARGUMENT = 3;
050
051  /**
052   * The byte array where the 'type_path' structure - as defined in the Java Virtual Machine
053   * Specification (JVMS) - corresponding to this TypePath is stored. The first byte of the
054   * structure in this array is given by {@link #typePathOffset}.
055   *
056   * @see <a
057   *     href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.2">JVMS
058   *     4.7.20.2</a>
059   */
060  private final byte[] typePathContainer;
061
062  /** The offset of the first byte of the type_path JVMS structure in {@link #typePathContainer}. */
063  private final int typePathOffset;
064
065  /**
066   * Constructs a new TypePath.
067   *
068   * @param typePathContainer a byte array containing a type_path JVMS structure.
069   * @param typePathOffset the offset of the first byte of the type_path structure in
070   *     typePathContainer.
071   */
072  TypePath(final byte[] typePathContainer, final int typePathOffset) {
073    this.typePathContainer = typePathContainer;
074    this.typePathOffset = typePathOffset;
075  }
076
077  /**
078   * Returns the length of this path, i.e. its number of steps.
079   *
080   * @return the length of this path.
081   */
082  public int getLength() {
083    // path_length is stored in the first byte of a type_path.
084    return typePathContainer[typePathOffset];
085  }
086
087  /**
088   * Returns the value of the given step of this path.
089   *
090   * @param index an index between 0 and {@link #getLength()}, exclusive.
091   * @return one of {@link #ARRAY_ELEMENT}, {@link #INNER_TYPE}, {@link #WILDCARD_BOUND}, or {@link
092   *     #TYPE_ARGUMENT}.
093   */
094  public int getStep(final int index) {
095    // Returns the type_path_kind of the path element of the given index.
096    return typePathContainer[typePathOffset + 2 * index + 1];
097  }
098
099  /**
100   * Returns the index of the type argument that the given step is stepping into. This method should
101   * only be used for steps whose value is {@link #TYPE_ARGUMENT}.
102   *
103   * @param index an index between 0 and {@link #getLength()}, exclusive.
104   * @return the index of the type argument that the given step is stepping into.
105   */
106  public int getStepArgument(final int index) {
107    // Returns the type_argument_index of the path element of the given index.
108    return typePathContainer[typePathOffset + 2 * index + 2];
109  }
110
111  /**
112   * Converts a type path in string form, in the format used by {@link #toString()}, into a TypePath
113   * object.
114   *
115   * @param typePath a type path in string form, in the format used by {@link #toString()}. May be
116   *     {@literal null} or empty.
117   * @return the corresponding TypePath object, or {@literal null} if the path is empty.
118   */
119  public static TypePath fromString(final String typePath) {
120    if (typePath == null || typePath.length() == 0) {
121      return null;
122    }
123    int typePathLength = typePath.length();
124    ByteVector output = new ByteVector(typePathLength);
125    output.putByte(0);
126    int typePathIndex = 0;
127    while (typePathIndex < typePathLength) {
128      char c = typePath.charAt(typePathIndex++);
129      if (c == '[') {
130        output.put11(ARRAY_ELEMENT, 0);
131      } else if (c == '.') {
132        output.put11(INNER_TYPE, 0);
133      } else if (c == '*') {
134        output.put11(WILDCARD_BOUND, 0);
135      } else if (c >= '0' && c <= '9') {
136        int typeArg = c - '0';
137        while (typePathIndex < typePathLength) {
138          c = typePath.charAt(typePathIndex++);
139          if (c >= '0' && c <= '9') {
140            typeArg = typeArg * 10 + c - '0';
141          } else if (c == ';') {
142            break;
143          } else {
144            throw new IllegalArgumentException();
145          }
146        }
147        output.put11(TYPE_ARGUMENT, typeArg);
148      } else {
149        throw new IllegalArgumentException();
150      }
151    }
152    output.data[0] = (byte) (output.length / 2);
153    return new TypePath(output.data, 0);
154  }
155
156  /**
157   * Returns a string representation of this type path. {@link #ARRAY_ELEMENT} steps are represented
158   * with '[', {@link #INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND} steps with '*' and {@link
159   * #TYPE_ARGUMENT} steps with their type argument index in decimal form followed by ';'.
160   */
161  @Override
162  public String toString() {
163    int length = getLength();
164    StringBuilder result = new StringBuilder(length * 2);
165    for (int i = 0; i < length; ++i) {
166      switch (getStep(i)) {
167        case ARRAY_ELEMENT:
168          result.append('[');
169          break;
170        case INNER_TYPE:
171          result.append('.');
172          break;
173        case WILDCARD_BOUND:
174          result.append('*');
175          break;
176        case TYPE_ARGUMENT:
177          result.append(getStepArgument(i)).append(';');
178          break;
179        default:
180          throw new AssertionError();
181      }
182    }
183    return result.toString();
184  }
185
186  /**
187   * Puts the type_path JVMS structure corresponding to the given TypePath into the given
188   * ByteVector.
189   *
190   * @param typePath a TypePath instance, or {@literal null} for empty paths.
191   * @param output where the type path must be put.
192   */
193  static void put(final TypePath typePath, final ByteVector output) {
194    if (typePath == null) {
195      output.putByte(0);
196    } else {
197      int length = typePath.typePathContainer[typePath.typePathOffset] * 2 + 1;
198      output.putByteArray(typePath.typePathContainer, typePath.typePathOffset, length);
199    }
200  }
201}