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 doThrow(String par)26 static private void doThrow(String par) { 27 throw new Error("you are null: " + par); 28 } 29 checkNotNullDirect(Object obj, String par)30 static private void checkNotNullDirect(Object obj, String par) { 31 if (obj == null) 32 throw new Error("you are null: " + par); 33 } 34 checkNotNullSplit(Object obj, String par)35 static private void checkNotNullSplit(Object obj, String par) { 36 if (obj == null) 37 doThrow(par); 38 } 39 checkNotNullSplitAlt(Object obj, String par)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 doit1(int[] a)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 doit2(int[] a)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 doit3(int[] a)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 doit4(int[] a)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. doit5(int[] a)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 deleteNullCheck(int[] a)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 deleteNullCheckAlt(int[] a)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 deleteNullChecks3(int[] a, int[] b, int[] c)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 main(String[] args)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 expectEquals(int expected, int result)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