Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2018 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 /**
     18  * Tests for detecting throwing methods for code sinking.
     19  */
     20 public class Main {
     21 
     22   //
     23   // Some "runtime library" methods.
     24   //
     25 
     26   static private void doThrow(String par) {
     27     throw new Error("you are null: " + par);
     28   }
     29 
     30   static private void checkNotNullDirect(Object obj, String par) {
     31     if (obj == null)
     32       throw new Error("you are null: " + par);
     33   }
     34 
     35   static private void checkNotNullSplit(Object obj, String par) {
     36     if (obj == null)
     37       doThrow(par);
     38   }
     39 
     40   static private void checkNotNullSplitAlt(Object obj, String par) {
     41     if (obj != null)
     42       return;
     43     doThrow(par);
     44   }
     45 
     46   //
     47   // Various ways of enforcing non-null parameter.
     48   // In all cases, par should be subject to code sinking.
     49   //
     50 
     51   /// CHECK-START: void Main.doit1(int[]) code_sinking (before)
     52   /// CHECK: begin_block
     53   /// CHECK:   <<Str:l\d+>> LoadString
     54   /// CHECK:   <<Tst:z\d+>> NotEqual
     55   /// CHECK:                If [<<Tst>>]
     56   /// CHECK: end_block
     57   /// CHECK: begin_block
     58   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
     59   /// CHECK:                Throw
     60   /// CHECK: end_block
     61   //
     62   /// CHECK-START: void Main.doit1(int[]) code_sinking (after)
     63   /// CHECK: begin_block
     64   /// CHECK:   <<Tst:z\d+>> NotEqual
     65   /// CHECK:                If [<<Tst>>]
     66   /// CHECK: end_block
     67   /// CHECK: begin_block
     68   /// CHECK:   <<Str:l\d+>> LoadString
     69   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
     70   /// CHECK:                Throw
     71   /// CHECK: end_block
     72   static public void doit1(int[] a) {
     73     String par = "a";
     74     if (a == null)
     75       throw new Error("you are null: " + par);
     76     for (int i = 0; i < a.length; i++) {
     77       a[i] = 1;
     78     }
     79   }
     80 
     81   /// CHECK-START: void Main.doit2(int[]) code_sinking (before)
     82   /// CHECK: begin_block
     83   /// CHECK:   <<Str:l\d+>> LoadString
     84   /// CHECK:   <<Tst:z\d+>> NotEqual
     85   /// CHECK:                If [<<Tst>>]
     86   /// CHECK: end_block
     87   /// CHECK: begin_block
     88   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
     89   /// CHECK: end_block
     90   //
     91   /// CHECK-START: void Main.doit2(int[]) code_sinking (after)
     92   /// CHECK: begin_block
     93   /// CHECK:   <<Tst:z\d+>> NotEqual
     94   /// CHECK:                If [<<Tst>>]
     95   /// CHECK: end_block
     96   /// CHECK: begin_block
     97   /// CHECK:   <<Str:l\d+>> LoadString
     98   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
     99   /// CHECK: end_block
    100   static public void doit2(int[] a) {
    101     String par = "a";
    102     if (a == null)
    103       doThrow(par);
    104     for (int i = 0; i < a.length; i++) {
    105       a[i] = 2;
    106     }
    107   }
    108 
    109   /// CHECK-START: void Main.doit3(int[]) code_sinking (before)
    110   /// CHECK: begin_block
    111   /// CHECK:   <<Str:l\d+>> LoadString
    112   /// CHECK:   <<Tst:z\d+>> NotEqual
    113   /// CHECK:                If [<<Tst>>]
    114   /// CHECK: end_block
    115   /// CHECK: begin_block
    116   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
    117   /// CHECK:                Throw
    118   /// CHECK: end_block
    119   //
    120   /// CHECK-START: void Main.doit3(int[]) code_sinking (after)
    121   /// CHECK: begin_block
    122   /// CHECK:   <<Tst:z\d+>> NotEqual
    123   /// CHECK:                If [<<Tst>>]
    124   /// CHECK: end_block
    125   /// CHECK: begin_block
    126   /// CHECK:   <<Str:l\d+>> LoadString
    127   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
    128   /// CHECK:                Throw
    129   /// CHECK: end_block
    130   static public void doit3(int[] a) {
    131     String par = "a";
    132     checkNotNullDirect(a, par);
    133     for (int i = 0; i < a.length; i++) {
    134       a[i] = 3;
    135     }
    136   }
    137 
    138   /// CHECK-START: void Main.doit4(int[]) code_sinking (before)
    139   /// CHECK: begin_block
    140   /// CHECK:   <<Str:l\d+>> LoadString
    141   /// CHECK:   <<Tst:z\d+>> NotEqual
    142   /// CHECK:                If [<<Tst>>]
    143   /// CHECK: end_block
    144   /// CHECK: begin_block
    145   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
    146   /// CHECK: end_block
    147   //
    148   /// CHECK-START: void Main.doit4(int[]) code_sinking (after)
    149   /// CHECK: begin_block
    150   /// CHECK:   <<Tst:z\d+>> NotEqual
    151   /// CHECK:                If [<<Tst>>]
    152   /// CHECK: end_block
    153   /// CHECK: begin_block
    154   /// CHECK:   <<Str:l\d+>> LoadString
    155   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
    156   /// CHECK: end_block
    157   static public void doit4(int[] a) {
    158     String par = "a";
    159     checkNotNullSplit(a, par);  // resembles Kotlin runtime lib
    160                                 // (test is lined, doThrow is not)
    161     for (int i = 0; i < a.length; i++) {
    162       a[i] = 4;
    163     }
    164   }
    165 
    166   // Ensures Phi values are merged properly.
    167   static public int doit5(int[] a) {
    168     int t = 100;
    169     String par = "a";
    170     if (a == null) {
    171       doThrow(par);
    172     } else {
    173       t = 1000;
    174     }
    175     for (int i = 0; i < a.length; i++) {
    176       a[i] = 5;
    177     }
    178     // Phi on t, even though doThrow never reaches.
    179     return t;
    180   }
    181 
    182   //
    183   // Various ways of exploiting non-null parameter.
    184   // In all cases, implicit null checks are redundant.
    185   //
    186 
    187   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (before)
    188   /// CHECK:   <<Par:l\d+>>   ParameterValue
    189   /// CHECK:   <<Zero:i\d+>>  IntConstant 0
    190   /// CHECK:   <<Null:l\d+>>  NullCheck [<<Par>>]
    191   /// CHECK:   <<Len:i\d+>>   ArrayLength [<<Null>>]
    192   /// CHECK:   <<Check:i\d+>> BoundsCheck [<<Zero>>,<<Len>>]
    193   /// CHECK:   <<Get:i\d+>>   ArrayGet [<<Null>>,<<Check>>]
    194   /// CHECK:                  Return [<<Get>>]
    195   //
    196   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
    197   /// CHECK:   <<Par:l\d+>>   ParameterValue
    198   /// CHECK:   <<Zero:i\d+>>  IntConstant 0
    199   /// CHECK:   <<BT:l\d+>>    BoundType [<<Par>>]
    200   /// CHECK:   <<Len:i\d+>>   ArrayLength [<<BT>>]
    201   /// CHECK:   <<Check:i\d+>> BoundsCheck [<<Zero>>,<<Len>>]
    202   /// CHECK:   <<Get:i\d+>>   ArrayGet [<<BT>>,<<Check>>]
    203   /// CHECK:                  Return [<<Get>>]
    204   //
    205   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
    206   /// CHECK-NOT:              NullCheck
    207   static public int deleteNullCheck(int[] a) {
    208     checkNotNullSplit(a, "a");
    209     return a[0];
    210   }
    211 
    212   /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (before)
    213   /// CHECK:     NullCheck
    214   //
    215   /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (after)
    216   /// CHECK-NOT: NullCheck
    217   static public int deleteNullCheckAlt(int[] a) {
    218     checkNotNullSplitAlt(a, "a");
    219     return a[0];
    220   }
    221 
    222   /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (before)
    223   /// CHECK:     NullCheck
    224   /// CHECK:     NullCheck
    225   /// CHECK:     NullCheck
    226   //
    227   /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (after)
    228   /// CHECK-NOT: NullCheck
    229   static public int deleteNullChecks3(int[] a, int[] b, int[] c) {
    230     checkNotNullSplit(a, "a");
    231     checkNotNullSplit(b, "b");
    232     checkNotNullSplit(c, "c");
    233     return a[0] + b[0] + c[0];
    234   }
    235 
    236   //
    237   // Test driver.
    238   //
    239 
    240   static public void main(String[] args) {
    241     int[] a = new int[100];
    242     for (int i = 0; i < 100; i++) {
    243       a[i] = 0;
    244     }
    245 
    246     try {
    247       doit1(null);
    248       System.out.println("should not reach this!");
    249     } catch (Error e) {
    250       doit1(a);
    251     }
    252     for (int i = 0; i < 100; i++) {
    253       expectEquals(1, a[i]);
    254     }
    255 
    256     try {
    257       doit2(null);
    258       System.out.println("should not reach this!");
    259     } catch (Error e) {
    260       doit2(a);
    261     }
    262     for (int i = 0; i < 100; i++) {
    263       expectEquals(2, a[i]);
    264     }
    265 
    266     try {
    267       doit3(null);
    268       System.out.println("should not reach this!");
    269     } catch (Error e) {
    270       doit3(a);
    271     }
    272     for (int i = 0; i < 100; i++) {
    273       expectEquals(3, a[i]);
    274     }
    275 
    276     try {
    277       doit4(null);
    278       System.out.println("should not reach this!");
    279     } catch (Error e) {
    280       doit4(a);
    281     }
    282     for (int i = 0; i < 100; i++) {
    283       expectEquals(4, a[i]);
    284     }
    285 
    286     try {
    287       doit5(null);
    288       System.out.println("should not reach this!");
    289     } catch (Error e) {
    290       expectEquals(1000, doit5(a));
    291     }
    292     for (int i = 0; i < 100; i++) {
    293       expectEquals(5, a[i]);
    294     }
    295 
    296     int[] x = { 11 } ;
    297     expectEquals(11, deleteNullCheck(x));
    298     int[] y = { 55 } ;
    299     int[] z = { 22 } ;
    300     expectEquals(88, deleteNullChecks3(x, y, z));
    301 
    302     try {
    303       deleteNullCheck(null);
    304       System.out.println("should not reach this!");
    305     } catch (Error e) {
    306     }
    307 
    308     System.out.println("passed");
    309   }
    310 
    311   private static void expectEquals(int expected, int result) {
    312     if (expected != result) {
    313       throw new Error("Expected: " + expected + ", found: " + result);
    314     }
    315   }
    316 }
    317