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