1 /*
2  * Copyright (C) 2016 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 
17 import java.lang.reflect.Method;
18 import java.util.Arrays;
19 import java.util.Comparator;
20 import java.util.HashMap;
21 
22 class TestObject {
23     public static boolean sHashCodeInvoked = false;
24     private int i;
25 
TestObject(int i)26     public TestObject(int i) {
27         this.i = i;
28     }
29 
equals(Object obj)30     public boolean equals(Object obj) {
31         return (obj instanceof TestObject) && (i == ((TestObject)obj).i);
32     }
33 
hashCode()34     public int hashCode() {
35         sHashCodeInvoked = true;
36         Main.deoptimizeAll();
37         return i % 64;
38     }
39 }
40 
41 public class Main {
42     static boolean sFlag = false;
43 
deoptimizeAll()44     public static native void deoptimizeAll();
undeoptimizeAll()45     public static native void undeoptimizeAll();
46 
execute(Runnable runnable)47     public static void execute(Runnable runnable) throws Exception {
48       Thread t = new Thread(runnable);
49       t.start();
50       t.join();
51     }
52 
main(String[] args)53     public static void main(String[] args) throws Exception {
54         System.loadLibrary(args[0]);
55         final HashMap<TestObject, Long> map = new HashMap<TestObject, Long>();
56 
57         // Single-frame deoptimization that covers partial fragment.
58         execute(new Runnable() {
59             public void run() {
60                 int[] arr = new int[3];
61                 int res = $noinline$run1(arr);
62                 if (res != 79) {
63                     System.out.println("Failure 1!");
64                     System.exit(0);
65                 }
66             }
67         });
68 
69         // Single-frame deoptimization that covers a full fragment.
70         execute(new Runnable() {
71             public void run() {
72                 try {
73                     int[] arr = new int[3];
74                     // Use reflection to call $noinline$run2 so that it does
75                     // full-fragment deoptimization since that is an upcall.
76                     Class<?> cls = Class.forName("Main");
77                     Method method = cls.getDeclaredMethod("$noinline$run2", int[].class);
78                     double res = (double)method.invoke(Main.class, arr);
79                     if (res != 79.3d) {
80                         System.out.println("Failure 2!");
81                         System.exit(0);
82                     }
83                 } catch (Exception e) {
84                     e.printStackTrace(System.out);
85                 }
86             }
87         });
88 
89         // Full-fragment deoptimization.
90         execute(new Runnable() {
91             public void run() {
92                 float res = $noinline$run3B();
93                 if (res != 0.034f) {
94                     System.out.println("Failure 3!");
95                     System.exit(0);
96                 }
97             }
98         });
99 
100         undeoptimizeAll();  // Make compiled code useable again.
101 
102         // Partial-fragment deoptimization.
103         execute(new Runnable() {
104             public void run() {
105                 try {
106                     map.put(new TestObject(10), Long.valueOf(100));
107                     if (map.get(new TestObject(10)) == null) {
108                         System.out.println("Expected map to contain TestObject(10)");
109                     }
110                 } catch (Exception e) {
111                     e.printStackTrace(System.out);
112                 }
113             }
114         });
115 
116         undeoptimizeAll();  // Make compiled code useable again.
117 
118         if (!TestObject.sHashCodeInvoked) {
119             System.out.println("hashCode() method not invoked!");
120         }
121         if (map.get(new TestObject(10)) != 100) {
122             System.out.println("Wrong hashmap value!");
123         }
124         System.out.println("Finishing");
125     }
126 
$noinline$run1(int[] arr)127     public static int $noinline$run1(int[] arr) {
128         // Prevent inlining.
129         if (sFlag) {
130             throw new Error();
131         }
132         boolean caught = false;
133         // BCE will use deoptimization for the code below.
134         try {
135             arr[0] = 1;
136             arr[1] = 1;
137             arr[2] = 1;
138             // This causes AIOOBE and triggers deoptimization from compiled code.
139             arr[3] = 1;
140         } catch (ArrayIndexOutOfBoundsException e) {
141             caught = true;
142         }
143         if (!caught) {
144             System.out.println("Expected exception");
145         }
146         return 79;
147     }
148 
$noinline$run2(int[] arr)149     public static double $noinline$run2(int[] arr) {
150         // Prevent inlining.
151         if (sFlag) {
152             throw new Error();
153         }
154         boolean caught = false;
155         // BCE will use deoptimization for the code below.
156         try {
157             arr[0] = 1;
158             arr[1] = 1;
159             arr[2] = 1;
160             // This causes AIOOBE and triggers deoptimization from compiled code.
161             arr[3] = 1;
162         } catch (ArrayIndexOutOfBoundsException e) {
163             caught = true;
164         }
165         if (!caught) {
166             System.out.println("Expected exception");
167         }
168         return 79.3d;
169     }
170 
$noinline$run3A()171     public static float $noinline$run3A() {
172         // Prevent inlining.
173         if (sFlag) {
174             throw new Error();
175         }
176         // Deoptimize callers.
177         deoptimizeAll();
178         return 0.034f;
179     }
180 
$noinline$run3B()181     public static float $noinline$run3B() {
182         // Prevent inlining.
183         if (sFlag) {
184             throw new Error();
185         }
186         float res = $noinline$run3A();
187         return res;
188     }
189 }
190