1 /*
2  * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.tools.classfile;
27 
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 import com.sun.tools.classfile.TypeAnnotation.Position.TypePathEntry;
33 
34 /**
35  * See JSR 308 specification, Section 3.
36  *
37  *  <p><b>This is NOT part of any supported API.
38  *  If you write code that depends on this, you do so at your own risk.
39  *  This code and its internal interfaces are subject to change or
40  *  deletion without notice.</b>
41  */
42 public class TypeAnnotation {
TypeAnnotation(ClassReader cr)43     TypeAnnotation(ClassReader cr) throws IOException, Annotation.InvalidAnnotation {
44         constant_pool = cr.getConstantPool();
45         position = read_position(cr);
46         annotation = new Annotation(cr);
47     }
48 
TypeAnnotation(ConstantPool constant_pool, Annotation annotation, Position position)49     public TypeAnnotation(ConstantPool constant_pool,
50             Annotation annotation, Position position) {
51         this.constant_pool = constant_pool;
52         this.position = position;
53         this.annotation = annotation;
54     }
55 
length()56     public int length() {
57         int n = annotation.length();
58         n += position_length(position);
59         return n;
60     }
61 
62     @Override
toString()63     public String toString() {
64         try {
65             return "@" + constant_pool.getUTF8Value(annotation.type_index).toString().substring(1) +
66                     " pos: " + position.toString();
67         } catch (Exception e) {
68             e.printStackTrace();
69             return e.toString();
70         }
71     }
72 
73     public final ConstantPool constant_pool;
74     public final Position position;
75     public final Annotation annotation;
76 
read_position(ClassReader cr)77     private static Position read_position(ClassReader cr) throws IOException, Annotation.InvalidAnnotation {
78         // Copied from ClassReader
79         int tag = cr.readUnsignedByte(); // TargetType tag is a byte
80         if (!TargetType.isValidTargetTypeValue(tag))
81             throw new Annotation.InvalidAnnotation("TypeAnnotation: Invalid type annotation target type value: " + String.format("0x%02X", tag));
82 
83         TargetType type = TargetType.fromTargetTypeValue(tag);
84 
85         Position position = new Position();
86         position.type = type;
87 
88         switch (type) {
89         // instanceof
90         case INSTANCEOF:
91         // new expression
92         case NEW:
93         // constructor/method reference receiver
94         case CONSTRUCTOR_REFERENCE:
95         case METHOD_REFERENCE:
96             position.offset = cr.readUnsignedShort();
97             break;
98         // local variable
99         case LOCAL_VARIABLE:
100         // resource variable
101         case RESOURCE_VARIABLE:
102             int table_length = cr.readUnsignedShort();
103             position.lvarOffset = new int[table_length];
104             position.lvarLength = new int[table_length];
105             position.lvarIndex = new int[table_length];
106             for (int i = 0; i < table_length; ++i) {
107                 position.lvarOffset[i] = cr.readUnsignedShort();
108                 position.lvarLength[i] = cr.readUnsignedShort();
109                 position.lvarIndex[i] = cr.readUnsignedShort();
110             }
111             break;
112         // exception parameter
113         case EXCEPTION_PARAMETER:
114             position.exception_index = cr.readUnsignedShort();
115             break;
116         // method receiver
117         case METHOD_RECEIVER:
118             // Do nothing
119             break;
120         // type parameter
121         case CLASS_TYPE_PARAMETER:
122         case METHOD_TYPE_PARAMETER:
123             position.parameter_index = cr.readUnsignedByte();
124             break;
125         // type parameter bound
126         case CLASS_TYPE_PARAMETER_BOUND:
127         case METHOD_TYPE_PARAMETER_BOUND:
128             position.parameter_index = cr.readUnsignedByte();
129             position.bound_index = cr.readUnsignedByte();
130             break;
131         // class extends or implements clause
132         case CLASS_EXTENDS:
133             position.type_index = cr.readUnsignedShort();;
134             break;
135         // throws
136         case THROWS:
137             position.type_index = cr.readUnsignedShort();
138             break;
139         // method parameter
140         case METHOD_FORMAL_PARAMETER:
141             position.parameter_index = cr.readUnsignedByte();
142             break;
143         // type cast
144         case CAST:
145         // method/constructor/reference type argument
146         case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
147         case METHOD_INVOCATION_TYPE_ARGUMENT:
148         case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
149         case METHOD_REFERENCE_TYPE_ARGUMENT:
150             position.offset = cr.readUnsignedShort();
151             position.type_index = cr.readUnsignedByte();
152             break;
153         // We don't need to worry about these
154         case METHOD_RETURN:
155         case FIELD:
156             break;
157         case UNKNOWN:
158             throw new AssertionError("TypeAnnotation: UNKNOWN target type should never occur!");
159         default:
160             throw new AssertionError("TypeAnnotation: Unknown target type: " + type);
161         }
162 
163         { // Write type path
164             int len = cr.readUnsignedByte();
165             List<Integer> loc = new ArrayList<>(len);
166             for (int i = 0; i < len * TypePathEntry.bytesPerEntry; ++i)
167                 loc.add(cr.readUnsignedByte());
168             position.location = Position.getTypePathFromBinary(loc);
169         }
170         return position;
171     }
172 
position_length(Position pos)173     private static int position_length(Position pos) {
174         int n = 0;
175         n += 1; // TargetType tag is a byte
176         switch (pos.type) {
177         // instanceof
178         case INSTANCEOF:
179         // new expression
180         case NEW:
181         // constructor/method reference receiver
182         case CONSTRUCTOR_REFERENCE:
183         case METHOD_REFERENCE:
184             n += 2; // offset
185             break;
186         // local variable
187         case LOCAL_VARIABLE:
188         // resource variable
189         case RESOURCE_VARIABLE:
190             n += 2; // table_length;
191             int table_length = pos.lvarOffset.length;
192             n += 2 * table_length; // offset
193             n += 2 * table_length; // length
194             n += 2 * table_length; // index
195             break;
196         // exception parameter
197         case EXCEPTION_PARAMETER:
198             n += 2; // exception_index
199             break;
200         // method receiver
201         case METHOD_RECEIVER:
202             // Do nothing
203             break;
204         // type parameter
205         case CLASS_TYPE_PARAMETER:
206         case METHOD_TYPE_PARAMETER:
207             n += 1; // parameter_index
208             break;
209         // type parameter bound
210         case CLASS_TYPE_PARAMETER_BOUND:
211         case METHOD_TYPE_PARAMETER_BOUND:
212             n += 1; // parameter_index
213             n += 1; // bound_index
214             break;
215         // class extends or implements clause
216         case CLASS_EXTENDS:
217             n += 2; // type_index
218             break;
219         // throws
220         case THROWS:
221             n += 2; // type_index
222             break;
223         // method parameter
224         case METHOD_FORMAL_PARAMETER:
225             n += 1; // parameter_index
226             break;
227         // type cast
228         case CAST:
229         // method/constructor/reference type argument
230         case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
231         case METHOD_INVOCATION_TYPE_ARGUMENT:
232         case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
233         case METHOD_REFERENCE_TYPE_ARGUMENT:
234             n += 2; // offset
235             n += 1; // type index
236             break;
237         // We don't need to worry about these
238         case METHOD_RETURN:
239         case FIELD:
240             break;
241         case UNKNOWN:
242             throw new AssertionError("TypeAnnotation: UNKNOWN target type should never occur!");
243         default:
244             throw new AssertionError("TypeAnnotation: Unknown target type: " + pos.type);
245         }
246 
247         {
248             n += 1; // length
249             n += TypePathEntry.bytesPerEntry * pos.location.size(); // bytes for actual array
250         }
251 
252         return n;
253     }
254 
255     // Code duplicated from com.sun.tools.javac.code.TypeAnnotationPosition
256     public static class Position {
257         public enum TypePathEntryKind {
258             ARRAY(0),
259             INNER_TYPE(1),
260             WILDCARD(2),
261             TYPE_ARGUMENT(3);
262 
263             public final int tag;
264 
TypePathEntryKind(int tag)265             private TypePathEntryKind(int tag) {
266                 this.tag = tag;
267             }
268         }
269 
270         public static class TypePathEntry {
271             /** The fixed number of bytes per TypePathEntry. */
272             public static final int bytesPerEntry = 2;
273 
274             public final TypePathEntryKind tag;
275             public final int arg;
276 
277             public static final TypePathEntry ARRAY = new TypePathEntry(TypePathEntryKind.ARRAY);
278             public static final TypePathEntry INNER_TYPE = new TypePathEntry(TypePathEntryKind.INNER_TYPE);
279             public static final TypePathEntry WILDCARD = new TypePathEntry(TypePathEntryKind.WILDCARD);
280 
TypePathEntry(TypePathEntryKind tag)281             private TypePathEntry(TypePathEntryKind tag) {
282                 if (!(tag == TypePathEntryKind.ARRAY ||
283                         tag == TypePathEntryKind.INNER_TYPE ||
284                         tag == TypePathEntryKind.WILDCARD)) {
285                     throw new AssertionError("Invalid TypePathEntryKind: " + tag);
286                 }
287                 this.tag = tag;
288                 this.arg = 0;
289             }
290 
TypePathEntry(TypePathEntryKind tag, int arg)291             public TypePathEntry(TypePathEntryKind tag, int arg) {
292                 if (tag != TypePathEntryKind.TYPE_ARGUMENT) {
293                     throw new AssertionError("Invalid TypePathEntryKind: " + tag);
294                 }
295                 this.tag = tag;
296                 this.arg = arg;
297             }
298 
fromBinary(int tag, int arg)299             public static TypePathEntry fromBinary(int tag, int arg) {
300                 if (arg != 0 && tag != TypePathEntryKind.TYPE_ARGUMENT.tag) {
301                     throw new AssertionError("Invalid TypePathEntry tag/arg: " + tag + "/" + arg);
302                 }
303                 switch (tag) {
304                 case 0:
305                     return ARRAY;
306                 case 1:
307                     return INNER_TYPE;
308                 case 2:
309                     return WILDCARD;
310                 case 3:
311                     return new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, arg);
312                 default:
313                     throw new AssertionError("Invalid TypePathEntryKind tag: " + tag);
314                 }
315             }
316 
317             @Override
toString()318             public String toString() {
319                 return tag.toString() +
320                         (tag == TypePathEntryKind.TYPE_ARGUMENT ? ("(" + arg + ")") : "");
321             }
322 
323             @Override
equals(Object other)324             public boolean equals(Object other) {
325                 if (! (other instanceof TypePathEntry)) {
326                     return false;
327                 }
328                 TypePathEntry tpe = (TypePathEntry) other;
329                 return this.tag == tpe.tag && this.arg == tpe.arg;
330             }
331 
332             @Override
hashCode()333             public int hashCode() {
334                 return this.tag.hashCode() * 17 + this.arg;
335             }
336         }
337 
338         public TargetType type = TargetType.UNKNOWN;
339 
340         // For generic/array types.
341         // TODO: or should we use null? Noone will use this object.
342         public List<TypePathEntry> location = new ArrayList<>(0);
343 
344         // Tree position.
345         public int pos = -1;
346 
347         // For typecasts, type tests, new (and locals, as start_pc).
348         public boolean isValidOffset = false;
349         public int offset = -1;
350 
351         // For locals. arrays same length
352         public int[] lvarOffset = null;
353         public int[] lvarLength = null;
354         public int[] lvarIndex = null;
355 
356         // For type parameter bound
357         public int bound_index = Integer.MIN_VALUE;
358 
359         // For type parameter and method parameter
360         public int parameter_index = Integer.MIN_VALUE;
361 
362         // For class extends, implements, and throws clauses
363         public int type_index = Integer.MIN_VALUE;
364 
365         // For exception parameters, index into exception table
366         public int exception_index = Integer.MIN_VALUE;
367 
Position()368         public Position() {}
369 
370         @Override
toString()371         public String toString() {
372             StringBuilder sb = new StringBuilder();
373             sb.append('[');
374             sb.append(type);
375 
376             switch (type) {
377             // instanceof
378             case INSTANCEOF:
379             // new expression
380             case NEW:
381             // constructor/method reference receiver
382             case CONSTRUCTOR_REFERENCE:
383             case METHOD_REFERENCE:
384                 sb.append(", offset = ");
385                 sb.append(offset);
386                 break;
387             // local variable
388             case LOCAL_VARIABLE:
389             // resource variable
390             case RESOURCE_VARIABLE:
391                 if (lvarOffset == null) {
392                     sb.append(", lvarOffset is null!");
393                     break;
394                 }
395                 sb.append(", {");
396                 for (int i = 0; i < lvarOffset.length; ++i) {
397                     if (i != 0) sb.append("; ");
398                     sb.append("start_pc = ");
399                     sb.append(lvarOffset[i]);
400                     sb.append(", length = ");
401                     sb.append(lvarLength[i]);
402                     sb.append(", index = ");
403                     sb.append(lvarIndex[i]);
404                 }
405                 sb.append("}");
406                 break;
407             // method receiver
408             case METHOD_RECEIVER:
409                 // Do nothing
410                 break;
411             // type parameter
412             case CLASS_TYPE_PARAMETER:
413             case METHOD_TYPE_PARAMETER:
414                 sb.append(", param_index = ");
415                 sb.append(parameter_index);
416                 break;
417             // type parameter bound
418             case CLASS_TYPE_PARAMETER_BOUND:
419             case METHOD_TYPE_PARAMETER_BOUND:
420                 sb.append(", param_index = ");
421                 sb.append(parameter_index);
422                 sb.append(", bound_index = ");
423                 sb.append(bound_index);
424                 break;
425             // class extends or implements clause
426             case CLASS_EXTENDS:
427                 sb.append(", type_index = ");
428                 sb.append(type_index);
429                 break;
430             // throws
431             case THROWS:
432                 sb.append(", type_index = ");
433                 sb.append(type_index);
434                 break;
435             // exception parameter
436             case EXCEPTION_PARAMETER:
437                 sb.append(", exception_index = ");
438                 sb.append(exception_index);
439                 break;
440             // method parameter
441             case METHOD_FORMAL_PARAMETER:
442                 sb.append(", param_index = ");
443                 sb.append(parameter_index);
444                 break;
445             // type cast
446             case CAST:
447             // method/constructor/reference type argument
448             case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
449             case METHOD_INVOCATION_TYPE_ARGUMENT:
450             case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
451             case METHOD_REFERENCE_TYPE_ARGUMENT:
452                 sb.append(", offset = ");
453                 sb.append(offset);
454                 sb.append(", type_index = ");
455                 sb.append(type_index);
456                 break;
457             // We don't need to worry about these
458             case METHOD_RETURN:
459             case FIELD:
460                 break;
461             case UNKNOWN:
462                 sb.append(", position UNKNOWN!");
463                 break;
464             default:
465                 throw new AssertionError("Unknown target type: " + type);
466             }
467 
468             // Append location data for generics/arrays.
469             if (!location.isEmpty()) {
470                 sb.append(", location = (");
471                 sb.append(location);
472                 sb.append(")");
473             }
474 
475             sb.append(", pos = ");
476             sb.append(pos);
477 
478             sb.append(']');
479             return sb.toString();
480         }
481 
482         /**
483          * Indicates whether the target tree of the annotation has been optimized
484          * away from classfile or not.
485          * @return true if the target has not been optimized away
486          */
emitToClassfile()487         public boolean emitToClassfile() {
488             return !type.isLocal() || isValidOffset;
489         }
490 
491         /**
492          * Decode the binary representation for a type path and set
493          * the {@code location} field.
494          *
495          * @param list The bytecode representation of the type path.
496          */
getTypePathFromBinary(List<Integer> list)497         public static List<TypePathEntry> getTypePathFromBinary(List<Integer> list) {
498             List<TypePathEntry> loc = new ArrayList<>(list.size() / TypePathEntry.bytesPerEntry);
499             int idx = 0;
500             while (idx < list.size()) {
501                 if (idx + 1 == list.size()) {
502                     throw new AssertionError("Could not decode type path: " + list);
503                 }
504                 loc.add(TypePathEntry.fromBinary(list.get(idx), list.get(idx + 1)));
505                 idx += 2;
506             }
507             return loc;
508         }
509 
getBinaryFromTypePath(List<TypePathEntry> locs)510         public static List<Integer> getBinaryFromTypePath(List<TypePathEntry> locs) {
511             List<Integer> loc = new ArrayList<>(locs.size() * TypePathEntry.bytesPerEntry);
512             for (TypePathEntry tpe : locs) {
513                 loc.add(tpe.tag.tag);
514                 loc.add(tpe.arg);
515             }
516             return loc;
517         }
518     }
519 
520     // Code duplicated from com.sun.tools.javac.code.TargetType
521     // The IsLocal flag could be removed here.
522     public enum TargetType {
523         /** For annotations on a class type parameter declaration. */
524         CLASS_TYPE_PARAMETER(0x00),
525 
526         /** For annotations on a method type parameter declaration. */
527         METHOD_TYPE_PARAMETER(0x01),
528 
529         /** For annotations on the type of an "extends" or "implements" clause. */
530         CLASS_EXTENDS(0x10),
531 
532         /** For annotations on a bound of a type parameter of a class. */
533         CLASS_TYPE_PARAMETER_BOUND(0x11),
534 
535         /** For annotations on a bound of a type parameter of a method. */
536         METHOD_TYPE_PARAMETER_BOUND(0x12),
537 
538         /** For annotations on a field. */
539         FIELD(0x13),
540 
541         /** For annotations on a method return type. */
542         METHOD_RETURN(0x14),
543 
544         /** For annotations on the method receiver. */
545         METHOD_RECEIVER(0x15),
546 
547         /** For annotations on a method parameter. */
548         METHOD_FORMAL_PARAMETER(0x16),
549 
550         /** For annotations on a throws clause in a method declaration. */
551         THROWS(0x17),
552 
553         /** For annotations on a local variable. */
554         LOCAL_VARIABLE(0x40, true),
555 
556         /** For annotations on a resource variable. */
557         RESOURCE_VARIABLE(0x41, true),
558 
559         /** For annotations on an exception parameter. */
560         EXCEPTION_PARAMETER(0x42, true),
561 
562         /** For annotations on a type test. */
563         INSTANCEOF(0x43, true),
564 
565         /** For annotations on an object creation expression. */
566         NEW(0x44, true),
567 
568         /** For annotations on a constructor reference receiver. */
569         CONSTRUCTOR_REFERENCE(0x45, true),
570 
571         /** For annotations on a method reference receiver. */
572         METHOD_REFERENCE(0x46, true),
573 
574         /** For annotations on a typecast. */
575         CAST(0x47, true),
576 
577         /** For annotations on a type argument of an object creation expression. */
578         CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT(0x48, true),
579 
580         /** For annotations on a type argument of a method call. */
581         METHOD_INVOCATION_TYPE_ARGUMENT(0x49, true),
582 
583         /** For annotations on a type argument of a constructor reference. */
584         CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT(0x4A, true),
585 
586         /** For annotations on a type argument of a method reference. */
587         METHOD_REFERENCE_TYPE_ARGUMENT(0x4B, true),
588 
589         /** For annotations with an unknown target. */
590         UNKNOWN(0xFF);
591 
592         private static final int MAXIMUM_TARGET_TYPE_VALUE = 0x4B;
593 
594         private final int targetTypeValue;
595         private final boolean isLocal;
596 
TargetType(int targetTypeValue)597         private TargetType(int targetTypeValue) {
598             this(targetTypeValue, false);
599         }
600 
TargetType(int targetTypeValue, boolean isLocal)601         private TargetType(int targetTypeValue, boolean isLocal) {
602             if (targetTypeValue < 0
603                     || targetTypeValue > 255)
604                     throw new AssertionError("Attribute type value needs to be an unsigned byte: " + String.format("0x%02X", targetTypeValue));
605             this.targetTypeValue = targetTypeValue;
606             this.isLocal = isLocal;
607         }
608 
609         /**
610          * Returns whether or not this TargetType represents an annotation whose
611          * target is exclusively a tree in a method body
612          *
613          * Note: wildcard bound targets could target a local tree and a class
614          * member declaration signature tree
615          */
isLocal()616         public boolean isLocal() {
617             return isLocal;
618         }
619 
targetTypeValue()620         public int targetTypeValue() {
621             return this.targetTypeValue;
622         }
623 
624         private static final TargetType[] targets;
625 
626         static {
627             targets = new TargetType[MAXIMUM_TARGET_TYPE_VALUE + 1];
628             TargetType[] alltargets = values();
629             for (TargetType target : alltargets) {
630                 if (target.targetTypeValue != UNKNOWN.targetTypeValue)
631                     targets[target.targetTypeValue] = target;
632             }
633             for (int i = 0; i <= MAXIMUM_TARGET_TYPE_VALUE; ++i) {
634                 if (targets[i] == null)
635                     targets[i] = UNKNOWN;
636             }
637         }
638 
isValidTargetTypeValue(int tag)639         public static boolean isValidTargetTypeValue(int tag) {
640             if (tag == UNKNOWN.targetTypeValue)
641                 return true;
642             return (tag >= 0 && tag < targets.length);
643         }
644 
fromTargetTypeValue(int tag)645         public static TargetType fromTargetTypeValue(int tag) {
646             if (tag == UNKNOWN.targetTypeValue)
647                 return UNKNOWN;
648 
649             if (tag < 0 || tag >= targets.length)
650                 throw new AssertionError("Unknown TargetType: " + tag);
651             return targets[tag];
652         }
653     }
654 }
655