blob: e1d7db170d8fadd05c3063ad9e56561c8853e55c [file] [log] [blame]
Mingyao Yangae6c1892017-01-05 13:46:36 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17abstract class Base {
18 abstract void foo(int i);
19
20 void printError(String msg) {
21 System.out.println(msg);
22 }
23}
24
25class Main1 extends Base {
26 void foo(int i) {
27 if (i != 1) {
28 printError("error1");
29 }
30 }
31}
32
33class Main2 extends Main1 {
34 void foo(int i) {
35 if (i != 2) {
36 printError("error2");
37 }
38 }
39}
40
41public class Main {
42 static Main1 sMain1;
43 static Main1 sMain2;
44
45 static boolean sIsOptimizing = true;
46 static boolean sHasJIT = true;
47 static volatile boolean sOtherThreadStarted;
48
49 private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
50 if (hasSingleImplementation(clazz, method_name) != b) {
51 System.out.println(clazz + "." + method_name +
52 " doesn't have single implementation value of " + b);
53 }
54 }
55
56 // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
57 // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
58 // After Dummy.createMain2() which links in Main2, live testOverride() on stack
59 // should be deoptimized.
60 static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
61 if (setHasJIT) {
62 if (isInterpreted()) {
63 sHasJIT = false;
64 }
65 return;
66 }
67
68 if (createMain2 && (sIsOptimizing || sHasJIT)) {
69 assertIsManaged();
70 }
71
72 sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
73
74 if (createMain2) {
75 // Wait for the other thread to start.
76 while (!sOtherThreadStarted);
77 // Create an Main2 instance and assign it to sMain2.
78 // sMain1 is kept the same.
79 sMain2 = Dummy.createMain2();
80 // Wake up the other thread.
81 synchronized(Main.class) {
82 Main.class.notify();
83 }
84 } else if (wait) {
85 // This is the other thread.
86 synchronized(Main.class) {
87 sOtherThreadStarted = true;
88 // Wait for Main2 to be linked and deoptimization is triggered.
89 try {
90 Main.class.wait();
91 } catch (Exception e) {
92 }
93 }
94 }
95
96 // There should be a deoptimization here right after Main2 is linked by
97 // calling Dummy.createMain2(), even though sMain1 didn't change.
98 // The behavior here would be different if inline-cache is used, which
99 // doesn't deoptimize since sMain1 still hits the type cache.
100 sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
101 if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
102 // This method should be deoptimized right after Main2 is created.
103 assertIsInterpreted();
104 }
105
106 if (sMain2 != null) {
107 sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
108 }
109 }
110
111 // Test scenarios under which CHA-based devirtualization happens,
112 // and class loading that overrides a method can invalidate compiled code.
113 public static void main(String[] args) {
114 System.loadLibrary(args[0]);
115
116 if (isInterpreted()) {
117 sIsOptimizing = false;
118 }
119
120 // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
121 sMain1 = new Main1();
122
123 ensureJitCompiled(Main.class, "testOverride");
124 testOverride(false, false, true);
125
126 if (sHasJIT && !sIsOptimizing) {
127 assertSingleImplementation(Base.class, "foo", true);
128 assertSingleImplementation(Main1.class, "foo", true);
129 } else {
130 // Main2 is verified ahead-of-time so it's linked in already.
131 }
132
133 // Create another thread that also calls sMain1.foo().
134 // Try to test suspend and deopt another thread.
135 new Thread() {
136 public void run() {
137 testOverride(false, true, false);
138 }
139 }.start();
140
141 // This will create Main2 instance in the middle of testOverride().
142 testOverride(true, false, false);
143 assertSingleImplementation(Base.class, "foo", false);
144 assertSingleImplementation(Main1.class, "foo", false);
145 }
146
147 private static native void ensureJitCompiled(Class<?> itf, String method_name);
148 private static native void assertIsInterpreted();
149 private static native void assertIsManaged();
150 private static native boolean isInterpreted();
151 private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
152}
153
154// Put createMain2() in another class to avoid class loading due to verifier.
155class Dummy {
156 static Main1 createMain2() {
157 return new Main2();
158 }
159}