Tue, 14 May 2013 15:04:06 -0700
8013852: update reference impl for type-annotations
Reviewed-by: jjg
Contributed-by: wdietl@gmail.com, steve.sides@oracle.com, joel.franck@oracle.com, alex.buckley@oracle.com
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 */
26 package com.sun.tools.classfile;
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.List;
32 import com.sun.tools.classfile.TypeAnnotation.Position.TypePathEntry;
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 {
43 TypeAnnotation(ClassReader cr) throws IOException, Annotation.InvalidAnnotation {
44 constant_pool = cr.getConstantPool();
45 position = read_position(cr);
46 annotation = new Annotation(cr);
47 }
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 }
56 public int length() {
57 int n = annotation.length();
58 n += position_length(position);
59 return n;
60 }
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 }
73 public final ConstantPool constant_pool;
74 public final Position position;
75 public final Annotation annotation;
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));
83 TargetType type = TargetType.fromTargetTypeValue(tag);
85 Position position = new Position();
86 position.type = type;
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 }
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 }
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 }
250 {
251 n += 1; // length
252 n += TypePathEntry.bytesPerEntry * pos.location.size(); // bytes for actual array
253 }
255 return n;
256 }
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);
266 public final int tag;
268 private TypePathEntryKind(int tag) {
269 this.tag = tag;
270 }
271 }
273 public static class TypePathEntry {
274 /** The fixed number of bytes per TypePathEntry. */
275 public static final int bytesPerEntry = 2;
277 public final TypePathEntryKind tag;
278 public final int arg;
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);
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 }
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 }
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 }
320 @Override
321 public String toString() {
322 return tag.toString() +
323 (tag == TypePathEntryKind.TYPE_ARGUMENT ? ("(" + arg + ")") : "");
324 }
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 }
335 @Override
336 public int hashCode() {
337 return this.tag.hashCode() * 17 + this.arg;
338 }
339 }
341 public TargetType type = TargetType.UNKNOWN;
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);
347 // Tree position.
348 public int pos = -1;
350 // For typecasts, type tests, new (and locals, as start_pc).
351 public boolean isValidOffset = false;
352 public int offset = -1;
354 // For locals. arrays same length
355 public int[] lvarOffset = null;
356 public int[] lvarLength = null;
357 public int[] lvarIndex = null;
359 // For type parameter bound
360 public int bound_index = Integer.MIN_VALUE;
362 // For type parameter and method parameter
363 public int parameter_index = Integer.MIN_VALUE;
365 // For class extends, implements, and throws clauses
366 public int type_index = Integer.MIN_VALUE;
368 // For exception parameters, index into exception table
369 public int exception_index = Integer.MIN_VALUE;
371 public Position() {}
373 @Override
374 public String toString() {
375 StringBuilder sb = new StringBuilder();
376 sb.append('[');
377 sb.append(type);
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 }
471 // Append location data for generics/arrays.
472 if (!location.isEmpty()) {
473 sb.append(", location = (");
474 sb.append(location);
475 sb.append(")");
476 }
478 sb.append(", pos = ");
479 sb.append(pos);
481 sb.append(']');
482 return sb.toString();
483 }
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 }
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<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 }
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 }
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),
529 /** For annotations on a method type parameter declaration. */
530 METHOD_TYPE_PARAMETER(0x01),
532 /** For annotations on the type of an "extends" or "implements" clause. */
533 CLASS_EXTENDS(0x10),
535 /** For annotations on a bound of a type parameter of a class. */
536 CLASS_TYPE_PARAMETER_BOUND(0x11),
538 /** For annotations on a bound of a type parameter of a method. */
539 METHOD_TYPE_PARAMETER_BOUND(0x12),
541 /** For annotations on a field. */
542 FIELD(0x13),
544 /** For annotations on a method return type. */
545 METHOD_RETURN(0x14),
547 /** For annotations on the method receiver. */
548 METHOD_RECEIVER(0x15),
550 /** For annotations on a method parameter. */
551 METHOD_FORMAL_PARAMETER(0x16),
553 /** For annotations on a throws clause in a method declaration. */
554 THROWS(0x17),
556 /** For annotations on a local variable. */
557 LOCAL_VARIABLE(0x40, true),
559 /** For annotations on a resource variable. */
560 RESOURCE_VARIABLE(0x41, true),
562 /** For annotations on an exception parameter. */
563 EXCEPTION_PARAMETER(0x42, true),
565 /** For annotations on a type test. */
566 INSTANCEOF(0x43, true),
568 /** For annotations on an object creation expression. */
569 NEW(0x44, true),
571 /** For annotations on a constructor reference receiver. */
572 CONSTRUCTOR_REFERENCE(0x45, true),
574 /** For annotations on a method reference receiver. */
575 METHOD_REFERENCE(0x46, true),
577 /** For annotations on a typecast. */
578 CAST(0x47, true),
580 /** For annotations on a type argument of an object creation expression. */
581 CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT(0x48, true),
583 /** For annotations on a type argument of a method call. */
584 METHOD_INVOCATION_TYPE_ARGUMENT(0x49, true),
586 /** For annotations on a type argument of a constructor reference. */
587 CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT(0x4A, true),
589 /** For annotations on a type argument of a method reference. */
590 METHOD_REFERENCE_TYPE_ARGUMENT(0x4B, true),
592 /** For annotations with an unknown target. */
593 UNKNOWN(0xFF);
595 private static final int MAXIMUM_TARGET_TYPE_VALUE = 0x4B;
597 private final int targetTypeValue;
598 private final boolean isLocal;
600 private TargetType(int targetTypeValue) {
601 this(targetTypeValue, false);
602 }
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 }
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 }
623 public int targetTypeValue() {
624 return this.targetTypeValue;
625 }
627 private static final TargetType[] targets;
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 }
642 public static boolean isValidTargetTypeValue(int tag) {
643 if (tag == UNKNOWN.targetTypeValue)
644 return true;
645 return (tag >= 0 && tag < targets.length);
646 }
648 public static TargetType fromTargetTypeValue(int tag) {
649 if (tag == UNKNOWN.targetTypeValue)
650 return UNKNOWN;
652 if (tag < 0 || tag >= targets.length)
653 throw new AssertionError("Unknown TargetType: " + tag);
654 return targets[tag];
655 }
656 }
657 }