| // ASM: a very small and fast Java bytecode manipulation framework |
| // Copyright (c) 2000-2011 INRIA, France Telecom |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // 1. Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // 2. Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // 3. Neither the name of the copyright holders nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| // THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package org.apache.tapestry5.internal.plastic.asm; |
| |
| /** |
| * The path to a type argument, wildcard bound, array element type, or static inner type within an |
| * enclosing type. |
| * |
| * @author Eric Bruneton |
| */ |
| public final class TypePath { |
| |
| /** A type path step that steps into the element type of an array type. See {@link #getStep}. */ |
| public static final int ARRAY_ELEMENT = 0; |
| |
| /** A type path step that steps into the nested type of a class type. See {@link #getStep}. */ |
| public static final int INNER_TYPE = 1; |
| |
| /** A type path step that steps into the bound of a wildcard type. See {@link #getStep}. */ |
| public static final int WILDCARD_BOUND = 2; |
| |
| /** A type path step that steps into a type argument of a generic type. See {@link #getStep}. */ |
| public static final int TYPE_ARGUMENT = 3; |
| |
| /** |
| * The byte array where the 'type_path' structure - as defined in the Java Virtual Machine |
| * Specification (JVMS) - corresponding to this TypePath is stored. The first byte of the |
| * structure in this array is given by {@link #typePathOffset}. |
| * |
| * @see <a |
| * href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.2">JVMS |
| * 4.7.20.2</a> |
| */ |
| private final byte[] typePathContainer; |
| |
| /** The offset of the first byte of the type_path JVMS structure in {@link #typePathContainer}. */ |
| private final int typePathOffset; |
| |
| /** |
| * Constructs a new TypePath. |
| * |
| * @param typePathContainer a byte array containing a type_path JVMS structure. |
| * @param typePathOffset the offset of the first byte of the type_path structure in |
| * typePathContainer. |
| */ |
| TypePath(final byte[] typePathContainer, final int typePathOffset) { |
| this.typePathContainer = typePathContainer; |
| this.typePathOffset = typePathOffset; |
| } |
| |
| /** |
| * Returns the length of this path, i.e. its number of steps. |
| * |
| * @return the length of this path. |
| */ |
| public int getLength() { |
| // path_length is stored in the first byte of a type_path. |
| return typePathContainer[typePathOffset]; |
| } |
| |
| /** |
| * Returns the value of the given step of this path. |
| * |
| * @param index an index between 0 and {@link #getLength()}, exclusive. |
| * @return one of {@link #ARRAY_ELEMENT}, {@link #INNER_TYPE}, {@link #WILDCARD_BOUND}, or {@link |
| * #TYPE_ARGUMENT}. |
| */ |
| public int getStep(final int index) { |
| // Returns the type_path_kind of the path element of the given index. |
| return typePathContainer[typePathOffset + 2 * index + 1]; |
| } |
| |
| /** |
| * Returns the index of the type argument that the given step is stepping into. This method should |
| * only be used for steps whose value is {@link #TYPE_ARGUMENT}. |
| * |
| * @param index an index between 0 and {@link #getLength()}, exclusive. |
| * @return the index of the type argument that the given step is stepping into. |
| */ |
| public int getStepArgument(final int index) { |
| // Returns the type_argument_index of the path element of the given index. |
| return typePathContainer[typePathOffset + 2 * index + 2]; |
| } |
| |
| /** |
| * Converts a type path in string form, in the format used by {@link #toString()}, into a TypePath |
| * object. |
| * |
| * @param typePath a type path in string form, in the format used by {@link #toString()}. May be |
| * {@literal null} or empty. |
| * @return the corresponding TypePath object, or {@literal null} if the path is empty. |
| */ |
| public static TypePath fromString(final String typePath) { |
| if (typePath == null || typePath.length() == 0) { |
| return null; |
| } |
| int typePathLength = typePath.length(); |
| ByteVector output = new ByteVector(typePathLength); |
| output.putByte(0); |
| int typePathIndex = 0; |
| while (typePathIndex < typePathLength) { |
| char c = typePath.charAt(typePathIndex++); |
| if (c == '[') { |
| output.put11(ARRAY_ELEMENT, 0); |
| } else if (c == '.') { |
| output.put11(INNER_TYPE, 0); |
| } else if (c == '*') { |
| output.put11(WILDCARD_BOUND, 0); |
| } else if (c >= '0' && c <= '9') { |
| int typeArg = c - '0'; |
| while (typePathIndex < typePathLength) { |
| c = typePath.charAt(typePathIndex++); |
| if (c >= '0' && c <= '9') { |
| typeArg = typeArg * 10 + c - '0'; |
| } else if (c == ';') { |
| break; |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| } |
| output.put11(TYPE_ARGUMENT, typeArg); |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| } |
| output.data[0] = (byte) (output.length / 2); |
| return new TypePath(output.data, 0); |
| } |
| |
| /** |
| * Returns a string representation of this type path. {@link #ARRAY_ELEMENT} steps are represented |
| * with '[', {@link #INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND} steps with '*' and {@link |
| * #TYPE_ARGUMENT} steps with their type argument index in decimal form followed by ';'. |
| */ |
| @Override |
| public String toString() { |
| int length = getLength(); |
| StringBuilder result = new StringBuilder(length * 2); |
| for (int i = 0; i < length; ++i) { |
| switch (getStep(i)) { |
| case ARRAY_ELEMENT: |
| result.append('['); |
| break; |
| case INNER_TYPE: |
| result.append('.'); |
| break; |
| case WILDCARD_BOUND: |
| result.append('*'); |
| break; |
| case TYPE_ARGUMENT: |
| result.append(getStepArgument(i)).append(';'); |
| break; |
| default: |
| throw new AssertionError(); |
| } |
| } |
| return result.toString(); |
| } |
| |
| /** |
| * Puts the type_path JVMS structure corresponding to the given TypePath into the given |
| * ByteVector. |
| * |
| * @param typePath a TypePath instance, or {@literal null} for empty paths. |
| * @param output where the type path must be put. |
| */ |
| static void put(final TypePath typePath, final ByteVector output) { |
| if (typePath == null) { |
| output.putByte(0); |
| } else { |
| int length = typePath.typePathContainer[typePath.typePathOffset] * 2 + 1; |
| output.putByteArray(typePath.typePathContainer, typePath.typePathOffset, length); |
| } |
| } |
| } |