Add test for local-refs being updated by structural-redef

We don't have a good test that local-JNI-refs are correctly updated by
structural redefinition. This adds such a test.

Test: ./test.py --host
Change-Id: I56697fe1a4f9d726b5666c7be2d4036ee248bc58
diff --git a/test/2009-structural-local-ref/expected.txt b/test/2009-structural-local-ref/expected.txt
new file mode 100644
index 0000000..3ad1fbd
--- /dev/null
+++ b/test/2009-structural-local-ref/expected.txt
@@ -0,0 +1,8 @@
+Doing redefinition for instance field
+Result was VirtualString
+Doing redefinition for static field
+Result was StaticString
+Doing redefinition for instance method
+Result was meth
+Doing redefinition for static method
+Result was static-meth
diff --git a/test/2009-structural-local-ref/info.txt b/test/2009-structural-local-ref/info.txt
new file mode 100644
index 0000000..4c9f871
--- /dev/null
+++ b/test/2009-structural-local-ref/info.txt
@@ -0,0 +1,3 @@
+Tests structural redefinition with local-refs
+
+Tests that using the structural redefinition updates JNI local-refs.
diff --git a/test/2009-structural-local-ref/local-ref.cc b/test/2009-structural-local-ref/local-ref.cc
new file mode 100644
index 0000000..9f6ef0b
--- /dev/null
+++ b/test/2009-structural-local-ref/local-ref.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <stdio.h>
+
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "jni.h"
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test2009StructuralLocalRef {
+
+extern "C" JNIEXPORT jstring JNICALL Java_art_Test2009_NativeLocalCallStatic(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jobject thnk) {
+  jclass obj_klass = env->GetObjectClass(obj);
+  jmethodID run_meth = env->GetMethodID(env->FindClass("java/lang/Runnable"), "run", "()V");
+  env->CallVoidMethod(thnk, run_meth);
+  jmethodID new_method =
+      env->GetStaticMethodID(obj_klass, "getGreetingStatic", "()Ljava/lang/String;");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else {
+    return reinterpret_cast<jstring>(env->CallStaticObjectMethod(obj_klass, new_method));
+  }
+}
+
+extern "C" JNIEXPORT jstring JNICALL Java_art_Test2009_NativeLocalCallVirtual(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jobject thnk) {
+  jclass obj_klass = env->GetObjectClass(obj);
+  jmethodID run_meth = env->GetMethodID(env->FindClass("java/lang/Runnable"), "run", "()V");
+  env->CallVoidMethod(thnk, run_meth);
+  jmethodID new_method = env->GetMethodID(obj_klass, "getGreeting", "()Ljava/lang/String;");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else {
+    return reinterpret_cast<jstring>(env->CallObjectMethod(obj, new_method));
+  }
+}
+extern "C" JNIEXPORT jstring JNICALL Java_art_Test2009_NativeLocalGetIField(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jobject thnk) {
+  jclass obj_klass = env->GetObjectClass(obj);
+  jmethodID run_meth = env->GetMethodID(env->FindClass("java/lang/Runnable"), "run", "()V");
+  env->CallVoidMethod(thnk, run_meth);
+  jfieldID new_field = env->GetFieldID(obj_klass, "greeting", "Ljava/lang/String;");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else {
+    env->SetObjectField(obj, new_field, env->NewStringUTF("VirtualString"));
+    return reinterpret_cast<jstring>(env->GetObjectField(obj, new_field));
+  }
+}
+extern "C" JNIEXPORT jstring JNICALL Java_art_Test2009_NativeLocalGetSField(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jobject thnk) {
+  jclass obj_klass = env->GetObjectClass(obj);
+  jmethodID run_meth = env->GetMethodID(env->FindClass("java/lang/Runnable"), "run", "()V");
+  env->CallVoidMethod(thnk, run_meth);
+  jfieldID new_field = env->GetStaticFieldID(obj_klass, "static_greeting", "Ljava/lang/String;");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else {
+    env->SetStaticObjectField(obj_klass, new_field, env->NewStringUTF("StaticString"));
+    return reinterpret_cast<jstring>(env->GetStaticObjectField(obj_klass, new_field));
+  }
+}
+
+}  // namespace Test2009StructuralLocalRef
+}  // namespace art
diff --git a/test/2009-structural-local-ref/run b/test/2009-structural-local-ref/run
new file mode 100755
index 0000000..03e41a5
--- /dev/null
+++ b/test/2009-structural-local-ref/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2009-structural-local-ref/src-art/Main.java b/test/2009-structural-local-ref/src-art/Main.java
new file mode 100644
index 0000000..6635228
--- /dev/null
+++ b/test/2009-structural-local-ref/src-art/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test2009.run();
+  }
+}
diff --git a/test/2009-structural-local-ref/src-art/art/Redefinition.java b/test/2009-structural-local-ref/src-art/art/Redefinition.java
new file mode 120000
index 0000000..81eaf31
--- /dev/null
+++ b/test/2009-structural-local-ref/src-art/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/2009-structural-local-ref/src-art/art/Test2009.java b/test/2009-structural-local-ref/src-art/art/Test2009.java
new file mode 100644
index 0000000..7ef9a8b
--- /dev/null
+++ b/test/2009-structural-local-ref/src-art/art/Test2009.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 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 dalvik.system.InMemoryDexClassLoader;
+import java.lang.ref.*;
+import java.nio.ByteBuffer;
+import java.util.Base64;
+import java.util.concurrent.atomic.*;
+
+public class Test2009 {
+  public static class Transform {
+    public Transform() {}
+  }
+  /*
+   * base64 encoded class/dex file for
+   *
+   * package art;
+   * public class Transform {
+   *   public Transform() { }
+   * }
+   */
+  private static final byte[] DEX_BYTES_INITIAL =
+      Base64.getDecoder()
+          .decode(
+              "ZGV4CjAzNQBMYVKB9B8EiEj/K5pUWVbEqHPGshupr2RkAgAAcAAAAHhWNBIAAAAAAAAAANABAAAG"
+                  + "AAAAcAAAAAMAAACIAAAAAQAAAJQAAAAAAAAAAAAAAAIAAACgAAAAAQAAALAAAACUAQAA0AAAAPAA"
+                  + "AAD4AAAACQEAAB0BAAAtAQAAMAEAAAEAAAACAAAABAAAAAQAAAACAAAAAAAAAAAAAAAAAAAAAQAA"
+                  + "AAAAAAAAAAAAAQAAAAEAAAAAAAAAAwAAAAAAAAC/AQAAAAAAAAEAAQABAAAA6AAAAAQAAABwEAEA"
+                  + "AAAOAAMADjwAAAAABjxpbml0PgAPTGFydC9UcmFuc2Zvcm07ABJMamF2YS9sYW5nL09iamVjdDsA"
+                  + "DlRyYW5zZm9ybS5qYXZhAAFWAIwBfn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwiaGFz"
+                  + "LWNoZWNrc3VtcyI6ZmFsc2UsIm1pbi1hcGkiOjEsInNoYS0xIjoiZDFkNTFjMWNiM2U4NWFhMzBl"
+                  + "MDBhNjgyMmNjYTgzYmJlMWRmZTk0NSIsInZlcnNpb24iOiIyLjAuMTMtZGV2In0AAAABAACBgATQ"
+                  + "AQAAAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAYAAABwAAAAAgAAAAMAAACIAAAAAwAAAAEAAACU"
+                  + "AAAABQAAAAIAAACgAAAABgAAAAEAAACwAAAAASAAAAEAAADQAAAAAyAAAAEAAADoAAAAAiAAAAYA"
+                  + "AADwAAAAACAAAAEAAAC/AQAAAxAAAAEAAADMAQAAABAAAAEAAADQAQAA");
+
+  /*
+   * base64 encoded class/dex file for
+   * package art;
+   * public static class Transform {
+   *   public String greeting;
+   *   public static String static_greeting;
+   *
+   *   public Transform() {
+   *     greeting = "Hello";
+   *   }
+   *   public static String getGreetingStatic() {
+   *     static_greeting = "static-meth";
+   *     return static_greeting;
+   *   }
+   *   public String getGreeting() { greeting = "meth"; return greeting; }
+   * }
+   */
+  private static final byte[] DEX_BYTES =
+      Base64.getDecoder()
+          .decode(
+              "ZGV4CjAzNQB6kDahLt0Aoqc///gYs0Vgd/hpukfKc5mEAwAAcAAAAHhWNBIAAAAAAAAAAOQCAAAP"
+                  + "AAAAcAAAAAQAAACsAAAAAgAAALwAAAACAAAA1AAAAAQAAADkAAAAAQAAAAQBAABgAgAAJAEAAIwB"
+                  + "AACUAQAAmwEAAJ4BAACvAQAAwwEAANcBAADnAQAA6gEAAPcBAAAKAgAAFAIAABoCAAAnAgAAOAIA"
+                  + "AAMAAAAEAAAABQAAAAcAAAACAAAAAgAAAAAAAAAHAAAAAwAAAAAAAAAAAAIACgAAAAAAAgANAAAA"
+                  + "AAABAAAAAAAAAAAACAAAAAAAAAAJAAAAAQABAAAAAAAAAAAAAQAAAAEAAAAAAAAABgAAAAAAAADH"
+                  + "AgAAAAAAAAIAAQAAAAAAhgEAAAUAAAAaAAsAWxAAABEAAAABAAAAAAAAAIIBAAAFAAAAGgAMAGkA"
+                  + "AQARAAAAAgABAAEAAAB8AQAACAAAAHAQAwABABoAAQBbEAAADgAGAA48SwAJAA4ACgAOAAAABjxp"
+                  + "bml0PgAFSGVsbG8AAUwAD0xhcnQvVHJhbnNmb3JtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2"
+                  + "YS9sYW5nL1N0cmluZzsADlRyYW5zZm9ybS5qYXZhAAFWAAtnZXRHcmVldGluZwARZ2V0R3JlZXRp"
+                  + "bmdTdGF0aWMACGdyZWV0aW5nAARtZXRoAAtzdGF0aWMtbWV0aAAPc3RhdGljX2dyZWV0aW5nAIwB"
+                  + "fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwiaGFzLWNoZWNrc3VtcyI6ZmFsc2UsIm1p"
+                  + "bi1hcGkiOjEsInNoYS0xIjoiZDFkNTFjMWNiM2U4NWFhMzBlMDBhNjgyMmNjYTgzYmJlMWRmZTk0"
+                  + "NSIsInZlcnNpb24iOiIyLjAuMTMtZGV2In0AAQECAQEJAAEAgYAE3AICCcACAQGkAgAAAAAAAAAN"
+                  + "AAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAQAAACsAAAAAwAAAAIAAAC8AAAABAAAAAIA"
+                  + "AADUAAAABQAAAAQAAADkAAAABgAAAAEAAAAEAQAAASAAAAMAAAAkAQAAAyAAAAMAAAB8AQAAAiAA"
+                  + "AA8AAACMAQAAACAAAAEAAADHAgAAAxAAAAEAAADgAgAAABAAAAEAAADkAgAA");
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest();
+  }
+
+  public static Class<?> MakeClass() throws Exception {
+    return new InMemoryDexClassLoader(
+            ByteBuffer.wrap(DEX_BYTES_INITIAL), Test2009.class.getClassLoader())
+        .loadClass("art.Transform");
+  }
+
+  public static void doTest() throws Exception {
+    // Make a transform
+    Class<?> ifields = MakeClass();
+    String res =
+        NativeLocalGetIField(
+            ifields.newInstance(),
+            () -> {
+              System.out.println("Doing redefinition for instance field");
+              Redefinition.doCommonStructuralClassRedefinition(ifields, DEX_BYTES);
+            });
+    System.out.println("Result was " + res);
+    Class<?> sfields = MakeClass();
+    res =
+        NativeLocalGetSField(
+            sfields.newInstance(),
+            () -> {
+              System.out.println("Doing redefinition for static field");
+              Redefinition.doCommonStructuralClassRedefinition(sfields, DEX_BYTES);
+            });
+    System.out.println("Result was " + res);
+    Class<?> imeths = MakeClass();
+    res =
+        NativeLocalCallVirtual(
+            imeths.newInstance(),
+            () -> {
+              System.out.println("Doing redefinition for instance method");
+              Redefinition.doCommonStructuralClassRedefinition(imeths, DEX_BYTES);
+            });
+    System.out.println("Result was " + res);
+    Class<?> smeths = MakeClass();
+    res =
+        NativeLocalCallStatic(
+            smeths.newInstance(),
+            () -> {
+              System.out.println("Doing redefinition for static method");
+              Redefinition.doCommonStructuralClassRedefinition(smeths, DEX_BYTES);
+            });
+    System.out.println("Result was " + res);
+  }
+
+  public static native String NativeLocalCallVirtual(Object t, Runnable thnk);
+
+  public static native String NativeLocalCallStatic(Object t, Runnable thnk);
+
+  public static native String NativeLocalGetIField(Object t, Runnable thnk);
+
+  public static native String NativeLocalGetSField(Object t, Runnable thnk);
+}
diff --git a/test/2009-structural-local-ref/src/Main.java b/test/2009-structural-local-ref/src/Main.java
new file mode 100644
index 0000000..89b8557
--- /dev/null
+++ b/test/2009-structural-local-ref/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.out.println("FAIL: Test is only for art!");
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index d33d4bf..89538c6 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -371,6 +371,7 @@
         "1975-hello-structural-transformation/structural_transform.cc",
         "1976-hello-structural-static-methods/structural_transform_methods.cc",
         "2005-pause-all-redefine-multithreaded/pause-all.cc",
+        "2009-structural-local-ref/local-ref.cc",
     ],
     // Use NDK-compatible headers for ctstiagent.
     header_libs: [