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.constant.ClassConstant;
27import proguard.classfile.instruction.*;
28import proguard.classfile.util.*;
29import proguard.classfile.visitor.*;
30import proguard.evaluation.*;
31import proguard.evaluation.value.*;
32import proguard.optimize.peephole.BranchTargetFinder;
33
34import java.util.Arrays;
35
36/**
37 * This AttributeVisitor performs partial evaluation on the code attributes
38 * that it visits.
39 *
40 * @author Eric Lafortune
41 */
42public class PartialEvaluator
43extends      SimplifiedVisitor
44implements   AttributeVisitor,
45             ExceptionInfoVisitor
46{
47    //*
48    private static final boolean DEBUG         = false;
49    private static final boolean DEBUG_RESULTS = false;
50    /*/
51    private static boolean DEBUG         = true;
52    private static boolean DEBUG_RESULTS = true;
53    //*/
54
55    private static final int MAXIMUM_EVALUATION_COUNT = 5;
56
57    public static final int NONE            = -2;
58    public static final int AT_METHOD_ENTRY = -1;
59    public static final int AT_CATCH_ENTRY  = -1;
60
61    private final ValueFactory   valueFactory;
62    private final InvocationUnit invocationUnit;
63    private final boolean        evaluateAllCode;
64
65    private InstructionOffsetValue[] branchOriginValues   = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
66    private InstructionOffsetValue[] branchTargetValues   = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
67    private TracedVariables[]        variablesBefore      = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
68    private TracedStack[]            stacksBefore         = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
69    private TracedVariables[]        variablesAfter       = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
70    private TracedStack[]            stacksAfter          = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
71    private boolean[]                generalizedContexts  = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
72    private int[]                    evaluationCounts     = new int[ClassConstants.TYPICAL_CODE_LENGTH];
73    private boolean                  evaluateExceptions;
74
75    private final BasicBranchUnit    branchUnit;
76    private final BranchTargetFinder branchTargetFinder;
77
78    private final java.util.Stack callingInstructionBlockStack;
79    private final java.util.Stack instructionBlockStack = new java.util.Stack();
80
81
82    /**
83     * Creates a simple PartialEvaluator.
84     */
85    public PartialEvaluator()
86    {
87        this(new ValueFactory(),
88             new BasicInvocationUnit(new ValueFactory()),
89             true);
90    }
91
92
93    /**
94     * Creates a new PartialEvaluator.
95     * @param valueFactory    the value factory that will create all values
96     *                        during evaluation.
97     * @param invocationUnit  the invocation unit that will handle all
98     *                        communication with other fields and methods.
99     * @param evaluateAllCode a flag that specifies whether all casts, branch
100     *                        targets, and exception handlers should be
101     *                        evaluated, even if they are unnecessary or
102     *                        unreachable.
103     */
104    public PartialEvaluator(ValueFactory   valueFactory,
105                            InvocationUnit invocationUnit,
106                            boolean        evaluateAllCode)
107    {
108        this(valueFactory,
109             invocationUnit,
110             evaluateAllCode,
111             evaluateAllCode ?
112                 new BasicBranchUnit() :
113                 new TracedBranchUnit(),
114             new BranchTargetFinder(),
115             null);
116    }
117
118
119    /**
120     * Creates a new PartialEvaluator, based on an existing one.
121     * @param partialEvaluator the subroutine calling partial evaluator.
122     */
123    private PartialEvaluator(PartialEvaluator partialEvaluator)
124    {
125        this(partialEvaluator.valueFactory,
126             partialEvaluator.invocationUnit,
127             partialEvaluator.evaluateAllCode,
128             partialEvaluator.branchUnit,
129             partialEvaluator.branchTargetFinder,
130             partialEvaluator.instructionBlockStack);
131    }
132
133
134    /**
135     * Creates a new PartialEvaluator.
136     * @param valueFactory                 the value factory that will create
137     *                                     all values during evaluation.
138     * @param invocationUnit               the invocation unit that will handle
139     *                                     all communication with other fields
140     *                                     and methods.
141     * @param evaluateAllCode              a flag that specifies whether all
142     *                                     casts, branch targets, and exception
143     *                                     handlers should be evaluated, even
144     *                                     if they are unnecessary or
145     *                                     unreachable.
146     * @param branchUnit                   the branch unit that will handle all
147     *                                     branches.
148     * @param branchTargetFinder           the utility class that will find all
149     *                                     branches.
150     * @param callingInstructionBlockStack the stack of instruction blocks to
151     *                                     be evaluated
152     */
153    private PartialEvaluator(ValueFactory       valueFactory,
154                             InvocationUnit     invocationUnit,
155                             boolean            evaluateAllCode,
156                             BasicBranchUnit    branchUnit,
157                             BranchTargetFinder branchTargetFinder,
158                             java.util.Stack    callingInstructionBlockStack)
159    {
160        this.valueFactory       = valueFactory;
161        this.invocationUnit     = invocationUnit;
162        this.evaluateAllCode    = evaluateAllCode;
163        this.branchUnit         = branchUnit;
164        this.branchTargetFinder = branchTargetFinder;
165        this.callingInstructionBlockStack = callingInstructionBlockStack == null ?
166            this.instructionBlockStack :
167            callingInstructionBlockStack;
168    }
169
170
171    // Implementations for AttributeVisitor.
172
173    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
174
175
176    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
177    {
178//        DEBUG = DEBUG_RESULTS =
179//            clazz.getName().equals("abc/Def") &&
180//            method.getName(clazz).equals("abc");
181
182        // TODO: Remove this when the partial evaluator has stabilized.
183        // Catch any unexpected exceptions from the actual visiting method.
184        try
185        {
186            // Process the code.
187            visitCodeAttribute0(clazz, method, codeAttribute);
188        }
189        catch (RuntimeException ex)
190        {
191            System.err.println("Unexpected error while performing partial evaluation:");
192            System.err.println("  Class       = ["+clazz.getName()+"]");
193            System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
194            System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
195
196            if (DEBUG)
197            {
198                method.accept(clazz, new ClassPrinter());
199
200                System.out.println("Evaluation results:");
201
202                int offset = 0;
203                do
204                {
205                    if (isBranchOrExceptionTarget(offset))
206                    {
207                        System.out.println("Branch target from ["+branchOriginValues[offset]+"]:");
208                        if (isTraced(offset))
209                        {
210                            System.out.println("  Vars:  "+variablesBefore[offset]);
211                            System.out.println("  Stack: "+stacksBefore[offset]);
212                        }
213                    }
214
215                    Instruction instruction = InstructionFactory.create(codeAttribute.code,
216                                                                        offset);
217                    System.out.println(instruction.toString(offset));
218
219                    if (isTraced(offset))
220                    {
221                        int initializationOffset = branchTargetFinder.initializationOffset(offset);
222                        if (initializationOffset != NONE)
223                        {
224                            System.out.println("     is to be initialized at ["+initializationOffset+"]");
225                        }
226
227                        InstructionOffsetValue branchTargets = branchTargets(offset);
228                        if (branchTargets != null)
229                        {
230                            System.out.println("     has overall been branching to "+branchTargets);
231                        }
232
233                        System.out.println("  Vars:  "+variablesAfter[offset]);
234                        System.out.println("  Stack: "+stacksAfter[offset]);
235                    }
236
237                    offset += instruction.length(offset);
238                }
239                while (offset < codeAttribute.u4codeLength);
240            }
241
242            throw ex;
243        }
244    }
245
246
247    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
248    {
249        // Evaluate the instructions, starting at the entry point.
250        if (DEBUG)
251        {
252            System.out.println();
253            System.out.println("Partial evaluation: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
254            System.out.println("  Max locals = "+codeAttribute.u2maxLocals);
255            System.out.println("  Max stack  = "+codeAttribute.u2maxStack);
256        }
257
258        // Reuse the existing variables and stack objects, ensuring the right size.
259        TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals);
260        TracedStack     stack     = new TracedStack(codeAttribute.u2maxStack);
261
262        // Initialize the reusable arrays and variables.
263        initializeArrays(codeAttribute);
264        initializeParameters(clazz, method, codeAttribute, variables);
265
266        // Find all instruction offsets,...
267        codeAttribute.accept(clazz, method, branchTargetFinder);
268
269        // Start executing the first instruction block.
270        evaluateInstructionBlockAndExceptionHandlers(clazz,
271                                                     method,
272                                                     codeAttribute,
273                                                     variables,
274                                                     stack,
275                                                     0,
276                                                     codeAttribute.u4codeLength);
277
278        if (DEBUG_RESULTS)
279        {
280            System.out.println("Evaluation results:");
281
282            int offset = 0;
283            do
284            {
285                if (isBranchOrExceptionTarget(offset))
286                {
287                    System.out.println("Branch target from ["+branchOriginValues[offset]+"]:");
288                    if (isTraced(offset))
289                    {
290                        System.out.println("  Vars:  "+variablesBefore[offset]);
291                        System.out.println("  Stack: "+stacksBefore[offset]);
292                    }
293                }
294
295                Instruction instruction = InstructionFactory.create(codeAttribute.code,
296                                                                    offset);
297                System.out.println(instruction.toString(offset));
298
299                if (isTraced(offset))
300                {
301                    int initializationOffset = branchTargetFinder.initializationOffset(offset);
302                    if (initializationOffset >= 0)
303                    {
304                        System.out.println("     is to be initialized at ["+initializationOffset+"]");
305                    }
306
307                    InstructionOffsetValue branchTargets = branchTargets(offset);
308                    if (branchTargets != null)
309                    {
310                        System.out.println("     has overall been branching to "+branchTargets);
311                    }
312
313                    System.out.println("  Vars:  "+variablesAfter[offset]);
314                    System.out.println("  Stack: "+stacksAfter[offset]);
315                }
316
317                offset += instruction.length(offset);
318            }
319            while (offset < codeAttribute.u4codeLength);
320        }
321    }
322
323
324    /**
325     * Returns whether a block of instructions is ever used.
326     */
327    public boolean isTraced(int startOffset, int endOffset)
328    {
329        for (int index = startOffset; index < endOffset; index++)
330        {
331            if (isTraced(index))
332            {
333                return true;
334            }
335        }
336
337        return false;
338    }
339
340
341    /**
342     * Returns whether the instruction at the given offset has ever been
343     * executed during the partial evaluation.
344     */
345    public boolean isTraced(int instructionOffset)
346    {
347        return evaluationCounts[instructionOffset] > 0;
348    }
349
350
351    /**
352     * Returns whether there is an instruction at the given offset.
353     */
354    public boolean isInstruction(int instructionOffset)
355    {
356        return branchTargetFinder.isInstruction(instructionOffset);
357    }
358
359
360    /**
361     * Returns whether the instruction at the given offset is the target of a
362     * branch instruction or an exception.
363     */
364    public boolean isBranchOrExceptionTarget(int instructionOffset)
365    {
366        return branchTargetFinder.isBranchTarget(instructionOffset) ||
367               branchTargetFinder.isExceptionHandler(instructionOffset);
368    }
369
370
371    /**
372     * Returns whether the instruction at the given offset is the start of a
373     * subroutine.
374     */
375    public boolean isSubroutineStart(int instructionOffset)
376    {
377        return branchTargetFinder.isSubroutineStart(instructionOffset);
378    }
379
380
381    /**
382     * Returns whether the instruction at the given offset is a subroutine
383     * invocation.
384     */
385    public boolean isSubroutineInvocation(int instructionOffset)
386    {
387        return branchTargetFinder.isSubroutineInvocation(instructionOffset);
388    }
389
390
391    /**
392     * Returns whether the instruction at the given offset is part of a
393     * subroutine.
394     */
395    public boolean isSubroutine(int instructionOffset)
396    {
397        return branchTargetFinder.isSubroutine(instructionOffset);
398    }
399
400
401    /**
402     * Returns whether the subroutine at the given offset is ever returning
403     * by means of a regular 'ret' instruction.
404     */
405    public boolean isSubroutineReturning(int instructionOffset)
406    {
407        return branchTargetFinder.isSubroutineReturning(instructionOffset);
408    }
409
410
411    /**
412     * Returns the offset after the subroutine that starts at the given
413     * offset.
414     */
415    public int subroutineEnd(int instructionOffset)
416    {
417        return branchTargetFinder.subroutineEnd(instructionOffset);
418    }
419
420
421    /**
422     * Returns the instruction offset at which the object instance that is
423     * created at the given 'new' instruction offset is initialized, or
424     * <code>NONE</code> if it is not being created.
425     */
426    public int initializationOffset(int instructionOffset)
427    {
428        return branchTargetFinder.initializationOffset(instructionOffset);
429    }
430
431
432    /**
433     * Returns whether the method is an instance initializer.
434     */
435    public boolean isInitializer()
436    {
437        return branchTargetFinder.isInitializer();
438    }
439
440
441    /**
442     * Returns the instruction offset at which this initializer is calling
443     * the "super" or "this" initializer method, or <code>NONE</code> if it is
444     * not an initializer.
445     */
446    public int superInitializationOffset()
447    {
448        return branchTargetFinder.superInitializationOffset();
449    }
450
451
452    /**
453     * Returns the offset of the 'new' instruction that corresponds to the
454     * invocation of the instance initializer at the given offset, or
455     * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or
456     * "this" initializer method, , or <code>NONE</code> if it is not a 'new'
457     * instruction.
458     */
459    public int creationOffset(int offset)
460    {
461        return branchTargetFinder.creationOffset(offset);
462    }
463
464
465    /**
466     * Returns the variables before execution of the instruction at the given
467     * offset.
468     */
469    public TracedVariables getVariablesBefore(int instructionOffset)
470    {
471        return variablesBefore[instructionOffset];
472    }
473
474
475    /**
476     * Returns the variables after execution of the instruction at the given
477     * offset.
478     */
479    public TracedVariables getVariablesAfter(int instructionOffset)
480    {
481        return variablesAfter[instructionOffset];
482    }
483
484
485    /**
486     * Returns the stack before execution of the instruction at the given
487     * offset.
488     */
489    public TracedStack getStackBefore(int instructionOffset)
490    {
491        return stacksBefore[instructionOffset];
492    }
493
494
495    /**
496     * Returns the stack after execution of the instruction at the given
497     * offset.
498     */
499    public TracedStack getStackAfter(int instructionOffset)
500    {
501        return stacksAfter[instructionOffset];
502    }
503
504
505    /**
506     * Returns the instruction offsets that branch to the given instruction
507     * offset.
508     */
509    public InstructionOffsetValue branchOrigins(int instructionOffset)
510    {
511        return branchOriginValues[instructionOffset];
512    }
513
514
515    /**
516     * Returns the instruction offsets to which the given instruction offset
517     * branches.
518     */
519    public InstructionOffsetValue branchTargets(int instructionOffset)
520    {
521        return branchTargetValues[instructionOffset];
522    }
523
524
525    // Utility methods to evaluate instruction blocks.
526
527    /**
528     * Pushes block of instructions to be executed in the calling partial
529     * evaluator.
530     */
531    private void pushCallingInstructionBlock(TracedVariables variables,
532                                             TracedStack     stack,
533                                             int             startOffset)
534    {
535        callingInstructionBlockStack.push(new MyInstructionBlock(variables,
536                                                                 stack,
537                                                                 startOffset));
538    }
539
540
541    /**
542     * Pushes block of instructions to be executed in this partial evaluator.
543     */
544    private void pushInstructionBlock(TracedVariables variables,
545                                      TracedStack     stack,
546                                      int             startOffset)
547    {
548        instructionBlockStack.push(new MyInstructionBlock(variables,
549                                                          stack,
550                                                          startOffset));
551    }
552
553
554    /**
555     * Evaluates the instruction block and the exception handlers covering the
556     * given instruction range in the given code.
557     */
558    private void evaluateInstructionBlockAndExceptionHandlers(Clazz           clazz,
559                                                              Method          method,
560                                                              CodeAttribute   codeAttribute,
561                                                              TracedVariables variables,
562                                                              TracedStack     stack,
563                                                              int             startOffset,
564                                                              int             endOffset)
565    {
566        evaluateInstructionBlock(clazz,
567                                 method,
568                                 codeAttribute,
569                                 variables,
570                                 stack,
571                                 startOffset);
572
573        evaluateExceptionHandlers(clazz,
574                                  method,
575                                  codeAttribute,
576                                  startOffset,
577                                  endOffset);
578    }
579
580
581    /**
582     * Evaluates a block of instructions, starting at the given offset and ending
583     * at a branch instruction, a return instruction, or a throw instruction.
584     */
585    private void evaluateInstructionBlock(Clazz           clazz,
586                                          Method          method,
587                                          CodeAttribute   codeAttribute,
588                                          TracedVariables variables,
589                                          TracedStack     stack,
590                                          int             startOffset)
591    {
592        // Execute the initial instruction block.
593        evaluateSingleInstructionBlock(clazz,
594                                       method,
595                                       codeAttribute,
596                                       variables,
597                                       stack,
598                                       startOffset);
599
600        // Execute all resulting instruction blocks on the execution stack.
601        while (!instructionBlockStack.empty())
602        {
603            if (DEBUG) System.out.println("Popping alternative branch out of "+instructionBlockStack.size()+" blocks");
604
605            MyInstructionBlock instructionBlock =
606                (MyInstructionBlock)instructionBlockStack.pop();
607
608            evaluateSingleInstructionBlock(clazz,
609                                           method,
610                                           codeAttribute,
611                                           instructionBlock.variables,
612                                           instructionBlock.stack,
613                                           instructionBlock.startOffset);
614        }
615    }
616
617
618    /**
619     * Evaluates a block of instructions, starting at the given offset and ending
620     * at a branch instruction, a return instruction, or a throw instruction.
621     * Instruction blocks that are to be evaluated as a result are pushed on
622     * the given stack.
623     */
624    private void evaluateSingleInstructionBlock(Clazz            clazz,
625                                                Method           method,
626                                                CodeAttribute    codeAttribute,
627                                                TracedVariables  variables,
628                                                TracedStack      stack,
629                                                int              startOffset)
630    {
631        byte[] code = codeAttribute.code;
632
633        if (DEBUG)
634        {
635             System.out.println("Instruction block starting at ["+startOffset+"] in "+
636                                ClassUtil.externalFullMethodDescription(clazz.getName(),
637                                                                        0,
638                                                                        method.getName(clazz),
639                                                                        method.getDescriptor(clazz)));
640             System.out.println("Init vars:  "+variables);
641             System.out.println("Init stack: "+stack);
642        }
643
644        Processor processor = new Processor(variables,
645                                            stack,
646                                            valueFactory,
647                                            branchUnit,
648                                            invocationUnit,
649                                            evaluateAllCode);
650
651        int instructionOffset = startOffset;
652
653        int maxOffset = startOffset;
654
655        // Evaluate the subsequent instructions.
656        while (true)
657        {
658            if (maxOffset < instructionOffset)
659            {
660                maxOffset = instructionOffset;
661            }
662
663            // Maintain a generalized local variable frame and stack at this
664            // instruction offset, before execution.
665            int evaluationCount = evaluationCounts[instructionOffset];
666            if (evaluationCount == 0)
667            {
668                // First time we're passing by this instruction.
669                if (variablesBefore[instructionOffset] == null)
670                {
671                    // There's not even a context at this index yet.
672                    variablesBefore[instructionOffset] = new TracedVariables(variables);
673                    stacksBefore[instructionOffset]    = new TracedStack(stack);
674                }
675                else
676                {
677                    // Reuse the context objects at this index.
678                    variablesBefore[instructionOffset].initialize(variables);
679                    stacksBefore[instructionOffset].copy(stack);
680                }
681
682                // We'll execute in the generalized context, because it is
683                // the same as the current context.
684                generalizedContexts[instructionOffset] = true;
685            }
686            else
687            {
688                // Merge in the current context.
689                boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables, true);
690                boolean stackChanged     = stacksBefore[instructionOffset].generalize(stack);
691
692                //System.out.println("GVars:  "+variablesBefore[instructionOffset]);
693                //System.out.println("GStack: "+stacksBefore[instructionOffset]);
694
695                // Bail out if the current context is the same as last time.
696                if (!variablesChanged &&
697                    !stackChanged     &&
698                    generalizedContexts[instructionOffset])
699                {
700                    if (DEBUG) System.out.println("Repeated variables, stack, and branch targets");
701
702                    break;
703                }
704
705                // See if this instruction has been evaluated an excessive number
706                // of times.
707                if (evaluationCount >= MAXIMUM_EVALUATION_COUNT)
708                {
709                    if (DEBUG) System.out.println("Generalizing current context after "+evaluationCount+" evaluations");
710
711                    // Continue, but generalize the current context.
712                    // Note that the most recent variable values have to remain
713                    // last in the generalizations, for the sake of the ret
714                    // instruction.
715                    variables.generalize(variablesBefore[instructionOffset], false);
716                    stack.generalize(stacksBefore[instructionOffset]);
717
718                    // We'll execute in the generalized context.
719                    generalizedContexts[instructionOffset] = true;
720                }
721                else
722                {
723                    // We'll execute in the current context.
724                    generalizedContexts[instructionOffset] = false;
725                }
726            }
727
728            // We'll evaluate this instruction.
729            evaluationCounts[instructionOffset]++;
730
731            // Remember this instruction's offset with any stored value.
732            Value storeValue = new InstructionOffsetValue(instructionOffset);
733            variables.setProducerValue(storeValue);
734            stack.setProducerValue(storeValue);
735
736            // Reset the trace value.
737            InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE;
738
739            // Note that the instruction is only volatile.
740            Instruction instruction = InstructionFactory.create(code, instructionOffset);
741
742            // By default, the next instruction will be the one after this
743            // instruction.
744            int nextInstructionOffset = instructionOffset +
745                                        instruction.length(instructionOffset);
746            InstructionOffsetValue nextInstructionOffsetValue = new InstructionOffsetValue(nextInstructionOffset);
747            branchUnit.resetCalled();
748            branchUnit.setTraceBranchTargets(nextInstructionOffsetValue);
749
750            if (DEBUG)
751            {
752                System.out.println(instruction.toString(instructionOffset));
753            }
754
755            try
756            {
757                // Process the instruction. The processor may modify the
758                // variables and the stack, and it may call the branch unit
759                // and the invocation unit.
760                instruction.accept(clazz,
761                                   method,
762                                   codeAttribute,
763                                   instructionOffset,
764                                   processor);
765            }
766            catch (RuntimeException ex)
767            {
768                System.err.println("Unexpected error while evaluating instruction:");
769                System.err.println("  Class       = ["+clazz.getName()+"]");
770                System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
771                System.err.println("  Instruction = "+instruction.toString(instructionOffset));
772                System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
773
774                throw ex;
775            }
776
777            // Collect the branch targets from the branch unit.
778            InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets();
779            int branchTargetCount = branchTargets.instructionOffsetCount();
780
781            // Stop tracing.
782            branchUnit.setTraceBranchTargets(traceValue);
783
784            if (DEBUG)
785            {
786                if (branchUnit.wasCalled())
787                {
788                    System.out.println("     is branching to "+branchTargets);
789                }
790                if (branchTargetValues[instructionOffset] != null)
791                {
792                    System.out.println("     has up till now been branching to "+branchTargetValues[instructionOffset]);
793                }
794
795                System.out.println(" Vars:  "+variables);
796                System.out.println(" Stack: "+stack);
797            }
798
799            // Maintain a generalized local variable frame and stack at this
800            // instruction offset, after execution.
801            if (evaluationCount == 0)
802            {
803                // First time we're passing by this instruction.
804                if (variablesAfter[instructionOffset] == null)
805                {
806                    // There's not even a context at this index yet.
807                    variablesAfter[instructionOffset] = new TracedVariables(variables);
808                    stacksAfter[instructionOffset]    = new TracedStack(stack);
809                }
810                else
811                {
812                    // Reuse the context objects at this index.
813                    variablesAfter[instructionOffset].initialize(variables);
814                    stacksAfter[instructionOffset].copy(stack);
815                }
816            }
817            else
818            {
819                // Merge in the current context.
820                variablesAfter[instructionOffset].generalize(variables, true);
821                stacksAfter[instructionOffset].generalize(stack);
822            }
823
824            // Did the branch unit get called?
825            if (branchUnit.wasCalled())
826            {
827                // Accumulate the branch targets at this offset.
828                branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ?
829                    branchTargets :
830                    branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue();
831
832                // Are there no branch targets at all?
833                if (branchTargetCount == 0)
834                {
835                    // Exit from this code block.
836                    break;
837                }
838
839                // Accumulate the branch origins at the branch target offsets.
840                InstructionOffsetValue instructionOffsetValue = new InstructionOffsetValue(instructionOffset);
841                for (int index = 0; index < branchTargetCount; index++)
842                {
843                    int branchTarget = branchTargets.instructionOffset(index);
844                    branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ?
845                        instructionOffsetValue:
846                        branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue();
847                }
848
849                // Are there multiple branch targets?
850                if (branchTargetCount > 1)
851                {
852                    // Push them on the execution stack and exit from this block.
853                    for (int index = 0; index < branchTargetCount; index++)
854                    {
855                        if (DEBUG) System.out.println("Pushing alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]");
856
857                        pushInstructionBlock(new TracedVariables(variables),
858                                             new TracedStack(stack),
859                                             branchTargets.instructionOffset(index));
860                    }
861
862                    break;
863                }
864
865                if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]");
866            }
867
868            // Just continue with the next instruction.
869            instructionOffset = branchTargets.instructionOffset(0);
870
871            // Is this a subroutine invocation?
872            if (instruction.opcode == InstructionConstants.OP_JSR ||
873                instruction.opcode == InstructionConstants.OP_JSR_W)
874            {
875                // Evaluate the subroutine in another partial evaluator.
876                evaluateSubroutine(clazz,
877                                   method,
878                                   codeAttribute,
879                                   variables,
880                                   stack,
881                                   instructionOffset,
882                                   instructionBlockStack);
883
884                break;
885            }
886            else if (instruction.opcode == InstructionConstants.OP_RET)
887            {
888                // Let the partial evaluator that has called the subroutine
889                // handle the evaluation after the return.
890                pushCallingInstructionBlock(new TracedVariables(variables),
891                                            new TracedStack(stack),
892                                            instructionOffset);
893                break;
894            }
895        }
896
897        if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]");
898    }
899
900
901    /**
902     * Evaluates a subroutine and its exception handlers, starting at the given
903     * offset and ending at a subroutine return instruction.
904     */
905    private void evaluateSubroutine(Clazz           clazz,
906                                    Method          method,
907                                    CodeAttribute   codeAttribute,
908                                    TracedVariables variables,
909                                    TracedStack     stack,
910                                    int             subroutineStart,
911                                    java.util.Stack instructionBlockStack)
912    {
913        int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart);
914
915        if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd);
916
917        // Create a temporary partial evaluator, so there are no conflicts
918        // with variables that are alive across subroutine invocations, between
919        // different invocations.
920        PartialEvaluator subroutinePartialEvaluator =
921            new PartialEvaluator(this);
922
923        subroutinePartialEvaluator.initializeArrays(codeAttribute);
924
925        // Evaluate the subroutine.
926        subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz,
927                                                                                method,
928                                                                                codeAttribute,
929                                                                                variables,
930                                                                                stack,
931                                                                                subroutineStart,
932                                                                                subroutineEnd);
933
934        // Merge back the temporary partial evaluator. This way, we'll get
935        // the lowest common denominator of stacks and variables.
936        generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength);
937
938        if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd);
939    }
940
941
942    /**
943     * Generalizes the results of this partial evaluator with those of another
944     * given partial evaluator, over a given range of instructions.
945     */
946    private void generalize(PartialEvaluator other,
947                            int              codeStart,
948                            int              codeEnd)
949    {
950        if (DEBUG) System.out.println("Generalizing with temporary partial evaluation");
951
952        for (int offset = codeStart; offset < codeEnd; offset++)
953        {
954            if (other.branchOriginValues[offset] != null)
955            {
956                branchOriginValues[offset] = branchOriginValues[offset] == null ?
957                    other.branchOriginValues[offset] :
958                    branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue();
959            }
960
961            if (other.isTraced(offset))
962            {
963                if (other.branchTargetValues[offset] != null)
964                {
965                    branchTargetValues[offset] = branchTargetValues[offset] == null ?
966                        other.branchTargetValues[offset] :
967                        branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue();
968                }
969
970                if (evaluationCounts[offset] == 0)
971                {
972                    variablesBefore[offset]     = other.variablesBefore[offset];
973                    stacksBefore[offset]        = other.stacksBefore[offset];
974                    variablesAfter[offset]      = other.variablesAfter[offset];
975                    stacksAfter[offset]         = other.stacksAfter[offset];
976                    generalizedContexts[offset] = other.generalizedContexts[offset];
977                    evaluationCounts[offset]    = other.evaluationCounts[offset];
978                }
979                else
980                {
981                    variablesBefore[offset].generalize(other.variablesBefore[offset], false);
982                    stacksBefore[offset]   .generalize(other.stacksBefore[offset]);
983                    variablesAfter[offset] .generalize(other.variablesAfter[offset], false);
984                    stacksAfter[offset]    .generalize(other.stacksAfter[offset]);
985                    //generalizedContexts[offset]
986                    evaluationCounts[offset] += other.evaluationCounts[offset];
987                }
988            }
989        }
990    }
991
992
993    /**
994     * Evaluates the exception handlers covering and targeting the given
995     * instruction range in the given code.
996     */
997    private void evaluateExceptionHandlers(Clazz         clazz,
998                                           Method        method,
999                                           CodeAttribute codeAttribute,
1000                                           int           startOffset,
1001                                           int           endOffset)
1002    {
1003        if (DEBUG) System.out.println("Evaluating exceptions covering ["+startOffset+" -> "+endOffset+"]:");
1004
1005        ExceptionHandlerFilter exceptionEvaluator =
1006            new ExceptionHandlerFilter(startOffset,
1007                                       endOffset,
1008                                       this);
1009
1010        // Evaluate the exception catch blocks, until their entry variables
1011        // have stabilized.
1012        do
1013        {
1014            // Reset the flag to stop evaluating.
1015            evaluateExceptions = false;
1016
1017            // Evaluate all relevant exception catch blocks once.
1018            codeAttribute.exceptionsAccept(clazz,
1019                                           method,
1020                                           startOffset,
1021                                           endOffset,
1022                                           exceptionEvaluator);
1023        }
1024        while (evaluateExceptions);
1025    }
1026
1027
1028    // Implementations for ExceptionInfoVisitor.
1029
1030    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
1031    {
1032        int startPC = exceptionInfo.u2startPC;
1033        int endPC   = exceptionInfo.u2endPC;
1034
1035        // Do we have to evaluate this exception catch block?
1036        if (isTraced(startPC, endPC))
1037        {
1038            int handlerPC = exceptionInfo.u2handlerPC;
1039            int catchType = exceptionInfo.u2catchType;
1040
1041            if (DEBUG) System.out.println("Evaluating exception ["+startPC +" -> "+endPC +": "+handlerPC+"]:");
1042
1043            // Reuse the existing variables and stack objects, ensuring the
1044            // right size.
1045            TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals);
1046            TracedStack     stack     = new TracedStack(codeAttribute.u2maxStack);
1047
1048            // Initialize the trace values.
1049            Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY);
1050            variables.setProducerValue(storeValue);
1051            stack.setProducerValue(storeValue);
1052
1053            // Initialize the variables by generalizing the variables of the
1054            // try block. Make sure to include the results of the last
1055            // instruction for preverification.
1056            generalizeVariables(startPC,
1057                                endPC,
1058                                evaluateAllCode,
1059                                variables);
1060
1061            // Initialize the the stack.
1062            //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false));
1063            String catchClassName = catchType != 0 ?
1064                 clazz.getClassName(catchType) :
1065                 ClassConstants.NAME_JAVA_LANG_THROWABLE;
1066
1067            Clazz catchClass = catchType != 0 ?
1068                ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass :
1069                null;
1070
1071            stack.push(valueFactory.createReferenceValue(catchClassName,
1072                                                         catchClass,
1073                                                         false));
1074
1075            int evaluationCount = evaluationCounts[handlerPC];
1076
1077            // Evaluate the instructions, starting at the entry point.
1078            evaluateInstructionBlock(clazz,
1079                                     method,
1080                                     codeAttribute,
1081                                     variables,
1082                                     stack,
1083                                     handlerPC);
1084
1085            // Remember to evaluate all exception handlers once more.
1086            if (!evaluateExceptions)
1087            {
1088                evaluateExceptions = evaluationCount < evaluationCounts[handlerPC];
1089            }
1090        }
1091//        else if (evaluateAllCode)
1092//        {
1093//            if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"] yet");
1094//
1095//            // We don't have any information on the try block yet, but we do
1096//            // have to evaluate the exception handler.
1097//            // Remember to evaluate all exception handlers once more.
1098//            evaluateExceptions = true;
1099//        }
1100        else
1101        {
1102            if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"]");
1103        }
1104    }
1105
1106
1107    // Small utility methods.
1108
1109    /**
1110     * Initializes the data structures for the variables, stack, etc.
1111     */
1112    private void initializeArrays(CodeAttribute codeAttribute)
1113    {
1114        int codeLength = codeAttribute.u4codeLength;
1115
1116        // Create new arrays for storing information at each instruction offset.
1117        if (variablesAfter.length < codeLength)
1118        {
1119            // Create new arrays.
1120            branchOriginValues  = new InstructionOffsetValue[codeLength];
1121            branchTargetValues  = new InstructionOffsetValue[codeLength];
1122            variablesBefore     = new TracedVariables[codeLength];
1123            stacksBefore        = new TracedStack[codeLength];
1124            variablesAfter      = new TracedVariables[codeLength];
1125            stacksAfter         = new TracedStack[codeLength];
1126            generalizedContexts = new boolean[codeLength];
1127            evaluationCounts    = new int[codeLength];
1128        }
1129        else
1130        {
1131            // Reset the arrays.
1132            Arrays.fill(branchOriginValues,  null);
1133            Arrays.fill(branchTargetValues,  null);
1134            Arrays.fill(generalizedContexts, false);
1135            Arrays.fill(evaluationCounts,    0);
1136
1137            for (int index = 0; index < codeLength; index++)
1138            {
1139                if (variablesBefore[index] != null)
1140                {
1141                    variablesBefore[index].reset(codeAttribute.u2maxLocals);
1142                }
1143
1144                if (stacksBefore[index] != null)
1145                {
1146                    stacksBefore[index].reset(codeAttribute.u2maxStack);
1147                }
1148
1149                if (variablesAfter[index] != null)
1150                {
1151                    variablesAfter[index].reset(codeAttribute.u2maxLocals);
1152                }
1153
1154                if (stacksAfter[index] != null)
1155                {
1156                    stacksAfter[index].reset(codeAttribute.u2maxStack);
1157                }
1158            }
1159        }
1160    }
1161
1162
1163    /**
1164     * Initializes the data structures for the variables, stack, etc.
1165     */
1166    private void initializeParameters(Clazz           clazz,
1167                                      Method          method,
1168                                      CodeAttribute   codeAttribute,
1169                                      TracedVariables variables)
1170    {
1171        // Create the method parameters.
1172        TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals);
1173
1174        // Remember this instruction's offset with any stored value.
1175        Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY);
1176        parameters.setProducerValue(storeValue);
1177
1178        // Initialize the method parameters.
1179        invocationUnit.enterMethod(clazz, method, parameters);
1180
1181        if (DEBUG)
1182        {
1183            System.out.println("  Params: "+parameters);
1184        }
1185
1186        // Initialize the variables with the parameters.
1187        variables.initialize(parameters);
1188
1189        // Set the store value of each parameter variable.
1190        InstructionOffsetValue atMethodEntry = new InstructionOffsetValue(AT_METHOD_ENTRY);
1191
1192        for (int index = 0; index < parameters.size(); index++)
1193        {
1194            variables.setProducerValue(index, atMethodEntry);
1195        }
1196    }
1197
1198
1199    /**
1200     * Generalize the local variable frames of a block of instructions.
1201     */
1202    private void generalizeVariables(int             startOffset,
1203                                     int             endOffset,
1204                                     boolean         includeAfterLastInstruction,
1205                                     TracedVariables generalizedVariables)
1206    {
1207        boolean first     = true;
1208        int     lastIndex = -1;
1209
1210        // Generalize the variables before each of the instructions in the block.
1211        for (int index = startOffset; index < endOffset; index++)
1212        {
1213            if (isTraced(index))
1214            {
1215                TracedVariables tracedVariables = variablesBefore[index];
1216
1217                if (first)
1218                {
1219                    // Initialize the variables with the first traced local
1220                    // variable frame.
1221                    generalizedVariables.initialize(tracedVariables);
1222
1223                    first = false;
1224                }
1225                else
1226                {
1227                    // Generalize the variables with the traced local variable
1228                    // frame. We can't use the return value, because local
1229                    // generalization can be different a couple of times,
1230                    // with the global generalization being the same.
1231                    generalizedVariables.generalize(tracedVariables, false);
1232                }
1233
1234                lastIndex = index;
1235            }
1236        }
1237
1238        // Generalize the variables after the last instruction in the block,
1239        // if required.
1240        if (includeAfterLastInstruction &&
1241            lastIndex >= 0)
1242        {
1243            TracedVariables tracedVariables = variablesAfter[lastIndex];
1244
1245            if (first)
1246            {
1247                // Initialize the variables with the local variable frame.
1248                generalizedVariables.initialize(tracedVariables);
1249            }
1250            else
1251            {
1252                // Generalize the variables with the local variable frame.
1253                generalizedVariables.generalize(tracedVariables, false);
1254            }
1255        }
1256
1257        // Just clear the variables if there aren't any traced instructions
1258        // in the block.
1259        if (first)
1260        {
1261            generalizedVariables.reset(generalizedVariables.size());
1262        }
1263    }
1264
1265
1266    private static class MyInstructionBlock
1267    {
1268        private TracedVariables variables;
1269        private TracedStack     stack;
1270        private int             startOffset;
1271
1272
1273        private MyInstructionBlock(TracedVariables variables,
1274                                   TracedStack     stack,
1275                                   int             startOffset)
1276        {
1277            this.variables   = variables;
1278            this.stack       = stack;
1279            this.startOffset = startOffset;
1280        }
1281    }
1282}
1283