1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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.AttributeVisitor;
26import proguard.classfile.editor.*;
27import proguard.classfile.instruction.*;
28import proguard.classfile.instruction.visitor.InstructionVisitor;
29import proguard.classfile.util.*;
30import proguard.classfile.visitor.*;
31import proguard.evaluation.*;
32import proguard.evaluation.value.*;
33import proguard.optimize.info.*;
34
35/**
36 * This AttributeVisitor simplifies the code attributes that it visits, based
37 * on partial evaluation.
38 *
39 * @author Eric Lafortune
40 */
41public class EvaluationSimplifier
42extends      SimplifiedVisitor
43implements   AttributeVisitor,
44             InstructionVisitor
45{
46    //*
47    private static final boolean DEBUG = false;
48    /*/
49    private static boolean DEBUG       = true;
50    //*/
51
52    private final InstructionVisitor extraInstructionVisitor;
53
54    private final PartialEvaluator             partialEvaluator;
55    private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
56    private final CodeAttributeEditor          codeAttributeEditor          = new CodeAttributeEditor(false);
57
58
59    /**
60     * Creates a new EvaluationSimplifier.
61     */
62    public EvaluationSimplifier()
63    {
64        this(new PartialEvaluator(), null);
65    }
66
67
68    /**
69     * Creates a new EvaluationSimplifier.
70     * @param partialEvaluator        the partial evaluator that will
71     *                                execute the code and provide
72     *                                information about the results.
73     * @param extraInstructionVisitor an optional extra visitor for all
74     *                                simplified instructions.
75     */
76    public EvaluationSimplifier(PartialEvaluator   partialEvaluator,
77                                InstructionVisitor extraInstructionVisitor)
78    {
79        this.partialEvaluator        = partialEvaluator;
80        this.extraInstructionVisitor = extraInstructionVisitor;
81    }
82
83
84    // Implementations for AttributeVisitor.
85
86    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
87
88
89    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
90    {
91//        DEBUG =
92//            clazz.getName().equals("abc/Def") &&
93//            method.getName(clazz).equals("abc");
94
95        // TODO: Remove this when the evaluation simplifier has stabilized.
96        // Catch any unexpected exceptions from the actual visiting method.
97        try
98        {
99            // Process the code.
100            visitCodeAttribute0(clazz, method, codeAttribute);
101        }
102        catch (RuntimeException ex)
103        {
104            System.err.println("Unexpected error while simplifying instructions after partial evaluation:");
105            System.err.println("  Class       = ["+clazz.getName()+"]");
106            System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
107            System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
108            System.err.println("Not optimizing this method");
109
110            if (DEBUG)
111            {
112                method.accept(clazz, new ClassPrinter());
113
114                throw ex;
115            }
116        }
117    }
118
119
120    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
121    {
122        if (DEBUG)
123        {
124            System.out.println();
125            System.out.println("Class "+ClassUtil.externalClassName(clazz.getName()));
126            System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(),
127                                                                                 0,
128                                                                                 method.getName(clazz),
129                                                                                 method.getDescriptor(clazz)));
130        }
131
132        // Evaluate the method.
133        partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
134
135        int codeLength = codeAttribute.u4codeLength;
136
137        // Reset the code changes.
138        codeAttributeEditor.reset(codeLength);
139
140        // Replace any instructions that can be simplified.
141        for (int offset = 0; offset < codeLength; offset++)
142        {
143            if (partialEvaluator.isTraced(offset))
144            {
145                Instruction instruction = InstructionFactory.create(codeAttribute.code,
146                                                                    offset);
147
148                instruction.accept(clazz, method, codeAttribute, offset, this);
149            }
150        }
151
152        // Apply all accumulated changes to the code.
153        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
154    }
155
156
157    // Implementations for InstructionVisitor.
158
159    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
160    {
161        switch (simpleInstruction.opcode)
162        {
163            case InstructionConstants.OP_IALOAD:
164            case InstructionConstants.OP_BALOAD:
165            case InstructionConstants.OP_CALOAD:
166            case InstructionConstants.OP_SALOAD:
167            case InstructionConstants.OP_IADD:
168            case InstructionConstants.OP_ISUB:
169            case InstructionConstants.OP_IMUL:
170            case InstructionConstants.OP_IDIV:
171            case InstructionConstants.OP_IREM:
172            case InstructionConstants.OP_INEG:
173            case InstructionConstants.OP_ISHL:
174            case InstructionConstants.OP_ISHR:
175            case InstructionConstants.OP_IUSHR:
176            case InstructionConstants.OP_IAND:
177            case InstructionConstants.OP_IOR:
178            case InstructionConstants.OP_IXOR:
179            case InstructionConstants.OP_L2I:
180            case InstructionConstants.OP_F2I:
181            case InstructionConstants.OP_D2I:
182            case InstructionConstants.OP_I2B:
183            case InstructionConstants.OP_I2C:
184            case InstructionConstants.OP_I2S:
185                replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
186                break;
187
188            case InstructionConstants.OP_LALOAD:
189            case InstructionConstants.OP_LADD:
190            case InstructionConstants.OP_LSUB:
191            case InstructionConstants.OP_LMUL:
192            case InstructionConstants.OP_LDIV:
193            case InstructionConstants.OP_LREM:
194            case InstructionConstants.OP_LNEG:
195            case InstructionConstants.OP_LSHL:
196            case InstructionConstants.OP_LSHR:
197            case InstructionConstants.OP_LUSHR:
198            case InstructionConstants.OP_LAND:
199            case InstructionConstants.OP_LOR:
200            case InstructionConstants.OP_LXOR:
201            case InstructionConstants.OP_I2L:
202            case InstructionConstants.OP_F2L:
203            case InstructionConstants.OP_D2L:
204                replaceLongPushInstruction(clazz, offset, simpleInstruction);
205                break;
206
207            case InstructionConstants.OP_FALOAD:
208            case InstructionConstants.OP_FADD:
209            case InstructionConstants.OP_FSUB:
210            case InstructionConstants.OP_FMUL:
211            case InstructionConstants.OP_FDIV:
212            case InstructionConstants.OP_FREM:
213            case InstructionConstants.OP_FNEG:
214            case InstructionConstants.OP_I2F:
215            case InstructionConstants.OP_L2F:
216            case InstructionConstants.OP_D2F:
217                replaceFloatPushInstruction(clazz, offset, simpleInstruction);
218                break;
219
220            case InstructionConstants.OP_DALOAD:
221            case InstructionConstants.OP_DADD:
222            case InstructionConstants.OP_DSUB:
223            case InstructionConstants.OP_DMUL:
224            case InstructionConstants.OP_DDIV:
225            case InstructionConstants.OP_DREM:
226            case InstructionConstants.OP_DNEG:
227            case InstructionConstants.OP_I2D:
228            case InstructionConstants.OP_L2D:
229            case InstructionConstants.OP_F2D:
230                replaceDoublePushInstruction(clazz, offset, simpleInstruction);
231                break;
232
233            case InstructionConstants.OP_AALOAD:
234                replaceReferencePushInstruction(clazz, offset, simpleInstruction);
235                break;
236        }
237    }
238
239
240    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
241    {
242        int variableIndex = variableInstruction.variableIndex;
243
244        switch (variableInstruction.opcode)
245        {
246            case InstructionConstants.OP_ILOAD:
247            case InstructionConstants.OP_ILOAD_0:
248            case InstructionConstants.OP_ILOAD_1:
249            case InstructionConstants.OP_ILOAD_2:
250            case InstructionConstants.OP_ILOAD_3:
251                replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex);
252                break;
253
254            case InstructionConstants.OP_LLOAD:
255            case InstructionConstants.OP_LLOAD_0:
256            case InstructionConstants.OP_LLOAD_1:
257            case InstructionConstants.OP_LLOAD_2:
258            case InstructionConstants.OP_LLOAD_3:
259                replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex);
260                break;
261
262            case InstructionConstants.OP_FLOAD:
263            case InstructionConstants.OP_FLOAD_0:
264            case InstructionConstants.OP_FLOAD_1:
265            case InstructionConstants.OP_FLOAD_2:
266            case InstructionConstants.OP_FLOAD_3:
267                replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex);
268                break;
269
270            case InstructionConstants.OP_DLOAD:
271            case InstructionConstants.OP_DLOAD_0:
272            case InstructionConstants.OP_DLOAD_1:
273            case InstructionConstants.OP_DLOAD_2:
274            case InstructionConstants.OP_DLOAD_3:
275                replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex);
276                break;
277
278            case InstructionConstants.OP_ALOAD:
279            case InstructionConstants.OP_ALOAD_0:
280            case InstructionConstants.OP_ALOAD_1:
281            case InstructionConstants.OP_ALOAD_2:
282            case InstructionConstants.OP_ALOAD_3:
283                replaceReferencePushInstruction(clazz, offset, variableInstruction);
284                break;
285
286            case InstructionConstants.OP_ASTORE:
287            case InstructionConstants.OP_ASTORE_0:
288            case InstructionConstants.OP_ASTORE_1:
289            case InstructionConstants.OP_ASTORE_2:
290            case InstructionConstants.OP_ASTORE_3:
291                deleteReferencePopInstruction(clazz, offset, variableInstruction);
292                break;
293
294            case InstructionConstants.OP_RET:
295                replaceBranchInstruction(clazz, offset, variableInstruction);
296                break;
297        }
298    }
299
300
301    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
302    {
303        switch (constantInstruction.opcode)
304        {
305            case InstructionConstants.OP_GETSTATIC:
306            case InstructionConstants.OP_GETFIELD:
307                replaceAnyPushInstruction(clazz, offset, constantInstruction);
308                break;
309
310            case InstructionConstants.OP_INVOKEVIRTUAL:
311            case InstructionConstants.OP_INVOKESPECIAL:
312            case InstructionConstants.OP_INVOKESTATIC:
313            case InstructionConstants.OP_INVOKEINTERFACE:
314                if (constantInstruction.stackPushCount(clazz) > 0 &&
315                    !sideEffectInstructionChecker.hasSideEffects(clazz,
316                                                                 method,
317                                                                 codeAttribute,
318                                                                 offset,
319                                                                 constantInstruction))
320                {
321                    replaceAnyPushInstruction(clazz, offset, constantInstruction);
322                }
323
324                break;
325
326            case InstructionConstants.OP_CHECKCAST:
327                replaceReferencePushInstruction(clazz, offset, constantInstruction);
328                break;
329        }
330    }
331
332
333    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
334    {
335        switch (branchInstruction.opcode)
336        {
337            case InstructionConstants.OP_GOTO:
338            case InstructionConstants.OP_GOTO_W:
339                // Don't replace unconditional branches.
340                break;
341
342            case InstructionConstants.OP_JSR:
343            case InstructionConstants.OP_JSR_W:
344                replaceJsrInstruction(clazz, offset, branchInstruction);
345                break;
346
347            default:
348                replaceBranchInstruction(clazz, offset, branchInstruction);
349                break;
350        }
351    }
352
353
354    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
355    {
356        // First try to simplify it to a simple branch.
357        replaceBranchInstruction(clazz, offset, switchInstruction);
358
359        // Otherwise make sure all branch targets are valid.
360        if (!codeAttributeEditor.isModified(offset))
361        {
362            replaceSwitchInstruction(clazz, offset, switchInstruction);
363        }
364    }
365
366
367    // Small utility methods.
368
369    /**
370     * Replaces the push instruction at the given offset by a simpler push
371     * instruction, if possible.
372     */
373    private void replaceAnyPushInstruction(Clazz       clazz,
374                                           int         offset,
375                                           Instruction instruction)
376    {
377        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
378        if (pushedValue.isParticular())
379        {
380            switch (pushedValue.computationalType())
381            {
382                case Value.TYPE_INTEGER:
383                    replaceIntegerPushInstruction(clazz, offset, instruction);
384                    break;
385                case Value.TYPE_LONG:
386                    replaceLongPushInstruction(clazz, offset, instruction);
387                    break;
388                case Value.TYPE_FLOAT:
389                    replaceFloatPushInstruction(clazz, offset, instruction);
390                    break;
391                case Value.TYPE_DOUBLE:
392                    replaceDoublePushInstruction(clazz, offset, instruction);
393                    break;
394                case Value.TYPE_REFERENCE:
395                    replaceReferencePushInstruction(clazz, offset, instruction);
396                    break;
397            }
398        }
399    }
400
401
402    /**
403     * Replaces the integer pushing instruction at the given offset by a simpler
404     * push instruction, if possible.
405     */
406    private void replaceIntegerPushInstruction(Clazz       clazz,
407                                               int         offset,
408                                               Instruction instruction)
409    {
410        replaceIntegerPushInstruction(clazz,
411                                      offset,
412                                      instruction,
413                                      partialEvaluator.getVariablesBefore(offset).size());
414    }
415
416
417    /**
418     * Replaces the integer pushing instruction at the given offset by a simpler
419     * push instruction, if possible.
420     */
421    private void replaceIntegerPushInstruction(Clazz       clazz,
422                                               int         offset,
423                                               Instruction instruction,
424                                               int         maxVariableIndex)
425    {
426        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
427        if (pushedValue.isParticular())
428        {
429            int value = pushedValue.integerValue().value();
430            if (value << 16 >> 16 == value)
431            {
432                replaceConstantPushInstruction(clazz,
433                                               offset,
434                                               instruction,
435                                               InstructionConstants.OP_SIPUSH,
436                                               value);
437            }
438            else
439            {
440                ConstantPoolEditor constantPoolEditor =
441                    new ConstantPoolEditor((ProgramClass)clazz);
442
443                Instruction replacementInstruction =
444                    new ConstantInstruction(InstructionConstants.OP_LDC,
445                                            constantPoolEditor.addIntegerConstant(value)).shrink();
446
447                replaceInstruction(clazz, offset, instruction, replacementInstruction);
448            }
449        }
450        else if (pushedValue.isSpecific())
451        {
452            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
453            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
454            {
455                if (pushedValue.equals(variables.load(variableIndex)))
456                {
457                    replaceVariablePushInstruction(clazz,
458                                                   offset,
459                                                   instruction,
460                                                   InstructionConstants.OP_ILOAD,
461                                                   variableIndex);
462                }
463            }
464        }
465    }
466
467
468    /**
469     * Replaces the long pushing instruction at the given offset by a simpler
470     * push instruction, if possible.
471     */
472    private void replaceLongPushInstruction(Clazz       clazz,
473                                            int         offset,
474                                            Instruction instruction)
475    {
476        replaceLongPushInstruction(clazz,
477                                   offset,
478                                   instruction,
479                                   partialEvaluator.getVariablesBefore(offset).size());
480    }
481
482
483    /**
484     * Replaces the long pushing instruction at the given offset by a simpler
485     * push instruction, if possible.
486     */
487    private void replaceLongPushInstruction(Clazz       clazz,
488                                            int         offset,
489                                            Instruction instruction,
490                                            int         maxVariableIndex)
491    {
492        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
493        if (pushedValue.isParticular())
494        {
495            long value = pushedValue.longValue().value();
496            if (value == 0L ||
497                value == 1L)
498            {
499                replaceConstantPushInstruction(clazz,
500                                       offset,
501                                       instruction,
502                                       InstructionConstants.OP_LCONST_0,
503                                       (int)value);
504            }
505            else
506            {
507                ConstantPoolEditor constantPoolEditor =
508                    new ConstantPoolEditor((ProgramClass)clazz);
509
510                Instruction replacementInstruction =
511                    new ConstantInstruction(InstructionConstants.OP_LDC2_W,
512                                            constantPoolEditor.addLongConstant(value)).shrink();
513
514                replaceInstruction(clazz, offset, instruction, replacementInstruction);
515            }
516        }
517        else if (pushedValue.isSpecific())
518        {
519            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
520            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
521            {
522                if (pushedValue.equals(variables.load(variableIndex)))
523                {
524                    replaceVariablePushInstruction(clazz,
525                                                   offset,
526                                                   instruction,
527                                                   InstructionConstants.OP_LLOAD,
528                                                   variableIndex);
529                }
530            }
531        }
532    }
533
534
535    /**
536     * Replaces the float pushing instruction at the given offset by a simpler
537     * push instruction, if possible.
538     */
539    private void replaceFloatPushInstruction(Clazz       clazz,
540                                             int         offset,
541                                             Instruction instruction)
542    {
543        replaceFloatPushInstruction(clazz,
544                                    offset,
545                                    instruction,
546                                    partialEvaluator.getVariablesBefore(offset).size());
547    }
548
549
550    /**
551     * Replaces the float pushing instruction at the given offset by a simpler
552     * push instruction, if possible.
553     */
554    private void replaceFloatPushInstruction(Clazz       clazz,
555                                             int         offset,
556                                             Instruction instruction,
557                                             int         maxVariableIndex)
558    {
559        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
560        if (pushedValue.isParticular())
561        {
562            float value = pushedValue.floatValue().value();
563            if (value == 0f ||
564                value == 1f ||
565                value == 2f)
566            {
567                replaceConstantPushInstruction(clazz,
568                                               offset,
569                                               instruction,
570                                               InstructionConstants.OP_FCONST_0,
571                                               (int)value);
572            }
573            else
574            {
575                ConstantPoolEditor constantPoolEditor =
576                    new ConstantPoolEditor((ProgramClass)clazz);
577
578                Instruction replacementInstruction =
579                    new ConstantInstruction(InstructionConstants.OP_LDC,
580                                            constantPoolEditor.addFloatConstant(value)).shrink();
581
582                replaceInstruction(clazz, offset, instruction, replacementInstruction);
583            }
584        }
585        else if (pushedValue.isSpecific())
586        {
587            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
588            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
589            {
590                if (pushedValue.equals(variables.load(variableIndex)))
591                {
592                    replaceVariablePushInstruction(clazz,
593                                                   offset,
594                                                   instruction,
595                                                   InstructionConstants.OP_FLOAD,
596                                                   variableIndex);
597                }
598            }
599        }
600    }
601
602
603    /**
604     * Replaces the double pushing instruction at the given offset by a simpler
605     * push instruction, if possible.
606     */
607    private void replaceDoublePushInstruction(Clazz       clazz,
608                                              int         offset,
609                                              Instruction instruction)
610    {
611        replaceDoublePushInstruction(clazz,
612                                     offset,
613                                     instruction,
614                                     partialEvaluator.getVariablesBefore(offset).size());
615    }
616
617
618    /**
619     * Replaces the double pushing instruction at the given offset by a simpler
620     * push instruction, if possible.
621     */
622    private void replaceDoublePushInstruction(Clazz       clazz,
623                                              int         offset,
624                                              Instruction instruction,
625                                              int         maxVariableIndex)
626    {
627        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
628        if (pushedValue.isParticular())
629        {
630            double value = pushedValue.doubleValue().value();
631            if (value == 0.0 ||
632                value == 1.0)
633            {
634                replaceConstantPushInstruction(clazz,
635                                               offset,
636                                               instruction,
637                                               InstructionConstants.OP_DCONST_0,
638                                               (int)value);
639            }
640            else
641            {
642                ConstantPoolEditor constantPoolEditor =
643                    new ConstantPoolEditor((ProgramClass)clazz);
644
645                Instruction replacementInstruction =
646                    new ConstantInstruction(InstructionConstants.OP_LDC2_W,
647                                            constantPoolEditor.addDoubleConstant(value)).shrink();
648
649                replaceInstruction(clazz, offset, instruction, replacementInstruction);
650            }
651        }
652        else if (pushedValue.isSpecific())
653        {
654            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
655            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
656            {
657                if (pushedValue.equals(variables.load(variableIndex)))
658                {
659                    replaceVariablePushInstruction(clazz,
660                                                   offset,
661                                                   instruction,
662                                                   InstructionConstants.OP_DLOAD,
663                                                   variableIndex);
664                }
665            }
666        }
667    }
668
669
670    /**
671     * Replaces the reference pushing instruction at the given offset by a
672     * simpler push instruction, if possible.
673     */
674    private void replaceReferencePushInstruction(Clazz       clazz,
675                                                 int         offset,
676                                                 Instruction instruction)
677    {
678        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
679        if (pushedValue.isParticular())
680        {
681            // A reference value can only be specific if it is null.
682            replaceConstantPushInstruction(clazz,
683                                           offset,
684                                           instruction,
685                                           InstructionConstants.OP_ACONST_NULL,
686                                           0);
687        }
688    }
689
690
691    /**
692     * Replaces the instruction at a given offset by a given push instruction
693     * of a constant.
694     */
695    private void replaceConstantPushInstruction(Clazz       clazz,
696                                                int         offset,
697                                                Instruction instruction,
698                                                byte        replacementOpcode,
699                                                int         value)
700    {
701        Instruction replacementInstruction =
702            new SimpleInstruction(replacementOpcode, value).shrink();
703
704        replaceInstruction(clazz, offset, instruction, replacementInstruction);
705    }
706
707
708    /**
709     * Replaces the instruction at a given offset by a given push instruction
710     * of a variable.
711     */
712    private void replaceVariablePushInstruction(Clazz       clazz,
713                                                int         offset,
714                                                Instruction instruction,
715                                                byte        replacementOpcode,
716                                                int         variableIndex)
717    {
718        Instruction replacementInstruction =
719            new VariableInstruction(replacementOpcode, variableIndex).shrink();
720
721        replaceInstruction(clazz, offset, instruction, replacementInstruction);
722    }
723
724
725    /**
726     * Replaces the given 'jsr' instruction by a simpler branch instruction,
727     * if it jumps to a subroutine that doesn't return or a subroutine that
728     * is only called from one place.
729     */
730    private void replaceJsrInstruction(Clazz             clazz,
731                                       int               offset,
732                                       BranchInstruction branchInstruction)
733    {
734        // Is the subroutine ever returning?
735        int subroutineStart = offset + branchInstruction.branchOffset;
736        if (!partialEvaluator.isSubroutineReturning(subroutineStart) ||
737            partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1)
738        {
739            // All 'jsr' instructions to this subroutine can be replaced
740            // by unconditional branch instructions.
741            replaceBranchInstruction(clazz, offset, branchInstruction);
742        }
743        else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset)))
744        {
745            // We have to make sure the instruction after this 'jsr'
746            // instruction is valid, even if it is never reached.
747            replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction);
748        }
749    }
750
751
752    /**
753     * Deletes the reference popping instruction at the given offset, if
754     * it is at the start of a subroutine that doesn't return or a subroutine
755     * that is only called from one place.
756     */
757    private void deleteReferencePopInstruction(Clazz       clazz,
758                                               int         offset,
759                                               Instruction instruction)
760    {
761        if (partialEvaluator.isSubroutineStart(offset) &&
762            (!partialEvaluator.isSubroutineReturning(offset) ||
763             partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1))
764        {
765            if (DEBUG) System.out.println("  Deleting store of subroutine return address "+instruction.toString(offset));
766
767            // A reference value can only be specific if it is null.
768            codeAttributeEditor.deleteInstruction(offset);
769        }
770    }
771
772
773    /**
774     * Deletes the given branch instruction, or replaces it by a simpler branch
775     * instruction, if possible.
776     */
777    private void replaceBranchInstruction(Clazz       clazz,
778                                          int         offset,
779                                          Instruction instruction)
780    {
781        InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
782
783        // Is there exactly one branch target (not from a goto or jsr)?
784        if (branchTargets != null &&
785            branchTargets.instructionOffsetCount() == 1)
786        {
787            // Is it branching to the next instruction?
788            int branchOffset = branchTargets.instructionOffset(0) - offset;
789            if (branchOffset == instruction.length(offset))
790            {
791                if (DEBUG) System.out.println("  Ignoring zero branch instruction at ["+offset+"]");
792            }
793            else
794            {
795                // Replace the branch instruction by a simple branch instruction.
796                Instruction replacementInstruction =
797                    new BranchInstruction(InstructionConstants.OP_GOTO_W,
798                                          branchOffset).shrink();
799
800                replaceInstruction(clazz, offset, instruction, replacementInstruction);
801            }
802        }
803    }
804
805
806    /**
807     * Makes sure all branch targets of the given switch instruction are valid.
808     */
809    private void replaceSwitchInstruction(Clazz             clazz,
810                                          int               offset,
811                                          SwitchInstruction switchInstruction)
812    {
813        // Get the actual branch targets.
814        InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
815
816        // Get an offset that can serve as a valid default offset.
817        int defaultOffset =
818            branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) -
819            offset;
820
821        Instruction replacementInstruction = null;
822
823        // Check the jump offsets.
824        int[] jumpOffsets = switchInstruction.jumpOffsets;
825        for (int index = 0; index < jumpOffsets.length; index++)
826        {
827            if (!branchTargets.contains(offset + jumpOffsets[index]))
828            {
829                // Replace the unused offset.
830                jumpOffsets[index] = defaultOffset;
831
832                // Remember to replace the instruction.
833                replacementInstruction = switchInstruction;
834            }
835        }
836
837        // Check the default offset.
838        if (!branchTargets.contains(offset + switchInstruction.defaultOffset))
839        {
840            // Replace the unused offset.
841            switchInstruction.defaultOffset = defaultOffset;
842
843            // Remember to replace the instruction.
844            replacementInstruction = switchInstruction;
845        }
846
847        if (replacementInstruction != null)
848        {
849            replaceInstruction(clazz, offset, switchInstruction, replacementInstruction);
850        }
851    }
852
853
854    /**
855     * Replaces the given instruction by an infinite loop.
856     */
857    private void replaceByInfiniteLoop(Clazz       clazz,
858                                       int         offset,
859                                       Instruction instruction)
860    {
861        // Replace the instruction by an infinite loop.
862        Instruction replacementInstruction =
863            new BranchInstruction(InstructionConstants.OP_GOTO, 0);
864
865        if (DEBUG) System.out.println("  Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset));
866
867        codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
868
869        // Visit the instruction, if required.
870        if (extraInstructionVisitor != null)
871        {
872            // Note: we're not passing the right arguments for now, knowing that
873            // they aren't used anyway.
874            instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
875        }
876    }
877
878
879    /**
880     * Replaces the instruction at a given offset by a given push instruction.
881     */
882    private void replaceInstruction(Clazz       clazz,
883                                    int         offset,
884                                    Instruction instruction,
885                                    Instruction replacementInstruction)
886    {
887        // Pop unneeded stack entries if necessary.
888        int popCount =
889            instruction.stackPopCount(clazz) -
890            replacementInstruction.stackPopCount(clazz);
891
892        insertPopInstructions(offset, popCount);
893
894        if (DEBUG) System.out.println("  Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)"));
895
896        codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
897
898        // Visit the instruction, if required.
899        if (extraInstructionVisitor != null)
900        {
901            // Note: we're not passing the right arguments for now, knowing that
902            // they aren't used anyway.
903            instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
904        }
905    }
906
907
908    /**
909     * Pops the given number of stack entries before the instruction at the
910     * given offset.
911     */
912    private void insertPopInstructions(int offset, int popCount)
913    {
914        switch (popCount)
915        {
916            case 0:
917            {
918                break;
919            }
920            case 1:
921            {
922                // Insert a single pop instruction.
923                Instruction popInstruction =
924                    new SimpleInstruction(InstructionConstants.OP_POP);
925
926                codeAttributeEditor.insertBeforeInstruction(offset,
927                                                            popInstruction);
928                break;
929            }
930            case 2:
931            {
932                // Insert a single pop2 instruction.
933                Instruction popInstruction =
934                    new SimpleInstruction(InstructionConstants.OP_POP2);
935
936                codeAttributeEditor.insertBeforeInstruction(offset,
937                                                            popInstruction);
938                break;
939            }
940            default:
941            {
942                // Insert the specified number of pop instructions.
943                Instruction[] popInstructions =
944                    new Instruction[popCount / 2 + popCount % 2];
945
946                Instruction popInstruction =
947                    new SimpleInstruction(InstructionConstants.OP_POP2);
948
949                for (int index = 0; index < popCount / 2; index++)
950                {
951                      popInstructions[index] = popInstruction;
952                }
953
954                if (popCount % 2 == 1)
955                {
956                    popInstruction =
957                        new SimpleInstruction(InstructionConstants.OP_POP);
958
959                    popInstructions[popCount / 2] = popInstruction;
960                }
961
962                codeAttributeEditor.insertBeforeInstruction(offset,
963                                                            popInstructions);
964                break;
965            }
966        }
967    }
968}
969