2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package gov.nasa.jpf.vm;
20 import gov.nasa.jpf.JPFException;
21 import java.util.HashMap;
24 * the JPF internal representation for Java Annotations
26 * AnnotationInfos represent a separate type system. While we could have used normal ClassInfos
27 * (Java annotations are just restricted interfaces with some syntactic sugar), we keep this separate because
28 * ClassInfos would be overkill. Besides, our runtime behavior differs in that we synchronously load
29 * annotation class files when we encounter them during normal ClassInfo construction (i.e. we parse recursively),
30 * whereas a normal JVM only loads them once they are referenced. The reason why we deviate is that
31 * annotations are used more often in tools than via reflection within the SUT, i.e. they most likely will
32 * be read either by JPF or by listeners, so we want them as soon as possible to avoid additional class state.
33 * This also means we do not faithfully model ClassNotFoundExceptions on annotations due to reflection
34 * calls within the SUT, but that seems less important than having them available during ClassInfo construction.
35 * This mostly matters because of default values and inherited class annotations.
37 * AnnotationInfo serves as the concrete type of declaration annotations, and as the base for
38 * type annotations, holding all the info that comes from the annotation class file. In the first
39 * case, AnnotationInfo instances can be shared if there are no explicit values. Sharing does not
40 * make sense for type annotations which need to store site specific target info (from the classfile).
42 * Note - AnnotationInfos loaded by the same ClassLoader that do not have explicitly set values are shared
43 * between annotated objects
46 public class AnnotationInfo implements Cloneable {
48 // NOTE - never modify an Entry object since it might be shared between
49 // different instances of the same annotation type
50 public static class Entry implements Cloneable {
54 public String getKey() {
58 public Object getValue() {
62 public Entry (String key, Object value){
70 return (Entry) super.clone();
71 } catch (CloneNotSupportedException cnsx){
72 throw new JPFException("AnnotationInfo.Entry clone() failed");
77 public static class EnumValue {
81 EnumValue (String clsName, String constName){
85 public String getEnumClassName(){
88 public String getEnumConstName(){
92 public String toString(){
93 return eClassName + '.' + eConst;
97 public static class ClassValue {
100 ClassValue (String cn){
104 public String getName(){
108 public String toString(){
113 static final Entry[] NONE = new Entry[0];
115 // we have to jump through a lot of hoops to handle default annotation parameter values
116 // this is not ideal, since it causes the classfile to be re-read if the SUT
117 // uses annotation reflection (which creates a ClassInfo), but this is rather
118 // exotic, so we save some time by not creating a ClassInfo (which would hold
119 // the default vals as method annotations) and directly store the default values here
121 static HashMap<String, AnnotationAttribute> annotationAttributes = new HashMap<String, AnnotationAttribute>();
123 public static class AnnotationAttribute {
124 Entry[] defaultEntries;
127 AnnotationAttribute (Entry[] defaultEntries, boolean isInherited) {
128 this.defaultEntries = defaultEntries;
129 this.isInherited = isInherited;
133 public static Object getEnumValue(String eType, String eConst){
134 return new EnumValue( Types.getClassNameFromTypeName(eType), eConst);
137 public static Object getClassValue(String type){
138 return new ClassValue( Types.getClassNameFromTypeName(type));
141 protected String name;
142 protected Entry[] entries;
143 protected boolean isInherited = false;
146 * this records if the associated class file has been loaded. If it isn't resolved yet,
147 * we don't know about default values, hence we need to check before retrieving field values
148 * that have not been explicitly set. Note this is search global and hence does not need to
149 * be state managed since we only check for default values, i.e. there are no side effects.
150 * Loading has to happen with the right ClassLoader though
152 protected ClassLoaderInfo classLoader; // set once it is resolved (i.e. the corresponding classfile is read)
155 public AnnotationInfo (String name, ClassLoaderInfo classLoader, AnnotationParser parser) throws ClassParseException {
157 this.classLoader = classLoader;
163 * this is the base ctor for AbstractTypeAnnotationInfos, which add additional
164 * target information from the classfile
166 protected AnnotationInfo (AnnotationInfo exemplar){
167 this.name = exemplar.name;
168 this.classLoader = exemplar.classLoader;
169 this.entries = exemplar.entries;
170 this.isInherited = exemplar.isInherited;
173 //--- the init API used by AnnotationParsers
174 public void setName (String name) throws ClassParseException {
175 if (!this.name.equals(name)){
176 throw new ClassParseException("wrong annotation name in classfile, expected " + this.name + ", found " + name);
180 public void setEntries (Entry[] entries){
181 this.entries = entries;
184 public void setInherited (boolean isInherited){
185 this.isInherited = isInherited;
189 public AnnotationInfo (String name, Entry[] entries, boolean isInherited){
191 this.entries = entries;
192 this.isInherited = isInherited;
196 public boolean isInherited (){
197 return this.isInherited;
200 public ClassLoaderInfo getClassLoaderInfo(){
204 public String getName() {
208 protected AnnotationInfo cloneFor (ClassLoaderInfo cl){
210 AnnotationInfo ai = (AnnotationInfo) clone();
212 // <2do> once we support class/enum values we have to clone these too
218 } catch (CloneNotSupportedException cnsx){
219 throw new JPFException("AnnotationInfo cloneFor() failed");
224 * this returns a clone that can be used to explicitly set values.
225 * NOTE - Entry instances are still shared, i.e. to change values we have to create and set
226 * new Entry instances
228 public AnnotationInfo cloneForOverriddenValues(){
230 AnnotationInfo ai = (AnnotationInfo) clone();
231 ai.entries = entries.clone();
234 } catch (CloneNotSupportedException cnsx){
235 throw new JPFException("AnnotationInfo cloneFor() failed");
239 public void setClonedEntryValue (String key, Object newValue){
240 for (int i=0; i<entries.length; i++){
241 if (entries[i].getKey().equals(key)){
242 entries[i] = new Entry( key, newValue);
248 public Entry[] getEntries() {
253 * this is the common getter that should trigger parsing the corresponding class file
255 public Object getValue (String key){
256 for (int i=0; i<entries.length; i++){
257 if (entries[i].getKey().equals(key)){
258 return entries[i].getValue();
265 // convenience method for single-attribute annotations
266 public Object value() {
267 return getValue("value");
270 public String valueAsString(){
272 return (v != null) ? v.toString() : null;
275 public String getValueAsString (String key){
276 Object v = getValue(key);
277 return (v != null) ? v.toString() : null;
280 public String[] getValueAsStringArray() {
283 if (v != null && v instanceof Object[]) {
284 Object[] va = (Object[])v;
285 a = new String[va.length];
286 for (int i=0; i<a.length; i++) {
288 a[i] = va[i].toString();
296 public String[] getValueAsStringArray (String key) {
297 // <2do> not very efficient
299 Object v = getValue(key);
300 if (v != null && v instanceof Object[]) {
301 Object[] va = (Object[])v;
302 a = new String[va.length];
303 for (int i=0; i<a.length; i++) {
305 a[i] = va[i].toString();
313 public <T> T getValue (String key, Class<T> type){
314 Object v = getValue(key);
315 if (type.isInstance(v)){
322 public boolean getValueAsBoolean (String key){
323 Object v = getValue(key);
324 if (v instanceof Boolean){
325 return ((Boolean)v).booleanValue();
327 throw new JPFException("annotation element @" + name + '.' + key + "() not a boolean: " + v);
331 public int getValueAsInt (String key){
332 Object v = getValue(key);
333 if (v instanceof Integer){
334 return ((Integer)v).intValue();
336 throw new JPFException("annotation element @" + name + '.' + key + "() not an int: " + v);
340 public long getValueAsLong (String key){
341 Object v = getValue(key);
342 if (v instanceof Long){
343 return ((Long)v).longValue();
345 throw new JPFException("annotation element @" + name + '.' + key + "() not a long: " + v);
349 public float getValueAsFloat (String key){
350 Object v = getValue(key);
351 if (v instanceof Float){
352 return ((Float)v).floatValue();
354 throw new JPFException("annotation element @" + name + '.' + key + "() not a float: " + v);
358 public double getValueAsDouble (String key){
359 Object v = getValue(key);
360 if (v instanceof Double){
361 return ((Double)v).doubleValue();
363 throw new JPFException("annotation element @" + name + '.' + key + "() not a double: " + v);
367 public String asString() {
368 StringBuilder sb = new StringBuilder();
372 for (int i=0; i<entries.length; i++){
376 sb.append(entries[i].getKey());
378 sb.append(entries[i].getValue());
382 return sb.toString();