1 /* 2 * Copyright (c) 2016, 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 package selectionresolution; 25 26 import java.io.File; 27 import java.io.FileWriter; 28 import java.util.HashMap; 29 30 /** 31 * One individual test case. This class also defines a builder, which 32 * can be used to build up cases. 33 */ 34 public class SelectionResolutionTestCase { 35 36 public enum InvokeInstruction { 37 INVOKESTATIC, 38 INVOKESPECIAL, 39 INVOKEINTERFACE, 40 INVOKEVIRTUAL; 41 } 42 43 /** 44 * The class data (includes interface data). 45 */ 46 public final HashMap<Integer, ClassData> classdata; 47 /** 48 * The hierarchy shape. 49 */ 50 public final HierarchyShape hier; 51 /** 52 * The invoke instruction to use. 53 */ 54 public final InvokeInstruction invoke; 55 /** 56 * Which class is the methodref (or interface methodref). 57 */ 58 public final int methodref; 59 /** 60 * Which class is the objectref. 61 */ 62 public final int objectref; 63 /** 64 * Which class is the callsite (this must be a class, not an interface. 65 */ 66 public final int callsite; 67 /** 68 * The expected result. 69 */ 70 public final Result result; 71 72 private SelectionResolutionTestCase(final HashMap<Integer, ClassData> classdata, 73 final HierarchyShape hier, 74 final InvokeInstruction invoke, 75 final int methodref, 76 final int objectref, 77 final int callsite, 78 final int expected) { 79 this.classdata = classdata; 80 this.hier = hier; 81 this.invoke = invoke; 82 this.methodref = methodref; 83 this.objectref = objectref; 84 this.callsite = callsite; 85 this.result = Result.is(expected); 86 } 87 88 private SelectionResolutionTestCase(final HashMap<Integer, ClassData> classdata, 89 final HierarchyShape hier, 90 final InvokeInstruction invoke, 91 final int methodref, 92 final int objectref, 93 final int callsite, 94 final Result result) { 95 this.classdata = classdata; 96 this.hier = hier; 97 this.invoke = invoke; 98 this.methodref = methodref; 99 this.objectref = objectref; 100 this.callsite = callsite; 101 this.result = result; 102 } 103 104 private static int currError = 0; 105 106 private String dumpClasses(final ClassConstruct[] classes) 107 throws Exception { 108 final String errorDirName = "error_" + currError++; 109 final File errorDir = new File(errorDirName); 110 errorDir.mkdirs(); 111 for (int i = 0; i < classes.length; i++) { 112 classes[i].writeClass(errorDir); 113 } 114 try (final FileWriter fos = 115 new FileWriter(new File(errorDir, "description.txt"))) { 116 fos.write(this.toString()); 117 } 118 return errorDirName; 119 } 120 121 /** 122 * Run this case, return an error message, or null. 123 * 124 * @return An error message, or null if the case succeeded. 125 */ 126 public String run() { 127 /* Uncomment this line to print EVERY case */ 128 //System.err.println("Running\n" + this); 129 final ClassBuilder builder = 130 new ClassBuilder(this, ClassBuilder.ExecutionMode.DIRECT); 131 try { 132 final ByteCodeClassLoader bcl = new ByteCodeClassLoader(); 133 final ClassConstruct[] classes = builder.build(); 134 135 try { 136 bcl.addClasses(classes); 137 bcl.loadAll(); 138 139 // Grab the callsite class. 140 final Class testclass = 141 bcl.findClass(builder.getCallsiteClass().getDottedName()); 142 143 // Get the 'test' method out of it and call it. The 144 // return value tess which class that got selected. 145 final java.lang.reflect.Method method = 146 testclass.getDeclaredMethod("test"); 147 final int actual = (Integer) method.invoke(null); 148 // Check the result. 149 if (!result.complyWith(actual)) { 150 final String dump = dumpClasses(classes); 151 return "Failed:\n" + this + "\nExpected " + result + " got " + actual + "\nClasses written to " + dump; 152 } 153 } catch (Throwable t) { 154 // This catch block is handling exceptions that we 155 // might expect to see. 156 final Throwable actual = t.getCause(); 157 if (actual == null) { 158 final String dump = dumpClasses(classes); 159 System.err.println("Unexpected exception in test\n" + this + "\nClasses written to " + dump); 160 throw t; 161 } else if (result == null) { 162 final String dump = dumpClasses(classes); 163 return "Failed:\n" + this + "\nUnexpected exception " + actual + "\nClasses written to " + dump; 164 } else if (!result.complyWith(actual)) { 165 final String dump = dumpClasses(classes); 166 return "Failed:\n" + this + "\nExpected " + this.result + " got " + actual + "\nClasses written to " + dump; 167 } 168 } 169 } catch(Throwable e) { 170 throw new RuntimeException(e); 171 } 172 return null; 173 } 174 175 private static void addPackage(final StringBuilder sb, 176 final ClassData cd) { 177 switch (cd.packageId) { 178 case SAME: sb.append("Same."); break; 179 case DIFFERENT: sb.append("Different."); break; 180 case OTHER: sb.append("Other."); break; 181 case PLACEHOLDER: sb.append("_."); break; 182 default: throw new RuntimeException("Impossible case"); 183 } 184 } 185 186 public String toString() { 187 final StringBuilder sb = new StringBuilder(); 188 //sb.append("hierarchy:\n" + hier + "\n"); 189 sb.append("invoke: " + invoke + "\n"); 190 if (methodref != -1) { 191 if (hier.isClass(methodref)) { 192 sb.append("methodref: C" + methodref + "\n"); 193 } else { 194 sb.append("methodref: I" + methodref + "\n"); 195 } 196 } 197 if (objectref != -1) { 198 if (hier.isClass(objectref)) { 199 sb.append("objectref: C" + objectref + "\n"); 200 } else { 201 sb.append("objectref: I" + objectref + "\n"); 202 } 203 } 204 if (callsite != -1) { 205 if (hier.isClass(callsite)) { 206 sb.append("callsite: C" + callsite + "\n"); 207 } else { 208 sb.append("callsite: I" + callsite + "\n"); 209 } 210 } 211 sb.append("result: " + result + "\n"); 212 sb.append("classes:\n\n"); 213 214 for(int i = 0; classdata.containsKey(i); i++) { 215 final ClassData cd = classdata.get(i); 216 217 if (hier.isClass(i)) { 218 sb.append("class "); 219 addPackage(sb, cd); 220 sb.append("C" + i); 221 } else { 222 sb.append("interface "); 223 addPackage(sb, cd); 224 sb.append("I" + i); 225 } 226 227 boolean first = true; 228 for(final int j : hier.classes()) { 229 if (hier.inherits(i, j)) { 230 if (first) { 231 sb.append(" extends C" + j); 232 } else { 233 sb.append(", C" + j); 234 } 235 } 236 } 237 238 first = true; 239 for(final int j : hier.interfaces()) { 240 if (hier.inherits(i, j)) { 241 if (first) { 242 sb.append(" implements I" + j); 243 } else { 244 sb.append(", I" + j); 245 } 246 } 247 } 248 249 sb.append(cd); 250 } 251 252 return sb.toString(); 253 } 254 255 /** 256 * A builder, facilitating building up test cases. 257 */ 258 public static class Builder { 259 /** 260 * A map from class (or interface) id's to ClassDatas 261 */ 262 public final HashMap<Integer, ClassData> classdata; 263 /** 264 * The hierarchy shape. 265 */ 266 public final HierarchyShape hier; 267 /** 268 * Which invoke instruction to use. 269 */ 270 public InvokeInstruction invoke; 271 /** 272 * The id of the methodref (or interface methodref). 273 */ 274 public int methodref = -1; 275 /** 276 * The id of the object ref. Note that for the generator 277 * framework to work, this must be set to something. If an 278 * objectref isn't used, just set it to the methodref. 279 */ 280 public int objectref = -1; 281 /** 282 * The id of the callsite. 283 */ 284 public int callsite = -1; 285 /** 286 * The id of the expected result. This is used to store the 287 * expected resolution result. 288 */ 289 public int expected; 290 /** 291 * The expected result. This needs to be set before the final 292 * test case is built. 293 */ 294 public Result result; 295 296 /** 297 * Create an empty Builder object. 298 */ 299 public Builder() { 300 classdata = new HashMap<>(); 301 hier = new HierarchyShape(); 302 } 303 304 private Builder(final HashMap<Integer, ClassData> classdata, 305 final HierarchyShape hier, 306 final InvokeInstruction invoke, 307 final int methodref, 308 final int objectref, 309 final int callsite, 310 final int expected, 311 final Result result) { 312 this.classdata = classdata; 313 this.hier = hier; 314 this.invoke = invoke; 315 this.methodref = methodref; 316 this.objectref = objectref; 317 this.callsite = callsite; 318 this.expected = expected; 319 this.result = result; 320 } 321 322 private Builder(final Builder other) { 323 this((HashMap<Integer, ClassData>) other.classdata.clone(), 324 other.hier.copy(), other.invoke, other.methodref, other.objectref, 325 other.callsite, other.expected, other.result); 326 } 327 328 public SelectionResolutionTestCase build() { 329 if (result != null) { 330 return new SelectionResolutionTestCase(classdata, hier, invoke, 331 methodref, objectref, 332 callsite, result); 333 } else { 334 return new SelectionResolutionTestCase(classdata, hier, invoke, 335 methodref, objectref, 336 callsite, expected); 337 } 338 } 339 340 /** 341 * Set the expected result. 342 */ 343 public void setResult(final Result result) { 344 this.result = result; 345 } 346 347 /** 348 * Add a class, and return its id. 349 * 350 * @return The new class' id. 351 */ 352 public int addClass(final ClassData data) { 353 final int id = hier.addClass(); 354 classdata.put(id, data); 355 return id; 356 } 357 358 /** 359 * Add an interface, and return its id. 360 * 361 * @return The new class' id. 362 */ 363 public int addInterface(final ClassData data) { 364 final int id = hier.addInterface(); 365 classdata.put(id, data); 366 return id; 367 } 368 369 /** 370 * Make a copy of this builder. 371 */ 372 public Builder copy() { 373 return new Builder(this); 374 } 375 376 public String toString() { 377 final StringBuilder sb = new StringBuilder(); 378 //sb.append("hierarchy:\n" + hier + "\n"); 379 sb.append("invoke: " + invoke + "\n"); 380 if (methodref != -1) { 381 if (hier.isClass(methodref)) { 382 sb.append("methodref: C" + methodref + "\n"); 383 } else { 384 sb.append("methodref: I" + methodref + "\n"); 385 } 386 } 387 if (objectref != -1) { 388 if (hier.isClass(objectref)) { 389 sb.append("objectref: C" + objectref + "\n"); 390 } else { 391 sb.append("objectref: I" + objectref + "\n"); 392 } 393 } 394 if (callsite != -1) { 395 if (hier.isClass(callsite)) { 396 sb.append("callsite: C" + callsite + "\n"); 397 } else { 398 sb.append("callsite: I" + callsite + "\n"); 399 } 400 } 401 if (expected != -1) { 402 if (hier.isClass(expected)) { 403 sb.append("expected: C" + expected + "\n"); 404 } else { 405 sb.append("expected: I" + expected + "\n"); 406 } 407 } 408 sb.append("result: " + result + "\n"); 409 sb.append("classes:\n\n"); 410 411 for(int i = 0; classdata.containsKey(i); i++) { 412 final ClassData cd = classdata.get(i); 413 414 if (hier.isClass(i)) { 415 sb.append("class "); 416 addPackage(sb, cd); 417 sb.append("C" + i); 418 } else { 419 sb.append("interface "); 420 addPackage(sb, cd); 421 sb.append("I" + i); 422 } 423 424 boolean first = true; 425 for(final int j : hier.classes()) { 426 if (hier.inherits(i, j)) { 427 if (first) { 428 sb.append(" extends C" + j); 429 } else { 430 sb.append(", C" + j); 431 } 432 } 433 } 434 435 first = true; 436 for(final int j : hier.interfaces()) { 437 if (hier.inherits(i, j)) { 438 if (first) { 439 sb.append(" implements I" + j); 440 } else { 441 sb.append(", I" + j); 442 } 443 } 444 } 445 446 sb.append(cd); 447 } 448 449 return sb.toString(); 450 } 451 } 452 }