1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2018 SAP SE. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /** 26 * @test 27 * @summary Check that the verbose message of ICCE is printed correctly. 28 * The test forces errors in vtable stubs and interpreter. 29 * @requires !(os.arch=="arm") & vm.flavor == "server" & !vm.emulatedClient & vm.compMode=="Xmixed" & (!vm.graal.enabled | vm.opt.TieredCompilation == true) & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) 30 * @library /test/lib / 31 * @build sun.hotspot.WhiteBox 32 * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission 33 * @compile IncompatibleClassChangeErrorTest.java 34 * @compile ImplementsSomeInterfaces.jasm ICC2_B.jasm ICC3_B.jasm ICC4_B.jasm 35 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 36 * -XX:CompileThreshold=1000 -XX:-BackgroundCompilation -XX:-Inline 37 * -XX:CompileCommand=exclude,test.IncompatibleClassChangeErrorTest::test_iccInt 38 * test.IncompatibleClassChangeErrorTest 39 */ 40 41 package test; 42 43 import sun.hotspot.WhiteBox; 44 import compiler.whitebox.CompilerWhiteBoxTest; 45 import java.lang.reflect.Method; 46 47 // This test assembles an erroneous installation of classes. 48 // First, compile the test by @compile. This results in a legal set 49 // of classes. 50 // Then, with jasm, generate incompatible classes that overwrite 51 // the class files in the build directory. 52 // Last, call the real tests throwing IncompatibleClassChangeErrors 53 // and check the messages generated. 54 public class IncompatibleClassChangeErrorTest { 55 56 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 57 58 private static boolean enableChecks = true; 59 60 private static String expectedErrorMessageInterpreted = 61 "Class test.ImplementsSomeInterfaces " + 62 "does not implement the requested interface test.InterfaceICCE1"; 63 private static String expectedErrorMessageCompiled = 64 "Class test.ICC2_B does not implement the requested interface test.ICC2_iB"; 65 // old message: "vtable stub" 66 67 68 private static boolean compile(Class<?> clazz, String name) { 69 try { 70 Method method = clazz.getMethod(name); 71 boolean enqueued = WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); 72 if (!enqueued) { 73 System.out.println("Warning: Blocking compilation failed for " + clazz.getName() + "." + name + " (timeout?)"); 74 return false; 75 } else if (!WHITE_BOX.isMethodCompiled(method)) { 76 throw new RuntimeException(clazz.getName() + "." + name + " is not compiled"); 77 } 78 } catch (NoSuchMethodException e) { 79 throw new RuntimeException(clazz.getName() + "." + name + " not found", e); 80 } 81 return true; 82 } 83 84 public static boolean setup_test() { 85 // Assure all exceptions are loaded. 86 new AbstractMethodError(); 87 new IncompatibleClassChangeError(); 88 89 enableChecks = false; 90 // Warmup 91 System.out.println("warmup:"); 92 test_iccInt(); 93 test_icc_compiled_itable_stub(); 94 enableChecks = true; 95 96 // Compile 97 if (!compile(IncompatibleClassChangeErrorTest.class, "test_icc_compiled_itable_stub") || 98 !compile(ICC2_C.class, "b") || 99 !compile(ICC2_D.class, "b") || 100 !compile(ICC2_E.class, "b")) { 101 return false; 102 } 103 104 System.out.println("warmup done."); 105 return true; 106 } 107 108 // Should never be compiled. 109 public static void test_iccInt() { 110 boolean caught_icc = false; 111 try { 112 InterfaceICCE1 objectInterface = new ImplementsSomeInterfaces(); 113 // IncompatibleClassChangeError gets thrown in 114 // - TemplateTable::invokeinterface() 115 // - LinkResolver::runtime_resolve_interface_method() 116 objectInterface.aFunctionOfMyInterface(); 117 } catch (IncompatibleClassChangeError e) { 118 String errorMsg = e.getMessage(); 119 if (enableChecks && !errorMsg.equals(expectedErrorMessageInterpreted)) { 120 System.out.println("Expected: " + expectedErrorMessageInterpreted + "\n" + 121 "but got: " + errorMsg); 122 throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); 123 } 124 if (enableChecks) { 125 System.out.println("Test 1 passed with message: " + errorMsg); 126 } 127 caught_icc = true; 128 } catch (Throwable e) { 129 throw new RuntimeException("Caught unexpected exception: " + e); 130 } 131 132 // Check we got the exception. 133 if (!caught_icc) { 134 throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); 135 } 136 } 137 138 // ------------------------------------------------------------------------- 139 // Test AbstractMethodErrors detected in itable stubs. 140 // Note: How can we verify that we really stepped through the vtable stub? 141 // - Bimorphic inlining should not happen since we have no profiling data when 142 // we compile the method 143 // - As a result, an inline cache call should be generated 144 // - This inline cache call is patched into a real vtable call at the first 145 // re-resolve, which happens constantly during the first 10 iterations of the loop. 146 // => we should be fine! :-) 147 public static void test_icc_compiled_itable_stub() { 148 // Allocated the objects we need and call a valid method. 149 boolean caught_icc = false; 150 ICC2_B b = new ICC2_B(); 151 ICC2_C c = new ICC2_C(); 152 ICC2_D d = new ICC2_D(); 153 ICC2_E e = new ICC2_E(); 154 b.a(); 155 c.a(); 156 d.a(); 157 e.a(); 158 159 try { 160 final int iterations = 10; 161 // Test: calls b.b() in the last iteration. 162 for (int i = 0; i < iterations; i++) { 163 ICC2_iB a = b; 164 if (i % 3 == 0 && i < iterations - 1) { 165 a = c; 166 } 167 if (i % 3 == 1 && i < iterations - 1) { 168 a = d; 169 } 170 if (i % 3 == 2 && i < iterations - 1) { 171 a = e; 172 } 173 a.b(); 174 } 175 } catch (AbstractMethodError exc) { 176 // It's a subclass of IncompatibleClassChangeError, so we must catch this first. 177 System.out.println(); 178 System.out.println(exc); 179 if (enableChecks) { 180 String errorMsg = exc.getMessage(); 181 if (errorMsg == null) { 182 throw new RuntimeException("Caught unexpected AbstractMethodError with empty message."); 183 } 184 throw new RuntimeException("Caught unexpected AbstractMethodError."); 185 } 186 } catch (IncompatibleClassChangeError exc) { 187 caught_icc = true; 188 System.out.println(); 189 String errorMsg = exc.getMessage(); 190 if (enableChecks && errorMsg == null) { 191 System.out.println(exc); 192 throw new RuntimeException("Empty error message of IncompatibleClassChangeError."); 193 } 194 if (enableChecks && 195 !errorMsg.equals(expectedErrorMessageCompiled)) { 196 System.out.println("Expected: " + expectedErrorMessageCompiled + "\n" + 197 "but got: " + errorMsg); 198 System.out.println(exc); 199 throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); 200 } 201 if (enableChecks) { 202 System.out.println("Test 2 passed with message: " + errorMsg); 203 } 204 } catch (Throwable exc) { 205 throw exc; // new RuntimeException("Caught unexpected exception: " + exc); 206 } 207 208 // Check we got the exception at some point. 209 if (enableChecks && !caught_icc) { 210 throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); 211 } 212 } 213 214 private static String expectedErrorMessage3 = 215 "class test.ICC3_B can not implement test.ICC3_A, because it is not an interface (test.ICC3_A is in unnamed module of loader 'app')"; 216 217 public static void test3_implementsClass() throws Exception { 218 try { 219 new ICC3_B(); 220 throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); 221 } catch (IncompatibleClassChangeError e) { 222 String errorMsg = e.getMessage(); 223 if (!errorMsg.equals(expectedErrorMessage3)) { 224 System.out.println("Expected: " + expectedErrorMessage3 + "\n" + 225 "but got: " + errorMsg); 226 throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); 227 } 228 System.out.println("Test 3 passed with message: " + errorMsg); 229 } catch (Throwable e) { 230 throw new RuntimeException("Caught unexpected exception: " + e); 231 } 232 } 233 234 private static String expectedErrorMessage4 = 235 "class test.ICC4_B has interface test.ICC4_iA as super class"; 236 237 public static void test4_extendsInterface() throws Exception { 238 try { 239 new ICC4_B(); 240 throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); 241 } catch (IncompatibleClassChangeError e) { 242 String errorMsg = e.getMessage(); 243 if (!errorMsg.equals(expectedErrorMessage4)) { 244 System.out.println("Expected: " + expectedErrorMessage4 + "\n" + 245 "but got: " + errorMsg); 246 throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); 247 } 248 System.out.println("Test 4 passed with message: " + errorMsg); 249 } catch (Throwable e) { 250 throw new RuntimeException("Caught unexpected exception: " + e); 251 } 252 } 253 254 public static void main(String[] args) throws Exception { 255 if (!setup_test()) { 256 return; 257 } 258 test_iccInt(); 259 test_icc_compiled_itable_stub(); 260 test3_implementsClass(); 261 test4_extendsInterface(); 262 } 263 } 264 265 266 // Helper classes to test incompatible class change in interpreter. 267 // 268 // The test also contains .jasm files with implementations 269 // of the classes that shall generate the errors. 270 271 272 // I0 // interface defining aFunctionOfMyInterface() 273 // | 274 // | I1 // interface 275 // | | 276 // A0 | // abstract class 277 // \ / 278 // C // class not implementing I1 and 279 // not implementing I0::aFunctionOfMyInterface() 280 // 281 // Test is expected to throw error because of missing interface and not 282 // because of missing method. 283 284 interface InterfaceICCE0 { 285 public String firstFunctionOfMyInterface0(); 286 public String secondFunctionOfMyInterface0(); 287 } 288 289 interface InterfaceICCE1 { 290 291 public String firstFunctionOfMyInterface(); 292 293 public String secondFunctionOfMyInterface(); 294 295 public String aFunctionOfMyInterface(); 296 } 297 298 abstract class AbstractICCE0 implements InterfaceICCE0 { 299 abstract public String firstAbstractMethod(); 300 abstract public String secondAbstractMethod(); 301 302 abstract public String anAbstractMethod(); 303 } 304 305 class ImplementsSomeInterfaces extends 306 AbstractICCE0 307 // This interface is missing in the .jasm implementation. 308 implements InterfaceICCE1 309 { 310 311 public String firstAbstractMethod() { 312 return this.getClass().getName(); 313 } 314 315 public String secondAbstractMethod() { 316 return this.getClass().getName(); 317 } 318 319 // This method is missing in the .jasm implementation. 320 public String anAbstractMethod() { 321 return this.getClass().getName(); 322 } 323 324 public String firstFunctionOfMyInterface0() { 325 return this.getClass().getName(); 326 } 327 328 public String secondFunctionOfMyInterface0() { 329 return this.getClass().getName(); 330 } 331 332 public String firstFunctionOfMyInterface() { 333 return this.getClass().getName(); 334 } 335 336 public String secondFunctionOfMyInterface() { 337 return this.getClass().getName(); 338 } 339 340 // This method is missing in the .jasm implementation. 341 public String aFunctionOfMyInterface() { 342 return this.getClass().getName(); 343 } 344 } 345 346 // Helper classes to test incompatible class change in itable stub. 347 // 348 // Class hierachy: 349 // 350 // iA,iB (interfaces) 351 // /|\ \ 352 // C D E \ 353 // B (bad class, missing interface implementation) 354 355 interface ICC2_iA { 356 public void a(); 357 } 358 359 interface ICC2_iB { 360 public void b(); 361 } 362 363 // This is the erroneous class. A variant of it not 364 // implementing ICC2_iB is copied into the test before 365 // it is run. 366 class ICC2_B implements ICC2_iA, 367 // This interface is missing in the .jasm implementation. 368 ICC2_iB { 369 public void a() { 370 System.out.print("B.a() "); 371 } 372 373 public void b() { 374 System.out.print("B.b() "); 375 } 376 } 377 378 class ICC2_C implements ICC2_iA, ICC2_iB { 379 public void a() { 380 System.out.print("C.a() "); 381 } 382 383 public void b() { 384 System.out.print("C.b() "); 385 } 386 } 387 388 class ICC2_D implements ICC2_iA, ICC2_iB { 389 public void a() { 390 System.out.print("D.a() "); 391 } 392 393 public void b() { 394 System.out.print("D.b() "); 395 } 396 } 397 398 class ICC2_E implements ICC2_iA, ICC2_iB { 399 public void a() { 400 System.out.print("E.a() "); 401 } 402 403 public void b() { 404 System.out.print("E.b() "); 405 } 406 } 407 408 // Helper classes to test error where class appears in implements statement. 409 // 410 // Class hierachy: 411 // 412 // A Some Class. 413 // | 414 // B erroneous class. Correct B extends A, incorrect B (from jasm) implements A. 415 416 class ICC3_A { 417 } 418 419 class ICC3_B extends ICC3_A { 420 } 421 422 // Helper classes to test error where interface appears in extends statement. 423 // 424 // Class hierachy: 425 // 426 // A Some Interface. 427 // | 428 // B erroneous class. Correct B implements A, incorrect B (from jasm) extends A. 429 430 interface ICC4_iA { 431 } 432 433 class ICC4_B implements ICC4_iA { 434 }