1 package annotations.el; 2 3 import java.util.LinkedHashMap; 4 import java.util.LinkedHashSet; 5 import java.util.Map; 6 import java.util.Set; 7 8 import annotations.Annotation; 9 import annotations.io.IndexFileParser; 10 import annotations.util.coll.VivifyingMap; 11 12 /*>>> 13 import org.checkerframework.checker.nullness.qual.Nullable; 14 */ 15 16 /** 17 * An <code>AScene</code> (annotated scene) represents the annotations on a 18 * set of Java classes and packages along with the definitions of some or all of 19 * the annotation types used. 20 * 21 * <p> 22 * Each client of the annotation library may wish to use its own representation 23 * for certain kinds of annotations instead of a simple name-value map; thus, a 24 * layer of abstraction in the storage of annotations was introduced. 25 * 26 * <p> 27 * <code>AScene</code>s and many {@link AElement}s can contain other 28 * {@link AElement}s. When these objects are created, their collections of 29 * subelements are empty. In order to associate an annotation with a particular 30 * Java element in an <code>AScene</code>, one must first ensure that an 31 * appropriate {@link AElement} exists in the <code>AScene</code>. To this 32 * end, the maps of subelements have a <code>vivify</code> method. Calling 33 * <code>vivify</code> to access a particular subelement will return the 34 * subelement if it already exists; otherwise it will create and then return the 35 * subelement. (Compare to vivification in Perl.) For example, the following 36 * code will obtain an {@link AMethod} representing <code>Foo.bar</code> in 37 * the <code>AScene</code> <code>s</code>, creating it if it did not 38 * already exist: 39 * 40 * <pre> 41 * AMethod<A> m = s.classes.vivify("Foo").methods.vivify("bar"); 42 * </pre> 43 * 44 * <p> 45 * Then one can add an annotation to the method: 46 * 47 * <pre> 48 * m.annotationsHere.add(new Annotation( 49 * new AnnotationDef(taintedDef, RetentionPolicy.RUNTIME, true), 50 * new Annotation(taintedDef, Collections.emptyMap()) 51 * )); 52 * </pre> 53 */ 54 public final class AScene implements Cloneable { 55 private static boolean checkClones = true; 56 public static boolean debugFoundMap = false; 57 58 /** This scene's annotated packages; map key is package name */ 59 public final VivifyingMap<String, AElement> packages = 60 AElement.<String>newVivifyingLHMap_AE(); 61 62 /** 63 * Contains for each annotation type a set of imports to be added to 64 * the source if the annotation is inserted with the "abbreviate" 65 * option on. 66 */ 67 public final Map<String, Set<String>> imports = 68 new LinkedHashMap<String, Set<String>>(); 69 70 /** This scene's annotated classes; map key is class name */ 71 public final VivifyingMap<String, AClass> classes = 72 new VivifyingMap<String, AClass>( 73 new LinkedHashMap<String, AClass>()) { 74 @Override 75 public AClass createValueFor( 76 String k) { 77 return new AClass(k); 78 } 79 80 @Override 81 public boolean subPrune(AClass v) { 82 return v.prune(); 83 } 84 }; 85 86 /** 87 * Creates a new {@link AScene} with no classes or packages. 88 */ AScene()89 public AScene() { 90 } 91 92 /** 93 * Copy constructor for {@link AScene}. 94 */ AScene(AScene scene)95 public AScene(AScene scene) { 96 for (String key : scene.packages.keySet()) { 97 AElement val = scene.packages.get(key); 98 packages.put(key, val.clone()); 99 } 100 for (String key : scene.imports.keySet()) { 101 // copy could in principle have different Set implementation 102 Set<String> value = scene.imports.get(key); 103 Set<String> copy = new LinkedHashSet<String>(); 104 copy.addAll(value); 105 imports.put(key, copy); 106 } 107 for (String key : scene.classes.keySet()) { 108 AClass clazz = scene.classes.get(key); 109 classes.put(key, clazz.clone()); 110 } 111 if (checkClones) { 112 checkClone(this, scene); 113 } 114 } 115 116 @Override clone()117 public AScene clone() { 118 return new AScene(this); 119 } 120 121 /** 122 * Returns whether this {@link AScene} equals <code>o</code>; the 123 * commentary and the cautionary remarks on {@link AElement#equals(Object)} 124 * also apply to {@link AScene#equals(Object)}. 125 */ 126 @Override equals(Object o)127 public boolean equals(Object o) { 128 return o instanceof AScene 129 && ((AScene) o).equals(this); 130 } 131 132 /** 133 * Returns whether this {@link AScene} equals <code>o</code>; a 134 * slightly faster variant of {@link #equals(Object)} for when the argument 135 * is statically known to be another nonnull {@link AScene}. 136 */ equals(AScene o)137 public boolean equals(AScene o) { 138 return o.classes.equals(classes) && o.packages.equals(packages); 139 } 140 141 /** 142 * {@inheritDoc} 143 */ 144 @Override hashCode()145 public int hashCode() { 146 return classes.hashCode() + packages.hashCode(); 147 } 148 149 /** 150 * Removes empty subelements of this {@link AScene} depth-first; returns 151 * whether this {@link AScene} is itself empty after pruning. 152 */ prune()153 public boolean prune() { 154 return classes.prune() & packages.prune(); 155 } 156 157 /** Returns a string representation. */ unparse()158 public String unparse() { 159 StringBuilder sb = new StringBuilder(); 160 sb.append("packages:\n"); 161 for (Map.Entry<String, AElement> entry : packages.entrySet()) { 162 sb.append(" " + entry.getKey() + " => " + entry.getValue() + "\n"); 163 } 164 sb.append("classes:\n"); 165 for (Map.Entry<String, AClass> entry : classes.entrySet()) { 166 sb.append(" " + entry.getKey() + " => " + "\n"); 167 sb.append(entry.getValue().unparse(" ")); 168 } 169 return sb.toString(); 170 } 171 172 @Override toString()173 public String toString() { 174 return unparse(); 175 } 176 177 /** 178 * Throws exception if the arguments 1) are the same reference; 179 * 2) are not equal() in both directions; or 3) contain 180 * corresponding elements that meet either of the preceding two 181 * conditions. 182 */ checkClone(AScene s0, AScene s1)183 public static void checkClone(AScene s0, AScene s1) { 184 if (s0 == null) { 185 if (s1 != null) { 186 cloneCheckFail(); 187 } 188 } else { 189 if (s1 == null) { 190 cloneCheckFail(); 191 } 192 s0.prune(); 193 s1.prune(); 194 if (s0 == s1) { 195 cloneCheckFail(); 196 } 197 checkElems(s0.packages, s1.packages); 198 checkElems(s0.classes, s1.classes); 199 } 200 } 201 202 public static <K, V extends AElement> void checkElems(VivifyingMap<K, V> m0, VivifyingMap<K, V> m1)203 checkElems(VivifyingMap<K, V> m0, VivifyingMap<K, V> m1) { 204 if (m0 == null) { 205 if (m1 != null) { 206 cloneCheckFail(); 207 } 208 } else if (m1 == null) { 209 cloneCheckFail(); 210 } else { 211 for (K k : m0.keySet()) { 212 checkElem(m0.get(k), m1.get(k)); 213 } 214 } 215 } 216 217 /** 218 * Throw exception on visit if e0 == e1 or !e0.equals(e1). 219 * (See {@link #checkClone(AScene, AScene)} for explanation.) 220 */ checkElem(AElement e0, AElement e1)221 public static void checkElem(AElement e0, AElement e1) { 222 checkObject(e0, e1); 223 if (e0 != null) { 224 if (e0 == e1) { 225 cloneCheckFail(); 226 } 227 e0.accept(checkVisitor, e1); 228 } 229 } 230 231 /** 232 * Throw exception on visit if !el.equals(arg) or !arg.equals(el). 233 * (See {@link #checkClone(AScene, AScene)} for explanation.) 234 */ checkObject(Object o0, Object o1)235 public static void checkObject(Object o0, Object o1) { 236 if (o0 == null ? o1 != null 237 : !(o0.equals(o1) && o1.equals(o0))) { // ok if == 238 throw new RuntimeException("clone check failed"); 239 } 240 } 241 242 /** 243 * Throw exception on visit if el == arg or !el.equals(arg). 244 * (See {@link checkClone(AScene, AScene)} for explanation.) 245 */ 246 private static ElementVisitor<Void, AElement> checkVisitor = 247 new ElementVisitor<Void, AElement>() { 248 @Override 249 public Void visitAnnotationDef(AnnotationDef el, 250 AElement arg) { 251 return null; 252 } 253 254 @Override 255 public Void visitBlock(ABlock el, AElement arg) { 256 ABlock b = (ABlock) arg; 257 checkElems(el.locals, b.locals); 258 return null; 259 } 260 261 @Override 262 public Void visitClass(AClass el, AElement arg) { 263 AClass c = (AClass) arg; 264 checkElems(el.bounds, c.bounds); 265 checkElems(el.extendsImplements, c.extendsImplements); 266 checkElems(el.fieldInits, c.fieldInits); 267 checkElems(el.fields, c.fields); 268 checkElems(el.instanceInits, c.instanceInits); 269 checkElems(el.methods, c.methods); 270 checkElems(el.staticInits, c.staticInits); 271 return visitDeclaration(el, arg); 272 } 273 274 @Override 275 public Void visitDeclaration(ADeclaration el, AElement arg) { 276 ADeclaration d = (ADeclaration) arg; 277 checkElems(el.insertAnnotations, d.insertAnnotations); 278 checkElems(el.insertTypecasts, d.insertTypecasts); 279 return visitElement(el, arg); 280 } 281 282 @Override 283 public Void visitExpression(AExpression el, AElement arg) { 284 AExpression e = (AExpression) arg; 285 checkObject(el.id, e.id); 286 checkElems(el.calls, e.calls); 287 checkElems(el.funs, e.funs); 288 checkElems(el.instanceofs, e.instanceofs); 289 checkElems(el.news, e.news); 290 checkElems(el.refs, e.refs); 291 checkElems(el.typecasts, e.typecasts); 292 return visitElement(el, arg); 293 } 294 295 @Override 296 public Void visitField(AField el, AElement arg) { 297 AField f = (AField) arg; 298 checkElem(el.init, f.init); 299 return visitDeclaration(el, arg); 300 } 301 302 @Override 303 public Void visitMethod(AMethod el, AElement arg) { 304 AMethod m = (AMethod) arg; 305 checkObject(el.methodName, m.methodName); 306 checkElem(el.body, m.body); 307 checkElem(el.returnType, m.returnType); 308 checkElems(el.bounds, m.bounds); 309 checkElems(el.parameters, m.parameters); 310 checkElems(el.throwsException, m.throwsException); 311 return null; 312 } 313 314 @Override 315 public Void visitTypeElement(ATypeElement el, AElement arg) { 316 ATypeElement t = (ATypeElement) arg; 317 checkObject(el.description, t.description); 318 checkElems(el.innerTypes, t.innerTypes); 319 return null; 320 } 321 322 @Override 323 public Void visitTypeElementWithType(ATypeElementWithType el, 324 AElement arg) { 325 ATypeElementWithType t = (ATypeElementWithType) arg; 326 checkObject(el.getType(), t.getType()); 327 return visitTypeElement(el, arg); 328 } 329 330 @Override 331 public Void visitElement(AElement el, AElement arg) { 332 checkObject(el.description, arg.description); 333 if (el.tlAnnotationsHere.size() != 334 arg.tlAnnotationsHere.size()) { 335 cloneCheckFail(); 336 } 337 for (Annotation a : el.tlAnnotationsHere) { 338 if (!arg.tlAnnotationsHere.contains(a)) { 339 cloneCheckFail(); 340 } 341 } 342 checkElem(el.type, arg.type); 343 return null; 344 } 345 }; 346 cloneCheckFail()347 private static void cloneCheckFail() { 348 throw new RuntimeException("clone check failed"); 349 } 350 351 // temporary main for easy testing on JAIFs main(String[] args)352 public static void main(String[] args) { 353 int status = 0; 354 checkClones = true; 355 356 for (int i = 0; i < args.length; i++) { 357 AScene s0 = new AScene(); 358 System.out.print(args[i] + ": "); 359 try { 360 IndexFileParser.parseFile(args[i], s0); 361 s0.clone(); 362 System.out.println("ok"); 363 } catch (Throwable e) { 364 status = 1; 365 System.out.println("failed"); 366 e.printStackTrace(); 367 } 368 } 369 System.exit(status); 370 } 371 } 372