1 /*
2  * Copyright (C) 2022 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 public class Main {
main(String[] args)18   public static void main(String[] args) {
19     $noinline$testSingleTryCatch();
20     $noinline$testSingleTryCatchTwice();
21     $noinline$testSingleTryCatchDifferentInputs();
22     $noinline$testDifferentTryCatches();
23     $noinline$testTryCatchFinally();
24     $noinline$testTryCatchFinallyDifferentInputs();
25     $noinline$testRecursiveTryCatch();
26     $noinline$testDoNotInlineInsideTryInlineInsideCatch();
27     $noinline$testInlineInsideNestedCatches();
28     $noinline$testBeforeAfterTryCatch();
29     $noinline$testDifferentTypes();
30     $noinline$testRawThrow();
31     $noinline$testRawThrowTwice();
32     $noinline$testThrowCaughtInOuterMethod();
33   }
34 
$noinline$assertEquals(int expected, int result)35   public static void $noinline$assertEquals(int expected, int result) {
36     if (expected != result) {
37       throw new Error("Expected: " + expected + ", found: " + result);
38     }
39   }
40 
41   // Basic try catch inline.
$noinline$testSingleTryCatch()42   private static void $noinline$testSingleTryCatch() {
43     int[] numbers = {};
44     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
45   }
46 
47   // Two instances of the same method with a try catch.
$noinline$testSingleTryCatchTwice()48   private static void $noinline$testSingleTryCatchTwice() {
49     int[] numbers = {};
50     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
51     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
52   }
53 
54   // Triggering both normal and the exceptional flow.
$noinline$testSingleTryCatchDifferentInputs()55   private static void $noinline$testSingleTryCatchDifferentInputs() {
56     $noinline$assertEquals(1, $inline$OOBTryCatch(null));
57     int[] numbers = {};
58     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
59     int[] filled_numbers = {42};
60     $noinline$assertEquals(42, $inline$OOBTryCatch(filled_numbers));
61   }
62 
63 
64   // Two different try catches, with the same catch's dex_pc.
$noinline$testDifferentTryCatches()65   private static void $noinline$testDifferentTryCatches() {
66     int[] numbers = {};
67     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
68     $noinline$assertEquals(2, $inline$OtherOOBTryCatch(numbers));
69   }
70 
71   // Basic try/catch/finally.
$noinline$testTryCatchFinally()72   private static void $noinline$testTryCatchFinally() {
73     int[] numbers = {};
74     $noinline$assertEquals(3, $inline$OOBTryCatchFinally(numbers));
75   }
76 
77   // Triggering both normal and the exceptional flow.
$noinline$testTryCatchFinallyDifferentInputs()78   private static void $noinline$testTryCatchFinallyDifferentInputs() {
79     $noinline$assertEquals(3, $inline$OOBTryCatchFinally(null));
80     int[] numbers = {};
81     $noinline$assertEquals(3, $inline$OOBTryCatchFinally(numbers));
82     int[] filled_numbers = {42};
83     $noinline$assertEquals(42, $inline$OOBTryCatchFinally(filled_numbers));
84   }
85 
86   // Test that we can inline even when the try catch is several levels deep.
$noinline$testRecursiveTryCatch()87   private static void $noinline$testRecursiveTryCatch() {
88     int[] numbers = {};
89     $noinline$assertEquals(1, $inline$OOBTryCatchLevel4(numbers));
90   }
91 
92   // Tests that we don't inline inside outer tries, but we do inline inside of catches.
93   /// CHECK-START: void Main.$noinline$testDoNotInlineInsideTryInlineInsideCatch() inliner (before)
94   /// CHECK:       InvokeStaticOrDirect method_name:Main.DoNotInlineOOBTryCatch
95   /// CHECK:       InvokeStaticOrDirect method_name:Main.$inline$OOBTryCatch
96 
97   /// CHECK-START: void Main.$noinline$testDoNotInlineInsideTryInlineInsideCatch() inliner (after)
98   /// CHECK:       InvokeStaticOrDirect method_name:Main.DoNotInlineOOBTryCatch
$noinline$testDoNotInlineInsideTryInlineInsideCatch()99   private static void $noinline$testDoNotInlineInsideTryInlineInsideCatch() {
100     int val = 0;
101     try {
102       int[] numbers = {};
103       val = DoNotInlineOOBTryCatch(numbers);
104     } catch (Exception ex) {
105       unreachable();
106       // This is unreachable but we will still compile it so it works for checking that it inlines.
107       int[] numbers = {};
108       $inline$OOBTryCatch(numbers);
109     }
110     $noinline$assertEquals(1, val);
111   }
112 
$noinline$emptyMethod()113   private static void $noinline$emptyMethod() {}
114 
$inline$testInlineInsideNestedCatches_inner()115   private static void $inline$testInlineInsideNestedCatches_inner() {
116     try {
117       $noinline$emptyMethod();
118     } catch (Exception ex) {
119       int[] numbers = {};
120       $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
121     }
122   }
123 
$noinline$testInlineInsideNestedCatches()124   private static void $noinline$testInlineInsideNestedCatches() {
125     try {
126       $noinline$emptyMethod();
127     } catch (Exception ex) {
128       $inline$testInlineInsideNestedCatches_inner();
129     }
130   }
131 
132   // Tests that outer tries or catches don't affect as long as we are not inlining the inner
133   // try/catch inside of them.
$noinline$testBeforeAfterTryCatch()134   private static void $noinline$testBeforeAfterTryCatch() {
135     int[] numbers = {};
136     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
137 
138     // Unrelated try catch does not block inlining outside of it. We fill it in to make sure it is
139     // still there by the time the inliner runs.
140     int val = 0;
141     try {
142       int[] other_array = {};
143       val = other_array[0];
144     } catch (Exception ex) {
145       $noinline$assertEquals(0, val);
146       val = 1;
147     }
148     $noinline$assertEquals(1, val);
149 
150     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
151   }
152 
153   // Tests different try catch types in the same outer method.
$noinline$testDifferentTypes()154   private static void $noinline$testDifferentTypes() {
155     int[] numbers = {};
156     $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
157     $noinline$assertEquals(2, $inline$OtherOOBTryCatch(numbers));
158     $noinline$assertEquals(123, $inline$ParseIntTryCatch("123"));
159     $noinline$assertEquals(-1, $inline$ParseIntTryCatch("abc"));
160   }
161 
162   // Tests a raw throw (rather than an instruction that happens to throw).
$noinline$testRawThrow()163   private static void $noinline$testRawThrow() {
164     $noinline$assertEquals(1, $inline$rawThrowCaught());
165   }
166 
167   // Tests a raw throw twice.
$noinline$testRawThrowTwice()168   private static void $noinline$testRawThrowTwice() {
169     $noinline$assertEquals(1, $inline$rawThrowCaught());
170     $noinline$assertEquals(1, $inline$rawThrowCaught());
171   }
172 
173   // Tests that the outer method can successfully catch the throw in the inner method.
$noinline$testThrowCaughtInOuterMethod()174   private static void $noinline$testThrowCaughtInOuterMethod() {
175     int[] numbers = {};
176     $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_simpleTryCatch(numbers));
177     $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(numbers));
178     $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_withFinally(numbers));
179   }
180 
181   // Building blocks for the test functions.
$inline$OOBTryCatch(int[] array)182   private static int $inline$OOBTryCatch(int[] array) {
183     try {
184       return array[0];
185     } catch (Exception e) {
186       return 1;
187     }
188   }
189 
$inline$OtherOOBTryCatch(int[] array)190   private static int $inline$OtherOOBTryCatch(int[] array) {
191     try {
192       return array[0];
193     } catch (Exception e) {
194       return 2;
195     }
196   }
197 
$inline$OOBTryCatchFinally(int[] array)198   private static int $inline$OOBTryCatchFinally(int[] array) {
199     int val = 0;
200     try {
201       val = 1;
202       return array[0];
203     } catch (Exception e) {
204       val = 2;
205     } finally {
206       val = 3;
207     }
208     return val;
209   }
210 
211   // If we make the depthness a parameter, we wouldn't be able to mark as $inline$ and we would
212   // need extra CHECKer statements.
$inline$OOBTryCatchLevel4(int[] array)213   private static int $inline$OOBTryCatchLevel4(int[] array) {
214     return $inline$OOBTryCatchLevel3(array);
215   }
216 
$inline$OOBTryCatchLevel3(int[] array)217   private static int $inline$OOBTryCatchLevel3(int[] array) {
218     return $inline$OOBTryCatchLevel2(array);
219   }
220 
$inline$OOBTryCatchLevel2(int[] array)221   private static int $inline$OOBTryCatchLevel2(int[] array) {
222     return $inline$OOBTryCatchLevel1(array);
223   }
224 
$inline$OOBTryCatchLevel1(int[] array)225   private static int $inline$OOBTryCatchLevel1(int[] array) {
226     return $inline$OOBTryCatch(array);
227   }
228 
DoNotInlineOOBTryCatch(int[] array)229   private static int DoNotInlineOOBTryCatch(int[] array) {
230     try {
231       return array[0];
232     } catch (Exception e) {
233       return 1;
234     }
235   }
236 
unreachable()237   private static void unreachable() {
238     throw new Error("Unreachable");
239   }
240 
$inline$ParseIntTryCatch(String str)241   private static int $inline$ParseIntTryCatch(String str) {
242     try {
243       return Integer.parseInt(str);
244     } catch (NumberFormatException ex) {
245       return -1;
246     }
247   }
248 
$inline$rawThrowCaught()249   private static int $inline$rawThrowCaught() {
250     try {
251       throw new Error();
252     } catch (Error e) {
253       return 1;
254     }
255   }
256 
$inline$testThrowCaughtInOuterMethod_simpleTryCatch(int[] array)257   private static int $inline$testThrowCaughtInOuterMethod_simpleTryCatch(int[] array) {
258     int val = 0;
259     try {
260       $noinline$throwingMethod(array);
261     } catch (Exception ex) {
262       val = 1;
263     }
264     return val;
265   }
266 
$noinline$throwingMethod(int[] array)267   private static int $noinline$throwingMethod(int[] array) {
268     return array[0];
269   }
270 
$inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(int[] array)271   private static int $inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(int[] array) {
272     int val = 0;
273     try {
274       $inline$throwingMethod(array);
275     } catch (Exception ex) {
276       val = 1;
277     }
278     return val;
279   }
280 
$inline$throwingMethod(int[] array)281   private static int $inline$throwingMethod(int[] array) {
282     return array[0];
283   }
284 
$inline$testThrowCaughtInOuterMethod_withFinally(int[] array)285   private static int $inline$testThrowCaughtInOuterMethod_withFinally(int[] array) {
286     int val = 0;
287     try {
288       $noinline$throwingMethodWithFinally(array);
289     } catch (Exception ex) {
290       System.out.println("Our battle it will be legendary!");
291       val = 1;
292     }
293     return val;
294   }
295 
$noinline$throwingMethodWithFinally(int[] array)296   private static int $noinline$throwingMethodWithFinally(int[] array) {
297     try {
298       return array[0];
299     } finally {
300       System.out.println("Finally, a worthy opponent!");
301     }
302   }
303 }
304