summaryrefslogtreecommitdiff
path: root/test/1976-hello-structural-static-methods/src
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2019-08-13 10:50:38 -0700
committer Treehugger Robot <treehugger-gerrit@google.com> 2019-09-17 23:52:12 +0000
commitc971eafeff43e4e26959a6e86b62ab0a8f1a6e1c (patch)
treef8647487e7465712fd73118ceb89e13167a12648 /test/1976-hello-structural-static-methods/src
parent1ba7e8c10af4e270864a417044244d63db53ccf5 (diff)
Basic structural redefinition support
This adds basic support for adding methods and fields to already loaded classes using redefinition. This 'structural class redefinition' is currently limited to classes without any virtual methods or instance fields. One cannot currently structurally redefine multiple classes at once nor will structural redefinition trigger the standard redefinition events. After structural redefinition all references to the old class, and its fields and methods are atomically updated. Any memory associated with the static fields of the old class is zeroed. Offsets for field access might change. If there are any active stack frames for methods from the redefined class the original (obsolete method) code will continue to execute. The identity hash code of the redefined class will not change. Any locks being held, waited or blocked on by the old class will be transferred to the new class. To use this feature the process must be debuggable and running with -Xopaque-jni-ids:true. For device testing use a wrap.sh that adds the following flags: '-Xopaque-jni-ids:true -Xcompiler-option --debuggable -XjdwpProvider:adbconnection' Structural redefinition only available using the "com.android.art.UNSAFE.class.structurally_redefine_class_direct" extension. This will not trigger the normal class-redefinition events. Only one class may be redefined at a time. NB There are still some holes in this potentially allowing obsolete methods/fields to be visible. Most notably during jni-id, MethodHandle and VarHandle creation as well as potentially other places in the runtime. These holes will be closed by later CLs. Until then the extension to access structural class redefinition will remain tagged as UNSAFE. Test: ./test.py --host --all-compiler Bug: 134162467 Change-Id: I825d3a4bdb9594c0147223ae69f433ce9bbfc307
Diffstat (limited to 'test/1976-hello-structural-static-methods/src')
-rw-r--r--test/1976-hello-structural-static-methods/src/Main.java21
l---------test/1976-hello-structural-static-methods/src/art/Redefinition.java1
-rw-r--r--test/1976-hello-structural-static-methods/src/art/Test1976.java203
-rw-r--r--test/1976-hello-structural-static-methods/src/art/Transform1976.java74
4 files changed, 299 insertions, 0 deletions
diff --git a/test/1976-hello-structural-static-methods/src/Main.java b/test/1976-hello-structural-static-methods/src/Main.java
new file mode 100644
index 0000000000..5abf28f25a
--- /dev/null
+++ b/test/1976-hello-structural-static-methods/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1976.run();
+ }
+}
diff --git a/test/1976-hello-structural-static-methods/src/art/Redefinition.java b/test/1976-hello-structural-static-methods/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1976-hello-structural-static-methods/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1976-hello-structural-static-methods/src/art/Test1976.java b/test/1976-hello-structural-static-methods/src/art/Test1976.java
new file mode 100644
index 0000000000..169d2363b1
--- /dev/null
+++ b/test/1976-hello-structural-static-methods/src/art/Test1976.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package art;
+
+import java.lang.ref.*;
+import java.lang.reflect.*;
+import java.lang.invoke.*;
+import java.util.*;
+
+public class Test1976 {
+
+ // The fact that the target is having methods added makes it annoying to test since we cannot
+ // initially call them. To work around this in a simple-ish way just use (non-structural)
+ // redefinition to change the implementation of the caller of Transform1976 after redefining the
+ // target.
+ public static final class RunTransformMethods implements Runnable {
+ public void run() {
+ System.out.println("Saying everything!");
+ Transform1976.sayEverything();
+ System.out.println("Saying hi!");
+ Transform1976.sayHi();
+ }
+ }
+
+ /* Base64 encoded dex bytes of:
+ * public static final class RunTransformMethods implements Runnable {
+ * public void run() {
+ * System.out.println("Saying everything!");
+ * Transform1976.sayEverything();
+ * System.out.println("Saying hi!");
+ * Transform1976.sayHi();
+ * System.out.println("Saying bye!");
+ * Transform1976.sayBye();
+ * }
+ * }
+ */
+ public static final byte[] RUN_DEX_BYTES =
+ Base64.getDecoder()
+ .decode(
+ "ZGV4CjAzNQCv3eV8jFcpSsqMGl1ZXRk2iraZO41D0TIgBQAAcAAAAHhWNBIAAAAAAAAAAFwEAAAc"
+ + "AAAAcAAAAAsAAADgAAAAAgAAAAwBAAABAAAAJAEAAAcAAAAsAQAAAQAAAGQBAACcAwAAhAEAAAYC"
+ + "AAAOAgAAMgIAAEICAABXAgAAewIAAJsCAACyAgAAxgIAANwCAADwAgAABAMAABkDAAAmAwAAOgMA"
+ + "AEYDAABVAwAAWAMAAFwDAABpAwAAbwMAAHQDAAB9AwAAggMAAIoDAACZAwAAoAMAAKcDAAABAAAA"
+ + "AgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAAEAAAABAAAAAKAAAAAAAAABEAAAAK"
+ + "AAAAAAIAAAkABQAUAAAAAAAAAAAAAAAAAAAAFgAAAAIAAAAXAAAAAgAAABgAAAACAAAAGQAAAAUA"
+ + "AQAVAAAABgAAAAAAAAAAAAAAEQAAAAYAAAD4AQAADwAAAEwEAAAuBAAAAAAAAAEAAQABAAAA6gEA"
+ + "AAQAAABwEAYAAAAOAAMAAQACAAAA7gEAAB8AAABiAAAAGgENAG4gBQAQAHEAAwAAAGIAAAAaAQ4A"
+ + "biAFABAAcQAEAAAAYgAAABoBDABuIAUAEABxAAIAAAAOAAYADgAIAA54PHg8eDwAAQAAAAcAAAAB"
+ + "AAAACAAGPGluaXQ+ACJMYXJ0L1Rlc3QxOTc2JFJ1blRyYW5zZm9ybU1ldGhvZHM7AA5MYXJ0L1Rl"
+ + "c3QxOTc2OwATTGFydC9UcmFuc2Zvcm0xOTc2OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu"
+ + "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9QcmludFN0"
+ + "cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xh"
+ + "bmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABNSdW5UcmFuc2Zvcm1NZXRob2RzAAtTYXlp"
+ + "bmcgYnllIQASU2F5aW5nIGV2ZXJ5dGhpbmchAApTYXlpbmcgaGkhAA1UZXN0MTk3Ni5qYXZhAAFW"
+ + "AAJWTAALYWNjZXNzRmxhZ3MABG5hbWUAA291dAAHcHJpbnRsbgADcnVuAAZzYXlCeWUADXNheUV2"
+ + "ZXJ5dGhpbmcABXNheUhpAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwi"
+ + "bWluLWFwaSI6MSwic2hhLTEiOiJhODM1MmYyNTQ4ODUzNjJjY2Q4ZDkwOWQzNTI5YzYwMDk0ZGQ4"
+ + "OTZlIiwidmVyc2lvbiI6IjEuNi4yMC1kZXYifQACAwEaGAECBAISBBkTFwsAAAEBAIGABIQDAQGc"
+ + "AwAAAAACAAAAHwQAACUEAABABAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAA"
+ + "cAAAAAIAAAALAAAA4AAAAAMAAAACAAAADAEAAAQAAAABAAAAJAEAAAUAAAAHAAAALAEAAAYAAAAB"
+ + "AAAAZAEAAAEgAAACAAAAhAEAAAMgAAACAAAA6gEAAAEQAAACAAAA+AEAAAIgAAAcAAAABgIAAAQg"
+ + "AAACAAAAHwQAAAAgAAABAAAALgQAAAMQAAACAAAAPAQAAAYgAAABAAAATAQAAAAQAAABAAAAXAQA"
+ + "AA==");
+
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
+
+ private static final boolean PRINT_ID_NUM = false;
+
+ public static void printRun(long id, Method m) {
+ if (PRINT_ID_NUM) {
+ System.out.println("Running method " + id + " " + m + " using JNI.");
+ } else {
+ System.out.println("Running method " + m + " using JNI.");
+ }
+ }
+
+ public static final class MethodHandleWrapper {
+ private MethodHandle mh;
+ private Method m;
+ public MethodHandleWrapper(MethodHandle mh, Method m) {
+ this.m = m;
+ this.mh = mh;
+ }
+ public MethodHandle getHandle() {
+ return mh;
+ }
+ public Method getMethod() {
+ return m;
+ }
+ public Object invoke() throws Throwable {
+ return mh.invoke();
+ }
+ public String toString() {
+ return mh.toString();
+ }
+ }
+
+ public static MethodHandleWrapper[] getMethodHandles(Method[] methods) throws Exception {
+ final MethodHandles.Lookup l = MethodHandles.lookup();
+ ArrayList<MethodHandleWrapper> res = new ArrayList<>();
+ for (Method m : methods) {
+ if (!Modifier.isStatic(m.getModifiers())) {
+ continue;
+ }
+ res.add(new MethodHandleWrapper(l.unreflect(m), m));
+ }
+ return res.toArray(new MethodHandleWrapper[0]);
+ }
+
+ public static void runMethodHandles(MethodHandleWrapper[] handles) throws Exception {
+ for (MethodHandleWrapper h : handles) {
+ try {
+ System.out.println("Invoking " + h + " (" + h.getMethod() + ")");
+ h.invoke();
+ } catch (Throwable t) {
+ if (t instanceof Exception) {
+ throw (Exception)t;
+ } else if (t instanceof Error) {
+ throw (Error)t;
+ } else {
+ throw new RuntimeException("Unexpected throwable thrown!", t);
+ }
+ }
+ }
+ }
+
+ public static void doTest() throws Exception {
+ Runnable r = new RunTransformMethods();
+ System.out.println("Running directly");
+ r.run();
+ System.out.println("Running reflective");
+ Method[] methods = Transform1976.class.getDeclaredMethods();
+ for (Method m : methods) {
+ if (Modifier.isStatic(m.getModifiers())) {
+ System.out.println("Reflectively invoking " + m);
+ m.invoke(null);
+ } else {
+ System.out.println("Not invoking non-static method " + m);
+ }
+ }
+ System.out.println("Running jni");
+ long[] mids = getMethodIds(methods);
+ callNativeMethods(Transform1976.class, mids);
+ MethodHandleWrapper[] handles = getMethodHandles(methods);
+ System.out.println("Running method handles");
+ runMethodHandles(handles);
+ Redefinition.doCommonStructuralClassRedefinition(
+ Transform1976.class, Transform1976.REDEFINED_DEX_BYTES);
+ // Change RunTransformMethods to also call the 'runBye' method. No RI support so no classfile
+ // bytes required.
+ Redefinition.doCommonClassRedefinition(RunTransformMethods.class, new byte[] {}, RUN_DEX_BYTES);
+ System.out.println("Running directly after redef");
+ r.run();
+ System.out.println("Running reflective after redef using old j.l.r.Method");
+ for (Method m : methods) {
+ if (Modifier.isStatic(m.getModifiers())) {
+ System.out.println("Reflectively invoking " + m + " on old j.l.r.Method");
+ m.invoke(null);
+ } else {
+ System.out.println("Not invoking non-static method " + m);
+ }
+ }
+ System.out.println("Running reflective after redef using new j.l.r.Method");
+ for (Method m : Transform1976.class.getDeclaredMethods()) {
+ if (Modifier.isStatic(m.getModifiers())) {
+ System.out.println("Reflectively invoking " + m + " on new j.l.r.Method");
+ m.invoke(null);
+ } else {
+ System.out.println("Not invoking non-static method " + m);
+ }
+ }
+ System.out.println("Running jni with old ids");
+ callNativeMethods(Transform1976.class, mids);
+ System.out.println("Running jni with new ids");
+ callNativeMethods(Transform1976.class, getMethodIds(Transform1976.class.getDeclaredMethods()));
+
+ System.out.println("Running method handles using old handles");
+ runMethodHandles(handles);
+ System.out.println("Running method handles using new handles");
+ runMethodHandles(getMethodHandles(Transform1976.class.getDeclaredMethods()));
+ }
+
+ public static native long[] getMethodIds(Method[] m);
+
+ public static native void callNativeMethods(Class<?> k, long[] smethods);
+}
diff --git a/test/1976-hello-structural-static-methods/src/art/Transform1976.java b/test/1976-hello-structural-static-methods/src/art/Transform1976.java
new file mode 100644
index 0000000000..e347711ff6
--- /dev/null
+++ b/test/1976-hello-structural-static-methods/src/art/Transform1976.java
@@ -0,0 +1,74 @@
+/*
+ * 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 java.util.Base64;
+
+public class Transform1976 {
+
+ static {
+ }
+
+ /* Dex file for:
+ * package art;
+ * public class Transform1976 {
+ * static {}
+ * public static byte[] REDEFINED_DEX_BYTES;
+ * public static void sayEverything() {
+ * sayHi();
+ * sayBye();
+ * }
+ * public static void sayBye() {
+ * System.out.println("Bye");
+ * }
+ * public static void sayHi() {
+ * System.out.println("Not saying hi again!");
+ * }
+ * }
+ */
+ public static byte[] REDEFINED_DEX_BYTES =
+ Base64.getDecoder()
+ .decode(
+ "ZGV4CjAzNQBHoxOnl1VNY5YvAENBMpZs9rgNOtJjgZFEBAAAcAAAAHhWNBIAAAAAAAAAAJgDAAAU"
+ + "AAAAcAAAAAcAAADAAAAAAgAAANwAAAACAAAA9AAAAAcAAAAEAQAAAQAAADwBAADoAgAAXAEAAAYC"
+ + "AAAQAgAAGAIAAB0CAAAyAgAASQIAAF0CAABxAgAAhQIAAJsCAACwAgAAxAIAAMcCAADLAgAAzwIA"
+ + "ANQCAADdAgAA5QIAAPQCAAD7AgAAAwAAAAQAAAAFAAAABgAAAAcAAAALAAAADQAAAAsAAAAFAAAA"
+ + "AAAAAAwAAAAFAAAAAAIAAAAABgAJAAAABAABAA4AAAAAAAAAAAAAAAAAAAABAAAAAAAAABAAAAAA"
+ + "AAAAEQAAAAAAAAASAAAAAQABAA8AAAACAAAAAQAAAAAAAAABAAAAAgAAAAAAAAAKAAAAAAAAAHMD"
+ + "AAAAAAAAAAAAAAAAAADoAQAAAQAAAA4AAAABAAEAAQAAAOwBAAAEAAAAcBAGAAAADgACAAAAAgAA"
+ + "APABAAAIAAAAYgABABoBAgBuIAUAEAAOAAAAAAAAAAAA9QEAAAcAAABxAAQAAABxAAIAAAAOAAAA"
+ + "AgAAAAIAAAD7AQAACAAAAGIAAQAaAQgAbiAFABAADgADAA4AAgAOAAoADngABgAOPDwADQAOeAAB"
+ + "AAAAAwAIPGNsaW5pdD4ABjxpbml0PgADQnllABNMYXJ0L1RyYW5zZm9ybTE5NzY7ABVMamF2YS9p"
+ + "by9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJM"
+ + "amF2YS9sYW5nL1N5c3RlbTsAFE5vdCBzYXlpbmcgaGkgYWdhaW4hABNSRURFRklORURfREVYX0JZ"
+ + "VEVTABJUcmFuc2Zvcm0xOTc2LmphdmEAAVYAAlZMAAJbQgADb3V0AAdwcmludGxuAAZzYXlCeWUA"
+ + "DXNheUV2ZXJ5dGhpbmcABXNheUhpAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJt"
+ + "aW4tYXBpIjoxLCJzaGEtMSI6ImE4MzUyZjI1NDg4NTM2MmNjZDhkOTA5ZDM1MjljNjAwOTRkZDg5"
+ + "NmUiLCJ2ZXJzaW9uIjoiMS42LjIwLWRldiJ9AAEABQAACQCIgATcAgGBgATwAgEJiAMBCagDAQnI"
+ + "AwAAAAAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABQAAABwAAAAAgAAAAcAAADAAAAAAwAAAAIAAADc"
+ + "AAAABAAAAAIAAAD0AAAABQAAAAcAAAAEAQAABgAAAAEAAAA8AQAAASAAAAUAAABcAQAAAyAAAAUA"
+ + "AADoAQAAARAAAAEAAAAAAgAAAiAAABQAAAAGAgAAACAAAAEAAABzAwAAAxAAAAEAAACUAwAAABAA"
+ + "AAEAAACYAwAA");
+
+ public static void sayEverything() {
+ sayHi();
+ }
+
+ public static void sayHi() {
+ System.out.println("hello");
+ }
+}