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}