• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.constant.ClassConstant;
27 import proguard.classfile.instruction.*;
28 import proguard.classfile.util.*;
29 import proguard.classfile.visitor.*;
30 import proguard.evaluation.*;
31 import proguard.evaluation.value.*;
32 import proguard.optimize.peephole.BranchTargetFinder;
33 
34 import 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  */
42 public class PartialEvaluator
43 extends      SimplifiedVisitor
44 implements   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      */
PartialEvaluator()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      */
PartialEvaluator(ValueFactory valueFactory, InvocationUnit invocationUnit, boolean evaluateAllCode)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      */
PartialEvaluator(PartialEvaluator partialEvaluator)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      */
PartialEvaluator(ValueFactory valueFactory, InvocationUnit invocationUnit, boolean evaluateAllCode, BasicBranchUnit branchUnit, BranchTargetFinder branchTargetFinder, java.util.Stack callingInstructionBlockStack)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 
visitAnyAttribute(Clazz clazz, Attribute attribute)173     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
174 
175 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)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 
visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)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      */
isTraced(int startOffset, int endOffset)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      */
isTraced(int instructionOffset)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      */
isInstruction(int instructionOffset)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      */
isBranchOrExceptionTarget(int instructionOffset)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      */
isSubroutineStart(int instructionOffset)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      */
isSubroutineInvocation(int instructionOffset)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      */
isSubroutine(int instructionOffset)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      */
isSubroutineReturning(int instructionOffset)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      */
subroutineEnd(int instructionOffset)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      */
initializationOffset(int instructionOffset)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      */
isInitializer()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      */
superInitializationOffset()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      */
creationOffset(int offset)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      */
getVariablesBefore(int instructionOffset)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      */
getVariablesAfter(int instructionOffset)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      */
getStackBefore(int instructionOffset)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      */
getStackAfter(int instructionOffset)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      */
branchOrigins(int instructionOffset)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      */
branchTargets(int instructionOffset)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      */
pushCallingInstructionBlock(TracedVariables variables, TracedStack stack, int startOffset)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      */
pushInstructionBlock(TracedVariables variables, TracedStack stack, int startOffset)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      */
evaluateInstructionBlockAndExceptionHandlers(Clazz clazz, Method method, CodeAttribute codeAttribute, TracedVariables variables, TracedStack stack, int startOffset, int endOffset)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      */
evaluateInstructionBlock(Clazz clazz, Method method, CodeAttribute codeAttribute, TracedVariables variables, TracedStack stack, int startOffset)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      */
evaluateSingleInstructionBlock(Clazz clazz, Method method, CodeAttribute codeAttribute, TracedVariables variables, TracedStack stack, int startOffset)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      */
evaluateSubroutine(Clazz clazz, Method method, CodeAttribute codeAttribute, TracedVariables variables, TracedStack stack, int subroutineStart, java.util.Stack instructionBlockStack)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      */
generalize(PartialEvaluator other, int codeStart, int codeEnd)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      */
evaluateExceptionHandlers(Clazz clazz, Method method, CodeAttribute codeAttribute, int startOffset, int endOffset)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 
visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)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