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 */
21package proguard.optimize.evaluation;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.visitor.*;
26import proguard.classfile.instruction.*;
27import proguard.classfile.instruction.visitor.InstructionVisitor;
28import proguard.classfile.util.SimplifiedVisitor;
29import 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 */
37public class LivenessAnalyzer
38extends      SimplifiedVisitor
39implements   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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
186    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
187
188
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