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 }