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.lang.reflect.Constructor;
20 import java.lang.reflect.Executable;
21 import java.lang.reflect.Method;
22 import java.nio.ByteBuffer;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.concurrent.Semaphore;
28 import java.util.function.Consumer;
29 import java.util.function.Function;
30 import java.util.function.IntConsumer;
31 import java.util.function.Predicate;
32 import java.util.function.Supplier;
33 
34 public class Test1966 {
35   public static final String TARGET_VAR = "TARGET";
36 
37   public static interface TestInterface {
doNothing()38     public default void doNothing() {}
39   }
40   public static class TestClass1 implements TestInterface {
41     public String id;
TestClass1(String id)42     public TestClass1(String id) {
43       this.id = id;
44     }
toString()45     public String toString() {
46       return String.format("TestClass1(\"%s\")", id);
47     }
48 
createInterface(String id)49     public static TestInterface createInterface(String id) {
50       return new TestClass1(id);
51     }
createExact(String id)52     public static TestClass1 createExact(String id) {
53       return new TestClass1(id);
54     }
create(String id)55     public static Object create(String id) {
56       return new TestClass1(id);
57     }
58   }
59 
60   public static class TestClass1ext extends TestClass1 {
TestClass1ext(String id)61     public TestClass1ext(String id) {
62       super(id);
63     }
toString()64     public String toString() {
65       return String.format("TestClass1ext(\"%s\")", super.toString());
66     }
67   }
68   public static class TestClass2 {
69     public String id;
TestClass2(String id)70     public TestClass2(String id) {
71       this.id = id;
72     }
toString()73     public String toString() {
74       return String.format("TestClass2(\"%s\")", id);
75     }
76   }
77   public static class TestClass2impl extends TestClass2 implements TestInterface {
TestClass2impl(String id)78     public TestClass2impl(String id) {
79       super(id);
80     }
toString()81     public String toString() {
82       return String.format("TestClass2impl(\"%s\")", super.toString());
83     }
84   }
85 
reportValue(Object val)86   public static void reportValue(Object val) {
87     System.out.println("\tValue is '" + val +
88                        "' (class: " + (val != null ? val.getClass() : "NULL") + ")");
89   }
90 
91   public static interface SafepointFunction {
invoke(Thread thread, Method target, int slot, int depth)92     public void invoke(Thread thread, Method target, int slot, int depth) throws Exception;
93   }
94 
95   public static interface SetterFunction {
SetVar(Thread t, int depth, int slot, Object v)96     public void SetVar(Thread t, int depth, int slot, Object v);
97   }
98 
GetVar(Thread t, int depth, int slot)99   public static interface GetterFunction { public Object GetVar(Thread t, int depth, int slot); }
100 
101   public static SafepointFunction
NamedSet(final String type, final SetterFunction get, final Object v)102   NamedSet(final String type, final SetterFunction get, final Object v) {
103     return new SafepointFunction() {
104       public void invoke(Thread t, Method method, int slot, int depth) {
105         try {
106           get.SetVar(t, depth, slot, v);
107           System.out.println(this + " on " + method + " set value: " + v);
108         } catch (Exception e) {
109           System.out.println(this + " on " + method + " failed to set value " + v + " due to " +
110                              e.getMessage());
111         }
112       }
113       public String toString() {
114         return "\"Set" + type + "\"";
115       }
116     };
117   }
118 
119   public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
120     return new SafepointFunction() {
121       public void invoke(Thread t, Method method, int slot, int depth) {
122         try {
123           Object res = get.GetVar(t, depth, slot);
124           System.out.println(this + " on " + method + " got value: " + res);
125         } catch (Exception e) {
126           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
127         }
128       }
129       public String toString() {
130         return "\"Get" + type + "\"";
131       }
132     };
133   }
134 
135   public static class TestCase {
136     public final Method target;
137 
138     public TestCase(Method target) {
139       this.target = target;
140     }
141 
142     public static class ThreadPauser implements IntConsumer {
143       public final Semaphore sem_wakeup_main;
144       public final Semaphore sem_wait;
145       public int slot = -1;
146 
147       public ThreadPauser() {
148         sem_wakeup_main = new Semaphore(0);
149         sem_wait = new Semaphore(0);
150       }
151 
152       public void accept(int i) {
153         try {
154           slot = i;
155           sem_wakeup_main.release();
156           sem_wait.acquire();
157         } catch (Exception e) {
158           throw new Error("Error with semaphores!", e);
159         }
160       }
161 
162       public void waitForOtherThreadToPause() throws Exception {
163         sem_wakeup_main.acquire();
164       }
165 
166       public void wakeupOtherThread() throws Exception {
167         sem_wait.release();
168       }
169     }
170 
171     public void exec(final SafepointFunction safepoint) throws Exception {
172       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
173       final ThreadPauser pause = new ThreadPauser();
174       Thread remote = new Thread(() -> {
175         try {
176           target.invoke(null, pause);
177         } catch (Exception e) {
178           throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
179         }
180       }, "remote thread for " + target + " with " + safepoint);
181       remote.start();
182       pause.waitForOtherThreadToPause();
183       try {
184         Suspension.suspend(remote);
185         StackTrace.StackFrameData frame = findStackFrame(remote);
186         safepoint.invoke(remote, target, pause.slot, frame.depth);
187       } finally {
188         Suspension.resume(remote);
189         pause.wakeupOtherThread();
190         remote.join();
191       }
192     }
193 
194     private StackTrace.StackFrameData findStackFrame(Thread thr) {
195       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
196         if (frame.method.equals(target)) {
197           return frame;
198         }
199       }
200       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
201     }
202   }
203   public static Method getMethod(String name) throws Exception {
204     return Class.forName("art_test.TestCases1966").getDeclaredMethod(name, IntConsumer.class);
205   }
206 
207   public static void run() throws Exception {
208     Locals.EnableLocalVariableAccess();
209     final TestCase[] MAIN_TEST_CASES = new TestCase[] {
210       new TestCase(getMethod("ObjectMethod")),
211       new TestCase(getMethod("CastInterfaceMethod")),
212       new TestCase(getMethod("CastExactMethod")),
213       new TestCase(getMethod("InterfaceMethod")),
214       new TestCase(getMethod("ExactClassMethod")),
215       new TestCase(getMethod("PrimitiveMethod")),
216       new TestCase(getMethod("NullMethod")),
217       new TestCase(getMethod("CastExactNullMethod")),
218       new TestCase(getMethod("CastInterfaceNullMethod")),
219     };
220 
221     final SetterFunction set_obj = Locals::SetLocalVariableObject;
222     final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
223       NamedGet("GetObject", Locals::GetLocalVariableObject),
224       NamedSet("Null", set_obj, null),
225       NamedSet("TestClass1", set_obj, new TestClass1("Set TestClass1")),
226       NamedSet("TestClass1ext", set_obj, new TestClass1ext("Set TestClass1ext")),
227       NamedSet("TestClass2", set_obj, new TestClass2("Set TestClass2")),
228       NamedSet("TestClass2impl", set_obj, new TestClass2impl("Set TestClass2impl")),
229     };
230 
231     for (TestCase t : MAIN_TEST_CASES) {
232       for (SafepointFunction s : SAFEPOINTS) {
233         t.exec(s);
234       }
235     }
236   }
237 }
238