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.InvocationHandler;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.nio.ByteBuffer;
25 import java.util.concurrent.Semaphore;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.function.Function;
31 import java.util.function.Predicate;
32 import java.util.function.Supplier;
33 import java.util.function.Consumer;
34 
35 public class Test1914 {
36   public static final String TARGET_VAR = "TARGET";
37 
reportValue(Object val)38   public static void reportValue(Object val) {
39     System.out.println("\tValue is '" + val + "' (class: "
40         + (val != null ? (val instanceof Proxy ? "PROXY CLASS" : val.getClass()) : "NULL") + ")");
41   }
42 
StaticMethod(Runnable safepoint)43   public static void StaticMethod(Runnable safepoint) {
44     safepoint.run();
45     reportValue(null);
46   }
47 
NativeStaticMethod(Runnable safepoint)48   public static native void NativeStaticMethod(Runnable safepoint);
49 
50   public static class TargetClass {
51     public String id;
toString()52     public String toString() { return String.format("TargetClass(\"%s\")", id); }
TargetClass(String id)53     public TargetClass(String id) { this.id = id; }
54 
InstanceMethod(Runnable safepoint)55     public void InstanceMethod(Runnable safepoint) {
56       safepoint.run();
57       reportValue(this);
58     }
59 
NativeInstanceMethod(Runnable safepoint)60     public native void NativeInstanceMethod(Runnable safepoint);
61   }
62 
63   public static interface SafepointFunction {
invoke( Thread thread, Method target, int depth)64     public void invoke(
65         Thread thread,
66         Method target,
67         int depth) throws Exception;
68   }
69 
70   public static interface GetterFunction {
GetVar(Thread t, int depth)71     public Object GetVar(Thread t, int depth);
72   }
73 
NamedGet(final String type, final GetterFunction get)74   public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
75     return new SafepointFunction() {
76       public void invoke(Thread t, Method method, int depth) {
77         try {
78           Object res = get.GetVar(t, depth);
79           System.out.println(this + " on " + method + " got value: " + res);
80         } catch (Exception e) {
81           System.out.println(this + " on " + method + " failed due to " + e.getMessage());
82         }
83       }
84       public String toString() {
85         return "\"Get" + type + "\"";
86       }
87     };
88   }
89 
90   public static class TestCase {
91     public final Object thiz;
92     public final Method target;
93 
94     public TestCase(Method target) {
95       this(null, target);
96     }
97     public TestCase(Object thiz, Method target) {
98       this.thiz = thiz;
99       this.target = target;
100     }
101 
102     public static class ThreadPauser implements Runnable {
103       public final Semaphore sem_wakeup_main;
104       public final Semaphore sem_wait;
105 
106       public ThreadPauser() {
107         sem_wakeup_main = new Semaphore(0);
108         sem_wait = new Semaphore(0);
109       }
110 
111       public void run() {
112         try {
113           sem_wakeup_main.release();
114           sem_wait.acquire();
115         } catch (Exception e) {
116           throw new Error("Error with semaphores!", e);
117         }
118       }
119 
120       public void waitForOtherThreadToPause() throws Exception {
121         sem_wakeup_main.acquire();
122       }
123 
124       public void wakeupOtherThread() throws Exception {
125         sem_wait.release();
126       }
127     }
128 
129     public void exec(final SafepointFunction safepoint) throws Exception {
130       System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
131       final ThreadPauser pause = new ThreadPauser();
132       Thread remote = new Thread(
133           () -> {
134             try {
135               target.invoke(thiz, pause);
136             } catch (Exception e) {
137               throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
138             }
139           },
140           "remote thread for " + target + " with " + safepoint);
141       remote.start();
142       pause.waitForOtherThreadToPause();
143       try {
144         Suspension.suspend(remote);
145         StackTrace.StackFrameData frame = findStackFrame(remote);
146         safepoint.invoke(remote, target, frame.depth);
147       } finally {
148         Suspension.resume(remote);
149         pause.wakeupOtherThread();
150         remote.join();
151       }
152     }
153 
154     private StackTrace.StackFrameData findStackFrame(Thread thr) {
155       for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
156         if (frame.method.equals(target) ||
157             (frame.method.getName().equals(target.getName()) &&
158              Arrays.deepEquals(frame.method.getParameterTypes(), target.getParameterTypes()) &&
159              ((Method)frame.method).getReturnType().equals(target.getReturnType()))) {
160           return frame;
161         }
162       }
163       throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
164     }
165   }
166 
167   public static Method getMethod(Class<?> klass, String name) throws Exception {
168     return klass.getDeclaredMethod(name, Runnable.class);
169   }
170 
171   public static interface Foo {
172     public void InterfaceProxyMethod(Runnable r);
173   }
174 
175   public static Object getProxyObject(final Class... k) {
176     return Proxy.newProxyInstance(
177         Test1914.class.getClassLoader(),
178         k,
179         (p, m, a) -> {
180           if (m.getName().equals("toString")) {
181             return "Proxy for " + Arrays.toString(k);
182           } else {
183             ((Runnable)a[0]).run();
184             reportValue(p);
185             return null;
186           }
187         });
188   }
189 
190   public static void run() throws Exception {
191     Locals.EnableLocalVariableAccess();
192     final TestCase[] MAIN_TEST_CASES = new TestCase[] {
193       new TestCase(null, getMethod(Test1914.class, "StaticMethod")),
194       new TestCase(null, getMethod(Test1914.class, "NativeStaticMethod")),
195       new TestCase(new TargetClass("InstanceMethodObject"),
196                    getMethod(TargetClass.class, "InstanceMethod")),
197       new TestCase(new TargetClass("NativeInstanceMethodObject"),
198                    getMethod(TargetClass.class, "NativeInstanceMethod")),
199       new TestCase(getProxyObject(Foo.class),
200                    getMethod(Foo.class, "InterfaceProxyMethod")),
201     };
202 
203     for (TestCase t: MAIN_TEST_CASES) {
204       t.exec(NamedGet("This", Locals::GetLocalInstance));
205     }
206   }
207 }
208 
209