001package daikon.tools.jtb; 002 003import java.io.StringWriter; 004import java.util.ArrayDeque; 005import java.util.ArrayList; 006import java.util.Deque; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Map; 010import jtb.syntaxtree.*; 011import jtb.visitor.*; 012import org.checkerframework.checker.nullness.qual.KeyFor; 013 014/** Replaces uses of generic type parameters with versions that do not use generics. */ 015public class ClassOrInterfaceTypeDecorateVisitor extends DepthFirstVisitor { 016 017 // A map from token images to "ungenerified" versions of the classes 018 // or interfaces that the given identifiers extend. 019 HashMap<String, Deque<ClassOrInterfaceType>> shadowingMap = new HashMap<>(); 020 021 // For debugging purposes. 022 @SuppressWarnings("UnusedMethod") // debugging code is currently commented out 023 private void printShadowingMap() { 024 System.out.println("Shadowing map:"); 025 for (Map.Entry<@KeyFor("shadowingMap") String, Deque<ClassOrInterfaceType>> e : 026 shadowingMap.entrySet()) { 027 System.out.print(" " + e.getKey() + " stack: "); 028 for (ClassOrInterfaceType t : e.getValue()) { 029 StringWriter w = new StringWriter(); 030 t.accept(new TreeFormatter()); 031 t.accept(new TreeDumper(w)); 032 System.out.print(w.toString().trim() + " "); 033 } 034 System.out.println(); 035 } 036 } 037 038 // f0 -> [ TypeParameters() ] 039 // f1 -> ResultType() 040 // f2 -> MethodDeclarator() 041 // f3 -> [ "throws" NameList() ] 042 // f4 -> ( Block() | ";" ) 043 @Override 044 public void visit(MethodDeclaration n) { 045 046 // A shallow clone, which is what we want. 047 HashMap<String, Deque<ClassOrInterfaceType>> oldShadowingMap = copy(shadowingMap); 048 049 if (n.f0.present()) { 050 augmentShadowingMap((TypeParameters) n.f0.node); 051 } 052 n.f1.accept(this); 053 n.f2.accept(this); 054 n.f3.accept(this); 055 n.f4.accept(this); 056 057 // Restore shadowing map because we're going out of scope from 058 // the TypeParameters declared in this method. 059 shadowingMap = oldShadowingMap; 060 // printShadowingMap(); 061 } 062 063 // f0 -> [ TypeParameters() ] 064 // f1 -> <IDENTIFIER> 065 // f2 -> FormalParameters() 066 // f3 -> [ "throws" NameList() ] 067 // f4 -> "{" 068 // f5 -> [ ExplicitConstructorInvocation() ] 069 // f6 -> ( BlockStatement() )* 070 // f7 -> "}" 071 @Override 072 public void visit(ConstructorDeclaration n) { 073 074 // A shallow clone, which is what we want. 075 HashMap<String, Deque<ClassOrInterfaceType>> oldShadowingMap = copy(shadowingMap); 076 077 if (n.f0.present()) { 078 augmentShadowingMap((TypeParameters) n.f0.node); 079 } 080 n.f1.accept(this); 081 n.f2.accept(this); 082 n.f3.accept(this); 083 n.f4.accept(this); 084 n.f5.accept(this); 085 n.f6.accept(this); 086 n.f7.accept(this); 087 088 // Restore shadowing map because we're going out of scope from 089 // the TypeParameters declared in this method. 090 shadowingMap = oldShadowingMap; 091 // printShadowingMap(); 092 } 093 094 // f0 -> ( "class" | "interface" ) 095 // f1 -> <IDENTIFIER> 096 // f2 -> [ TypeParameters() ] 097 // f3 -> [ ExtendsList(isInterface) ] 098 // f4 -> [ ImplementsList(isInterface) ] 099 // f5 -> ClassOrInterfaceBody(isInterface) 100 @Override 101 public void visit(ClassOrInterfaceDeclaration n) { 102 103 // A shallow clone, which is what we want. 104 HashMap<String, Deque<ClassOrInterfaceType>> oldShadowingMap = copy(shadowingMap); 105 106 n.f0.accept(this); 107 n.f1.accept(this); 108 109 if (n.f2.present()) { 110 augmentShadowingMap((TypeParameters) n.f2.node); 111 } 112 113 n.f2.accept(this); 114 n.f3.accept(this); 115 n.f4.accept(this); 116 n.f5.accept(this); 117 118 // Restore shadowing map because we're going out of scope from 119 // the TypeParameters declared in this method. 120 shadowingMap = oldShadowingMap; 121 // printShadowingMap(); 122 } 123 124 public void augmentShadowingMap(TypeParameters n) { 125 // Grammar production: 126 // f0 -> "<" 127 // f1 -> TypeParameter() 128 // f2 -> ( "," TypeParameter() )* 129 // f3 -> ">" 130 final List<TypeParameter> params = new ArrayList<>(); 131 n.accept( 132 new DepthFirstVisitor() { 133 @Override 134 public void visit(TypeParameter n) { 135 params.add(n); 136 } 137 }); 138 139 for (TypeParameter t : params) { 140 augmentShadowingMap(t); 141 } 142 } 143 144 // f0 -> <IDENTIFIER> 145 // f1 -> [ TypeBound() ] 146 public void augmentShadowingMap(TypeParameter n) { 147 n.f0.accept(this); 148 n.f1.accept(this); 149 TypeBound b = (TypeBound) n.f1.node; 150 151 if (n.f1.present()) { 152 153 // Grammar production for TypeBound: 154 // f0 -> "extends" 155 // f1 -> ClassOrInterfaceType() 156 // f2 -> ( "&" ClassOrInterfaceType() )* 157 158 // TODO figure out how/whether to handle f2 (currently it's just ignored). 159 160 assert b.f1.unGenerifiedVersionOfThis != null; 161 162 Deque<ClassOrInterfaceType> s = 163 shadowingMap.computeIfAbsent( 164 n.f0.tokenImage, __ -> new ArrayDeque<ClassOrInterfaceType>()); 165 s.push(b.f1.unGenerifiedVersionOfThis); 166 167 } else { 168 169 // No explicit bound means that bound is java.lang.Object. 170 171 Deque<ClassOrInterfaceType> s = 172 shadowingMap.computeIfAbsent( 173 n.f0.tokenImage, __ -> new ArrayDeque<ClassOrInterfaceType>()); 174 175 ClassOrInterfaceType objectType = 176 (ClassOrInterfaceType) Ast.create("ClassOrInterfaceType", "Object"); 177 s.push(objectType); 178 } 179 180 // printShadowingMap(); 181 } 182 183 // ClassOrInterfaceType: 184 // f0 -> <IDENTIFIER> 185 // f1 -> [ TypeArguments() ] 186 // f2 -> ( "." <IDENTIFIER> [ TypeArguments() ] )* 187 @Override 188 public void visit(ClassOrInterfaceType t) { 189 t.f0.accept(this); 190 t.f1.accept(this); // NO NEED TO DO THIS? 191 t.f2.accept(this); 192 193 // Make a copy of the ClassOrInterfaceType. 194 StringWriter w = new StringWriter(); 195 // t.accept(new TreeFormatter()); 196 t.accept(new TreeDumper(w)); 197 ClassOrInterfaceType n = 198 (ClassOrInterfaceType) Ast.create("ClassOrInterfaceType", w.toString()); 199 200 ungenerify(n); 201 202 t.unGenerifiedVersionOfThis = n; 203 } 204 205 // ClassOrInterfaceType: 206 // f0 -> <IDENTIFIER> 207 // f1 -> [ TypeArguments() ] 208 // f2 -> ( "." <IDENTIFIER> [ TypeArguments() ] )* 209 @SuppressWarnings("JdkObsolete") // JTB uses Vector 210 private void ungenerify(ClassOrInterfaceType n) { 211 212 // Drop all type arguments. 213 n.f1 = new NodeOptional(); // This removes optional node, if present. 214 List<Node> nodeSequenceList = n.f2.nodes; 215 for (int i = 0; i < nodeSequenceList.size(); i++) { 216 NodeSequence oldSequence = (NodeSequence) nodeSequenceList.get(i); 217 NodeSequence newSequence = new NodeSequence(3); 218 newSequence.addNode(oldSequence.elementAt(0)); // "." 219 newSequence.addNode(oldSequence.elementAt(1)); // <IDENTIFIER> 220 newSequence.addNode(new NodeOptional()); // get rid of type arguments 221 n.f2.nodes.set(i, newSequence); 222 } 223 224 // 2. Only the first <IDENTIFIER> may possibly be associated 225 // with a type argument. If we find it in typeParametersInScope, 226 // we replace t with [...] 227 for (Map.Entry<@KeyFor("shadowingMap") String, Deque<ClassOrInterfaceType>> entry : 228 shadowingMap.entrySet()) { 229 if (entry.getKey().equals(n.f0.tokenImage)) { 230 ClassOrInterfaceType c = entry.getValue().getFirst(); 231 // System.out.println("c:" + Ast.format(c)); 232 List<Node> cSequence = c.f2.nodes; 233 // System.out.print("cSequence:"); 234 // for (Node n2 : cSequence) { 235 // System.out.print(Ast.format(n2) + " "); 236 // } 237 // Prepend all-but-first identifiers to the list of identifiers in f2. 238 // Prepending in reverse order ensures the right prepending order. 239 for (int i = cSequence.size() - 1; i >= 0; i--) { 240 nodeSequenceList.add(0, cSequence.get(i)); 241 } 242 // Set f0 to the first identifier. 243 n.f0 = c.f0; 244 } 245 } 246 247 { 248 // StringWriter sw = new StringWriter(); 249 // n.accept(new TreeFormatter()); 250 // t.accept(new TreeDumper(sw)); 251 // System.out.print("t::::"); 252 // System.out.println(sw.toString().trim()); 253 } 254 { 255 // StringWriter sw = new StringWriter(); 256 // n.accept(new TreeFormatter()); 257 // n.accept(new TreeDumper(sw)); 258 // System.out.print("n::::"); 259 // System.out.println(sw.toString().trim()); 260 } 261 } 262 263 // Makes a copy of the stacks and of the map. The 264 // ClassOrInterfaceType objects are not copied. 265 private static HashMap<String, Deque<ClassOrInterfaceType>> copy( 266 HashMap<String, Deque<ClassOrInterfaceType>> m) { 267 268 HashMap<String, Deque<ClassOrInterfaceType>> newMap = new HashMap<>(); 269 270 for (Map.Entry<@KeyFor("m") String, Deque<ClassOrInterfaceType>> e : m.entrySet()) { 271 String key = e.getKey(); 272 Deque<ClassOrInterfaceType> oldStack = e.getValue(); 273 Deque<ClassOrInterfaceType> newStack = 274 new ArrayDeque<ClassOrInterfaceType>(oldStack); // clone 275 newMap.put(key, newStack); 276 } 277 278 return newMap; 279 } 280}