Main.java revision 21b0c29e6a7e5a9d2d31db27aea2fa8615d56f3b
1/*
2 * Copyright (C) 2016 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
17import static art.Redefinition.doCommonClassRedefinition;
18
19import java.util.Base64;
20import java.util.function.Consumer;
21import java.lang.reflect.Method;
22
23public class Main {
24
25  // import java.util.function.Consumer;
26  // class Transform {
27  //   public void sayHi(int recur, Consumer<String> reporter, Runnable r) {
28  //     reporter.accept("Hello" + recur + " - transformed");
29  //     if (recur == 1) {
30  //       r.run();
31  //       sayHi(recur - 1, reporter, r);
32  //     } else if (recur != 0) {
33  //       sayHi(recur - 1, reporter, r);
34  //     }
35  //     reporter.accept("Goodbye" + recur + " - transformed");
36  //   }
37  // }
38  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
39    "yv66vgAAADQAMwoADgAaBwAbCgACABoIABwKAAIAHQoAAgAeCAAfCgACACALACEAIgsAIwAkCgAN" +
40    "ACUIACYHACcHACgBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5" +
41    "SGkBADUoSUxqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7TGphdmEvbGFuZy9SdW5uYWJsZTsp" +
42    "VgEADVN0YWNrTWFwVGFibGUBAAlTaWduYXR1cmUBAEkoSUxqYXZhL3V0aWwvZnVuY3Rpb24vQ29u" +
43    "c3VtZXI8TGphdmEvbGFuZy9TdHJpbmc7PjtMamF2YS9sYW5nL1J1bm5hYmxlOylWAQAKU291cmNl" +
44    "RmlsZQEADlRyYW5zZm9ybS5qYXZhDAAPABABABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEABUhl" +
45    "bGxvDAApACoMACkAKwEADiAtIHRyYW5zZm9ybWVkDAAsAC0HAC4MAC8AMAcAMQwAMgAQDAATABQB" +
46    "AAdHb29kYnllAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAEABmFwcGVuZAEALShMamF2" +
47    "YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAHChJKUxqYXZhL2xhbmcv" +
48    "U3RyaW5nQnVpbGRlcjsBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAbamF2YS91" +
49    "dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQAS" +
50    "amF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADQAOAAAAAAACAAAADwAQAAEAEQAAAB0AAQABAAAA" +
51    "BSq3AAGxAAAAAQASAAAABgABAAAAAgABABMAFAACABEAAACfAAQABAAAAGEsuwACWbcAAxIEtgAF" +
52    "G7YABhIHtgAFtgAIuQAJAgAbBKAAFS25AAoBACobBGQsLbYAC6cAEBuZAAwqGwRkLC22AAssuwAC" +
53    "WbcAAxIMtgAFG7YABhIHtgAFtgAIuQAJAgCxAAAAAgASAAAAIgAIAAAABAAeAAUAIwAGACkABwA1" +
54    "AAgAOQAJAEIACwBgAAwAFQAAAAQAAjUMABYAAAACABcAAQAYAAAAAgAZ");
55  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
56    "ZGV4CjAzNQA7uevryhDgvad3G3EACTdspZGfNKv2i3kkBQAAcAAAAHhWNBIAAAAAAAAAAGwEAAAf" +
57    "AAAAcAAAAAkAAADsAAAABgAAABABAAAAAAAAAAAAAAkAAABYAQAAAQAAAKABAABkAwAAwAEAAMoC" +
58    "AADaAgAA3gIAAOICAADlAgAA7QIAAPECAAD6AgAAAQMAAAQDAAAHAwAACwMAAA8DAAAcAwAAOwMA" +
59    "AE8DAABlAwAAeQMAAJQDAACyAwAA0QMAAOEDAADkAwAA6gMAAO4DAAD2AwAA/gMAABIEAAAXBAAA" +
60    "HgQAACgEAAAIAAAADAAAAA0AAAAOAAAADwAAABAAAAARAAAAEwAAABUAAAAJAAAABQAAAAAAAAAK" +
61    "AAAABgAAAKgCAAALAAAABgAAALACAAAVAAAACAAAAAAAAAAWAAAACAAAALgCAAAXAAAACAAAAMQC" +
62    "AAABAAMABAAAAAEABAAcAAAAAwADAAQAAAAEAAMAGwAAAAYAAwAEAAAABgABABkAAAAGAAIAGQAA" +
63    "AAYAAAAdAAAABwAFABgAAAABAAAAAAAAAAMAAAAAAAAAFAAAAJACAABbBAAAAAAAAAEAAABHBAAA" +
64    "AQABAAEAAAAvBAAABAAAAHAQAgAAAA4ABgAEAAQAAAA0BAAAUAAAACIABgBwEAQAAAAbAQcAAABu" +
65    "IAYAEAAMAG4gBQAwAAwAGwEAAAAAbiAGABAADABuEAcAAAAMAHIgCAAEABIQMwMpAHIQAwAFANgA" +
66    "A/9uQAEAAlQiAAYAcBAEAAAAGwEGAAAAbiAGABAADABuIAUAMAAMABsBAAAAAG4gBgAQAAwAbhAH" +
67    "AAAADAByIAgABAAOADgD4f/YAAP/bkABAAJUKNoAAAAAAAAAAAEAAAAAAAAAAQAAAMABAAABAAAA" +
68    "AAAAAAEAAAAFAAAAAwAAAAAABwAEAAAAAQAAAAMADiAtIHRyYW5zZm9ybWVkAAIoSQACKVYAATwA" +
69    "Bjxpbml0PgACPjsAB0dvb2RieWUABUhlbGxvAAFJAAFMAAJMSQACTEwAC0xUcmFuc2Zvcm07AB1M" +
70    "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
71    "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxk" +
72    "ZXI7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
73    "bnN1bWVyOwAOVHJhbnNmb3JtLmphdmEAAVYABFZJTEwAAlZMAAZhY2NlcHQABmFwcGVuZAASZW1p" +
74    "dHRlcjogamFjay00LjI0AANydW4ABXNheUhpAAh0b1N0cmluZwAFdmFsdWUAAgAHDgAEAwAAAAcO" +
75    "AR4PPDxdAR4PGS0AAgIBHhwHFwEXEhcDFxAXBRcPFwIAAAEBAICABMgDAQHgAwAAAA8AAAAAAAAA" +
76    "AQAAAAAAAAABAAAAHwAAAHAAAAACAAAACQAAAOwAAAADAAAABgAAABABAAAFAAAACQAAAFgBAAAG" +
77    "AAAAAQAAAKABAAADEAAAAQAAAMABAAABIAAAAgAAAMgBAAAGIAAAAQAAAJACAAABEAAABAAAAKgC" +
78    "AAACIAAAHwAAAMoCAAADIAAAAgAAAC8EAAAEIAAAAQAAAEcEAAAAIAAAAQAAAFsEAAAAEAAAAQAA" +
79    "AGwEAAA=");
80
81  // A class that we can use to keep track of the output of this test.
82  private static class TestWatcher implements Consumer<String> {
83    private StringBuilder sb;
84    public TestWatcher() {
85      sb = new StringBuilder();
86    }
87
88    @Override
89    public void accept(String s) {
90      sb.append(s);
91      sb.append('\n');
92    }
93
94    public String getOutput() {
95      return sb.toString();
96    }
97
98    public void clear() {
99      sb = new StringBuilder();
100    }
101  }
102
103  public static void main(String[] args) {
104    doTest(new Transform());
105  }
106
107  private static boolean retry = false;
108
109  public static void doTest(Transform t) {
110    final TestWatcher reporter = new TestWatcher();
111    Method say_hi_method;
112    // Figure out if we can even JIT at all.
113    final boolean has_jit = hasJit();
114    try {
115      say_hi_method = Transform.class.getDeclaredMethod(
116          "sayHi", int.class, Consumer.class, Runnable.class);
117    } catch (Exception e) {
118      System.out.println("Unable to find methods!");
119      e.printStackTrace();
120      return;
121    }
122    // Makes sure the stack is the way we want it for the test and does the redefinition. It will
123    // set the retry boolean to true if we need to go around again due to jit code being GCd.
124    Runnable do_redefinition = () -> {
125      if (has_jit && Main.isInterpretedFunction(say_hi_method, true)) {
126        // Try again. We are not running the right jitted methods/cannot redefine them now.
127        retry = true;
128      } else {
129        // Actually do the redefinition. The stack looks good.
130        retry = false;
131        reporter.accept("transforming calling function");
132        doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
133      }
134    };
135    do {
136      // Run ensureJitCompiled here since it might get GCd
137      ensureJitCompiled(Transform.class, "sayHi");
138      // Clear output.
139      reporter.clear();
140      t.sayHi(2, reporter, () -> { reporter.accept("Not doing anything here"); });
141      t.sayHi(2, reporter, do_redefinition);
142      t.sayHi(2, reporter, () -> { reporter.accept("Not doing anything here"); });
143    } while(retry);
144    System.out.println(reporter.getOutput());
145  }
146
147  private static native boolean hasJit();
148
149  private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
150
151  private static native void ensureJitCompiled(Class c, String name);
152}
153