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 }