1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.optimize.evaluation; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.*; 25 import proguard.classfile.attribute.visitor.*; 26 import proguard.classfile.instruction.*; 27 import proguard.classfile.instruction.visitor.InstructionVisitor; 28 import proguard.classfile.util.SimplifiedVisitor; 29 import proguard.evaluation.value.*; 30 31 /** 32 * This AttributeVisitor analyzes the liveness of the variables in the code 33 * attributes that it visits, based on partial evaluation. 34 * 35 * @author Eric Lafortune 36 */ 37 public class LivenessAnalyzer 38 extends SimplifiedVisitor 39 implements AttributeVisitor, 40 InstructionVisitor, 41 ExceptionInfoVisitor 42 { 43 //* 44 private static final boolean DEBUG = false; 45 /*/ 46 private static boolean DEBUG = true; 47 //*/ 48 49 private static final int MAX_VARIABLES_SIZE = 64; 50 51 private final PartialEvaluator partialEvaluator; 52 53 private long[] isAliveBefore = new long[ClassConstants.TYPICAL_CODE_LENGTH]; 54 private long[] isAliveAfter = new long[ClassConstants.TYPICAL_CODE_LENGTH]; 55 private long[] isCategory2 = new long[ClassConstants.TYPICAL_CODE_LENGTH]; 56 57 // Fields acting as global temporary variables. 58 private boolean checkAgain; 59 private long alive; 60 61 62 /** 63 * Creates a new LivenessAnalyzer. 64 */ LivenessAnalyzer()65 public LivenessAnalyzer() 66 { 67 this(new PartialEvaluator()); 68 } 69 70 71 /** 72 * Creates a new LivenessAnalyzer that will use the given partial evaluator. 73 * It will run this evaluator on every code attribute that it visits. 74 */ LivenessAnalyzer(PartialEvaluator partialEvaluator)75 public LivenessAnalyzer(PartialEvaluator partialEvaluator) 76 { 77 this.partialEvaluator = partialEvaluator; 78 } 79 80 81 /** 82 * Returns whether the instruction at the given offset has ever been 83 * executed during the partial evaluation. 84 */ isTraced(int instructionOffset)85 public boolean isTraced(int instructionOffset) 86 { 87 return partialEvaluator.isTraced(instructionOffset); 88 } 89 90 91 /** 92 * Returns whether the specified variable is alive before the instruction 93 * at the given offset. 94 */ isAliveBefore(int instructionOffset, int variableIndex)95 public boolean isAliveBefore(int instructionOffset, int variableIndex) 96 { 97 return variableIndex >= MAX_VARIABLES_SIZE || 98 (isAliveBefore[instructionOffset] & (1L << variableIndex)) != 0; 99 } 100 101 102 /** 103 * Sets whether the specified variable is alive before the instruction 104 * at the given offset. 105 */ setAliveBefore(int instructionOffset, int variableIndex, boolean alive)106 public void setAliveBefore(int instructionOffset, int variableIndex, boolean alive) 107 { 108 if (variableIndex < MAX_VARIABLES_SIZE) 109 { 110 if (alive) 111 { 112 isAliveBefore[instructionOffset] |= 1L << variableIndex; 113 } 114 else 115 { 116 isAliveBefore[instructionOffset] &= ~(1L << variableIndex); 117 } 118 } 119 } 120 121 122 /** 123 * Returns whether the specified variable is alive after the instruction 124 * at the given offset. 125 */ isAliveAfter(int instructionOffset, int variableIndex)126 public boolean isAliveAfter(int instructionOffset, int variableIndex) 127 { 128 return variableIndex >= MAX_VARIABLES_SIZE || 129 (isAliveAfter[instructionOffset] & (1L << variableIndex)) != 0; 130 } 131 132 133 /** 134 * Sets whether the specified variable is alive after the instruction 135 * at the given offset. 136 */ setAliveAfter(int instructionOffset, int variableIndex, boolean alive)137 public void setAliveAfter(int instructionOffset, int variableIndex, boolean alive) 138 { 139 if (variableIndex < MAX_VARIABLES_SIZE) 140 { 141 if (alive) 142 { 143 isAliveAfter[instructionOffset] |= 1L << variableIndex; 144 } 145 else 146 { 147 isAliveAfter[instructionOffset] &= ~(1L << variableIndex); 148 } 149 } 150 } 151 152 153 /** 154 * Returns whether the specified variable takes up two entries after the 155 * instruction at the given offset. 156 */ isCategory2(int instructionOffset, int variableIndex)157 public boolean isCategory2(int instructionOffset, int variableIndex) 158 { 159 return variableIndex < MAX_VARIABLES_SIZE && 160 (isCategory2[instructionOffset] & (1L << variableIndex)) != 0; 161 } 162 163 164 /** 165 * Sets whether the specified variable takes up two entries after the 166 * instruction at the given offset. 167 */ setCategory2(int instructionOffset, int variableIndex, boolean category2)168 public void setCategory2(int instructionOffset, int variableIndex, boolean category2) 169 { 170 if (variableIndex < MAX_VARIABLES_SIZE) 171 { 172 if (category2) 173 { 174 isCategory2[instructionOffset] |= 1L << variableIndex; 175 } 176 else 177 { 178 isCategory2[instructionOffset] &= ~(1L << variableIndex); 179 } 180 } 181 } 182 183 184 // Implementations for AttributeVisitor. 185 visitAnyAttribute(Clazz clazz, Attribute attribute)186 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 187 188 visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)189 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 190 { 191 // DEBUG = 192 // clazz.getName().equals("abc/Def") && 193 // method.getName(clazz).equals("abc"); 194 195 if (DEBUG) 196 { 197 System.out.println(); 198 System.out.println("Liveness analysis: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 199 } 200 201 // Initialize the global arrays. 202 initializeArrays(codeAttribute); 203 204 // Evaluate the method. 205 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 206 207 int codeLength = codeAttribute.u4codeLength; 208 int variablesSize = codeAttribute.u2maxLocals; 209 210 // We'll only really analyze the first 64 variables. 211 if (variablesSize > MAX_VARIABLES_SIZE) 212 { 213 variablesSize = MAX_VARIABLES_SIZE; 214 } 215 216 // Mark liveness blocks, as many times as necessary. 217 do 218 { 219 checkAgain = false; 220 alive = 0L; 221 222 // Loop over all traced instructions, backward. 223 for (int offset = codeLength - 1; offset >= 0; offset--) 224 { 225 if (partialEvaluator.isTraced(offset)) 226 { 227 // Update the liveness based on the branch targets. 228 InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); 229 if (branchTargets != null) 230 { 231 // Update the liveness right after the branch instruction. 232 alive = combinedLiveness(branchTargets); 233 } 234 235 // Merge the current liveness. 236 alive |= isAliveAfter[offset]; 237 238 // Update the liveness after the instruction. 239 isAliveAfter[offset] = alive; 240 241 // Update the current liveness based on the instruction. 242 codeAttribute.instructionAccept(clazz, method, offset, this); 243 244 // Merge the current liveness. 245 alive |= isAliveBefore[offset]; 246 247 // Update the liveness before the instruction. 248 if ((~isAliveBefore[offset] & alive) != 0L) 249 { 250 isAliveBefore[offset] = alive; 251 252 // Do we have to check again after this loop? 253 checkAgain |= offset < maxOffset(partialEvaluator.branchOrigins(offset)); 254 } 255 } 256 } 257 258 // Account for the liveness at the start of the exception handlers. 259 codeAttribute.exceptionsAccept(clazz, method, this); 260 } 261 while (checkAgain); 262 263 // Loop over all instructions, to mark variables that take up two entries. 264 for (int offset = 0; offset < codeLength; offset++) 265 { 266 if (partialEvaluator.isTraced(offset)) 267 { 268 // Loop over all variables. 269 for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) 270 { 271 // Is the variable alive and a category 2 type? 272 if (isAliveBefore(offset, variableIndex)) 273 { 274 Value value = partialEvaluator.getVariablesBefore(offset).getValue(variableIndex); 275 if (value != null && value.isCategory2()) 276 { 277 // Mark it as such. 278 setCategory2(offset, variableIndex, true); 279 280 // Mark the next variable as well. 281 setAliveBefore(offset, variableIndex + 1, true); 282 setCategory2( offset, variableIndex + 1, true); 283 } 284 } 285 286 // Is the variable alive and a category 2 type? 287 if (isAliveAfter(offset, variableIndex)) 288 { 289 Value value = partialEvaluator.getVariablesAfter(offset).getValue(variableIndex); 290 if (value != null && value.isCategory2()) 291 { 292 // Mark it as such. 293 setCategory2(offset, variableIndex, true); 294 295 // Mark the next variable as well. 296 setAliveAfter(offset, variableIndex + 1, true); 297 setCategory2( offset, variableIndex + 1, true); 298 } 299 } 300 } 301 } 302 } 303 304 if (DEBUG) 305 { 306 // Loop over all instructions. 307 for (int offset = 0; offset < codeLength; offset++) 308 { 309 if (partialEvaluator.isTraced(offset)) 310 { 311 long aliveBefore = isAliveBefore[offset]; 312 long aliveAfter = isAliveAfter[offset]; 313 long category2 = isCategory2[offset]; 314 315 // Print out the liveness of all variables before the instruction. 316 for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) 317 { 318 long variableMask = (1L << variableIndex); 319 System.out.print((aliveBefore & variableMask) == 0L ? '.' : 320 (category2 & variableMask) == 0L ? 'x' : 321 '*'); 322 } 323 324 // Print out the instruction itself. 325 System.out.println(" "+ InstructionFactory.create(codeAttribute.code, offset).toString(offset)); 326 327 // Print out the liveness of all variables after the instruction. 328 for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) 329 { 330 long variableMask = (1L << variableIndex); 331 System.out.print((aliveAfter & variableMask) == 0L ? '.' : 332 (category2 & variableMask) == 0L ? 'x' : 333 '='); 334 } 335 336 System.out.println(); 337 } 338 } 339 } 340 } 341 342 343 // Implementations for InstructionVisitor. 344 345 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 346 347 348 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 349 { 350 int variableIndex = variableInstruction.variableIndex; 351 if (variableIndex < MAX_VARIABLES_SIZE) 352 { 353 long livenessMask = 1L << variableIndex; 354 355 // Is it a load instruction or a store instruction? 356 if (variableInstruction.isLoad()) 357 { 358 // Start marking the variable before the load instruction. 359 alive |= livenessMask; 360 } 361 else 362 { 363 // Stop marking the variable before the store instruction. 364 alive &= ~livenessMask; 365 366 // But do mark the variable right after the store instruction. 367 isAliveAfter[offset] |= livenessMask; 368 } 369 } 370 } 371 372 373 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 374 { 375 // Special case: variable 0 ('this') in an initializer has to be alive 376 // as long as it hasn't been initialized. 377 if (offset == partialEvaluator.superInitializationOffset()) 378 { 379 alive |= 1L; 380 } 381 } 382 383 384 // Implementations for ExceptionInfoVisitor. 385 386 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 387 { 388 // Are any variables alive at the start of the handler? 389 long alive = isAliveBefore[exceptionInfo.u2handlerPC]; 390 if (alive != 0L) 391 { 392 // Set the same liveness flags for the entire try block. 393 int startOffset = exceptionInfo.u2startPC; 394 int endOffset = exceptionInfo.u2endPC; 395 396 for (int offset = startOffset; offset < endOffset; offset++) 397 { 398 if (partialEvaluator.isTraced(offset)) 399 { 400 if ((~(isAliveBefore[offset] & isAliveAfter[offset]) & alive) != 0L) 401 { 402 isAliveBefore[offset] |= alive; 403 isAliveAfter[offset] |= alive; 404 405 // Check again after having marked this try block. 406 checkAgain = true; 407 } 408 } 409 } 410 } 411 } 412 413 414 // Small utility methods. 415 416 /** 417 * Initializes the global arrays. 418 */ 419 private void initializeArrays(CodeAttribute codeAttribute) 420 { 421 int codeLength = codeAttribute.u4codeLength; 422 423 // Create new arrays for storing information at each instruction offset. 424 if (isAliveBefore.length < codeLength) 425 { 426 isAliveBefore = new long[codeLength]; 427 isAliveAfter = new long[codeLength]; 428 isCategory2 = new long[codeLength]; 429 } 430 else 431 { 432 for (int index = 0; index < codeLength; index++) 433 { 434 isAliveBefore[index] = 0L; 435 isAliveAfter[index] = 0L; 436 isCategory2[index] = 0L; 437 } 438 } 439 } 440 441 442 /** 443 * Returns the combined liveness mask of the variables right before the 444 * specified instruction offsets. 445 */ 446 private long combinedLiveness(InstructionOffsetValue instructionOffsetValue) 447 { 448 long alive = 0L; 449 450 int count = instructionOffsetValue.instructionOffsetCount(); 451 for (int index = 0; index < count; index++) 452 { 453 alive |= isAliveBefore[instructionOffsetValue.instructionOffset(index)]; 454 } 455 456 return alive; 457 } 458 459 460 /** 461 * Returns the minimum offset from the given instruction offsets. 462 */ 463 private int minOffset(Value instructionOffsets) 464 { 465 return minOffset(instructionOffsets, Integer.MAX_VALUE); 466 } 467 468 469 /** 470 * Returns the minimum offset from the given instruction offsets. 471 */ 472 private int minOffset(Value instructionOffsets, int minOffset) 473 { 474 if (instructionOffsets != null) 475 { 476 InstructionOffsetValue instructionOffsetValue = 477 instructionOffsets.instructionOffsetValue(); 478 479 int count = instructionOffsetValue.instructionOffsetCount(); 480 for (int index = 0; index < count; index++) 481 { 482 int offset = instructionOffsetValue.instructionOffset(index); 483 if (minOffset > offset) 484 { 485 minOffset = offset; 486 } 487 } 488 } 489 490 return minOffset; 491 } 492 493 494 /** 495 * Returns the maximum offset from the given instruction offsets. 496 */ 497 private int maxOffset(Value instructionOffsets) 498 { 499 return maxOffset(instructionOffsets, Integer.MIN_VALUE); 500 } 501 502 503 /** 504 * Returns the maximum offset from the given instruction offsets. 505 */ 506 private int maxOffset(Value instructionOffsets, int maxOffset) 507 { 508 if (instructionOffsets != null) 509 { 510 InstructionOffsetValue instructionOffsetValue = 511 instructionOffsets.instructionOffsetValue(); 512 513 int count = instructionOffsetValue.instructionOffsetCount(); 514 for (int index = 0; index < count; index++) 515 { 516 int offset = instructionOffsetValue.instructionOffset(index); 517 if (maxOffset < offset) 518 { 519 maxOffset = offset; 520 } 521 } 522 } 523 524 return maxOffset; 525 } 526 } 527