/*
 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.classfile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.sun.tools.classfile.TypeAnnotation.Position.TypePathEntry;

See JSR 308 specification, Section 3.

This is NOT part of any supported API. If you write code that depends on this, you do so at your own risk. This code and its internal interfaces are subject to change or deletion without notice.

/** * See JSR 308 specification, Section 3. * * <p><b>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */
public class TypeAnnotation { TypeAnnotation(ClassReader cr) throws IOException, Annotation.InvalidAnnotation { constant_pool = cr.getConstantPool(); position = read_position(cr); annotation = new Annotation(cr); } public TypeAnnotation(ConstantPool constant_pool, Annotation annotation, Position position) { this.constant_pool = constant_pool; this.position = position; this.annotation = annotation; } public int length() { int n = annotation.length(); n += position_length(position); return n; } @Override public String toString() { try { return "@" + constant_pool.getUTF8Value(annotation.type_index).toString().substring(1) + " pos: " + position.toString(); } catch (Exception e) { e.printStackTrace(); return e.toString(); } } public final ConstantPool constant_pool; public final Position position; public final Annotation annotation; private static Position read_position(ClassReader cr) throws IOException, Annotation.InvalidAnnotation { // Copied from ClassReader int tag = cr.readUnsignedByte(); // TargetType tag is a byte if (!TargetType.isValidTargetTypeValue(tag)) throw new Annotation.InvalidAnnotation("TypeAnnotation: Invalid type annotation target type value: " + String.format("0x%02X", tag)); TargetType type = TargetType.fromTargetTypeValue(tag); Position position = new Position(); position.type = type; switch (type) { // instanceof case INSTANCEOF: // new expression case NEW: // constructor/method reference receiver case CONSTRUCTOR_REFERENCE: case METHOD_REFERENCE: position.offset = cr.readUnsignedShort(); break; // local variable case LOCAL_VARIABLE: // resource variable case RESOURCE_VARIABLE: int table_length = cr.readUnsignedShort(); position.lvarOffset = new int[table_length]; position.lvarLength = new int[table_length]; position.lvarIndex = new int[table_length]; for (int i = 0; i < table_length; ++i) { position.lvarOffset[i] = cr.readUnsignedShort(); position.lvarLength[i] = cr.readUnsignedShort(); position.lvarIndex[i] = cr.readUnsignedShort(); } break; // exception parameter case EXCEPTION_PARAMETER: position.exception_index = cr.readUnsignedShort(); break; // method receiver case METHOD_RECEIVER: // Do nothing break; // type parameter case CLASS_TYPE_PARAMETER: case METHOD_TYPE_PARAMETER: position.parameter_index = cr.readUnsignedByte(); break; // type parameter bound case CLASS_TYPE_PARAMETER_BOUND: case METHOD_TYPE_PARAMETER_BOUND: position.parameter_index = cr.readUnsignedByte(); position.bound_index = cr.readUnsignedByte(); break; // class extends or implements clause case CLASS_EXTENDS: position.type_index = cr.readUnsignedShort();; break; // throws case THROWS: position.type_index = cr.readUnsignedShort(); break; // method parameter case METHOD_FORMAL_PARAMETER: position.parameter_index = cr.readUnsignedByte(); break; // type cast case CAST: // method/constructor/reference type argument case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: case METHOD_INVOCATION_TYPE_ARGUMENT: case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: case METHOD_REFERENCE_TYPE_ARGUMENT: position.offset = cr.readUnsignedShort(); position.type_index = cr.readUnsignedByte(); break; // We don't need to worry about these case METHOD_RETURN: case FIELD: break; case UNKNOWN: throw new AssertionError("TypeAnnotation: UNKNOWN target type should never occur!"); default: throw new AssertionError("TypeAnnotation: Unknown target type: " + type); } { // Write type path int len = cr.readUnsignedByte(); List<Integer> loc = new ArrayList<>(len); for (int i = 0; i < len * TypePathEntry.bytesPerEntry; ++i) loc.add(cr.readUnsignedByte()); position.location = Position.getTypePathFromBinary(loc); } return position; } private static int position_length(Position pos) { int n = 0; n += 1; // TargetType tag is a byte switch (pos.type) { // instanceof case INSTANCEOF: // new expression case NEW: // constructor/method reference receiver case CONSTRUCTOR_REFERENCE: case METHOD_REFERENCE: n += 2; // offset break; // local variable case LOCAL_VARIABLE: // resource variable case RESOURCE_VARIABLE: n += 2; // table_length; int table_length = pos.lvarOffset.length; n += 2 * table_length; // offset n += 2 * table_length; // length n += 2 * table_length; // index break; // exception parameter case EXCEPTION_PARAMETER: n += 2; // exception_index break; // method receiver case METHOD_RECEIVER: // Do nothing break; // type parameter case CLASS_TYPE_PARAMETER: case METHOD_TYPE_PARAMETER: n += 1; // parameter_index break; // type parameter bound case CLASS_TYPE_PARAMETER_BOUND: case METHOD_TYPE_PARAMETER_BOUND: n += 1; // parameter_index n += 1; // bound_index break; // class extends or implements clause case CLASS_EXTENDS: n += 2; // type_index break; // throws case THROWS: n += 2; // type_index break; // method parameter case METHOD_FORMAL_PARAMETER: n += 1; // parameter_index break; // type cast case CAST: // method/constructor/reference type argument case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: case METHOD_INVOCATION_TYPE_ARGUMENT: case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: case METHOD_REFERENCE_TYPE_ARGUMENT: n += 2; // offset n += 1; // type index break; // We don't need to worry about these case METHOD_RETURN: case FIELD: break; case UNKNOWN: throw new AssertionError("TypeAnnotation: UNKNOWN target type should never occur!"); default: throw new AssertionError("TypeAnnotation: Unknown target type: " + pos.type); } { n += 1; // length n += TypePathEntry.bytesPerEntry * pos.location.size(); // bytes for actual array } return n; } // Code duplicated from com.sun.tools.javac.code.TypeAnnotationPosition public static class Position { public enum TypePathEntryKind { ARRAY(0), INNER_TYPE(1), WILDCARD(2), TYPE_ARGUMENT(3); public final int tag; private TypePathEntryKind(int tag) { this.tag = tag; } } public static class TypePathEntry {
The fixed number of bytes per TypePathEntry.
/** The fixed number of bytes per TypePathEntry. */
public static final int bytesPerEntry = 2; public final TypePathEntryKind tag; public final int arg; public static final TypePathEntry ARRAY = new TypePathEntry(TypePathEntryKind.ARRAY); public static final TypePathEntry INNER_TYPE = new TypePathEntry(TypePathEntryKind.INNER_TYPE); public static final TypePathEntry WILDCARD = new TypePathEntry(TypePathEntryKind.WILDCARD); private TypePathEntry(TypePathEntryKind tag) { if (!(tag == TypePathEntryKind.ARRAY || tag == TypePathEntryKind.INNER_TYPE || tag == TypePathEntryKind.WILDCARD)) { throw new AssertionError("Invalid TypePathEntryKind: " + tag); } this.tag = tag; this.arg = 0; } public TypePathEntry(TypePathEntryKind tag, int arg) { if (tag != TypePathEntryKind.TYPE_ARGUMENT) { throw new AssertionError("Invalid TypePathEntryKind: " + tag); } this.tag = tag; this.arg = arg; } public static TypePathEntry fromBinary(int tag, int arg) { if (arg != 0 && tag != TypePathEntryKind.TYPE_ARGUMENT.tag) { throw new AssertionError("Invalid TypePathEntry tag/arg: " + tag + "/" + arg); } switch (tag) { case 0: return ARRAY; case 1: return INNER_TYPE; case 2: return WILDCARD; case 3: return new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, arg); default: throw new AssertionError("Invalid TypePathEntryKind tag: " + tag); } } @Override public String toString() { return tag.toString() + (tag == TypePathEntryKind.TYPE_ARGUMENT ? ("(" + arg + ")") : ""); } @Override public boolean equals(Object other) { if (! (other instanceof TypePathEntry)) { return false; } TypePathEntry tpe = (TypePathEntry) other; return this.tag == tpe.tag && this.arg == tpe.arg; } @Override public int hashCode() { return this.tag.hashCode() * 17 + this.arg; } } public TargetType type = TargetType.UNKNOWN; // For generic/array types. // TODO: or should we use null? Noone will use this object. public List<TypePathEntry> location = new ArrayList<>(0); // Tree position. public int pos = -1; // For typecasts, type tests, new (and locals, as start_pc). public boolean isValidOffset = false; public int offset = -1; // For locals. arrays same length public int[] lvarOffset = null; public int[] lvarLength = null; public int[] lvarIndex = null; // For type parameter bound public int bound_index = Integer.MIN_VALUE; // For type parameter and method parameter public int parameter_index = Integer.MIN_VALUE; // For class extends, implements, and throws clauses public int type_index = Integer.MIN_VALUE; // For exception parameters, index into exception table public int exception_index = Integer.MIN_VALUE; public Position() {} @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); sb.append(type); switch (type) { // instanceof case INSTANCEOF: // new expression case NEW: // constructor/method reference receiver case CONSTRUCTOR_REFERENCE: case METHOD_REFERENCE: sb.append(", offset = "); sb.append(offset); break; // local variable case LOCAL_VARIABLE: // resource variable case RESOURCE_VARIABLE: if (lvarOffset == null) { sb.append(", lvarOffset is null!"); break; } sb.append(", {"); for (int i = 0; i < lvarOffset.length; ++i) { if (i != 0) sb.append("; "); sb.append("start_pc = "); sb.append(lvarOffset[i]); sb.append(", length = "); sb.append(lvarLength[i]); sb.append(", index = "); sb.append(lvarIndex[i]); } sb.append("}"); break; // method receiver case METHOD_RECEIVER: // Do nothing break; // type parameter case CLASS_TYPE_PARAMETER: case METHOD_TYPE_PARAMETER: sb.append(", param_index = "); sb.append(parameter_index); break; // type parameter bound case CLASS_TYPE_PARAMETER_BOUND: case METHOD_TYPE_PARAMETER_BOUND: sb.append(", param_index = "); sb.append(parameter_index); sb.append(", bound_index = "); sb.append(bound_index); break; // class extends or implements clause case CLASS_EXTENDS: sb.append(", type_index = "); sb.append(type_index); break; // throws case THROWS: sb.append(", type_index = "); sb.append(type_index); break; // exception parameter case EXCEPTION_PARAMETER: sb.append(", exception_index = "); sb.append(exception_index); break; // method parameter case METHOD_FORMAL_PARAMETER: sb.append(", param_index = "); sb.append(parameter_index); break; // type cast case CAST: // method/constructor/reference type argument case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: case METHOD_INVOCATION_TYPE_ARGUMENT: case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: case METHOD_REFERENCE_TYPE_ARGUMENT: sb.append(", offset = "); sb.append(offset); sb.append(", type_index = "); sb.append(type_index); break; // We don't need to worry about these case METHOD_RETURN: case FIELD: break; case UNKNOWN: sb.append(", position UNKNOWN!"); break; default: throw new AssertionError("Unknown target type: " + type); } // Append location data for generics/arrays. if (!location.isEmpty()) { sb.append(", location = ("); sb.append(location); sb.append(")"); } sb.append(", pos = "); sb.append(pos); sb.append(']'); return sb.toString(); }
Indicates whether the target tree of the annotation has been optimized away from classfile or not.
Returns:true if the target has not been optimized away
/** * Indicates whether the target tree of the annotation has been optimized * away from classfile or not. * @return true if the target has not been optimized away */
public boolean emitToClassfile() { return !type.isLocal() || isValidOffset; }
Decode the binary representation for a type path and set the location field.
Params:
  • list – The bytecode representation of the type path.
/** * Decode the binary representation for a type path and set * the {@code location} field. * * @param list The bytecode representation of the type path. */
public static List<TypePathEntry> getTypePathFromBinary(List<Integer> list) { List<TypePathEntry> loc = new ArrayList<>(list.size() / TypePathEntry.bytesPerEntry); int idx = 0; while (idx < list.size()) { if (idx + 1 == list.size()) { throw new AssertionError("Could not decode type path: " + list); } loc.add(TypePathEntry.fromBinary(list.get(idx), list.get(idx + 1))); idx += 2; } return loc; } public static List<Integer> getBinaryFromTypePath(List<TypePathEntry> locs) { List<Integer> loc = new ArrayList<>(locs.size() * TypePathEntry.bytesPerEntry); for (TypePathEntry tpe : locs) { loc.add(tpe.tag.tag); loc.add(tpe.arg); } return loc; } } // Code duplicated from com.sun.tools.javac.code.TargetType // The IsLocal flag could be removed here. public enum TargetType {
For annotations on a class type parameter declaration.
/** For annotations on a class type parameter declaration. */
CLASS_TYPE_PARAMETER(0x00),
For annotations on a method type parameter declaration.
/** For annotations on a method type parameter declaration. */
METHOD_TYPE_PARAMETER(0x01),
For annotations on the type of an "extends" or "implements" clause.
/** For annotations on the type of an "extends" or "implements" clause. */
CLASS_EXTENDS(0x10),
For annotations on a bound of a type parameter of a class.
/** For annotations on a bound of a type parameter of a class. */
CLASS_TYPE_PARAMETER_BOUND(0x11),
For annotations on a bound of a type parameter of a method.
/** For annotations on a bound of a type parameter of a method. */
METHOD_TYPE_PARAMETER_BOUND(0x12),
For annotations on a field.
/** For annotations on a field. */
FIELD(0x13),
For annotations on a method return type.
/** For annotations on a method return type. */
METHOD_RETURN(0x14),
For annotations on the method receiver.
/** For annotations on the method receiver. */
METHOD_RECEIVER(0x15),
For annotations on a method parameter.
/** For annotations on a method parameter. */
METHOD_FORMAL_PARAMETER(0x16),
For annotations on a throws clause in a method declaration.
/** For annotations on a throws clause in a method declaration. */
THROWS(0x17),
For annotations on a local variable.
/** For annotations on a local variable. */
LOCAL_VARIABLE(0x40, true),
For annotations on a resource variable.
/** For annotations on a resource variable. */
RESOURCE_VARIABLE(0x41, true),
For annotations on an exception parameter.
/** For annotations on an exception parameter. */
EXCEPTION_PARAMETER(0x42, true),
For annotations on a type test.
/** For annotations on a type test. */
INSTANCEOF(0x43, true),
For annotations on an object creation expression.
/** For annotations on an object creation expression. */
NEW(0x44, true),
For annotations on a constructor reference receiver.
/** For annotations on a constructor reference receiver. */
CONSTRUCTOR_REFERENCE(0x45, true),
For annotations on a method reference receiver.
/** For annotations on a method reference receiver. */
METHOD_REFERENCE(0x46, true),
For annotations on a typecast.
/** For annotations on a typecast. */
CAST(0x47, true),
For annotations on a type argument of an object creation expression.
/** For annotations on a type argument of an object creation expression. */
CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT(0x48, true),
For annotations on a type argument of a method call.
/** For annotations on a type argument of a method call. */
METHOD_INVOCATION_TYPE_ARGUMENT(0x49, true),
For annotations on a type argument of a constructor reference.
/** For annotations on a type argument of a constructor reference. */
CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT(0x4A, true),
For annotations on a type argument of a method reference.
/** For annotations on a type argument of a method reference. */
METHOD_REFERENCE_TYPE_ARGUMENT(0x4B, true),
For annotations with an unknown target.
/** For annotations with an unknown target. */
UNKNOWN(0xFF); private static final int MAXIMUM_TARGET_TYPE_VALUE = 0x4B; private final int targetTypeValue; private final boolean isLocal; private TargetType(int targetTypeValue) { this(targetTypeValue, false); } private TargetType(int targetTypeValue, boolean isLocal) { if (targetTypeValue < 0 || targetTypeValue > 255) throw new AssertionError("Attribute type value needs to be an unsigned byte: " + String.format("0x%02X", targetTypeValue)); this.targetTypeValue = targetTypeValue; this.isLocal = isLocal; }
Returns whether or not this TargetType represents an annotation whose target is exclusively a tree in a method body Note: wildcard bound targets could target a local tree and a class member declaration signature tree
/** * Returns whether or not this TargetType represents an annotation whose * target is exclusively a tree in a method body * * Note: wildcard bound targets could target a local tree and a class * member declaration signature tree */
public boolean isLocal() { return isLocal; } public int targetTypeValue() { return this.targetTypeValue; } private static final TargetType[] targets; static { targets = new TargetType[MAXIMUM_TARGET_TYPE_VALUE + 1]; TargetType[] alltargets = values(); for (TargetType target : alltargets) { if (target.targetTypeValue != UNKNOWN.targetTypeValue) targets[target.targetTypeValue] = target; } for (int i = 0; i <= MAXIMUM_TARGET_TYPE_VALUE; ++i) { if (targets[i] == null) targets[i] = UNKNOWN; } } public static boolean isValidTargetTypeValue(int tag) { if (tag == UNKNOWN.targetTypeValue) return true; return (tag >= 0 && tag < targets.length); } public static TargetType fromTargetTypeValue(int tag) { if (tag == UNKNOWN.targetTypeValue) return UNKNOWN; if (tag < 0 || tag >= targets.length) throw new AssertionError("Unknown TargetType: " + tag); return targets[tag]; } } }