Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2017 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 abstract class Base {
     18   abstract void foo(int i);
     19 
     20   void printError(String msg) {
     21     System.out.println(msg);
     22   }
     23 }
     24 
     25 class Main1 extends Base {
     26   void foo(int i) {
     27     if (i != 1) {
     28       printError("error1");
     29     }
     30   }
     31 }
     32 
     33 class Main2 extends Main1 {
     34   void foo(int i) {
     35     if (i != 2) {
     36       printError("error2");
     37     }
     38   }
     39 }
     40 
     41 public class Main {
     42   static Base sMain1;
     43   static Base sMain2;
     44 
     45   static boolean sIsOptimizing = true;
     46   static boolean sHasJIT = true;
     47   static volatile boolean sOtherThreadStarted;
     48 
     49   private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
     50     if (hasSingleImplementation(clazz, method_name) != b) {
     51       System.out.println(clazz + "." + method_name +
     52           " doesn't have single implementation value of " + b);
     53     }
     54   }
     55 
     56   // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
     57   // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
     58   // After Dummy.createMain2() which links in Main2, live testOverride() on stack
     59   // should be deoptimized.
     60   static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
     61     if (setHasJIT) {
     62       if (isInterpreted()) {
     63         sHasJIT = false;
     64       }
     65       return;
     66     }
     67 
     68     if (createMain2 && (sIsOptimizing || sHasJIT)) {
     69       assertIsManaged();
     70     }
     71 
     72     sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
     73 
     74     if (createMain2) {
     75       // Wait for the other thread to start.
     76       while (!sOtherThreadStarted);
     77       // Create an Main2 instance and assign it to sMain2.
     78       // sMain1 is kept the same.
     79       sMain2 = Dummy.createMain2();
     80       // Wake up the other thread.
     81       synchronized(Main.class) {
     82         Main.class.notify();
     83       }
     84     } else if (wait) {
     85       // This is the other thread.
     86       synchronized(Main.class) {
     87         sOtherThreadStarted = true;
     88         // Wait for Main2 to be linked and deoptimization is triggered.
     89         try {
     90           Main.class.wait();
     91         } catch (Exception e) {
     92         }
     93       }
     94     }
     95 
     96     // There should be a deoptimization here right after Main2 is linked by
     97     // calling Dummy.createMain2(), even though sMain1 didn't change.
     98     // The behavior here would be different if inline-cache is used, which
     99     // doesn't deoptimize since sMain1 still hits the type cache.
    100     sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
    101     if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
    102       // This method should be deoptimized right after Main2 is created.
    103       assertIsInterpreted();
    104     }
    105 
    106     if (sMain2 != null) {
    107       sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
    108     }
    109   }
    110 
    111   // Test scenarios under which CHA-based devirtualization happens,
    112   // and class loading that overrides a method can invalidate compiled code.
    113   public static void main(String[] args) {
    114     System.loadLibrary(args[0]);
    115 
    116     if (isInterpreted()) {
    117       sIsOptimizing = false;
    118     }
    119 
    120     // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
    121     sMain1 = new Main1();
    122 
    123     ensureJitCompiled(Main.class, "testOverride");
    124     testOverride(false, false, true);
    125 
    126     if (sHasJIT && !sIsOptimizing) {
    127       assertSingleImplementation(Base.class, "foo", true);
    128       assertSingleImplementation(Main1.class, "foo", true);
    129     } else {
    130       // Main2 is verified ahead-of-time so it's linked in already.
    131     }
    132 
    133     // Create another thread that also calls sMain1.foo().
    134     // Try to test suspend and deopt another thread.
    135     new Thread() {
    136       public void run() {
    137         testOverride(false, true, false);
    138       }
    139     }.start();
    140 
    141     // This will create Main2 instance in the middle of testOverride().
    142     testOverride(true, false, false);
    143     assertSingleImplementation(Base.class, "foo", false);
    144     assertSingleImplementation(Main1.class, "foo", false);
    145   }
    146 
    147   private static native void ensureJitCompiled(Class<?> itf, String method_name);
    148   private static native void assertIsInterpreted();
    149   private static native void assertIsManaged();
    150   private static native boolean isInterpreted();
    151   private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
    152 }
    153 
    154 // Put createMain2() in another class to avoid class loading due to verifier.
    155 class Dummy {
    156   static Main1 createMain2() {
    157     return new Main2();
    158   }
    159 }
    160