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