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