TypeAnnotation.java revision 3666:90dd93e668a5
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
26package com.sun.tools.classfile;
27
28import java.io.IOException;
29import java.util.ArrayList;
30import java.util.List;
31
32import 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 */
42public class TypeAnnotation {
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
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
56    public int length() {
57        int n = annotation.length();
58        n += position_length(position);
59        return n;
60    }
61
62    @Override
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
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
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
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
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
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
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
318            public String toString() {
319                return tag.toString() +
320                        (tag == TypePathEntryKind.TYPE_ARGUMENT ? ("(" + arg + ")") : "");
321            }
322
323            @Override
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
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
368        public Position() {}
369
370        @Override
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         */
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         */
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
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
597        private TargetType(int targetTypeValue) {
598            this(targetTypeValue, false);
599        }
600
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         */
616        public boolean isLocal() {
617            return isLocal;
618        }
619
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
639        public static boolean isValidTargetTypeValue(int tag) {
640            if (tag == UNKNOWN.targetTypeValue)
641                return true;
642            return (tag >= 0 && tag < targets.length);
643        }
644
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