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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.Map;
28 
29 import com.sun.tools.classfile.Attribute;
30 import com.sun.tools.classfile.ClassFile;
31 import com.sun.tools.classfile.Code_attribute;
32 import com.sun.tools.classfile.TypeAnnotation;
33 import com.sun.tools.classfile.Field;
34 import com.sun.tools.classfile.Method;
35 import com.sun.tools.classfile.RuntimeTypeAnnotations_attribute;
36 import com.sun.tools.classfile.ConstantPool.InvalidIndex;
37 import com.sun.tools.classfile.ConstantPool.UnexpectedEntry;
38 
39 public class ReferenceInfoUtil {
40 
41     public static final int IGNORE_VALUE = -321;
42 
extendedAnnotationsOf(ClassFile cf)43     public static List<TypeAnnotation> extendedAnnotationsOf(ClassFile cf) {
44         List<TypeAnnotation> annos = new ArrayList<>();
45         findAnnotations(cf, annos);
46         return annos;
47     }
48 
49     /////////////////// Extract type annotations //////////////////
findAnnotations(ClassFile cf, List<TypeAnnotation> annos)50     private static void findAnnotations(ClassFile cf, List<TypeAnnotation> annos) {
51         findAnnotations(cf, Attribute.RuntimeVisibleTypeAnnotations, annos);
52         findAnnotations(cf, Attribute.RuntimeInvisibleTypeAnnotations, annos);
53 
54         for (Field f : cf.fields) {
55             findAnnotations(cf, f, annos);
56         }
57         for (Method m: cf.methods) {
58             findAnnotations(cf, m, annos);
59         }
60     }
61 
findAnnotations(ClassFile cf, Method m, List<TypeAnnotation> annos)62     private static void findAnnotations(ClassFile cf, Method m, List<TypeAnnotation> annos) {
63         findAnnotations(cf, m, Attribute.RuntimeVisibleTypeAnnotations, annos);
64         findAnnotations(cf, m, Attribute.RuntimeInvisibleTypeAnnotations, annos);
65     }
66 
findAnnotations(ClassFile cf, Field m, List<TypeAnnotation> annos)67     private static void findAnnotations(ClassFile cf, Field m, List<TypeAnnotation> annos) {
68         findAnnotations(cf, m, Attribute.RuntimeVisibleTypeAnnotations, annos);
69         findAnnotations(cf, m, Attribute.RuntimeInvisibleTypeAnnotations, annos);
70     }
71 
72     // test the result of Attributes.getIndex according to expectations
73     // encoded in the method's name
findAnnotations(ClassFile cf, String name, List<TypeAnnotation> annos)74     private static void findAnnotations(ClassFile cf, String name, List<TypeAnnotation> annos) {
75         int index = cf.attributes.getIndex(cf.constant_pool, name);
76         if (index != -1) {
77             Attribute attr = cf.attributes.get(index);
78             assert attr instanceof RuntimeTypeAnnotations_attribute;
79             RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr;
80             annos.addAll(Arrays.asList(tAttr.annotations));
81         }
82     }
83 
84     // test the result of Attributes.getIndex according to expectations
85     // encoded in the method's name
findAnnotations(ClassFile cf, Method m, String name, List<TypeAnnotation> annos)86     private static void findAnnotations(ClassFile cf, Method m, String name, List<TypeAnnotation> annos) {
87         int index = m.attributes.getIndex(cf.constant_pool, name);
88         if (index != -1) {
89             Attribute attr = m.attributes.get(index);
90             assert attr instanceof RuntimeTypeAnnotations_attribute;
91             RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr;
92             annos.addAll(Arrays.asList(tAttr.annotations));
93         }
94 
95         int cindex = m.attributes.getIndex(cf.constant_pool, Attribute.Code);
96         if (cindex != -1) {
97             Attribute cattr = m.attributes.get(cindex);
98             assert cattr instanceof Code_attribute;
99             Code_attribute cAttr = (Code_attribute)cattr;
100             index = cAttr.attributes.getIndex(cf.constant_pool, name);
101             if (index != -1) {
102                 Attribute attr = cAttr.attributes.get(index);
103                 assert attr instanceof RuntimeTypeAnnotations_attribute;
104                 RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr;
105                 annos.addAll(Arrays.asList(tAttr.annotations));
106             }
107         }
108     }
109 
110     // test the result of Attributes.getIndex according to expectations
111     // encoded in the method's name
findAnnotations(ClassFile cf, Field m, String name, List<TypeAnnotation> annos)112     private static void findAnnotations(ClassFile cf, Field m, String name, List<TypeAnnotation> annos) {
113         int index = m.attributes.getIndex(cf.constant_pool, name);
114         if (index != -1) {
115             Attribute attr = m.attributes.get(index);
116             assert attr instanceof RuntimeTypeAnnotations_attribute;
117             RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr;
118             annos.addAll(Arrays.asList(tAttr.annotations));
119         }
120     }
121 
122     /////////////////////// Equality testing /////////////////////
areEquals(int a, int b)123     private static boolean areEquals(int a, int b) {
124         return a == b || a == IGNORE_VALUE || b == IGNORE_VALUE;
125     }
126 
areEquals(int[] a, int[] a2)127     private static boolean areEquals(int[] a, int[] a2) {
128         if (a==a2)
129             return true;
130         if (a==null || a2==null)
131             return false;
132 
133         int length = a.length;
134         if (a2.length != length)
135             return false;
136 
137         for (int i=0; i<length; i++)
138             if (a[i] != a2[i] && a[i] != IGNORE_VALUE && a2[i] != IGNORE_VALUE)
139                 return false;
140 
141         return true;
142     }
143 
areEquals(TypeAnnotation.Position p1, TypeAnnotation.Position p2)144     public static boolean areEquals(TypeAnnotation.Position p1, TypeAnnotation.Position p2) {
145         return p1 == p2 || !(p1 == null || p2 == null) &&
146                 p1.type == p2.type &&
147                 (p1.location.equals(p2.location)) &&
148                 areEquals(p1.offset, p2.offset) &&
149                 areEquals(p1.lvarOffset, p2.lvarOffset) &&
150                 areEquals(p1.lvarLength, p2.lvarLength) &&
151                 areEquals(p1.lvarIndex, p2.lvarIndex) &&
152                 areEquals(p1.bound_index, p2.bound_index) &&
153                 areEquals(p1.parameter_index, p2.parameter_index) &&
154                 areEquals(p1.type_index, p2.type_index) &&
155                 areEquals(p1.exception_index, p2.exception_index);
156 
157     }
158 
findAnnotation(String name, List<TypeAnnotation> annotations, ClassFile cf)159     private static TypeAnnotation findAnnotation(String name, List<TypeAnnotation> annotations, ClassFile cf) throws InvalidIndex, UnexpectedEntry {
160         String properName = "L" + name + ";";
161         for (TypeAnnotation anno : annotations) {
162             String actualName = cf.constant_pool.getUTF8Value(anno.annotation.type_index);
163             if (properName.equals(actualName))
164                 return anno;
165         }
166         return null;
167     }
168 
compare(Map<String, TypeAnnotation.Position> expectedAnnos, List<TypeAnnotation> actualAnnos, ClassFile cf)169     public static boolean compare(Map<String, TypeAnnotation.Position> expectedAnnos,
170             List<TypeAnnotation> actualAnnos, ClassFile cf) throws InvalidIndex, UnexpectedEntry {
171         if (actualAnnos.size() != expectedAnnos.size()) {
172             throw new ComparisionException("Wrong number of annotations",
173                     expectedAnnos,
174                     actualAnnos);
175         }
176 
177         for (Map.Entry<String, TypeAnnotation.Position> e : expectedAnnos.entrySet()) {
178             String aName = e.getKey();
179             TypeAnnotation.Position expected = e.getValue();
180             TypeAnnotation actual = findAnnotation(aName, actualAnnos, cf);
181             if (actual == null)
182                 throw new ComparisionException("Expected annotation not found: " + aName);
183 
184             if (!areEquals(expected, actual.position)) {
185                 throw new ComparisionException("Unexpected position for annotation : " + aName +
186                         "\n  Expected: " + expected.toString() +
187                         "\n  Found: " + actual.position.toString());
188             }
189         }
190         return true;
191     }
192 }
193 
194 class ComparisionException extends RuntimeException {
195     private static final long serialVersionUID = -3930499712333815821L;
196 
197     public final Map<String, TypeAnnotation.Position> expected;
198     public final List<TypeAnnotation> found;
199 
ComparisionException(String message)200     public ComparisionException(String message) {
201         this(message, null, null);
202     }
203 
ComparisionException(String message, Map<String, TypeAnnotation.Position> expected, List<TypeAnnotation> found)204     public ComparisionException(String message, Map<String, TypeAnnotation.Position> expected, List<TypeAnnotation> found) {
205         super(message);
206         this.expected = expected;
207         this.found = found;
208     }
209 
toString()210     public String toString() {
211         String str = super.toString();
212         if (expected != null && found != null) {
213             str += "\n\tExpected: " + expected.size() + " annotations; but found: " + found.size() + " annotations\n" +
214                    "  Expected: " + expected +
215                    "\n  Found: " + found;
216         }
217         return str;
218     }
219 }
220