File: IncompatibleClassChangeErrorTest.java

package info (click to toggle)
openjdk-11 11.0.29%2B7-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 781,596 kB
  • sloc: java: 5,209,028; xml: 1,192,267; cpp: 1,143,585; ansic: 462,354; javascript: 162,416; sh: 16,740; objc: 13,730; python: 4,757; asm: 3,570; makefile: 2,969; perl: 357; awk: 351; sed: 172; jsp: 24; csh: 3
file content (434 lines) | stat: -rw-r--r-- 15,350 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2018 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/**
 * @test
 * @summary Check that the verbose message of ICCE is printed correctly.
 *          The test forces errors in vtable stubs and interpreter.
 * @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)
 * @library /test/lib /
 * @build sun.hotspot.WhiteBox
 * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
 * @compile IncompatibleClassChangeErrorTest.java
 * @compile ImplementsSomeInterfaces.jasm ICC2_B.jasm ICC3_B.jasm ICC4_B.jasm
 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 *                   -XX:CompileThreshold=1000 -XX:-BackgroundCompilation -XX:-Inline
 *                   -XX:CompileCommand=exclude,test.IncompatibleClassChangeErrorTest::test_iccInt
 *                   test.IncompatibleClassChangeErrorTest
 */

package test;

import sun.hotspot.WhiteBox;
import compiler.whitebox.CompilerWhiteBoxTest;
import java.lang.reflect.Method;

// This test assembles an erroneous installation of classes.
// First, compile the test by @compile. This results in a legal set
// of classes.
// Then, with jasm, generate incompatible classes that overwrite
// the class files in the build directory.
// Last, call the real tests throwing IncompatibleClassChangeErrors
// and check the messages generated.
public class IncompatibleClassChangeErrorTest {

    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();

    private static boolean enableChecks = true;

    private static String expectedErrorMessageInterpreted =
        "Class test.ImplementsSomeInterfaces " +
        "does not implement the requested interface test.InterfaceICCE1";
    private static String expectedErrorMessageCompiled =
        "Class test.ICC2_B does not implement the requested interface test.ICC2_iB";
        // old message: "vtable stub"


    private static boolean compile(Class<?> clazz, String name) {
        try {
            Method method = clazz.getMethod(name);
            boolean enqueued = WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
            if (!enqueued) {
                System.out.println("Warning: Blocking compilation failed for " + clazz.getName() + "." + name + " (timeout?)");
                return false;
            } else if (!WHITE_BOX.isMethodCompiled(method)) {
                throw new RuntimeException(clazz.getName() + "." + name + " is not compiled");
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(clazz.getName() + "." + name + " not found", e);
        }
        return true;
    }

    public static boolean setup_test() {
        // Assure all exceptions are loaded.
        new AbstractMethodError();
        new IncompatibleClassChangeError();

        enableChecks = false;
        // Warmup
        System.out.println("warmup:");
        test_iccInt();
        test_icc_compiled_itable_stub();
        enableChecks = true;

        // Compile
        if (!compile(IncompatibleClassChangeErrorTest.class, "test_icc_compiled_itable_stub") ||
            !compile(ICC2_C.class, "b") ||
            !compile(ICC2_D.class, "b") ||
            !compile(ICC2_E.class, "b")) {
          return false;
        }

        System.out.println("warmup done.");
        return true;
    }

    // Should never be compiled.
    public static void test_iccInt() {
        boolean caught_icc = false;
        try {
            InterfaceICCE1 objectInterface = new ImplementsSomeInterfaces();
            // IncompatibleClassChangeError gets thrown in
            // - TemplateTable::invokeinterface()
            // - LinkResolver::runtime_resolve_interface_method()
            objectInterface.aFunctionOfMyInterface();
        } catch (IncompatibleClassChangeError e) {
            String errorMsg = e.getMessage();
            if (enableChecks && !errorMsg.equals(expectedErrorMessageInterpreted)) {
                System.out.println("Expected: " + expectedErrorMessageInterpreted + "\n" +
                                   "but got:  " + errorMsg);
                throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
            }
            if (enableChecks) {
                System.out.println("Test 1 passed with message: " + errorMsg);
            }
            caught_icc = true;
        } catch (Throwable e) {
            throw new RuntimeException("Caught unexpected exception: " + e);
        }

        // Check we got the exception.
        if (!caught_icc) {
            throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
        }
    }

    // -------------------------------------------------------------------------
    // Test AbstractMethodErrors detected in itable stubs.
    // Note: How can we verify that we really stepped through the vtable stub?
    // - Bimorphic inlining should not happen since we have no profiling data when
    //   we compile the method
    // - As a result, an inline cache call should be generated
    // - This inline cache call is patched into a real vtable call at the first
    //   re-resolve, which happens constantly during the first 10 iterations of the loop.
    // => we should be fine! :-)
    public static void test_icc_compiled_itable_stub() {
        // Allocated the objects we need and call a valid method.
        boolean caught_icc = false;
        ICC2_B b = new ICC2_B();
        ICC2_C c = new ICC2_C();
        ICC2_D d = new ICC2_D();
        ICC2_E e = new ICC2_E();
        b.a();
        c.a();
        d.a();
        e.a();

        try {
            final int iterations = 10;
            // Test: calls b.b() in the last iteration.
            for (int i = 0; i < iterations; i++) {
                ICC2_iB a = b;
                if (i % 3 == 0 && i < iterations - 1) {
                    a = c;
                }
                if (i % 3 == 1 && i < iterations - 1) {
                    a = d;
                }
                if (i % 3 == 2 && i < iterations - 1) {
                    a = e;
                }
                a.b();
            }
        } catch (AbstractMethodError exc) {
            // It's a subclass of IncompatibleClassChangeError, so we must catch this first.
            System.out.println();
            System.out.println(exc);
            if (enableChecks) {
                String errorMsg = exc.getMessage();
                if (errorMsg == null) {
                    throw new RuntimeException("Caught unexpected AbstractMethodError with empty message.");
                }
                throw new RuntimeException("Caught unexpected AbstractMethodError.");
            }
        } catch (IncompatibleClassChangeError exc) {
            caught_icc = true;
            System.out.println();
            String errorMsg = exc.getMessage();
            if (enableChecks && errorMsg == null) {
                System.out.println(exc);
                throw new RuntimeException("Empty error message of IncompatibleClassChangeError.");
            }
            if (enableChecks &&
                !errorMsg.equals(expectedErrorMessageCompiled)) {
                System.out.println("Expected: " + expectedErrorMessageCompiled + "\n" +
                                   "but got:  " + errorMsg);
                System.out.println(exc);
                throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
            }
            if (enableChecks) {
                System.out.println("Test 2 passed with message: " + errorMsg);
            }
        } catch (Throwable exc) {
            throw exc; // new RuntimeException("Caught unexpected exception: " + exc);
        }

        // Check we got the exception at some point.
        if (enableChecks && !caught_icc) {
            throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
        }
    }

    private static String expectedErrorMessage3 =
        "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')";

    public static void test3_implementsClass() throws Exception {
        try {
            new ICC3_B();
            throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
        } catch (IncompatibleClassChangeError e) {
            String errorMsg = e.getMessage();
            if (!errorMsg.equals(expectedErrorMessage3)) {
                System.out.println("Expected: " + expectedErrorMessage3 + "\n" +
                                   "but got:  " + errorMsg);
                throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
            }
            System.out.println("Test 3 passed with message: " + errorMsg);
        } catch (Throwable e) {
            throw new RuntimeException("Caught unexpected exception: " + e);
        }
    }

    private static String expectedErrorMessage4 =
        "class test.ICC4_B has interface test.ICC4_iA as super class";

    public static void test4_extendsInterface() throws Exception {
        try {
            new ICC4_B();
            throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
        } catch (IncompatibleClassChangeError e) {
            String errorMsg = e.getMessage();
            if (!errorMsg.equals(expectedErrorMessage4)) {
                System.out.println("Expected: " + expectedErrorMessage4 + "\n" +
                                   "but got:  " + errorMsg);
                throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
            }
            System.out.println("Test 4 passed with message: " + errorMsg);
        } catch (Throwable e) {
            throw new RuntimeException("Caught unexpected exception: " + e);
        }
    }

    public static void main(String[] args) throws Exception {
        if (!setup_test()) {
            return;
        }
        test_iccInt();
        test_icc_compiled_itable_stub();
        test3_implementsClass();
        test4_extendsInterface();
    }
}


// Helper classes to test incompatible class change in interpreter.
//
// The test also contains .jasm files with implementations
// of the classes that shall generate the errors.


//   I0         // interface defining aFunctionOfMyInterface()
//   |
//   |    I1    // interface
//   |     |
//   A0    |    // abstract class
//    \   /
//      C       // class not implementing I1 and
//                       not implementing I0::aFunctionOfMyInterface()
//
// Test is expected to throw error because of missing interface and not
// because of missing method.

interface InterfaceICCE0 {
    public String firstFunctionOfMyInterface0();
    public String secondFunctionOfMyInterface0();
}

interface InterfaceICCE1 {

    public String firstFunctionOfMyInterface();

    public String secondFunctionOfMyInterface();

    public String aFunctionOfMyInterface();
}

abstract class AbstractICCE0 implements InterfaceICCE0 {
    abstract public String firstAbstractMethod();
    abstract public String secondAbstractMethod();

    abstract public String anAbstractMethod();
}

class ImplementsSomeInterfaces extends
        AbstractICCE0
    // This interface is missing in the .jasm implementation.
    implements InterfaceICCE1
{

    public String firstAbstractMethod() {
        return this.getClass().getName();
    }

    public String secondAbstractMethod() {
        return this.getClass().getName();
    }

    // This method is missing in the .jasm implementation.
    public String anAbstractMethod() {
        return this.getClass().getName();
    }

    public String firstFunctionOfMyInterface0() {
        return this.getClass().getName();
    }

    public String secondFunctionOfMyInterface0() {
        return this.getClass().getName();
    }

    public String firstFunctionOfMyInterface() {
        return this.getClass().getName();
    }

    public String secondFunctionOfMyInterface() {
        return this.getClass().getName();
    }

    // This method is missing in the .jasm implementation.
    public String aFunctionOfMyInterface() {
        return this.getClass().getName();
    }
}

// Helper classes to test incompatible class change in itable stub.
//
// Class hierachy:
//
//          iA,iB   (interfaces)
//          /|\ \
//         C D E \
//                B (bad class, missing interface implementation)

interface ICC2_iA {
    public void a();
}

interface ICC2_iB {
    public void b();
}

// This is the erroneous class. A variant of it not
// implementing ICC2_iB is copied into the test before
// it is run.
class ICC2_B implements ICC2_iA,
                       // This interface is missing in the .jasm implementation.
                       ICC2_iB {
    public void a() {
        System.out.print("B.a() ");
    }

    public void b() {
        System.out.print("B.b() ");
    }
}

class ICC2_C implements ICC2_iA, ICC2_iB {
    public void a() {
        System.out.print("C.a() ");
    }

    public void b() {
        System.out.print("C.b() ");
    }
}

class ICC2_D implements ICC2_iA, ICC2_iB {
    public void a() {
        System.out.print("D.a() ");
    }

    public void b() {
        System.out.print("D.b() ");
    }
}

class ICC2_E implements ICC2_iA, ICC2_iB {
    public void a() {
        System.out.print("E.a() ");
    }

    public void b() {
        System.out.print("E.b() ");
    }
}

// Helper classes to test error where class appears in implements statement.
//
// Class hierachy:
//
//       A  Some Class.
//       |
//       B  erroneous class. Correct B extends A, incorrect B (from jasm) implements A.

class ICC3_A {
}

class ICC3_B extends ICC3_A {
}

// Helper classes to test error where interface appears in extends statement.
//
// Class hierachy:
//
//       A  Some Interface.
//       |
//       B  erroneous class. Correct B implements A, incorrect B (from jasm) extends A.

interface ICC4_iA {
}

class ICC4_B implements ICC4_iA {
}