Tests for JVMTI can_access_local_variables functions

These are tests for functions gated by can_access_local_variables and
other supporting code.

Test: ./test.py --host -j50
Bug: 34414073
Bug: 36892980
Change-Id: Iba7e9207683372fbe73510442856553510d90933
diff --git a/test/1914-get-local-instance/src/art/Test1914.java b/test/1914-get-local-instance/src/art/Test1914.java
new file mode 100644
index 0000000..c09f519
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/Test1914.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1914 {
+  public static final String TARGET_VAR = "TARGET";
+
+  public static void reportValue(Object val) {
+    System.out.println("\tValue is '" + val + "' (class: "
+        + (val != null ? val.getClass() : "NULL") + ")");
+  }
+
+  public static void StaticMethod(Runnable safepoint) {
+    safepoint.run();
+    reportValue(null);
+  }
+
+  public static native void NativeStaticMethod(Runnable safepoint);
+
+  public static class TargetClass {
+    public String id;
+    public String toString() { return String.format("TargetClass(\"%s\")", id); }
+    public TargetClass(String id) { this.id = id; }
+
+    public void InstanceMethod(Runnable safepoint) {
+      safepoint.run();
+      reportValue(this);
+    }
+
+    public native void NativeInstanceMethod(Runnable safepoint);
+  }
+
+  public static interface SafepointFunction {
+    public void invoke(
+        Thread thread,
+        Method target,
+        int depth) throws Exception;
+  }
+
+  public static interface GetterFunction {
+    public Object GetVar(Thread t, int depth);
+  }
+
+  public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+    return new SafepointFunction() {
+      public void invoke(Thread t, Method method, int depth) {
+        try {
+          Object res = get.GetVar(t, depth);
+          System.out.println(this + " on " + method + " got value: " + res);
+        } catch (Exception e) {
+          System.out.println(this + " on " + method + " failed due to " + e.getMessage());
+        }
+      }
+      public String toString() {
+        return "\"Get" + type + "\"";
+      }
+    };
+  }
+
+  public static class TestCase {
+    public final Object thiz;
+    public final Method target;
+
+    public TestCase(Method target) {
+      this(null, target);
+    }
+    public TestCase(Object thiz, Method target) {
+      this.thiz = thiz;
+      this.target = target;
+    }
+
+    public static class ThreadPauser implements Runnable {
+      public final Semaphore sem_wakeup_main;
+      public final Semaphore sem_wait;
+
+      public ThreadPauser() {
+        sem_wakeup_main = new Semaphore(0);
+        sem_wait = new Semaphore(0);
+      }
+
+      public void run() {
+        try {
+          sem_wakeup_main.release();
+          sem_wait.acquire();
+        } catch (Exception e) {
+          throw new Error("Error with semaphores!", e);
+        }
+      }
+
+      public void waitForOtherThreadToPause() throws Exception {
+        sem_wakeup_main.acquire();
+      }
+
+      public void wakeupOtherThread() throws Exception {
+        sem_wait.release();
+      }
+    }
+
+    public void exec(final SafepointFunction safepoint) throws Exception {
+      System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
+      final ThreadPauser pause = new ThreadPauser();
+      Thread remote = new Thread(
+          () -> {
+            try {
+              target.invoke(thiz, pause);
+            } catch (Exception e) {
+              throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
+            }
+          },
+          "remote thread for " + target + " with " + safepoint);
+      remote.start();
+      pause.waitForOtherThreadToPause();
+      try {
+        Suspension.suspend(remote);
+        StackTrace.StackFrameData frame = findStackFrame(remote);
+        safepoint.invoke(remote, target, frame.depth);
+      } finally {
+        Suspension.resume(remote);
+        pause.wakeupOtherThread();
+        remote.join();
+      }
+    }
+
+    private StackTrace.StackFrameData findStackFrame(Thread thr) {
+      for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+        if (frame.method.equals(target)) {
+          return frame;
+        }
+      }
+      throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+    }
+  }
+
+  public static Method getMethod(Class<?> klass, String name) throws Exception {
+    return klass.getDeclaredMethod(name, Runnable.class);
+  }
+
+  public static void run() throws Exception {
+    Locals.EnableLocalVariableAccess();
+    final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+      new TestCase(null, getMethod(Test1914.class, "StaticMethod")),
+      new TestCase(null, getMethod(Test1914.class, "NativeStaticMethod")),
+      new TestCase(new TargetClass("InstanceMethodObject"),
+                   getMethod(TargetClass.class, "InstanceMethod")),
+      new TestCase(new TargetClass("NativeInstanceMethodObject"),
+                   getMethod(TargetClass.class, "NativeInstanceMethod")),
+    };
+
+    for (TestCase t: MAIN_TEST_CASES) {
+      t.exec(NamedGet("This", Locals::GetLocalInstance));
+    }
+  }
+}
+