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             int in = cr.readUnsignedShort();
134             if (in == 0xFFFF)
135                 in = -1;
136             position.type_index = in;
137             break;
138         // throws
139         case THROWS:
140             position.type_index = cr.readUnsignedShort();
141             break;
142         // method parameter
143         case METHOD_FORMAL_PARAMETER:
144             position.parameter_index = cr.readUnsignedByte();
145             break;
146         // type cast
147         case CAST:
148         // method/constructor/reference type argument
149         case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
150         case METHOD_INVOCATION_TYPE_ARGUMENT:
151         case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
152         case METHOD_REFERENCE_TYPE_ARGUMENT:
153             position.offset = cr.readUnsignedShort();
154             position.type_index = cr.readUnsignedByte();
155             break;
156         // We don't need to worry about these
157         case METHOD_RETURN:
158         case FIELD:
159             break;
160         case UNKNOWN:
161             throw new AssertionError("TypeAnnotation: UNKNOWN target type should never occur!");
162         default:
163             throw new AssertionError("TypeAnnotation: Unknown target type: " + type);
164         }
165 
166         { // Write type path
167             int len = cr.readUnsignedByte();
168             List<Integer> loc = new ArrayList<Integer>(len);
169             for (int i = 0; i < len * TypePathEntry.bytesPerEntry; ++i)
170                 loc.add(cr.readUnsignedByte());
171             position.location = Position.getTypePathFromBinary(loc);
172         }
173         return position;
174     }
175 
position_length(Position pos)176     private static int position_length(Position pos) {
177         int n = 0;
178         n += 1; // TargetType tag is a byte
179         switch (pos.type) {
180         // instanceof
181         case INSTANCEOF:
182         // new expression
183         case NEW:
184         // constructor/method reference receiver
185         case CONSTRUCTOR_REFERENCE:
186         case METHOD_REFERENCE:
187             n += 2; // offset
188             break;
189         // local variable
190         case LOCAL_VARIABLE:
191         // resource variable
192         case RESOURCE_VARIABLE:
193             n += 2; // table_length;
194             int table_length = pos.lvarOffset.length;
195             n += 2 * table_length; // offset
196             n += 2 * table_length; // length
197             n += 2 * table_length; // index
198             break;
199         // exception parameter
200         case EXCEPTION_PARAMETER:
201             n += 2; // exception_index
202             break;
203         // method receiver
204         case METHOD_RECEIVER:
205             // Do nothing
206             break;
207         // type parameter
208         case CLASS_TYPE_PARAMETER:
209         case METHOD_TYPE_PARAMETER:
210             n += 1; // parameter_index
211             break;
212         // type parameter bound
213         case CLASS_TYPE_PARAMETER_BOUND:
214         case METHOD_TYPE_PARAMETER_BOUND:
215             n += 1; // parameter_index
216             n += 1; // bound_index
217             break;
218         // class extends or implements clause
219         case CLASS_EXTENDS:
220             n += 2; // type_index
221             break;
222         // throws
223         case THROWS:
224             n += 2; // type_index
225             break;
226         // method parameter
227         case METHOD_FORMAL_PARAMETER:
228             n += 1; // parameter_index
229             break;
230         // type cast
231         case CAST:
232         // method/constructor/reference type argument
233         case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
234         case METHOD_INVOCATION_TYPE_ARGUMENT:
235         case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
236         case METHOD_REFERENCE_TYPE_ARGUMENT:
237             n += 2; // offset
238             n += 1; // type index
239             break;
240         // We don't need to worry about these
241         case METHOD_RETURN:
242         case FIELD:
243             break;
244         case UNKNOWN:
245             throw new AssertionError("TypeAnnotation: UNKNOWN target type should never occur!");
246         default:
247             throw new AssertionError("TypeAnnotation: Unknown target type: " + pos.type);
248         }
249 
250         {
251             n += 1; // length
252             n += TypePathEntry.bytesPerEntry * pos.location.size(); // bytes for actual array
253         }
254 
255         return n;
256     }
257 
258     // Code duplicated from com.sun.tools.javac.code.TypeAnnotationPosition
259     public static class Position {
260         public enum TypePathEntryKind {
261             ARRAY(0),
262             INNER_TYPE(1),
263             WILDCARD(2),
264             TYPE_ARGUMENT(3);
265 
266             public final int tag;
267 
TypePathEntryKind(int tag)268             private TypePathEntryKind(int tag) {
269                 this.tag = tag;
270             }
271         }
272 
273         public static class TypePathEntry {
274             /** The fixed number of bytes per TypePathEntry. */
275             public static final int bytesPerEntry = 2;
276 
277             public final TypePathEntryKind tag;
278             public final int arg;
279 
280             public static final TypePathEntry ARRAY = new TypePathEntry(TypePathEntryKind.ARRAY);
281             public static final TypePathEntry INNER_TYPE = new TypePathEntry(TypePathEntryKind.INNER_TYPE);
282             public static final TypePathEntry WILDCARD = new TypePathEntry(TypePathEntryKind.WILDCARD);
283 
TypePathEntry(TypePathEntryKind tag)284             private TypePathEntry(TypePathEntryKind tag) {
285                 if (!(tag == TypePathEntryKind.ARRAY ||
286                         tag == TypePathEntryKind.INNER_TYPE ||
287                         tag == TypePathEntryKind.WILDCARD)) {
288                     throw new AssertionError("Invalid TypePathEntryKind: " + tag);
289                 }
290                 this.tag = tag;
291                 this.arg = 0;
292             }
293 
TypePathEntry(TypePathEntryKind tag, int arg)294             public TypePathEntry(TypePathEntryKind tag, int arg) {
295                 if (tag != TypePathEntryKind.TYPE_ARGUMENT) {
296                     throw new AssertionError("Invalid TypePathEntryKind: " + tag);
297                 }
298                 this.tag = tag;
299                 this.arg = arg;
300             }
301 
fromBinary(int tag, int arg)302             public static TypePathEntry fromBinary(int tag, int arg) {
303                 if (arg != 0 && tag != TypePathEntryKind.TYPE_ARGUMENT.tag) {
304                     throw new AssertionError("Invalid TypePathEntry tag/arg: " + tag + "/" + arg);
305                 }
306                 switch (tag) {
307                 case 0:
308                     return ARRAY;
309                 case 1:
310                     return INNER_TYPE;
311                 case 2:
312                     return WILDCARD;
313                 case 3:
314                     return new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, arg);
315                 default:
316                     throw new AssertionError("Invalid TypePathEntryKind tag: " + tag);
317                 }
318             }
319 
320             @Override
toString()321             public String toString() {
322                 return tag.toString() +
323                         (tag == TypePathEntryKind.TYPE_ARGUMENT ? ("(" + arg + ")") : "");
324             }
325 
326             @Override
equals(Object other)327             public boolean equals(Object other) {
328                 if (! (other instanceof TypePathEntry)) {
329                     return false;
330                 }
331                 TypePathEntry tpe = (TypePathEntry) other;
332                 return this.tag == tpe.tag && this.arg == tpe.arg;
333             }
334 
335             @Override
hashCode()336             public int hashCode() {
337                 return this.tag.hashCode() * 17 + this.arg;
338             }
339         }
340 
341         public TargetType type = TargetType.UNKNOWN;
342 
343         // For generic/array types.
344         // TODO: or should we use null? Noone will use this object.
345         public List<TypePathEntry> location = new ArrayList<TypePathEntry>(0);
346 
347         // Tree position.
348         public int pos = -1;
349 
350         // For typecasts, type tests, new (and locals, as start_pc).
351         public boolean isValidOffset = false;
352         public int offset = -1;
353 
354         // For locals. arrays same length
355         public int[] lvarOffset = null;
356         public int[] lvarLength = null;
357         public int[] lvarIndex = null;
358 
359         // For type parameter bound
360         public int bound_index = Integer.MIN_VALUE;
361 
362         // For type parameter and method parameter
363         public int parameter_index = Integer.MIN_VALUE;
364 
365         // For class extends, implements, and throws clauses
366         public int type_index = Integer.MIN_VALUE;
367 
368         // For exception parameters, index into exception table
369         public int exception_index = Integer.MIN_VALUE;
370 
Position()371         public Position() {}
372 
373         @Override
toString()374         public String toString() {
375             StringBuilder sb = new StringBuilder();
376             sb.append('[');
377             sb.append(type);
378 
379             switch (type) {
380             // instanceof
381             case INSTANCEOF:
382             // new expression
383             case NEW:
384             // constructor/method reference receiver
385             case CONSTRUCTOR_REFERENCE:
386             case METHOD_REFERENCE:
387                 sb.append(", offset = ");
388                 sb.append(offset);
389                 break;
390             // local variable
391             case LOCAL_VARIABLE:
392             // resource variable
393             case RESOURCE_VARIABLE:
394                 if (lvarOffset == null) {
395                     sb.append(", lvarOffset is null!");
396                     break;
397                 }
398                 sb.append(", {");
399                 for (int i = 0; i < lvarOffset.length; ++i) {
400                     if (i != 0) sb.append("; ");
401                     sb.append("start_pc = ");
402                     sb.append(lvarOffset[i]);
403                     sb.append(", length = ");
404                     sb.append(lvarLength[i]);
405                     sb.append(", index = ");
406                     sb.append(lvarIndex[i]);
407                 }
408                 sb.append("}");
409                 break;
410             // method receiver
411             case METHOD_RECEIVER:
412                 // Do nothing
413                 break;
414             // type parameter
415             case CLASS_TYPE_PARAMETER:
416             case METHOD_TYPE_PARAMETER:
417                 sb.append(", param_index = ");
418                 sb.append(parameter_index);
419                 break;
420             // type parameter bound
421             case CLASS_TYPE_PARAMETER_BOUND:
422             case METHOD_TYPE_PARAMETER_BOUND:
423                 sb.append(", param_index = ");
424                 sb.append(parameter_index);
425                 sb.append(", bound_index = ");
426                 sb.append(bound_index);
427                 break;
428             // class extends or implements clause
429             case CLASS_EXTENDS:
430                 sb.append(", type_index = ");
431                 sb.append(type_index);
432                 break;
433             // throws
434             case THROWS:
435                 sb.append(", type_index = ");
436                 sb.append(type_index);
437                 break;
438             // exception parameter
439             case EXCEPTION_PARAMETER:
440                 sb.append(", exception_index = ");
441                 sb.append(exception_index);
442                 break;
443             // method parameter
444             case METHOD_FORMAL_PARAMETER:
445                 sb.append(", param_index = ");
446                 sb.append(parameter_index);
447                 break;
448             // type cast
449             case CAST:
450             // method/constructor/reference type argument
451             case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
452             case METHOD_INVOCATION_TYPE_ARGUMENT:
453             case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
454             case METHOD_REFERENCE_TYPE_ARGUMENT:
455                 sb.append(", offset = ");
456                 sb.append(offset);
457                 sb.append(", type_index = ");
458                 sb.append(type_index);
459                 break;
460             // We don't need to worry about these
461             case METHOD_RETURN:
462             case FIELD:
463                 break;
464             case UNKNOWN:
465                 sb.append(", position UNKNOWN!");
466                 break;
467             default:
468                 throw new AssertionError("Unknown target type: " + type);
469             }
470 
471             // Append location data for generics/arrays.
472             if (!location.isEmpty()) {
473                 sb.append(", location = (");
474                 sb.append(location);
475                 sb.append(")");
476             }
477 
478             sb.append(", pos = ");
479             sb.append(pos);
480 
481             sb.append(']');
482             return sb.toString();
483         }
484 
485         /**
486          * Indicates whether the target tree of the annotation has been optimized
487          * away from classfile or not.
488          * @return true if the target has not been optimized away
489          */
emitToClassfile()490         public boolean emitToClassfile() {
491             return !type.isLocal() || isValidOffset;
492         }
493 
494         /**
495          * Decode the binary representation for a type path and set
496          * the {@code location} field.
497          *
498          * @param list The bytecode representation of the type path.
499          */
getTypePathFromBinary(List<Integer> list)500         public static List<TypePathEntry> getTypePathFromBinary(List<Integer> list) {
501             List<TypePathEntry> loc = new ArrayList<TypePathEntry>(list.size() / TypePathEntry.bytesPerEntry);
502             int idx = 0;
503             while (idx < list.size()) {
504                 if (idx + 1 == list.size()) {
505                     throw new AssertionError("Could not decode type path: " + list);
506                 }
507                 loc.add(TypePathEntry.fromBinary(list.get(idx), list.get(idx + 1)));
508                 idx += 2;
509             }
510             return loc;
511         }
512 
getBinaryFromTypePath(List<TypePathEntry> locs)513         public static List<Integer> getBinaryFromTypePath(List<TypePathEntry> locs) {
514             List<Integer> loc = new ArrayList<Integer>(locs.size() * TypePathEntry.bytesPerEntry);
515             for (TypePathEntry tpe : locs) {
516                 loc.add(tpe.tag.tag);
517                 loc.add(tpe.arg);
518             }
519             return loc;
520         }
521     }
522 
523     // Code duplicated from com.sun.tools.javac.code.TargetType
524     // The IsLocal flag could be removed here.
525     public enum TargetType {
526         /** For annotations on a class type parameter declaration. */
527         CLASS_TYPE_PARAMETER(0x00),
528 
529         /** For annotations on a method type parameter declaration. */
530         METHOD_TYPE_PARAMETER(0x01),
531 
532         /** For annotations on the type of an "extends" or "implements" clause. */
533         CLASS_EXTENDS(0x10),
534 
535         /** For annotations on a bound of a type parameter of a class. */
536         CLASS_TYPE_PARAMETER_BOUND(0x11),
537 
538         /** For annotations on a bound of a type parameter of a method. */
539         METHOD_TYPE_PARAMETER_BOUND(0x12),
540 
541         /** For annotations on a field. */
542         FIELD(0x13),
543 
544         /** For annotations on a method return type. */
545         METHOD_RETURN(0x14),
546 
547         /** For annotations on the method receiver. */
548         METHOD_RECEIVER(0x15),
549 
550         /** For annotations on a method parameter. */
551         METHOD_FORMAL_PARAMETER(0x16),
552 
553         /** For annotations on a throws clause in a method declaration. */
554         THROWS(0x17),
555 
556         /** For annotations on a local variable. */
557         LOCAL_VARIABLE(0x40, true),
558 
559         /** For annotations on a resource variable. */
560         RESOURCE_VARIABLE(0x41, true),
561 
562         /** For annotations on an exception parameter. */
563         EXCEPTION_PARAMETER(0x42, true),
564 
565         /** For annotations on a type test. */
566         INSTANCEOF(0x43, true),
567 
568         /** For annotations on an object creation expression. */
569         NEW(0x44, true),
570 
571         /** For annotations on a constructor reference receiver. */
572         CONSTRUCTOR_REFERENCE(0x45, true),
573 
574         /** For annotations on a method reference receiver. */
575         METHOD_REFERENCE(0x46, true),
576 
577         /** For annotations on a typecast. */
578         CAST(0x47, true),
579 
580         /** For annotations on a type argument of an object creation expression. */
581         CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT(0x48, true),
582 
583         /** For annotations on a type argument of a method call. */
584         METHOD_INVOCATION_TYPE_ARGUMENT(0x49, true),
585 
586         /** For annotations on a type argument of a constructor reference. */
587         CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT(0x4A, true),
588 
589         /** For annotations on a type argument of a method reference. */
590         METHOD_REFERENCE_TYPE_ARGUMENT(0x4B, true),
591 
592         /** For annotations with an unknown target. */
593         UNKNOWN(0xFF);
594 
595         private static final int MAXIMUM_TARGET_TYPE_VALUE = 0x4B;
596 
597         private final int targetTypeValue;
598         private final boolean isLocal;
599 
TargetType(int targetTypeValue)600         private TargetType(int targetTypeValue) {
601             this(targetTypeValue, false);
602         }
603 
TargetType(int targetTypeValue, boolean isLocal)604         private TargetType(int targetTypeValue, boolean isLocal) {
605             if (targetTypeValue < 0
606                     || targetTypeValue > 255)
607                     throw new AssertionError("Attribute type value needs to be an unsigned byte: " + String.format("0x%02X", targetTypeValue));
608             this.targetTypeValue = targetTypeValue;
609             this.isLocal = isLocal;
610         }
611 
612         /**
613          * Returns whether or not this TargetType represents an annotation whose
614          * target is exclusively a tree in a method body
615          *
616          * Note: wildcard bound targets could target a local tree and a class
617          * member declaration signature tree
618          */
isLocal()619         public boolean isLocal() {
620             return isLocal;
621         }
622 
targetTypeValue()623         public int targetTypeValue() {
624             return this.targetTypeValue;
625         }
626 
627         private static final TargetType[] targets;
628 
629         static {
630             targets = new TargetType[MAXIMUM_TARGET_TYPE_VALUE + 1];
631             TargetType[] alltargets = values();
632             for (TargetType target : alltargets) {
633                 if (target.targetTypeValue != UNKNOWN.targetTypeValue)
634                     targets[target.targetTypeValue] = target;
635             }
636             for (int i = 0; i <= MAXIMUM_TARGET_TYPE_VALUE; ++i) {
637                 if (targets[i] == null)
638                     targets[i] = UNKNOWN;
639             }
640         }
641 
isValidTargetTypeValue(int tag)642         public static boolean isValidTargetTypeValue(int tag) {
643             if (tag == UNKNOWN.targetTypeValue)
644                 return true;
645             return (tag >= 0 && tag < targets.length);
646         }
647 
fromTargetTypeValue(int tag)648         public static TargetType fromTargetTypeValue(int tag) {
649             if (tag == UNKNOWN.targetTypeValue)
650                 return UNKNOWN;
651 
652             if (tag < 0 || tag >= targets.length)
653                 throw new AssertionError("Unknown TargetType: " + tag);
654             return targets[tag];
655         }
656     }
657 }
658