001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.classfile; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.io.DataInput; 024import java.io.DataInputStream; 025import java.io.DataOutputStream; 026import java.io.IOException; 027import java.io.UncheckedIOException; 028import java.util.Arrays; 029 030import org.apache.bcel.util.Args; 031 032/** 033 * Represents one type annotation of a {@link TypeAnnotations} attribute (JVMS 4.7.20): an annotation, the kind of program element it applies to 034 * (the target type and its target info), and the path to the annotated type inside that element. 035 * 036 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.7.20">JVMS 4.7.20 The RuntimeVisibleTypeAnnotations attribute</a> 037 * @since 6.14.0 038 */ 039public class TypeAnnotationEntry { 040 041 /** Target type: type parameter declaration of a generic class or interface. */ 042 public static final int TARGET_CLASS_TYPE_PARAMETER = 0x00; 043 044 /** Target type: type parameter declaration of a generic method or constructor. */ 045 public static final int TARGET_METHOD_TYPE_PARAMETER = 0x01; 046 047 /** Target type: type in an extends or implements clause of a class or interface declaration. */ 048 public static final int TARGET_CLASS_EXTENDS = 0x10; 049 050 /** Target type: bound of a type parameter of a generic class or interface. */ 051 public static final int TARGET_CLASS_TYPE_PARAMETER_BOUND = 0x11; 052 053 /** Target type: bound of a type parameter of a generic method or constructor. */ 054 public static final int TARGET_METHOD_TYPE_PARAMETER_BOUND = 0x12; 055 056 /** Target type: type in a field or record component declaration. */ 057 public static final int TARGET_FIELD = 0x13; 058 059 /** Target type: return type of a method, or type of a newly constructed object. */ 060 public static final int TARGET_METHOD_RETURN = 0x14; 061 062 /** Target type: receiver type of a method or constructor. */ 063 public static final int TARGET_METHOD_RECEIVER = 0x15; 064 065 /** Target type: type in a formal parameter declaration of a method, constructor, or lambda expression. */ 066 public static final int TARGET_METHOD_FORMAL_PARAMETER = 0x16; 067 068 /** Target type: type in a throws clause of a method or constructor. */ 069 public static final int TARGET_THROWS = 0x17; 070 071 /** Target type: type in a local variable declaration. */ 072 public static final int TARGET_LOCAL_VARIABLE = 0x40; 073 074 /** Target type: type in a resource variable declaration. */ 075 public static final int TARGET_RESOURCE_VARIABLE = 0x41; 076 077 /** Target type: type in an exception parameter declaration. */ 078 public static final int TARGET_EXCEPTION_PARAMETER = 0x42; 079 080 /** Target type: type in an instanceof expression. */ 081 public static final int TARGET_INSTANCEOF = 0x43; 082 083 /** Target type: type in a new expression. */ 084 public static final int TARGET_NEW = 0x44; 085 086 /** Target type: type in a method reference expression using ::new. */ 087 public static final int TARGET_CONSTRUCTOR_REFERENCE = 0x45; 088 089 /** Target type: type in a method reference expression using ::Identifier. */ 090 public static final int TARGET_METHOD_REFERENCE = 0x46; 091 092 /** Target type: type in a cast expression. */ 093 public static final int TARGET_CAST = 0x47; 094 095 /** Target type: type argument for a generic constructor in a new expression or an explicit constructor invocation. */ 096 public static final int TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; 097 098 /** Target type: type argument for a generic method in a method invocation expression. */ 099 public static final int TARGET_METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; 100 101 /** Target type: type argument for a generic constructor in a method reference expression using ::new. */ 102 public static final int TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; 103 104 /** Target type: type argument for a generic method in a method reference expression using ::Identifier. */ 105 public static final int TARGET_METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; 106 107 /** 108 * Empty array of TypeAnnotationEntry objects. 109 */ 110 public static final TypeAnnotationEntry[] EMPTY_ARRAY = {}; 111 112 /** Maximum number of steps of a type path, which its length of one byte limits. */ 113 private static final int MAX_TYPE_PATH_LENGTH = 255; 114 115 /** Number of bytes of a {@code localvar_target} entry: start_pc, length and index. */ 116 private static final int LOCAL_VARIABLE_RANGE_SIZE = 6; 117 118 private static void checkTargetInfo(final int targetType, final byte[] targetInfo) { 119 final int size = targetInfoSize(targetType); 120 if (size >= 0) { 121 if (targetInfo.length != size) { 122 throw new IllegalArgumentException("The target info of the target type 0x" + Integer.toHexString(targetType) + " must have " + size 123 + " bytes, not " + targetInfo.length); 124 } 125 } else if (targetInfo.length < 2 || targetInfo.length != 2 + ((targetInfo[0] & 0xFF) << 8 | targetInfo[1] & 0xFF) * LOCAL_VARIABLE_RANGE_SIZE) { 126 throw new IllegalArgumentException("The target info of the target type 0x" + Integer.toHexString(targetType) 127 + " must be a length followed by that many ranges of 6 bytes"); 128 } 129 } 130 131 /** 132 * Factory method to create a TypeAnnotationEntry from a DataInput. 133 * 134 * @param input The input stream. 135 * @param constantPool The constant pool. 136 * @param isRuntimeVisible whether the annotation is runtime visible. 137 * @return The entry. 138 * @throws IOException Thrown if an I/O error occurs. 139 */ 140 public static TypeAnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException { 141 final int targetType = input.readUnsignedByte(); 142 final int size = targetInfoSize(targetType); 143 final byte[] targetInfo; 144 if (size >= 0) { 145 targetInfo = new byte[size]; 146 input.readFully(targetInfo); 147 } else { 148 final int tableLength = input.readUnsignedShort(); 149 targetInfo = new byte[2 + tableLength * LOCAL_VARIABLE_RANGE_SIZE]; 150 targetInfo[0] = (byte) (tableLength >>> 8); 151 targetInfo[1] = (byte) tableLength; 152 input.readFully(targetInfo, 2, targetInfo.length - 2); 153 } 154 final byte[] typePath = new byte[input.readUnsignedByte() * 2]; 155 input.readFully(typePath); 156 return new TypeAnnotationEntry(targetType, targetInfo, typePath, AnnotationEntry.read(input, constantPool, isRuntimeVisible)); 157 } 158 159 /** 160 * Gets the number of bytes of the target info that follows a target type. 161 * 162 * @param targetType The target type. 163 * @return the size of the target info, or -1 if it depends on its content (the table of a {@code localvar_target}). 164 * @throws ClassFormatException Thrown if the target type is not defined. 165 */ 166 private static int targetInfoSize(final int targetType) { 167 switch (targetType) { 168 case TARGET_CLASS_TYPE_PARAMETER: 169 case TARGET_METHOD_TYPE_PARAMETER: 170 case TARGET_METHOD_FORMAL_PARAMETER: 171 return 1; 172 case TARGET_CLASS_EXTENDS: 173 case TARGET_CLASS_TYPE_PARAMETER_BOUND: 174 case TARGET_METHOD_TYPE_PARAMETER_BOUND: 175 case TARGET_THROWS: 176 case TARGET_EXCEPTION_PARAMETER: 177 case TARGET_INSTANCEOF: 178 case TARGET_NEW: 179 case TARGET_CONSTRUCTOR_REFERENCE: 180 case TARGET_METHOD_REFERENCE: 181 return 2; 182 case TARGET_FIELD: 183 case TARGET_METHOD_RETURN: 184 case TARGET_METHOD_RECEIVER: 185 return 0; 186 case TARGET_LOCAL_VARIABLE: 187 case TARGET_RESOURCE_VARIABLE: 188 return -1; 189 case TARGET_CAST: 190 case TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: 191 case TARGET_METHOD_INVOCATION_TYPE_ARGUMENT: 192 case TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: 193 case TARGET_METHOD_REFERENCE_TYPE_ARGUMENT: 194 return 3; 195 default: 196 throw new ClassFormatException("Invalid type annotation target type: 0x" + Integer.toHexString(targetType)); 197 } 198 } 199 200 private final int targetType; 201 202 private final byte[] targetInfo; 203 204 private final byte[] typePath; 205 206 private final AnnotationEntry annotationEntry; 207 208 /** 209 * Constructs a TypeAnnotationEntry. 210 * 211 * @param targetType The target type, one of the {@code TARGET_*} constants. 212 * @param targetInfo The target info, as it is stored in the class file. 213 * @param typePath The type path, as it is stored in the class file without its length: a pair of bytes (path kind, type argument index) per step. 214 * @param annotationEntry The annotation. 215 * @throws ClassFormatException Thrown if the target type is not defined. 216 * @throws IllegalArgumentException Thrown if the target info does not have the size its target type requires, or if the type path is not a sequence of pairs 217 * of bytes of at most 255 steps. 218 */ 219 public TypeAnnotationEntry(final int targetType, final byte[] targetInfo, final byte[] typePath, final AnnotationEntry annotationEntry) { 220 checkTargetInfo(targetType, targetInfo); 221 if (typePath.length % 2 != 0 || typePath.length > MAX_TYPE_PATH_LENGTH * 2) { 222 throw new IllegalArgumentException("The type path must be at most " + MAX_TYPE_PATH_LENGTH + " pairs of bytes (kind, argument index), not " 223 + typePath.length + " bytes"); 224 } 225 this.targetType = targetType; 226 this.targetInfo = targetInfo.clone(); 227 this.typePath = typePath.clone(); 228 this.annotationEntry = annotationEntry; 229 } 230 231 /** 232 * Creates a deep copy of this entry, whose annotation is independent of this one and refers to the given constant pool. The indexes it holds are kept, as they 233 * are in {@link Attribute#copy(ConstantPool)}: the constant pool must have the same constants as the one of this entry at these indexes. 234 * 235 * @param constantPool The constant pool of the copy. 236 * @return A deep copy of this entry. 237 */ 238 public TypeAnnotationEntry copy(final ConstantPool constantPool) { 239 final ByteArrayOutputStream out = new ByteArrayOutputStream(); 240 try { 241 try (DataOutputStream dos = new DataOutputStream(out)) { 242 dump(dos); 243 } 244 return read(new DataInputStream(new ByteArrayInputStream(out.toByteArray())), constantPool, annotationEntry.isRuntimeVisible()); 245 } catch (final IOException e) { 246 throw new UncheckedIOException(e); 247 } 248 } 249 250 /** 251 * Writes this entry to a DataOutputStream. 252 * 253 * @param dos The data output stream. 254 * @throws IOException Thrown if an I/O error occurs. 255 */ 256 public void dump(final DataOutputStream dos) throws IOException { 257 dos.writeByte(targetType); 258 dos.write(targetInfo); 259 dos.writeByte(Args.requireU1(typePath.length / 2, "typePath.length")); 260 dos.write(typePath); 261 annotationEntry.dump(dos); 262 } 263 264 /** 265 * Gets the annotation. 266 * 267 * @return The annotation. 268 */ 269 public AnnotationEntry getAnnotationEntry() { 270 return annotationEntry; 271 } 272 273 /** 274 * Gets the target info, which depends on the target type (JVMS table 4.7.20-A): for instance the index of the type parameter for 275 * {@link #TARGET_CLASS_TYPE_PARAMETER}, the index of the type parameter then the index of the bound for {@link #TARGET_CLASS_TYPE_PARAMETER_BOUND}, or the 276 * index of the parameter for {@link #TARGET_METHOD_FORMAL_PARAMETER}. 277 * 278 * @return A copy of the target info, as it is stored in the class file. 279 */ 280 public byte[] getTargetInfo() { 281 return targetInfo.clone(); 282 } 283 284 /** 285 * Gets the kind of program element the annotation applies to. 286 * 287 * @return One of the {@code TARGET_*} constants. 288 */ 289 public int getTargetType() { 290 return targetType; 291 } 292 293 /** 294 * Gets the index of the type argument for a step of the type path of kind 3, else 0. 295 * 296 * @param step Index of the step, from 0 to {@link #getTypePathLength()} excluded. 297 * @return The index of the type argument. 298 */ 299 public int getTypePathArgumentIndex(final int step) { 300 return typePath[step * 2 + 1] & 0xFF; 301 } 302 303 /** 304 * Gets the kind of a step of the type path: 0 deeper in an array type, 1 deeper in a nested type, 2 on the bound of a wildcard type argument, 3 on a type 305 * argument of a parameterized type. 306 * 307 * @param step Index of the step, from 0 to {@link #getTypePathLength()} excluded. 308 * @return The kind of the step. 309 */ 310 public int getTypePathKind(final int step) { 311 return typePath[step * 2] & 0xFF; 312 } 313 314 /** 315 * Gets the number of steps in the type path. 316 * 317 * <p> 318 * The path leads from the type of the target to the part of it that is annotated. An empty path identifies the outermost part of the type where an 319 * annotation is allowed. For a non-static inner class, {@code @A Outer.Inner} has an empty path, while {@code Outer.@A Inner} requires one step. 320 * For a static nested class, {@code Outer.@A Nested} has an empty path. 321 * </p> 322 * 323 * @return The number of steps of the type path. 324 */ 325 public int getTypePathLength() { 326 return typePath.length / 2; 327 } 328 329 @Override 330 public String toString() { 331 return "TypeAnnotationEntry(target=0x" + Integer.toHexString(targetType) + ", info=" + Arrays.toString(targetInfo) + ", path=" + Arrays.toString(typePath) 332 + ", " + annotationEntry + ")"; 333 } 334}