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/d8/jack all 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   public static final class Impl {
Impl()156     private Impl() {}
throwCatchBaseTestExceptionTwiceImpl()157     public static void throwCatchBaseTestExceptionTwiceImpl() {
158       try {
159         try {
160           throw new TestException("throwCatchBaseTestExceptionTwice");
161         } catch (BaseTestException t) {
162           System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
163           if (PRINT_FULL_EXCEPTION) {
164             t.printStackTrace(System.out);
165           }
166         }
167       } catch (BaseTestException t) {
168         System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
169         if (PRINT_FULL_EXCEPTION) {
170           t.printStackTrace(System.out);
171         }
172       }
173     }
174 
throwCatchTestExceptionTwiceImpl()175     public static void throwCatchTestExceptionTwiceImpl() {
176       try {
177         try {
178           throw new TestException("throwCatchTestExceptionTwice");
179         } catch (TestException t) {
180           System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
181           if (PRINT_FULL_EXCEPTION) {
182             t.printStackTrace(System.out);
183           }
184         }
185       } catch (TestException t) {
186         System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
187         if (PRINT_FULL_EXCEPTION) {
188           t.printStackTrace(System.out);
189         }
190       }
191     }
192   }
193 
throwCatchBaseTestExceptionTwice()194   public static void throwCatchBaseTestExceptionTwice() {
195     // The implementation of this has to change depending upon the runtime slightly due to compiler
196     // optimizations present in DX/D8/Jack.
197     Impl.throwCatchBaseTestExceptionTwiceImpl();
198   }
199 
200   public static class DoThrowCatchBaseTestExceptionTwice implements Runnable {
run()201     public void run() { throwCatchBaseTestExceptionTwice(); }
202   }
203 
throwCatchTestException()204   public static void throwCatchTestException() {
205     try {
206       throw new TestException("throwCatchTestException");
207     } catch (TestException t) {
208       System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
209       if (PRINT_FULL_EXCEPTION) {
210         t.printStackTrace(System.out);
211       }
212     }
213   }
214 
215   public static class DoThrowCatchTestException implements Runnable {
run()216     public void run() { throwCatchTestException(); }
217   }
218 
throwCatchTestExceptionTwice()219   public static void throwCatchTestExceptionTwice() {
220     // The implementation of this has to change depending upon the runtime slightly due to compiler
221     // optimizations present in DX/D8/Jack.
222     Impl.throwCatchTestExceptionTwiceImpl();
223   }
224 
225   public static class DoThrowCatchTestExceptionTwice implements Runnable {
run()226     public void run() { throwCatchTestExceptionTwice(); }
227   }
228 
throwCatchTestExceptionNoRethrow()229   public static void throwCatchTestExceptionNoRethrow() {
230     try {
231       throw new TestException("throwCatchTestExceptionNoRethrow");
232     } catch (TestExceptionNoRethrow t) {
233       System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
234       if (PRINT_FULL_EXCEPTION) {
235         t.printStackTrace(System.out);
236       }
237     }
238   }
239 
240   public static class DoThrowCatchTestExceptionNoRethrow implements Runnable {
run()241     public void run() { throwCatchTestExceptionNoRethrow(); }
242   }
243 
run()244   public static void run() throws Exception {
245     // Set up breakpoints
246     Exceptions.setupExceptionTracing(
247         Test1929.class,
248         TestException.class,
249         null,
250         Test1929.class.getDeclaredMethod(
251             "ExceptionCatchEvent",
252             Thread.class,
253             Executable.class,
254             Long.TYPE,
255             Throwable.class));
256     Exceptions.enableExceptionCatchEvent(Thread.currentThread());
257 
258     ExceptionHandler[] handlers = new ExceptionHandler[] {
259       new DoNothingHandler(),
260       new ThrowCatchBase(),
261       new ThrowBaseTestExceptionHandler(),
262       new ThrowTestExceptionNoRethrowHandler(),
263     };
264 
265     Runnable[] tests = new Runnable[] {
266       new DoThrowClass(),
267       new DoThrowCatchBaseTestException(),
268       new DoThrowCatchBaseTestExceptionTwice(),
269       new DoThrowCatchTestException(),
270       new DoThrowCatchTestExceptionTwice(),
271       new DoThrowCatchTestExceptionNoRethrow(),
272     };
273 
274     for (ExceptionHandler handler : handlers) {
275       for (Runnable test : tests) {
276         try {
277           HANDLER = handler;
278           System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
279               test.getClass().getName(), handler.getClass().getName());
280           test.run();
281           System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
282               test.getClass().getName(), handler.getClass().getName());
283         } catch (Throwable e) {
284           System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
285               test.getClass().getName(),
286               e.getClass().getName(),
287               e.getMessage(),
288               handler.getClass().getName());
289           if (PRINT_FULL_EXCEPTION) {
290             e.printStackTrace(System.out);
291           }
292         }
293         System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n",
294             test.getClass().getName(), handler.getClass().getName());
295         HANDLER = null;
296       }
297     }
298     Exceptions.disableExceptionCatchEvent(Thread.currentThread());
299   }
300 }
301