1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.annotation.*;
26import proguard.classfile.attribute.annotation.target.*;
27import proguard.classfile.attribute.annotation.target.visitor.*;
28import proguard.classfile.attribute.annotation.visitor.TypeAnnotationVisitor;
29import proguard.classfile.attribute.preverification.*;
30import proguard.classfile.attribute.preverification.visitor.*;
31import proguard.classfile.attribute.visitor.*;
32import proguard.classfile.instruction.*;
33import proguard.classfile.instruction.visitor.InstructionVisitor;
34import proguard.classfile.util.SimplifiedVisitor;
35import proguard.util.ArrayUtil;
36
37import java.util.Arrays;
38
39/**
40 * This AttributeVisitor accumulates specified changes to code, and then applies
41 * these accumulated changes to the code attributes that it visits.
42 *
43 * @author Eric Lafortune
44 */
45public class CodeAttributeEditor
46extends      SimplifiedVisitor
47implements   AttributeVisitor,
48             InstructionVisitor,
49             ExceptionInfoVisitor,
50             StackMapFrameVisitor,
51             VerificationTypeVisitor,
52             LineNumberInfoVisitor,
53             LocalVariableInfoVisitor,
54             LocalVariableTypeInfoVisitor,
55             TypeAnnotationVisitor,
56             TargetInfoVisitor,
57             LocalVariableTargetElementVisitor
58{
59    //*
60    private static final boolean DEBUG = false;
61    /*/
62    public  static       boolean DEBUG = false;
63    //*/
64
65
66    private final boolean updateFrameSizes;
67    private final boolean shrinkInstructions;
68
69    private int     codeLength;
70    private boolean modified;
71    private boolean simple;
72
73    /*private*/public Instruction[]    preInsertions  = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
74    /*private*/public Instruction[]    replacements   = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
75    /*private*/public Instruction[]    postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
76    /*private*/public boolean[]        deleted        = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
77
78    private int[]   newInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
79    private int     newOffset;
80    private boolean lengthIncreased;
81
82    private int expectedStackMapFrameOffset;
83
84    private final StackSizeUpdater    stackSizeUpdater    = new StackSizeUpdater();
85    private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
86    private final InstructionWriter   instructionWriter   = new InstructionWriter();
87
88
89    /**
90     * Creates a new CodeAttributeEditor that automatically updates frame
91     * sizes and shrinks instructions.
92     */
93    public CodeAttributeEditor()
94    {
95        this(true, true);
96    }
97
98
99    /**
100     * Creates a new CodeAttributeEditor.
101     * @param updateFrameSizes   specifies whether frame sizes of edited code
102     *                           should be updated.
103     * @param shrinkInstructions specifies whether added instructions should
104     *                           automatically be shrunk before being written.
105     */
106    public CodeAttributeEditor(boolean updateFrameSizes,
107                               boolean shrinkInstructions)
108    {
109        this.updateFrameSizes   = updateFrameSizes;
110        this.shrinkInstructions = shrinkInstructions;
111    }
112
113
114    /**
115     * Resets the accumulated code changes.
116     * @param codeLength the length of the code that will be edited next.
117     */
118    public void reset(int codeLength)
119    {
120        // Try to reuse the previous arrays.
121        if (preInsertions.length < codeLength)
122        {
123            preInsertions  = new Instruction[codeLength];
124            replacements   = new Instruction[codeLength];
125            postInsertions = new Instruction[codeLength];
126            deleted        = new boolean[codeLength];
127        }
128        else
129        {
130            Arrays.fill(preInsertions,  0, codeLength, null);
131            Arrays.fill(replacements,   0, codeLength, null);
132            Arrays.fill(postInsertions, 0, codeLength, null);
133            Arrays.fill(deleted,        0, codeLength, false);
134        }
135
136        this.codeLength = codeLength;
137
138        modified = false;
139        simple   = true;
140    }
141
142
143    /**
144     * Extends the size of the accumulated code changes.
145     * @param codeLength the length of the code that will be edited next.
146     */
147    public void extend(int codeLength)
148    {
149        // Try to reuse the previous arrays.
150        if (preInsertions.length < codeLength)
151        {
152            preInsertions  = (Instruction[])ArrayUtil.extendArray(preInsertions,  codeLength);
153            replacements   = (Instruction[])ArrayUtil.extendArray(replacements,   codeLength);
154            postInsertions = (Instruction[])ArrayUtil.extendArray(postInsertions, codeLength);
155            deleted        = ArrayUtil.extendArray(deleted, codeLength);
156        }
157        else
158        {
159            Arrays.fill(preInsertions,  this.codeLength, codeLength, null);
160            Arrays.fill(replacements,   this.codeLength, codeLength, null);
161            Arrays.fill(postInsertions, this.codeLength, codeLength, null);
162            Arrays.fill(deleted,        this.codeLength, codeLength, false);
163        }
164
165        this.codeLength = codeLength;
166    }
167
168
169    /**
170     * Remembers to place the given instruction right before the instruction
171     * at the given offset.
172     * @param instructionOffset the offset of the instruction.
173     * @param instruction       the new instruction.
174     */
175    public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
176    {
177        if (instructionOffset < 0 ||
178            instructionOffset >= codeLength)
179        {
180            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
181        }
182
183        preInsertions[instructionOffset] = shrinkInstructions ?
184            instruction.shrink() :
185            instruction;
186
187        modified = true;
188        simple   = false;
189    }
190
191
192    /**
193     * Remembers to place the given instructions right before the instruction
194     * at the given offset.
195     * @param instructionOffset the offset of the instruction.
196     * @param instructions      the new instructions.
197     */
198    public void insertBeforeInstruction(int instructionOffset, Instruction[] instructions)
199    {
200        if (instructionOffset < 0 ||
201            instructionOffset >= codeLength)
202        {
203            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
204        }
205
206        CompositeInstruction instruction =
207            new CompositeInstruction(instructions);
208
209        preInsertions[instructionOffset] = shrinkInstructions ?
210            instruction.shrink() :
211            instruction;
212
213        modified = true;
214        simple   = false;
215    }
216
217
218    /**
219     * Remembers to replace the instruction at the given offset by the given
220     * instruction.
221     * @param instructionOffset the offset of the instruction to be replaced.
222     * @param instruction       the new instruction.
223     */
224    public void replaceInstruction(int instructionOffset, Instruction instruction)
225    {
226        if (instructionOffset < 0 ||
227            instructionOffset >= codeLength)
228        {
229            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
230        }
231
232        replacements[instructionOffset] = shrinkInstructions ?
233            instruction.shrink() :
234            instruction;
235
236        modified = true;
237    }
238
239
240    /**
241     * Remembers to replace the instruction at the given offset by the given
242     * instructions.
243     * @param instructionOffset the offset of the instruction to be replaced.
244     * @param instructions      the new instructions.
245     */
246    public void replaceInstruction(int instructionOffset, Instruction[] instructions)
247    {
248        if (instructionOffset < 0 ||
249            instructionOffset >= codeLength)
250        {
251            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
252        }
253
254        CompositeInstruction instruction =
255            new CompositeInstruction(instructions);
256
257        replacements[instructionOffset] = shrinkInstructions ?
258            instruction.shrink() :
259            instruction;
260
261        modified = true;
262    }
263
264
265    /**
266     * Remembers to place the given instruction right after the instruction
267     * at the given offset.
268     * @param instructionOffset the offset of the instruction.
269     * @param instruction       the new instruction.
270     */
271    public void insertAfterInstruction(int instructionOffset, Instruction instruction)
272    {
273        if (instructionOffset < 0 ||
274            instructionOffset >= codeLength)
275        {
276            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
277        }
278
279        postInsertions[instructionOffset] = shrinkInstructions ?
280            instruction.shrink() :
281            instruction;
282
283        modified = true;
284        simple   = false;
285    }
286
287
288    /**
289     * Remembers to place the given instructions right after the instruction
290     * at the given offset.
291     * @param instructionOffset the offset of the instruction.
292     * @param instructions      the new instructions.
293     */
294    public void insertAfterInstruction(int instructionOffset, Instruction[] instructions)
295    {
296        if (instructionOffset < 0 ||
297            instructionOffset >= codeLength)
298        {
299            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
300        }
301
302        CompositeInstruction instruction =
303            new CompositeInstruction(instructions);
304
305        postInsertions[instructionOffset] = shrinkInstructions ?
306            instruction.shrink() :
307            instruction;
308
309        modified = true;
310        simple   = false;
311    }
312
313
314    /**
315     * Remembers to delete the instruction at the given offset.
316     * @param instructionOffset the offset of the instruction to be deleted.
317     */
318    public void deleteInstruction(int instructionOffset)
319    {
320        if (instructionOffset < 0 ||
321            instructionOffset >= codeLength)
322        {
323            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
324        }
325
326        deleted[instructionOffset] = true;
327
328        modified = true;
329        simple   = false;
330    }
331
332
333    /**
334     * Remembers not to delete the instruction at the given offset.
335     * @param instructionOffset the offset of the instruction not to be deleted.
336     */
337    public void undeleteInstruction(int instructionOffset)
338    {
339        if (instructionOffset < 0 ||
340            instructionOffset >= codeLength)
341        {
342            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
343        }
344
345        deleted[instructionOffset] = false;
346    }
347
348
349    /**
350     * Clears all modifications of the instruction at the given offset.
351     * @param instructionOffset the offset of the instruction to be deleted.
352     */
353    public void clearModifications(int instructionOffset)
354    {
355        if (instructionOffset < 0 ||
356            instructionOffset >= codeLength)
357        {
358            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
359        }
360
361        preInsertions[instructionOffset]  = null;
362        replacements[instructionOffset]   = null;
363        postInsertions[instructionOffset] = null;
364        deleted[instructionOffset]        = false;
365    }
366
367
368    /**
369     * Returns whether the code has been modified in any way.
370     */
371    public boolean isModified()
372    {
373        return modified;
374    }
375
376
377    /**
378     * Returns whether the instruction at the given offset has been modified
379     * in any way.
380     */
381    public boolean isModified(int instructionOffset)
382    {
383        return preInsertions[instructionOffset]  != null ||
384               replacements[instructionOffset]   != null ||
385               postInsertions[instructionOffset] != null ||
386               deleted[instructionOffset];
387    }
388
389
390    // Implementations for AttributeVisitor.
391
392    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
393
394
395    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
396    {
397//        DEBUG =
398//            clazz.getName().equals("abc/Def") &&
399//            method.getName(clazz).equals("abc");
400
401        // TODO: Remove this when the code has stabilized.
402        // Catch any unexpected exceptions from the actual visiting method.
403        try
404        {
405            // Process the code.
406            visitCodeAttribute0(clazz, method, codeAttribute);
407        }
408        catch (RuntimeException ex)
409        {
410            System.err.println("Unexpected error while editing code:");
411            System.err.println("  Class       = ["+clazz.getName()+"]");
412            System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
413            System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
414
415            throw ex;
416        }
417    }
418
419
420    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
421    {
422        // Do we have to update the code?
423        if (modified)
424        {
425            if (DEBUG)
426            {
427                System.out.println("CodeAttributeEditor: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
428            }
429
430            // Can we perform a faster simple replacement of instructions?
431            if (canPerformSimpleReplacements(codeAttribute))
432            {
433                if (DEBUG)
434                {
435                    System.out.println("  Simple editing");
436                }
437
438                // Simply overwrite the instructions.
439                performSimpleReplacements(codeAttribute);
440            }
441            else
442            {
443                if (DEBUG)
444                {
445                    System.out.println("  Full editing");
446                }
447
448                // Move and remap the instructions.
449                codeAttribute.u4codeLength =
450                    updateInstructions(clazz, method, codeAttribute);
451
452                // Update the exception table.
453                codeAttribute.exceptionsAccept(clazz, method, this);
454
455                // Remove exceptions with empty code blocks.
456                codeAttribute.u2exceptionTableLength =
457                    removeEmptyExceptions(codeAttribute.exceptionTable,
458                                          codeAttribute.u2exceptionTableLength);
459
460                // Update the line number table and the local variable tables.
461                codeAttribute.attributesAccept(clazz, method, this);
462            }
463
464            // Make sure instructions are widened if necessary.
465            instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
466        }
467
468        // Update the maximum stack size and local variable frame size.
469        if (updateFrameSizes)
470        {
471            stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
472            variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
473        }
474    }
475
476
477    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
478    {
479        // Update all stack map entries.
480        expectedStackMapFrameOffset = -1;
481        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
482    }
483
484
485    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
486    {
487        // Update all stack map table entries.
488        expectedStackMapFrameOffset = 0;
489        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
490    }
491
492
493    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
494    {
495        // Update all line number table entries.
496        lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
497
498        // Remove line numbers with empty code blocks.
499        lineNumberTableAttribute.u2lineNumberTableLength =
500           removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
501                                  lineNumberTableAttribute.u2lineNumberTableLength,
502                                  codeAttribute.u4codeLength);
503    }
504
505
506    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
507    {
508        // Update all local variable table entries.
509        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
510    }
511
512
513    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
514    {
515        // Update all local variable table entries.
516        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
517    }
518
519
520    public void visitAnyTypeAnnotationsAttribute(Clazz clazz, TypeAnnotationsAttribute typeAnnotationsAttribute)
521    {
522        typeAnnotationsAttribute.typeAnnotationsAccept(clazz, this);
523    }
524
525
526    /**
527     * Checks if it is possible to modifies the given code without having to
528     * update any offsets.
529     * @param codeAttribute the code to be changed.
530     * @return the new code length.
531     */
532    private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute)
533    {
534        if (!simple)
535        {
536            return false;
537        }
538
539        byte[] code       = codeAttribute.code;
540        int    codeLength = codeAttribute.u4codeLength;
541
542        // Go over all replacement instructions.
543        for (int offset = 0; offset < codeLength; offset++)
544        {
545            // Check if the replacement instruction, if any, has a different
546            // length than the original instruction.
547            Instruction replacementInstruction = replacements[offset];
548            if (replacementInstruction != null &&
549                replacementInstruction.length(offset) !=
550                    InstructionFactory.create(code, offset).length(offset))
551            {
552                return false;
553            }
554        }
555
556        return true;
557    }
558
559
560    /**
561     * Modifies the given code without updating any offsets.
562     * @param codeAttribute the code to be changed.
563     */
564    private void performSimpleReplacements(CodeAttribute codeAttribute)
565    {
566        int codeLength = codeAttribute.u4codeLength;
567
568        // Go over all replacement instructions.
569        for (int offset = 0; offset < codeLength; offset++)
570        {
571            // Overwrite the original instruction with the replacement
572            // instruction if any.
573            Instruction replacementInstruction = replacements[offset];
574            if (replacementInstruction != null)
575            {
576                replacementInstruction.write(codeAttribute, offset);
577
578                if (DEBUG)
579                {
580                    System.out.println("  Replaced "+replacementInstruction.toString(offset));
581                }
582            }
583        }
584    }
585
586
587    /**
588     * Modifies the given code based on the previously specified changes.
589     * @param clazz         the class file of the code to be changed.
590     * @param method        the method of the code to be changed.
591     * @param codeAttribute the code to be changed.
592     * @return the new code length.
593     */
594    private int updateInstructions(Clazz         clazz,
595                                   Method        method,
596                                   CodeAttribute codeAttribute)
597    {
598        byte[] oldCode   = codeAttribute.code;
599        int    oldLength = codeAttribute.u4codeLength;
600
601        // Make sure there is a sufficiently large instruction offset map.
602        if (newInstructionOffsets.length < oldLength + 1)
603        {
604            newInstructionOffsets = new int[oldLength + 1];
605        }
606
607        // Fill out the instruction offset map.
608        int newLength = mapInstructions(oldCode,
609                                        oldLength);
610
611        // Create a new code array if necessary.
612        if (lengthIncreased)
613        {
614            codeAttribute.code = new byte[newLength];
615        }
616
617        // Prepare for possible widening of instructions.
618        instructionWriter.reset(newLength);
619
620        // Move the instructions into the new code array.
621        moveInstructions(clazz,
622                         method,
623                         codeAttribute,
624                         oldCode,
625                         oldLength);
626
627        // We can return the new length.
628        return newLength;
629    }
630
631
632    /**
633     * Fills out the instruction offset map for the given code block.
634     * @param oldCode   the instructions to be moved.
635     * @param oldLength the code length.
636     * @return the new code length.
637     */
638    private int mapInstructions(byte[] oldCode, int oldLength)
639    {
640        // Start mapping instructions at the beginning.
641        newOffset       = 0;
642        lengthIncreased = false;
643
644        int oldOffset = 0;
645        do
646        {
647            // Get the next instruction.
648            Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
649
650            // Compute the mapping of the instruction.
651            mapInstruction(oldOffset, instruction);
652
653            oldOffset += instruction.length(oldOffset);
654
655            if (newOffset > oldOffset)
656            {
657                lengthIncreased = true;
658            }
659        }
660        while (oldOffset < oldLength);
661
662        // Also add an entry for the first offset after the code.
663        newInstructionOffsets[oldOffset] = newOffset;
664
665        return newOffset;
666    }
667
668
669    /**
670     * Fills out the instruction offset map for the given instruction.
671     * @param oldOffset   the instruction's old offset.
672     * @param instruction the instruction to be moved.
673     */
674    private void mapInstruction(int         oldOffset,
675                                Instruction instruction)
676    {
677        newInstructionOffsets[oldOffset] = newOffset;
678
679        // Account for the pre-inserted instruction, if any.
680        Instruction preInstruction = preInsertions[oldOffset];
681        if (preInstruction != null)
682        {
683            newOffset += preInstruction.length(newOffset);
684        }
685
686        // Account for the replacement instruction, or for the current
687        // instruction, if it shouldn't be  deleted.
688        Instruction replacementInstruction = replacements[oldOffset];
689        if (replacementInstruction != null)
690        {
691            newOffset += replacementInstruction.length(newOffset);
692        }
693        else if (!deleted[oldOffset])
694        {
695            // Note that the instruction's length may change at its new offset,
696            // e.g. if it is a switch instruction.
697            newOffset += instruction.length(newOffset);
698        }
699
700        // Account for the post-inserted instruction, if any.
701        Instruction postInstruction = postInsertions[oldOffset];
702        if (postInstruction != null)
703        {
704            newOffset += postInstruction.length(newOffset);
705        }
706    }
707
708
709    /**
710     * Moves the given code block to the new offsets.
711     * @param clazz         the class file of the code to be changed.
712     * @param method        the method of the code to be changed.
713     * @param codeAttribute the code to be changed.
714     * @param oldCode       the original code to be moved.
715     * @param oldLength     the original code length.
716     */
717    private void moveInstructions(Clazz         clazz,
718                                  Method        method,
719                                  CodeAttribute codeAttribute,
720                                  byte[]        oldCode,
721                                  int           oldLength)
722    {
723        // Start writing instructions at the beginning.
724        newOffset = 0;
725
726        int oldOffset = 0;
727        do
728        {
729            // Get the next instruction.
730            Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
731
732            // Move the instruction to its new offset.
733            moveInstruction(clazz,
734                            method,
735                            codeAttribute,
736                            oldOffset,
737                            instruction);
738
739            oldOffset += instruction.length(oldOffset);
740        }
741        while (oldOffset < oldLength);
742    }
743
744
745    /**
746     * Moves the given instruction to its new offset.
747     * @param clazz         the class file of the code to be changed.
748     * @param method        the method of the code to be changed.
749     * @param codeAttribute the code to be changed.
750     * @param oldOffset     the original instruction offset.
751     * @param instruction   the original instruction.
752     */
753    private void moveInstruction(Clazz         clazz,
754                                 Method        method,
755                                 CodeAttribute codeAttribute,
756                                 int           oldOffset,
757                                 Instruction   instruction)
758    {
759        // Update and insert the pre-inserted instruction, if any.
760        Instruction preInstruction = preInsertions[oldOffset];
761        if (preInstruction != null)
762        {
763            if (DEBUG)
764            {
765                System.out.println("  Pre-inserted  ["+oldOffset+"] -> "+preInstruction.toString(newOffset));
766            }
767
768            // Update the instruction.
769            preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
770        }
771
772        // Update and insert the replacement instruction, or the current
773        // instruction, if it shouldn't be deleted.
774        Instruction replacementInstruction = replacements[oldOffset];
775        if (replacementInstruction != null)
776        {
777            if (DEBUG)
778            {
779                System.out.println("  Replaced      ["+oldOffset+"] -> "+replacementInstruction.toString(newOffset));
780            }
781
782            // Update the instruction.
783            replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
784        }
785        else if (!deleted[oldOffset])
786        {
787            if (DEBUG)
788            {
789                System.out.println("  Copied        ["+oldOffset+"] -> "+instruction.toString(newOffset));
790            }
791
792            // Update the instruction.
793            instruction.accept(clazz, method, codeAttribute, oldOffset, this);
794        }
795
796        // Update and insert the post-inserted instruction, if any.
797        Instruction postInstruction = postInsertions[oldOffset];
798        if (postInstruction != null)
799        {
800            if (DEBUG)
801            {
802                System.out.println("  Post-inserted ["+oldOffset+"] -> "+postInstruction.toString(newOffset));
803            }
804
805            // Update the instruction.
806            postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
807        }
808    }
809
810
811    // Implementations for InstructionVisitor.
812
813    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
814    {
815        // Write out the instruction.
816        instructionWriter.visitSimpleInstruction(clazz,
817                                                 method,
818                                                 codeAttribute,
819                                                 newOffset,
820                                                 simpleInstruction);
821
822        newOffset += simpleInstruction.length(newOffset);
823    }
824
825
826    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
827    {
828        // Write out the instruction.
829        instructionWriter.visitConstantInstruction(clazz,
830                                                   method,
831                                                   codeAttribute,
832                                                   newOffset,
833                                                   constantInstruction);
834
835        newOffset += constantInstruction.length(newOffset);
836    }
837
838
839    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
840    {
841        // Write out the instruction.
842        instructionWriter.visitVariableInstruction(clazz,
843                                                   method,
844                                                   codeAttribute,
845                                                   newOffset,
846                                                   variableInstruction);
847
848        newOffset += variableInstruction.length(newOffset);
849    }
850
851
852    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
853    {
854        // Update the branch offset, relative to the precise new offset.
855        branchInstruction.branchOffset =
856            newBranchOffset(offset, branchInstruction.branchOffset, newOffset);
857
858        // Write out the instruction.
859        instructionWriter.visitBranchInstruction(clazz,
860                                                 method,
861                                                 codeAttribute,
862                                                 newOffset,
863                                                 branchInstruction);
864
865        newOffset += branchInstruction.length(newOffset);
866    }
867
868
869    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
870    {
871        // Update the default jump offset, relative to the precise new offset.
872        tableSwitchInstruction.defaultOffset =
873            newBranchOffset(offset, tableSwitchInstruction.defaultOffset, newOffset);
874
875        // Update the jump offsets, relative to the precise new offset.
876        newJumpOffsets(offset,
877                       tableSwitchInstruction.jumpOffsets,
878                       newOffset);
879
880        // Write out the instruction.
881        instructionWriter.visitTableSwitchInstruction(clazz,
882                                                      method,
883                                                      codeAttribute,
884                                                      newOffset,
885                                                      tableSwitchInstruction);
886
887        newOffset += tableSwitchInstruction.length(newOffset);
888    }
889
890
891    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
892    {
893        // Update the default jump offset, relative to the precise new offset.
894        lookUpSwitchInstruction.defaultOffset =
895            newBranchOffset(offset, lookUpSwitchInstruction.defaultOffset, newOffset);
896
897        // Update the jump offsets, relative to the precise new offset.
898        newJumpOffsets(offset,
899                       lookUpSwitchInstruction.jumpOffsets,
900                       newOffset);
901
902        // Write out the instruction.
903        instructionWriter.visitLookUpSwitchInstruction(clazz,
904                                                       method,
905                                                       codeAttribute,
906                                                       newOffset,
907                                                       lookUpSwitchInstruction);
908
909        newOffset += lookUpSwitchInstruction.length(newOffset);
910    }
911
912
913    // Implementations for ExceptionInfoVisitor.
914
915    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
916    {
917        // Update the code offsets. Note that the instruction offset map also
918        // has an entry for the first offset after the code, for u2endPC.
919        exceptionInfo.u2startPC   = newInstructionOffset(exceptionInfo.u2startPC);
920        exceptionInfo.u2endPC     = newInstructionOffset(exceptionInfo.u2endPC);
921        exceptionInfo.u2handlerPC = newInstructionOffset(exceptionInfo.u2handlerPC);
922    }
923
924
925    // Implementations for StackMapFrameVisitor.
926
927    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
928    {
929        // Update the stack map frame offset.
930        int stackMapFrameOffset = newInstructionOffset(offset);
931
932        int offsetDelta = stackMapFrameOffset;
933
934        // Compute the offset delta if the frame is part of a stack map frame
935        // table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
936        if (expectedStackMapFrameOffset >= 0)
937        {
938            offsetDelta -= expectedStackMapFrameOffset;
939
940            expectedStackMapFrameOffset = stackMapFrameOffset + 1;
941        }
942
943        stackMapFrame.u2offsetDelta = offsetDelta;
944    }
945
946
947    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
948    {
949        // Update the stack map frame offset.
950        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
951
952        // Update the verification type offset.
953        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
954    }
955
956
957    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
958    {
959        // Update the stack map frame offset.
960        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
961
962        // Update the verification type offsets.
963        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
964    }
965
966
967    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
968    {
969        // Update the stack map frame offset.
970        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
971
972        // Update the verification type offsets.
973        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
974        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
975    }
976
977
978    // Implementations for VerificationTypeVisitor.
979
980    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
981
982
983    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
984    {
985        // Update the offset of the 'new' instruction.
986        uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset);
987    }
988
989
990    // Implementations for LineNumberInfoVisitor.
991
992    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
993    {
994        // Update the code offset.
995        lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC);
996    }
997
998
999    // Implementations for LocalVariableInfoVisitor.
1000
1001    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
1002    {
1003        // Update the code offset and length.
1004        // Be careful to update the length first.
1005        localVariableInfo.u2length  = newBranchOffset(localVariableInfo.u2startPC, localVariableInfo.u2length);
1006        localVariableInfo.u2startPC = newInstructionOffset(localVariableInfo.u2startPC);
1007    }
1008
1009
1010    // Implementations for LocalVariableTypeInfoVisitor.
1011
1012    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
1013    {
1014        // Update the code offset and length.
1015        // Be careful to update the length first.
1016        localVariableTypeInfo.u2length  = newBranchOffset(localVariableTypeInfo.u2startPC, localVariableTypeInfo.u2length);
1017        localVariableTypeInfo.u2startPC = newInstructionOffset(localVariableTypeInfo.u2startPC);
1018    }
1019
1020
1021    // Implementations for TypeAnnotationVisitor.
1022
1023    public void visitTypeAnnotation(Clazz clazz, TypeAnnotation typeAnnotation)
1024    {
1025        // Update all local variable targets.
1026        typeAnnotation.targetInfoAccept(clazz, this);
1027    }
1028
1029
1030    // Implementations for TargetInfoVisitor.
1031
1032    public void visitAnyTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, TargetInfo targetInfo) {}
1033
1034
1035    public void visitLocalVariableTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo)
1036    {
1037        // Update the offsets of the variables.
1038        localVariableTargetInfo.targetElementsAccept(clazz, method, codeAttribute, typeAnnotation, this);
1039    }
1040
1041
1042    public void visitOffsetTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, OffsetTargetInfo offsetTargetInfo)
1043    {
1044        // Update the offset.
1045        offsetTargetInfo.u2offset = newInstructionOffset(offsetTargetInfo.u2offset);
1046    }
1047
1048
1049    // Implementations for LocalVariableTargetElementVisitor.
1050
1051    public void visitLocalVariableTargetElement(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo, LocalVariableTargetElement localVariableTargetElement)
1052    {
1053        // Update the variable start offset and length.
1054        // Be careful to update the length first.
1055        localVariableTargetElement.u2length  = newBranchOffset(localVariableTargetElement.u2startPC, localVariableTargetElement.u2length);
1056        localVariableTargetElement.u2startPC = newInstructionOffset(localVariableTargetElement.u2startPC);
1057    }
1058
1059
1060    // Small utility methods.
1061
1062    /**
1063     * Updates the given jump offsets for the instruction at the given offset,
1064     * relative to the given new offset.
1065     */
1066    private void newJumpOffsets(int   oldInstructionOffset,
1067                                int[] oldJumpOffsets,
1068                                int   newInstructionOffset)
1069    {
1070        for (int index = 0; index < oldJumpOffsets.length; index++)
1071        {
1072            oldJumpOffsets[index] = newBranchOffset(oldInstructionOffset,
1073                                                    oldJumpOffsets[index],
1074                                                    newInstructionOffset);
1075        }
1076    }
1077
1078
1079    /**
1080     * Computes the new branch offset for the instruction at the given offset
1081     * with the given branch offset, relative to the new instruction (block)
1082     * offset.
1083     */
1084    private int newBranchOffset(int oldInstructionOffset,
1085                                int oldBranchOffset)
1086    {
1087        return newInstructionOffset(oldInstructionOffset + oldBranchOffset) -
1088               newInstructionOffset(oldInstructionOffset);
1089    }
1090
1091
1092    /**
1093     * Computes the new branch offset for the instruction at the given offset
1094     * with the given branch offset, relative to the given new offset.
1095     */
1096    private int newBranchOffset(int oldInstructionOffset,
1097                                int oldBranchOffset,
1098                                int newInstructionOffset)
1099    {
1100        return newInstructionOffset(oldInstructionOffset + oldBranchOffset) -
1101               newInstructionOffset;
1102    }
1103
1104
1105    /**
1106     * Computes the new instruction offset for the instruction at the given
1107     * offset.
1108     */
1109    private int newInstructionOffset(int oldInstructionOffset)
1110    {
1111        if (oldInstructionOffset < 0 ||
1112            oldInstructionOffset > codeLength)
1113        {
1114            throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset+"] in code with length ["+codeLength+"]");
1115        }
1116
1117        return newInstructionOffsets[oldInstructionOffset];
1118    }
1119
1120
1121    /**
1122     * Returns the given list of exceptions, without the ones that have empty
1123     * code blocks.
1124     */
1125    private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
1126                                      int             exceptionInfoCount)
1127    {
1128        // Overwrite all empty exceptions.
1129        int newIndex = 0;
1130        for (int index = 0; index < exceptionInfoCount; index++)
1131        {
1132            ExceptionInfo exceptionInfo = exceptionInfos[index];
1133            if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
1134            {
1135                exceptionInfos[newIndex++] = exceptionInfo;
1136            }
1137        }
1138
1139        return newIndex;
1140    }
1141
1142
1143    /**
1144     * Returns the given list of line numbers, without the ones that have empty
1145     * code blocks or that exceed the code size.
1146     */
1147    private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
1148                                       int              lineNumberInfoCount,
1149                                       int              codeLength)
1150    {
1151        // Overwrite all empty line number entries.
1152        int newIndex = 0;
1153        for (int index = 0; index < lineNumberInfoCount; index++)
1154        {
1155            LineNumberInfo lineNumberInfo = lineNumberInfos[index];
1156            int startPC = lineNumberInfo.u2startPC;
1157            if (startPC < codeLength &&
1158                (index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
1159            {
1160                lineNumberInfos[newIndex++] = lineNumberInfo;
1161            }
1162        }
1163
1164        return newIndex;
1165    }
1166
1167
1168    /**
1169     * This instruction is a composite of other instructions, for local use
1170     * inside the editor class only.
1171     */
1172    private class CompositeInstruction
1173    extends       Instruction
1174    {
1175        private Instruction[] instructions;
1176
1177
1178        private CompositeInstruction(Instruction[] instructions)
1179        {
1180            this.instructions = instructions;
1181        }
1182
1183
1184        // Implementations for Instruction.
1185
1186        public Instruction shrink()
1187        {
1188            for (int index = 0; index < instructions.length; index++)
1189            {
1190                instructions[index] = instructions[index].shrink();
1191            }
1192
1193            return this;
1194        }
1195
1196
1197        public void write(byte[] code, int offset)
1198        {
1199            for (int index = 0; index < instructions.length; index++)
1200            {
1201                Instruction instruction = instructions[index];
1202
1203                instruction.write(code, offset);
1204
1205                offset += instruction.length(offset);
1206            }
1207        }
1208
1209
1210        protected void readInfo(byte[] code, int offset)
1211        {
1212            throw new UnsupportedOperationException("Can't read composite instruction");
1213        }
1214
1215
1216        protected void writeInfo(byte[] code, int offset)
1217        {
1218            throw new UnsupportedOperationException("Can't write composite instruction");
1219        }
1220
1221
1222        public int length(int offset)
1223        {
1224            int newOffset = offset;
1225
1226            for (int index = 0; index < instructions.length; index++)
1227            {
1228                newOffset += instructions[index].length(newOffset);
1229            }
1230
1231            return newOffset - offset;
1232        }
1233
1234
1235        public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
1236        {
1237            if (instructionVisitor != CodeAttributeEditor.this)
1238            {
1239                throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]");
1240            }
1241
1242            for (int index = 0; index < instructions.length; index++)
1243            {
1244                Instruction instruction = instructions[index];
1245
1246                instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this);
1247
1248                offset += instruction.length(offset);
1249            }
1250        }
1251
1252
1253        // Implementations for Object.
1254
1255        public String toString()
1256        {
1257            StringBuffer stringBuffer = new StringBuffer();
1258
1259            for (int index = 0; index < instructions.length; index++)
1260            {
1261                stringBuffer.append(instructions[index].toString()).append("; ");
1262            }
1263
1264            return stringBuffer.toString();
1265        }
1266    }
1267}
1268