TypeAnnotation.java revision 2942:08092deced3f
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            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<>(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
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
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
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
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
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
321            public String toString() {
322                return tag.toString() +
323                        (tag == TypePathEntryKind.TYPE_ARGUMENT ? ("(" + arg + ")") : "");
324            }
325
326            @Override
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
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<>(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
371        public Position() {}
372
373        @Override
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         */
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         */
500        public static List<TypePathEntry> getTypePathFromBinary(List<Integer> list) {
501            List<TypePathEntry> loc = new ArrayList<>(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
513        public static List<Integer> getBinaryFromTypePath(List<TypePathEntry> locs) {
514            List<Integer> loc = new ArrayList<>(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
600        private TargetType(int targetTypeValue) {
601            this(targetTypeValue, false);
602        }
603
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         */
619        public boolean isLocal() {
620            return isLocal;
621        }
622
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
642        public static boolean isValidTargetTypeValue(int tag) {
643            if (tag == UNKNOWN.targetTypeValue)
644                return true;
645            return (tag >= 0 && tag < targets.length);
646        }
647
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