1 /*
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 
17 package art;
18 
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Objects;
22 import java.lang.reflect.Executable;
23 import java.lang.reflect.Method;
24 
25 public class Test1929 {
26   public static boolean PRINT_FULL_EXCEPTION = false;
27   public static ExceptionHandler HANDLER = null;
28   public static Collection<Executable> TEST_METHODS;
29 
doNothing()30   public static void doNothing() {};
31   static {
32     try {
33       TEST_METHODS = Arrays.asList(
34         Test1929.class.getDeclaredMethod("doThrow"),
35         Test1929.class.getDeclaredMethod("throwCatchBaseTestException"),
36         Test1929.class.getDeclaredMethod("throwCatchBaseTestExceptionTwice"),
37         Test1929.class.getDeclaredMethod("throwCatchTestException"),
38         Test1929.class.getDeclaredMethod("throwCatchTestExceptionTwice"),
39         Test1929.class.getDeclaredMethod("throwCatchTestExceptionNoRethrow"));
40     } catch (Exception e) {
41       throw new Error("Unable to list test methods!", e);
42     }
43   }
44 
45   public static interface ExceptionHandler {
exceptionOccurred( Executable m, long loc, Throwable exception)46     public void exceptionOccurred(
47         Executable m, long loc, Throwable exception);
48   }
49 
PrintStack()50   private static void PrintStack() {
51     System.out.println("\tCurrent Stack:");
52     for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) {
53       if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1929.class.getPackage())) {
54         System.out.println("\t\t" + e.method + " @ line = " +
55             Breakpoint.locationToLine(e.method, e.current_location));
56       }
57     }
58   }
59 
ExceptionCatchEvent( Thread thr, Executable method, long location, Throwable exception)60   public static void ExceptionCatchEvent(
61       Thread thr, Executable method, long location, Throwable exception) {
62     System.out.println(thr.getName() + ": " + method + " @ line = " +
63         Breakpoint.locationToLine(method, location) + " caught " +
64         exception.getClass() + ": " + exception.getMessage());
65     PrintStack();
66     if (PRINT_FULL_EXCEPTION) {
67       System.out.print("exception is: ");
68       exception.printStackTrace(System.out);
69     }
70     if (HANDLER != null && TEST_METHODS.contains(method)) {
71       HANDLER.exceptionOccurred(method, location, exception);
72     }
73   }
74 
75   public static class BaseTestException extends Error {
BaseTestException(String e)76     public BaseTestException(String e) { super(e); }
BaseTestException(String e, Throwable t)77     public BaseTestException(String e, Throwable t) { super(e, t); }
78   }
79   public static class TestException extends BaseTestException {
TestException(String e)80     public TestException(String e) { super(e); }
TestException(String e, Throwable t)81     public TestException(String e, Throwable t) { super(e, t); }
82   }
83 
84   public static class TestExceptionNoRethrow extends TestException {
TestExceptionNoRethrow(String e)85     public TestExceptionNoRethrow(String e) { super(e); }
TestExceptionNoRethrow(String e, Throwable t)86     public TestExceptionNoRethrow(String e, Throwable t) { super(e, t); }
87   }
88 
89   public static class DoNothingHandler implements ExceptionHandler {
exceptionOccurred(Executable m, long loc, Throwable exception)90     public void exceptionOccurred(Executable m, long loc, Throwable exception) {
91       System.out.println("\tDoing nothing!");
92       return;
93     }
94   }
95 
96   public static class ThrowCatchBase implements ExceptionHandler {
exceptionOccurred(Executable m, long loc, Throwable exception)97     public void exceptionOccurred(Executable m, long loc, Throwable exception) {
98       System.out.println("\tThrowing BaseTestException and catching it!");
99       try {
100         throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " +
101             Breakpoint.locationToLine(m, loc), exception);
102       } catch (BaseTestException t) {
103         System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
104         if (PRINT_FULL_EXCEPTION) {
105           t.printStackTrace(System.out);
106         }
107       }
108     }
109   }
110   public static class ThrowBaseTestExceptionHandler implements ExceptionHandler {
exceptionOccurred(Executable m, long loc, Throwable exception)111     public void exceptionOccurred(Executable m, long loc, Throwable exception) {
112       System.out.println("\tThrowing BaseTestException!");
113       throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " +
114           Breakpoint.locationToLine(m, loc), exception);
115     }
116   }
117 
118   public static class ThrowTestExceptionNoRethrowHandler implements ExceptionHandler {
exceptionOccurred(Executable m, long loc, Throwable exception)119     public void exceptionOccurred(Executable m, long loc, Throwable exception) {
120       if (exception instanceof TestExceptionNoRethrow) {
121         System.out.println("\tInstance of TestExceptionNoRethrow was thrown. Not throwing again.");
122       } else {
123         System.out.println("\tThrowing TestExceptionNoRethrow!");
124         throw new TestExceptionNoRethrow("ThrowTestExceptionNoRethrowHandler during throw from " +
125             m + " @ line = " + Breakpoint.locationToLine(m, loc), exception);
126       }
127     }
128   }
doThrow()129   public static void doThrow() {
130     throw new TestException("doThrow");
131   }
132 
133   public static class DoThrowClass implements Runnable {
run()134     public void run() { doThrow(); }
135   }
136 
throwCatchBaseTestException()137   public static void throwCatchBaseTestException() {
138     try {
139       throw new TestException("throwCatchBaseTestException");
140     } catch (BaseTestException t) {
141       System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
142       if (PRINT_FULL_EXCEPTION) {
143         t.printStackTrace(System.out);
144       }
145     }
146   }
147 
148   public static class DoThrowCatchBaseTestException implements Runnable {
run()149     public void run() { throwCatchBaseTestException(); }
150   }
151 
152   // dx and d8 do an optimization around catch blocks that (while legal) breaks assumptions
153   // this test relies on so we have the actual implementation be corrected smali. This does work
154   // for RI however.
155 
156   // For reference:
157 
158   // public static final class Impl {
159   //   private Impl() {}
160   //   public static void throwCatchBaseTestExceptionTwiceImpl() {
161   //     try {
162   //       try {
163   //         throw new TestException("throwCatchBaseTestExceptionTwice");
164   //       } catch (BaseTestException t) {
165   //         System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
166   //         if (PRINT_FULL_EXCEPTION) {
167   //           t.printStackTrace(System.out);
168   //         }
169   //       }
170   //     } catch (BaseTestException t) {
171   //       System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
172   //       if (PRINT_FULL_EXCEPTION) {
173   //         t.printStackTrace(System.out);
174   //       }
175   //     }
176   //   }
177 
178   //   public static void throwCatchTestExceptionTwiceImpl() {
179   //     try {
180   //       try {
181   //         throw new TestException("throwCatchTestExceptionTwice");
182   //       } catch (TestException t) {
183   //         System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
184   //         if (PRINT_FULL_EXCEPTION) {
185   //           t.printStackTrace(System.out);
186   //         }
187   //       }
188   //     } catch (TestException t) {
189   //       System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
190   //       if (PRINT_FULL_EXCEPTION) {
191   //         t.printStackTrace(System.out);
192   //       }
193   //     }
194   //   }
195   // }
196 
throwCatchBaseTestExceptionTwice()197   public static void throwCatchBaseTestExceptionTwice() {
198     // The implementation of this has to change depending upon the runtime slightly due to compiler
199     // optimizations present in DX/D8/Jack.
200     try {
201       Class<?> Impl = Class.forName("art.Test1929$Impl");
202       Method m = Impl.getMethod("throwCatchBaseTestExceptionTwiceImpl");
203       m.invoke(null);
204     } catch (Exception e) {
205       e.printStackTrace(System.out);
206     }
207   }
208 
209   public static class DoThrowCatchBaseTestExceptionTwice implements Runnable {
run()210     public void run() { throwCatchBaseTestExceptionTwice(); }
211   }
212 
throwCatchTestException()213   public static void throwCatchTestException() {
214     try {
215       throw new TestException("throwCatchTestException");
216     } catch (TestException t) {
217       System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
218       if (PRINT_FULL_EXCEPTION) {
219         t.printStackTrace(System.out);
220       }
221     }
222   }
223 
224   public static class DoThrowCatchTestException implements Runnable {
run()225     public void run() { throwCatchTestException(); }
226   }
227 
throwCatchTestExceptionTwice()228   public static void throwCatchTestExceptionTwice() {
229     // The implementation of this has to change depending upon the runtime slightly due to compiler
230     // optimizations present in DX/D8/Jack.
231     try {
232       Class<?> Impl = Class.forName("art.Test1929$Impl");
233       Method m = Impl.getMethod("throwCatchTestExceptionTwiceImpl");
234       m.invoke(null);
235     } catch (Exception e) {
236       e.printStackTrace(System.out);
237     }
238   }
239 
240   public static class DoThrowCatchTestExceptionTwice implements Runnable {
run()241     public void run() { throwCatchTestExceptionTwice(); }
242   }
243 
throwCatchTestExceptionNoRethrow()244   public static void throwCatchTestExceptionNoRethrow() {
245     try {
246       throw new TestException("throwCatchTestExceptionNoRethrow");
247     } catch (TestExceptionNoRethrow t) {
248       System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
249       if (PRINT_FULL_EXCEPTION) {
250         t.printStackTrace(System.out);
251       }
252     }
253   }
254 
255   public static class DoThrowCatchTestExceptionNoRethrow implements Runnable {
run()256     public void run() { throwCatchTestExceptionNoRethrow(); }
257   }
258 
run()259   public static void run() throws Exception {
260     // Set up breakpoints
261     Exceptions.setupExceptionTracing(
262         Test1929.class,
263         TestException.class,
264         null,
265         Test1929.class.getDeclaredMethod(
266             "ExceptionCatchEvent",
267             Thread.class,
268             Executable.class,
269             Long.TYPE,
270             Throwable.class));
271     Exceptions.enableExceptionCatchEvent(Thread.currentThread());
272 
273     ExceptionHandler[] handlers = new ExceptionHandler[] {
274       new DoNothingHandler(),
275       new ThrowCatchBase(),
276       new ThrowBaseTestExceptionHandler(),
277       new ThrowTestExceptionNoRethrowHandler(),
278     };
279 
280     Runnable[] tests = new Runnable[] {
281       new DoThrowClass(),
282       new DoThrowCatchBaseTestException(),
283       new DoThrowCatchBaseTestExceptionTwice(),
284       new DoThrowCatchTestException(),
285       new DoThrowCatchTestExceptionTwice(),
286       new DoThrowCatchTestExceptionNoRethrow(),
287     };
288 
289     for (ExceptionHandler handler : handlers) {
290       for (Runnable test : tests) {
291         try {
292           HANDLER = handler;
293           System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
294               test.getClass().getName(), handler.getClass().getName());
295           test.run();
296           System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
297               test.getClass().getName(), handler.getClass().getName());
298         } catch (Throwable e) {
299           System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
300               test.getClass().getName(),
301               e.getClass().getName(),
302               e.getMessage(),
303               handler.getClass().getName());
304           if (PRINT_FULL_EXCEPTION) {
305             e.printStackTrace(System.out);
306           }
307         }
308         System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n",
309             test.getClass().getName(), handler.getClass().getName());
310         HANDLER = null;
311       }
312     }
313     Exceptions.disableExceptionCatchEvent(Thread.currentThread());
314   }
315 }
316