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 17 import java.lang.reflect.InvocationHandler; 18 import java.lang.reflect.Method; 19 import java.lang.reflect.Proxy; 20 21 class Main1 { foo(int i)22 void foo(int i) { 23 if (i != 1) { 24 printError("error1"); 25 } 26 } 27 printError(String msg)28 void printError(String msg) { 29 System.out.println(msg); 30 } 31 } 32 33 class Main2 extends Main1 { foo(int i)34 void foo(int i) { 35 if (i != 2) { 36 printError("error2"); 37 } 38 } 39 } 40 41 class Proxied implements Runnable { run()42 public void run() { 43 synchronized(Main.class) { 44 Main.sOtherThreadStarted = true; 45 // Wait for Main2 to be linked and deoptimization is triggered. 46 try { 47 Main.class.wait(); 48 } catch (Exception e) { 49 } 50 } 51 } 52 } 53 54 class MyInvocationHandler implements InvocationHandler { 55 private final Proxied proxied; 56 MyInvocationHandler(Proxied proxied)57 public MyInvocationHandler(Proxied proxied) { 58 this.proxied = proxied; 59 } 60 invoke(Object proxy, Method method, Object[] args)61 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 62 return method.invoke(proxied, args); 63 } 64 } 65 66 public class Main { 67 static Main1 sMain1; 68 static Main1 sMain2; 69 static volatile boolean sOtherThreadStarted; 70 71 // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. 72 // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. 73 // After Helper.createMain2() which links in Main2, live testOverride() on stack 74 // should be deoptimized. testOverride()75 static void testOverride() { 76 sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); 77 78 // Wait for the other thread to start. 79 while (!sOtherThreadStarted); 80 // Create an Main2 instance and assign it to sMain2. 81 // sMain1 is kept the same. 82 sMain2 = Helper.createMain2(); 83 // Wake up the other thread. 84 synchronized(Main.class) { 85 Main.class.notify(); 86 } 87 88 // There should be a deoptimization here right after Main2 is linked by 89 // calling Helper.createMain2(), even though sMain1 didn't change. 90 // The behavior here would be different if inline-cache is used, which 91 // doesn't deoptimize since sMain1 still hits the type cache. 92 sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); 93 if (sMain2 != null) { 94 sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); 95 } 96 } 97 98 // Test scenarios under which CHA-based devirtualization happens, 99 // and class loading that overrides a method can invalidate compiled code. 100 // Also create a proxy method such that a proxy method's frame is visited 101 // during stack walking. main(String[] args)102 public static void main(String[] args) { 103 System.loadLibrary(args[0]); 104 // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. 105 sMain1 = new Main1(); 106 107 // Create another thread that calls a proxy method. 108 new Thread() { 109 public void run() { 110 Runnable proxy = (Runnable)Proxy.newProxyInstance( 111 Proxied.class.getClassLoader(), 112 new Class[] { Runnable.class }, 113 new MyInvocationHandler(new Proxied())); 114 proxy.run(); 115 } 116 }.start(); 117 118 ensureJitCompiled(Main.class, "testOverride"); 119 // This will create Main2 instance in the middle of testOverride(). 120 testOverride(); 121 } 122 ensureJitCompiled(Class<?> itf, String method_name)123 private static native void ensureJitCompiled(Class<?> itf, String method_name); 124 } 125 126 // Put createMain2() in another class to avoid class loading due to verifier. 127 class Helper { createMain2()128 static Main1 createMain2() { 129 return new Main2(); 130 } 131 } 132