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") 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 ICC_B.jasm 35 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 36 * -XX:-BackgroundCompilation -XX:-Inline 37 * -XX:CompileCommand=exclude,IncompatibleClassChangeErrorTest::test_iccInt 38 * IncompatibleClassChangeErrorTest 39 */ 40 41 import sun.hotspot.WhiteBox; 42 import compiler.whitebox.CompilerWhiteBoxTest; 43 import java.lang.reflect.Method; 44 45 // This test assembles an errorneous installation of classes. 46 // First, compile the test by @compile. This results in a legal set 47 // of classes. 48 // Then, with jasm, generate incompatible classes that overwrite 49 // the class files in the build directory. 50 // Last, call the real tests throwing IncompatibleClassChangeErrors 51 // and check the messages generated. 52 public class IncompatibleClassChangeErrorTest { 53 54 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 55 56 private static boolean enableChecks = true; 57 58 private static String expectedErrorMessageInterpreted = 59 "Class ImplementsSomeInterfaces " + 60 "does not implement the requested interface InterfaceICCE1"; 61 private static String expectedErrorMessageCompiled = 62 "Class ICC_B does not implement the requested interface ICC_iB"; 63 // old message: "vtable stub" 64 65 public static void setup_test() { 66 // Assure all exceptions are loaded. 67 new AbstractMethodError(); 68 new IncompatibleClassChangeError(); 69 70 enableChecks = false; 71 // Warmup 72 System.out.println("warmup:"); 73 test_iccInt(); 74 test_icc_compiled_itable_stub(); 75 enableChecks = true; 76 77 // Compile 78 try { 79 Method method = IncompatibleClassChangeErrorTest.class.getMethod("test_icc_compiled_itable_stub"); 80 WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); 81 if (!WHITE_BOX.isMethodCompiled(method)) { 82 throw new RuntimeException(method.getName() + " is not compiled"); 83 } 84 method = ICC_C.class.getMethod("b"); 85 WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); 86 if (!WHITE_BOX.isMethodCompiled(method)) { 87 throw new RuntimeException("ICC_C." + method.getName() + " is not compiled"); 88 } 89 method = ICC_D.class.getMethod("b"); 90 WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); 91 if (!WHITE_BOX.isMethodCompiled(method)) { 92 throw new RuntimeException("ICC_D." + method.getName() + " is not compiled"); 93 } 94 method = ICC_E.class.getMethod("b"); 95 WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); 96 if (!WHITE_BOX.isMethodCompiled(method)) { 97 throw new RuntimeException("ICC_E." + method.getName() + " is not compiled"); 98 } 99 } catch (NoSuchMethodException e) { } 100 System.out.println("warmup done."); 101 } 102 103 // Should never be compiled. 104 public static void test_iccInt() { 105 boolean caught_icc = false; 106 try { 107 InterfaceICCE1 objectInterface = new ImplementsSomeInterfaces(); 108 // IncompatibleClassChangeError gets thrown in 109 // - TemplateTable::invokeinterface() 110 // - LinkResolver::runtime_resolve_interface_method() 111 objectInterface.aFunctionOfMyInterface(); 112 } catch (IncompatibleClassChangeError e) { 113 String errorMsg = e.getMessage(); 114 if (enableChecks && !errorMsg.equals(expectedErrorMessageInterpreted)) { 115 System.out.println("Expected: " + expectedErrorMessageInterpreted + "\n" + 116 "but got: " + errorMsg); 117 throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); 118 } 119 caught_icc = true; 120 } catch (Throwable e) { 121 throw new RuntimeException("Caught unexpected exception: " + e); 122 } 123 124 // Check we got the exception. 125 if (!caught_icc) { 126 throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); 127 } 128 } 129 130 // ------------------------------------------------------------------------- 131 // Test AbstractMethodErrors detected in itable stubs. 132 // Note: How can we verify that we really stepped through the vtable stub? 133 // - Bimorphic inlining should not happen since we have no profiling data when 134 // we compile the method 135 // - As a result, an inline cache call should be generated 136 // - This inline cache call is patched into a real vtable call at the first 137 // re-resolve, which happens constantly during the first 10 iterations of the loop. 138 // => we should be fine! :-) 139 public static void test_icc_compiled_itable_stub() { 140 // Allocated the objects we need and call a valid method. 141 boolean caught_icc = false; 142 ICC_B b = new ICC_B(); 143 ICC_C c = new ICC_C(); 144 ICC_D d = new ICC_D(); 145 ICC_E e = new ICC_E(); 146 b.a(); 147 c.a(); 148 d.a(); 149 e.a(); 150 151 try { 152 final int iterations = 10; 153 // Test: calls b.b() in the last iteration. 154 for (int i = 0; i < iterations; i++) { 155 ICC_iB a = b; 156 if (i % 3 == 0 && i < iterations - 1) { 157 a = c; 158 } 159 if (i % 3 == 1 && i < iterations - 1) { 160 a = d; 161 } 162 if (i % 3 == 2 && i < iterations - 1) { 163 a = e; 164 } 165 a.b(); 166 } 167 } catch (AbstractMethodError exc) { 168 // It's a subclass of IncompatibleClassChangeError, so we must catch this first. 169 System.out.println(); 170 System.out.println(exc); 171 if (enableChecks) { 172 String errorMsg = exc.getMessage(); 173 if (errorMsg == null) { 174 throw new RuntimeException("Caught unexpected AbstractMethodError with empty message."); 175 } 176 throw new RuntimeException("Caught unexpected AbstractMethodError."); 177 } 178 } catch (IncompatibleClassChangeError exc) { 179 caught_icc = true; 180 System.out.println(); 181 String errorMsg = exc.getMessage(); 182 if (enableChecks && errorMsg == null) { 183 System.out.println(exc); 184 throw new RuntimeException("Empty error message of IncompatibleClassChangeError."); 185 } 186 if (enableChecks && 187 !errorMsg.equals(expectedErrorMessageCompiled)) { 188 System.out.println("Expected: " + expectedErrorMessageCompiled + "\n" + 189 "but got: " + errorMsg); 190 System.out.println(exc); 191 throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); 192 } 193 if (enableChecks) { 194 System.out.println("Passed with message: " + errorMsg); 195 } 196 } catch (Throwable exc) { 197 throw exc; // new RuntimeException("Caught unexpected exception: " + exc); 198 } 199 200 // Check we got the exception at some point. 201 if (enableChecks && !caught_icc) { 202 throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); 203 } 204 } 205 206 public static void main(String[] args) throws Exception { 207 setup_test(); 208 test_iccInt(); 209 test_icc_compiled_itable_stub(); 210 } 211 } 212 213 214 // Helper classes to test incompatible class change in interpreter. 215 // 216 // The test also contains .jasm files with implementations 217 // of the classes that shall generate the errors. 218 219 220 // I0 // interface defining aFunctionOfMyInterface() 221 // | 222 // | I1 // interface 223 // | | 224 // A0 | // abstract class 225 // \ / 226 // C // class not implementing I1 and 227 // not implementing I0::aFunctionOfMyInterface() 228 // 229 // Test is expected to throw error because of missing interface and not 230 // because of missing method. 231 232 interface InterfaceICCE0 { 233 public String firstFunctionOfMyInterface0(); 234 public String secondFunctionOfMyInterface0(); 235 } 236 237 interface InterfaceICCE1 { 238 239 public String firstFunctionOfMyInterface(); 240 241 public String secondFunctionOfMyInterface(); 242 243 public String aFunctionOfMyInterface(); 244 } 245 246 abstract class AbstractICCE0 implements InterfaceICCE0 { 247 abstract public String firstAbstractMethod(); 248 abstract public String secondAbstractMethod(); 249 250 abstract public String anAbstractMethod(); 251 } 252 253 class ImplementsSomeInterfaces extends 254 AbstractICCE0 255 // This interface is missing in the .jasm implementation. 256 implements InterfaceICCE1 257 { 258 259 public String firstAbstractMethod() { 260 return this.getClass().getName(); 261 } 262 263 public String secondAbstractMethod() { 264 return this.getClass().getName(); 265 } 266 267 // This method is missing in the .jasm implementation. 268 public String anAbstractMethod() { 269 return this.getClass().getName(); 270 } 271 272 public String firstFunctionOfMyInterface0() { 273 return this.getClass().getName(); 274 } 275 276 public String secondFunctionOfMyInterface0() { 277 return this.getClass().getName(); 278 } 279 280 public String firstFunctionOfMyInterface() { 281 return this.getClass().getName(); 282 } 283 284 public String secondFunctionOfMyInterface() { 285 return this.getClass().getName(); 286 } 287 288 // This method is missing in the .jasm implementation. 289 public String aFunctionOfMyInterface() { 290 return this.getClass().getName(); 291 } 292 } 293 294 // Helper classes to test incompatible class change in itable stub. 295 // 296 // Class hierachy: 297 // 298 // iA,iB (interfaces) 299 // /|\ \ 300 // C D E \ 301 // B (bad class, missing interface implementation) 302 303 interface ICC_iA { 304 public void a(); 305 } 306 307 interface ICC_iB { 308 public void b(); 309 } 310 311 // This is the errorneous class. A variant of it not 312 // implementing ICC_iB is copied into the test before 313 // it is run. 314 class ICC_B implements ICC_iA, 315 // This interface is missing in the .jasm implementation. 316 ICC_iB { 317 public void a() { 318 System.out.print("B.a() "); 319 } 320 321 public void b() { 322 System.out.print("B.b() "); 323 } 324 } 325 326 class ICC_C implements ICC_iA, ICC_iB { 327 public void a() { 328 System.out.print("C.a() "); 329 } 330 331 public void b() { 332 System.out.print("C.b() "); 333 } 334 } 335 336 class ICC_D implements ICC_iA, ICC_iB { 337 public void a() { 338 System.out.print("D.a() "); 339 } 340 341 public void b() { 342 System.out.print("D.b() "); 343 } 344 } 345 346 class ICC_E implements ICC_iA, ICC_iB { 347 public void a() { 348 System.out.print("E.a() "); 349 } 350 351 public void b() { 352 System.out.print("E.b() "); 353 } 354 }