Merge "Clean up DexInstuctionIterator."
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 10b3fe1..1218586 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -669,10 +669,10 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips
     case kMips: {
-      mips::PcRelativeFixups* pc_relative_fixups =
-          new (arena) mips::PcRelativeFixups(graph, codegen, stats);
       SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
       GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
+      mips::PcRelativeFixups* pc_relative_fixups =
+          new (arena) mips::PcRelativeFixups(graph, codegen, stats);
       HOptimization* mips_optimizations[] = {
           side_effects,
           gvn,
@@ -696,11 +696,15 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86
     case kX86: {
+      SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
+      GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
       x86::PcRelativeFixups* pc_relative_fixups =
           new (arena) x86::PcRelativeFixups(graph, codegen, stats);
       x86::X86MemoryOperandGeneration* memory_gen =
           new (arena) x86::X86MemoryOperandGeneration(graph, codegen, stats);
       HOptimization* x86_optimizations[] = {
+          side_effects,
+          gvn,
           pc_relative_fixups,
           memory_gen
       };
@@ -710,9 +714,13 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86_64
     case kX86_64: {
+      SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
+      GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
       x86::X86MemoryOperandGeneration* memory_gen =
           new (arena) x86::X86MemoryOperandGeneration(graph, codegen, stats);
       HOptimization* x86_64_optimizations[] = {
+          side_effects,
+          gvn,
           memory_gen
       };
       RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer);
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 7f841fc..f05977a 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -86,20 +86,38 @@
 
 TiMethodCallback gMethodCallback;
 
+// TODO We should make this much more selective in the future so we only return true when we
+// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
+// we can just assume that we care we are loaded at all.
+//
+// Even if we don't keep track of this at the method level we might want to keep track of it at the
+// level of enabled capabilities.
+struct TiMethodInspectionCallback : public art::MethodInspectionCallback {
+  bool IsMethodBeingInspected(art::ArtMethod* method ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    return true;
+  }
+};
+
+TiMethodInspectionCallback gMethodInspectionCallback;
+
 void MethodUtil::Register(EventHandler* handler) {
   gMethodCallback.event_handler = handler;
   art::ScopedThreadStateChange stsc(art::Thread::Current(),
                                     art::ThreadState::kWaitingForDebuggerToAttach);
   art::ScopedSuspendAll ssa("Add method callback");
-  art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
+  art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+  callbacks->AddMethodCallback(&gMethodCallback);
+  callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
 }
 
 void MethodUtil::Unregister() {
   art::ScopedThreadStateChange stsc(art::Thread::Current(),
                                     art::ThreadState::kWaitingForDebuggerToAttach);
   art::ScopedSuspendAll ssa("Remove method callback");
-  art::Runtime* runtime = art::Runtime::Current();
-  runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
+  art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+  callbacks->RemoveMethodCallback(&gMethodCallback);
+  callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
 }
 
 jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a3a45e1..711bc65 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -387,7 +387,7 @@
             ],
             shared_libs: [
                 "libziparchive",
-                "libz-host",
+                "libz",
             ],
         },
     },
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6daec72..b021ff1 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -324,6 +324,7 @@
 bool Dbg::gDebuggerActive = false;
 bool Dbg::gDisposed = false;
 ObjectRegistry* Dbg::gRegistry = nullptr;
+DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback;
 
 // Deoptimization support.
 std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
@@ -341,6 +342,10 @@
 Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
 Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
 
+bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) {
+  return Dbg::IsDebuggerActive();
+}
+
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
@@ -652,6 +657,7 @@
   }
   instrumentation_events_ = 0;
   gDebuggerActive = true;
+  Runtime::Current()->GetRuntimeCallbacks()->AddMethodInspectionCallback(&gDebugActiveCallback);
   LOG(INFO) << "Debugger is active";
 }
 
@@ -689,6 +695,8 @@
         runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
       }
       gDebuggerActive = false;
+      Runtime::Current()->GetRuntimeCallbacks()->RemoveMethodInspectionCallback(
+          &gDebugActiveCallback);
     }
   }
 
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 0be46d6..18126b1 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -34,6 +34,7 @@
 #include "jni.h"
 #include "jvalue.h"
 #include "obj_ptr.h"
+#include "runtime_callbacks.h"
 #include "thread.h"
 #include "thread_state.h"
 
@@ -51,6 +52,12 @@
 class StackVisitor;
 class Thread;
 
+struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
+  bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+
 /*
  * Invoke-during-breakpoint support.
  */
@@ -773,6 +780,8 @@
   // Indicates whether the debugger is making requests.
   static bool gDebuggerActive;
 
+  static DebuggerActiveMethodInspectionCallback gDebugActiveCallback;
+
   // Indicates whether we should drop the JDWP connection because the runtime stops or the
   // debugger called VirtualMachine.Dispose.
   static bool gDisposed;
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 8c27bfe..97a3b71 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -490,10 +490,10 @@
       return false;
     }
 
-    // Before allowing the jump, make sure the debugger is not active to avoid jumping from
-    // interpreter to OSR while e.g. single stepping. Note that we could selectively disable
-    // OSR when single stepping, but that's currently hard to know at this point.
-    if (Dbg::IsDebuggerActive()) {
+    // Before allowing the jump, make sure no code is actively inspecting the method to avoid
+    // jumping from interpreter to OSR while e.g. single stepping. Note that we could selectively
+    // disable OSR when single stepping, but that's currently hard to know at this point.
+    if (Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
       return false;
     }
 
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 88d3f28..f164f7c 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -26,10 +26,6 @@
 
 namespace art {
 
-void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
-  thread_callbacks_.push_back(cb);
-}
-
 template <typename T>
 ALWAYS_INLINE
 static inline void Remove(T* cb, std::vector<T*>* data) {
@@ -39,6 +35,27 @@
   }
 }
 
+void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) {
+  method_inspection_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveMethodInspectionCallback(MethodInspectionCallback* cb) {
+  Remove(cb, &method_inspection_callbacks_);
+}
+
+bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) {
+  for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
+    if (cb->IsMethodBeingInspected(m)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+  thread_callbacks_.push_back(cb);
+}
+
 void RuntimeCallbacks::MonitorContendedLocking(Monitor* m) {
   for (MonitorCallback* cb : monitor_callbacks_) {
     cb->MonitorContendedLocking(m);
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index fa686d3..c936049 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -94,6 +94,18 @@
   virtual ~MonitorCallback() {}
 };
 
+// A callback to let parts of the runtime note that they are currently relying on a particular
+// method remaining in it's current state. Users should not rely on always being called. If multiple
+// callbacks are added the runtime will short-circuit when the first one returns 'true'.
+class MethodInspectionCallback {
+ public:
+  virtual ~MethodInspectionCallback() {}
+
+  // Returns true if the method is being inspected currently and the runtime should not modify it in
+  // potentially dangerous ways (i.e. replace with compiled version, JIT it, etc).
+  virtual bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
 class RuntimeCallbacks {
  public:
   void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -151,6 +163,15 @@
   void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
   void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
+  // on by some code.
+  bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void AddMethodInspectionCallback(MethodInspectionCallback* cb)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  void RemoveMethodInspectionCallback(MethodInspectionCallback* cb)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
@@ -164,6 +185,8 @@
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<MonitorCallback*> monitor_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<MethodInspectionCallback*> method_inspection_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt
new file mode 100644
index 0000000..fed993c
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/expected.txt
@@ -0,0 +1,7 @@
+JNI_OnLoad called
+From GetLocalInt(), value is 42
+isInterpreted? true
+	Value is '42'
+Setting TARGET to 1337
+isInterpreted? true
+	Value is '1337'
diff --git a/test/1935-get-set-current-frame-jit/info.txt b/test/1935-get-set-current-frame-jit/info.txt
new file mode 100644
index 0000000..7342af7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable in the currently executing method frame.
+
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java
new file mode 100644
index 0000000..eb0a637
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/Main.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+import art.Locals;
+import art.StackTrace;
+import art.Suspension;
+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 Main {
+  public static final int SET_VALUE = 1337;
+  public static final String TARGET_VAR = "TARGET";
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    Locals.EnableLocalVariableAccess();
+    runGet();
+    runSet();
+  }
+
+  public static void reportValue(Object val) {
+    System.out.println("\tValue is '" + val + "'");
+  }
+
+  public static class IntRunner implements Runnable {
+    private volatile boolean continueBusyLoop;
+    private volatile boolean inBusyLoop;
+    public IntRunner() {
+      this.continueBusyLoop = true;
+      this.inBusyLoop = false;
+    }
+    public void run() {
+      int TARGET = 42;
+      // We will suspend the thread during this loop.
+      while (continueBusyLoop) {
+        inBusyLoop = true;
+      }
+      int i = 0;
+      while (Main.isInterpreted() && i < 10000) {
+        Main.ensureJitCompiled(IntRunner.class, "run");
+        i++;
+      }
+      // We shouldn't be doing OSR since we are using JVMTI and the get/set local will push us to
+      // interpreter.
+      System.out.println("isInterpreted? " + Main.isInterpreted());
+      reportValue(TARGET);
+    }
+    public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+    public void finish() {
+      continueBusyLoop = false;
+    }
+  }
+
+  public static void runGet() throws Exception {
+    Method target = IntRunner.class.getDeclaredMethod("run");
+    // Get Int
+    IntRunner int_runner = new IntRunner();
+    Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+    target_get.start();
+    int_runner.waitForBusyLoopStart();
+    try {
+      Suspension.suspend(target_get);
+    } catch (Exception e) {
+      System.out.println("FAIL: got " + e);
+      e.printStackTrace();
+      int_runner.finish();
+      target_get.join();
+      return;
+    }
+    try {
+      StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+      int depth = frame.depth;
+      if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+      int slot = FindSlot(frame);
+      int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+      System.out.println("From GetLocalInt(), value is " + value);
+    } finally {
+      Suspension.resume(target_get);
+      int_runner.finish();
+      target_get.join();
+    }
+  }
+
+  public static void runSet() throws Exception {
+    Method target = IntRunner.class.getDeclaredMethod("run");
+    // Set Int
+    IntRunner int_runner = new IntRunner();
+    Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+    target_set.start();
+    int_runner.waitForBusyLoopStart();
+    try {
+      Suspension.suspend(target_set);
+    } catch (Exception e) {
+      System.out.println("FAIL: got " + e);
+      e.printStackTrace();
+      int_runner.finish();
+      target_set.join();
+      return;
+    }
+    try {
+      StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+      int depth = frame.depth;
+      if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+      int slot = FindSlot(frame);
+      System.out.println("Setting TARGET to " + SET_VALUE);
+      Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+    } finally {
+      Suspension.resume(target_set);
+      int_runner.finish();
+      target_set.join();
+    }
+  }
+
+  public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+    long loc = frame.current_location;
+    for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(TARGET_VAR)) {
+        return var.slot;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+  }
+
+  private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+    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 native void ensureJitCompiled(Class k, String f);
+  public static native boolean isInterpreted();
+}
diff --git a/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java b/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+  public static class Manager {
+    public static class BP {
+      public final Executable method;
+      public final long location;
+
+      public BP(Executable method) {
+        this(method, getStartLocation(method));
+      }
+
+      public BP(Executable method, long location) {
+        this.method = method;
+        this.location = location;
+      }
+
+      @Override
+      public boolean equals(Object other) {
+        return (other instanceof BP) &&
+            method.equals(((BP)other).method) &&
+            location == ((BP)other).location;
+      }
+
+      @Override
+      public String toString() {
+        return method.toString() + " @ " + getLine();
+      }
+
+      @Override
+      public int hashCode() {
+        return Objects.hash(method, location);
+      }
+
+      public int getLine() {
+        try {
+          LineNumber[] lines = getLineNumberTable(method);
+          int best = -1;
+          for (LineNumber l : lines) {
+            if (l.location > location) {
+              break;
+            } else {
+              best = l.line;
+            }
+          }
+          return best;
+        } catch (Exception e) {
+          return -1;
+        }
+      }
+    }
+
+    private Set<BP> breaks = new HashSet<>();
+
+    public void setBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.add(b)) {
+          Breakpoint.setBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void setBreakpoint(Executable method, long location) {
+      setBreakpoints(new BP(method, location));
+    }
+
+    public void clearBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.remove(b)) {
+          Breakpoint.clearBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void clearBreakpoint(Executable method, long location) {
+      clearBreakpoints(new BP(method, location));
+    }
+
+    public void clearAllBreakpoints() {
+      clearBreakpoints(breaks.toArray(new BP[0]));
+    }
+  }
+
+  public static void startBreakpointWatch(Class<?> methodClass,
+                                          Executable breakpointReached,
+                                          Thread thr) {
+    startBreakpointWatch(methodClass, breakpointReached, false, thr);
+  }
+
+  /**
+   * Enables the trapping of breakpoint events.
+   *
+   * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+   */
+  public static native void startBreakpointWatch(Class<?> methodClass,
+                                                 Executable breakpointReached,
+                                                 boolean allowRecursive,
+                                                 Thread thr);
+  public static native void stopBreakpointWatch(Thread thr);
+
+  public static final class LineNumber implements Comparable<LineNumber> {
+    public final long location;
+    public final int line;
+
+    private LineNumber(long loc, int line) {
+      this.location = loc;
+      this.line = line;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof LineNumber && ((LineNumber)other).line == line &&
+          ((LineNumber)other).location == location;
+    }
+
+    public int compareTo(LineNumber other) {
+      int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+      if (v != 0) {
+        return v;
+      } else {
+        return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+      }
+    }
+  }
+
+  public static native void setBreakpoint(Executable m, long loc);
+  public static void setBreakpoint(Executable m, LineNumber l) {
+    setBreakpoint(m, l.location);
+  }
+
+  public static native void clearBreakpoint(Executable m, long loc);
+  public static void clearBreakpoint(Executable m, LineNumber l) {
+    clearBreakpoint(m, l.location);
+  }
+
+  private static native Object[] getLineNumberTableNative(Executable m);
+  public static LineNumber[] getLineNumberTable(Executable m) {
+    Object[] nativeTable = getLineNumberTableNative(m);
+    long[] location = (long[])(nativeTable[0]);
+    int[] lines = (int[])(nativeTable[1]);
+    if (lines.length != location.length) {
+      throw new Error("Lines and locations have different lengths!");
+    }
+    LineNumber[] out = new LineNumber[lines.length];
+    for (int i = 0; i < lines.length; i++) {
+      out[i] = new LineNumber(location[i], lines[i]);
+    }
+    return out;
+  }
+
+  public static native long getStartLocation(Executable m);
+
+  public static int locationToLine(Executable m, long location) {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      int best = -1;
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.location > location) {
+          break;
+        } else {
+          best = l.line;
+        }
+      }
+      return best;
+    } catch (Exception e) {
+      return -1;
+    }
+  }
+
+  public static long lineToLocation(Executable m, int line) throws Exception {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.line == line) {
+          return l.location;
+        }
+      }
+      throw new Exception("Unable to find line " + line + " in " + m);
+    } catch (Exception e) {
+      throw new Exception("Unable to get line number info for " + m, e);
+    }
+  }
+}
+
diff --git a/test/1935-get-set-current-frame-jit/src/art/Locals.java b/test/1935-get-set-current-frame-jit/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.Executable;
+import java.util.Objects;
+
+public class Locals {
+  public static native void EnableLocalVariableAccess();
+
+  public static class VariableDescription {
+    public final long start_location;
+    public final int length;
+    public final String name;
+    public final String signature;
+    public final String generic_signature;
+    public final int slot;
+
+    public VariableDescription(
+        long start, int length, String name, String sig, String gen_sig, int slot) {
+      this.start_location = start;
+      this.length = length;
+      this.name = name;
+      this.signature = sig;
+      this.generic_signature = gen_sig;
+      this.slot = slot;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "VariableDescription { " +
+            "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+          "}",
+          this.signature,
+          this.name,
+          this.generic_signature,
+          this.slot,
+          this.start_location,
+          this.length);
+    }
+    public boolean equals(Object other) {
+      if (!(other instanceof VariableDescription)) {
+        return false;
+      } else {
+        VariableDescription v = (VariableDescription)other;
+        return Objects.equals(v.signature, signature) &&
+            Objects.equals(v.name, name) &&
+            Objects.equals(v.generic_signature, generic_signature) &&
+            v.slot == slot &&
+            v.start_location == start_location &&
+            v.length == length;
+      }
+    }
+    public int hashCode() {
+      return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+          this.start_location, this.length);
+    }
+  }
+
+  public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+  public static VariableDescription GetVariableAtLine(
+      Executable e, String name, String sig, int line) throws Exception {
+    return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+  }
+
+  public static VariableDescription GetVariableAtLocation(
+      Executable e, String name, String sig, long loc) {
+    VariableDescription[] vars = GetLocalVariableTable(e);
+    for (VariableDescription var : vars) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(name) &&
+          var.signature.equals(sig)) {
+        return var;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+  }
+
+  public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+  public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+  public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+  public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+  public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+  public static native Object GetLocalInstance(Thread thr, int depth);
+
+  public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+  }
+  public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+  }
+  public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+  }
+  public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+  }
+  public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+  public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+  public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+  public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+  public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1935-get-set-current-frame-jit/src/art/StackTrace.java b/test/1935-get-set-current-frame-jit/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+  public static class StackFrameData {
+    public final Thread thr;
+    public final Executable method;
+    public final long current_location;
+    public final int depth;
+
+    public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+      this.thr = thr;
+      this.method = e;
+      this.current_location = loc;
+      this.depth = depth;
+    }
+    @Override
+    public String toString() {
+      return String.format(
+          "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+          this.thr,
+          this.method,
+          this.current_location,
+          this.depth);
+    }
+  }
+
+  public static native int GetStackDepth(Thread thr);
+
+  private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+  public static StackFrameData[] GetStackTrace(Thread thr) {
+    // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+    // suspended. The spec says that not being suspended is fine but since we want this to be
+    // consistent we will suspend for the RI.
+    boolean suspend_thread =
+        !System.getProperty("java.vm.name").equals("Dalvik") &&
+        !thr.equals(Thread.currentThread()) &&
+        !Suspension.isSuspended(thr);
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1935-get-set-current-frame-jit/src/art/Suspension.java b/test/1935-get-set-current-frame-jit/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/Android.bp b/test/Android.bp
index d2eccb0..2af03e3 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -67,6 +67,7 @@
         "libicuuc",
         "libicui18n",
         "libnativehelper",
+        "libz",
     ],
     whole_static_libs: [
         "libsigchain",
@@ -84,7 +85,6 @@
             ],
             shared_libs: [
                 "libziparchive",
-                "libz-host",
             ],
             cflags: [
                 // gtest issue
@@ -103,7 +103,6 @@
             ],
             shared_libs: [
                 "liblog",
-                "libz",
             ],
             cflags: [
                 // gtest issue