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.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.preverification.*;
26import proguard.classfile.attribute.preverification.visitor.*;
27import proguard.classfile.attribute.visitor.*;
28import proguard.classfile.instruction.*;
29import proguard.classfile.instruction.visitor.InstructionVisitor;
30import proguard.classfile.util.SimplifiedVisitor;
31
32/**
33 * This AttributeVisitor accumulates instructions and exceptions, and then
34 * copies them into code attributes that it visits.
35 *
36 * @author Eric Lafortune
37 */
38public class CodeAttributeComposer
39extends      SimplifiedVisitor
40implements   AttributeVisitor,
41             InstructionVisitor,
42             ExceptionInfoVisitor,
43             StackMapFrameVisitor,
44             VerificationTypeVisitor,
45             LineNumberInfoVisitor,
46             LocalVariableInfoVisitor,
47             LocalVariableTypeInfoVisitor
48{
49    //*
50    private static final boolean DEBUG = false;
51    /*/
52    public  static       boolean DEBUG = true;
53    //*/
54
55
56    private static final int MAXIMUM_LEVELS = 32;
57    private static final int INVALID        = -1;
58
59
60    private boolean allowExternalExceptionHandlers;
61
62    private int maximumCodeLength;
63    private int codeLength;
64    private int exceptionTableLength;
65    private int level = -1;
66
67    private byte[]  code                  = new byte[ClassConstants.TYPICAL_CODE_LENGTH];
68    private int[]   oldInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
69
70    private final int[]   codeFragmentOffsets  = new int[MAXIMUM_LEVELS];
71    private final int[]   codeFragmentLengths  = new int[MAXIMUM_LEVELS];
72    private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH + 1];
73
74    private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH];
75
76    private int expectedStackMapFrameOffset;
77
78    private final StackSizeUpdater    stackSizeUpdater    = new StackSizeUpdater();
79    private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
80//    private final InstructionWriter   instructionWriter   = new InstructionWriter();
81
82
83    /**
84     * Creates a new CodeAttributeComposer that doesn't allow external exception
85     * handlers.
86     */
87    public CodeAttributeComposer()
88    {
89        this(false);
90    }
91
92
93    /**
94     * Creates a new CodeAttributeComposer that optionally allows external
95     * exception handlers.
96     */
97    public CodeAttributeComposer(boolean allowExternalExceptionHandlers)
98    {
99        this.allowExternalExceptionHandlers = allowExternalExceptionHandlers;
100    }
101
102
103    /**
104     * Starts a new code definition.
105     */
106    public void reset()
107    {
108        maximumCodeLength    = 0;
109        codeLength           = 0;
110        exceptionTableLength = 0;
111        level                = -1;
112    }
113
114
115    /**
116     * Starts a new code fragment. Branch instructions that are added are
117     * assumed to be relative within such code fragments.
118     * @param maximumCodeFragmentLength the maximum length of the code that will
119     *                                  be added as part of this fragment.
120     */
121    public void beginCodeFragment(int maximumCodeFragmentLength)
122    {
123        level++;
124
125        if (level >= MAXIMUM_LEVELS)
126        {
127            throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]");
128        }
129
130//        // TODO: Figure out some length.
131//        if (level == 0)
132//        {
133//            // Prepare for possible widening of instructions.
134//            instructionWriter.reset(2 * maximumCodeFragmentLength);
135//        }
136
137        // Make sure there is sufficient space for adding the code fragment.
138        maximumCodeLength += maximumCodeFragmentLength;
139
140        ensureCodeLength(maximumCodeLength);
141
142        // Try to reuse the previous array for this code fragment.
143        if (instructionOffsetMap[level].length <= maximumCodeFragmentLength)
144        {
145            instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1];
146        }
147
148        // Initialize the offset map.
149        for (int index = 0; index <= maximumCodeFragmentLength; index++)
150        {
151            instructionOffsetMap[level][index] = INVALID;
152        }
153
154        // Remember the location of the code fragment.
155        codeFragmentOffsets[level] = codeLength;
156        codeFragmentLengths[level] = maximumCodeFragmentLength;
157    }
158
159
160    /**
161     * Appends the given instruction with the given old offset.
162     * @param oldInstructionOffset the old offset of the instruction, to which
163     *                             branches and other references in the current
164     *                             code fragment are pointing.
165     * @param instruction          the instruction to be appended.
166     */
167    public void appendInstruction(int         oldInstructionOffset,
168                                  Instruction instruction)
169    {
170        if (DEBUG)
171        {
172            println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset));
173        }
174
175        // Make sure the code array is large enough.
176        int newCodeLength = codeLength + instruction.length(codeLength);
177
178        ensureCodeLength(newCodeLength);
179
180        // Remember the old offset of the appended instruction.
181        oldInstructionOffsets[codeLength] = oldInstructionOffset;
182
183        // Write the instruction.
184//        instruction.accept(null,
185//                           null,
186//                           new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
187//                           codeLength,
188//                           instructionWriter);
189        instruction.write(code, codeLength);
190
191        // Fill out the new offset of the appended instruction.
192        instructionOffsetMap[level][oldInstructionOffset] = codeLength;
193
194        // Continue appending at the next instruction offset.
195        codeLength = newCodeLength;
196    }
197
198
199    /**
200     * Appends the given label with the given old offset.
201     * @param oldInstructionOffset the old offset of the label, to which
202     *                             branches and other references in the current
203     *                             code fragment are pointing.
204     */
205    public void appendLabel(int oldInstructionOffset)
206    {
207        if (DEBUG)
208        {
209            println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)");
210        }
211
212        // Fill out the new offset of the appended instruction.
213        instructionOffsetMap[level][oldInstructionOffset] = codeLength;
214    }
215
216
217    /**
218     * Appends the given exception to the exception table.
219     * @param exceptionInfo the exception to be appended.
220     */
221    public void appendException(ExceptionInfo exceptionInfo)
222    {
223        if (DEBUG)
224        {
225            print("         ", "Exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]");
226        }
227
228        // Remap the exception right away.
229        visitExceptionInfo(null, null, null, exceptionInfo);
230
231        if (DEBUG)
232        {
233            System.out.println(" -> ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]");
234        }
235
236        // Don't add the exception if its instruction range is empty.
237        if (exceptionInfo.u2startPC == exceptionInfo.u2endPC)
238        {
239            if (DEBUG)
240            {
241                println("         ", "  (not added because of empty instruction range)");
242            }
243
244            return;
245        }
246
247        // Make sure there is sufficient space in the exception table.
248        if (exceptionTable.length <= exceptionTableLength)
249        {
250            ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1];
251            System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength);
252            exceptionTable = newExceptionTable;
253        }
254
255        // Add the exception.
256        exceptionTable[exceptionTableLength++] = exceptionInfo;
257    }
258
259
260    /**
261     * Wraps up the current code fragment, continuing with the previous one on
262     * the stack.
263     */
264    public void endCodeFragment()
265    {
266        if (level < 0)
267        {
268            throw new IllegalArgumentException("Code fragment not begun ["+level+"]");
269        }
270
271        // Remap the instructions of the code fragment.
272        int instructionOffset = codeFragmentOffsets[level];
273        while (instructionOffset < codeLength)
274        {
275            // Get the next instruction.
276            Instruction instruction = InstructionFactory.create(code, instructionOffset);
277
278            // Does this instruction still have to be remapped?
279            if (oldInstructionOffsets[instructionOffset] >= 0)
280            {
281                // Adapt the instruction for its new offset.
282                instruction.accept(null, null, null, instructionOffset, this);
283
284                // Write the instruction back.
285//                instruction.accept(null,
286//                                   null,
287//                                   new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
288//                                   instructionOffset,
289//                                   instructionWriter);
290                instruction.write(code, instructionOffset);
291
292                // Don't remap this instruction again.
293                oldInstructionOffsets[instructionOffset] = -1;
294            }
295
296            // Continue remapping at the next instruction offset.
297            instructionOffset += instruction.length(instructionOffset);
298        }
299
300        // Correct the estimated maximum code length, now that we know the
301        // actual length of this code fragment.
302        maximumCodeLength += codeLength - codeFragmentOffsets[level] -
303                             codeFragmentLengths[level];
304
305        // Try to remap the exception handlers that couldn't be remapped before.
306        if (allowExternalExceptionHandlers)
307        {
308            for (int index = 0; index < exceptionTableLength; index++)
309            {
310                ExceptionInfo exceptionInfo = exceptionTable[index];
311
312                // Unmapped exception handlers are still negated.
313                int handlerPC = -exceptionInfo.u2handlerPC;
314                if (handlerPC > 0)
315                {
316                    if (remappableInstructionOffset(handlerPC))
317                    {
318                        exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC);
319                    }
320                    else if (level == 0)
321                    {
322                        throw new IllegalStateException("Couldn't remap exception handler offset ["+handlerPC+"]");
323                    }
324                }
325            }
326        }
327
328        level--;
329    }
330
331
332    // Implementations for AttributeVisitor.
333
334    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
335
336
337    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
338    {
339        if (DEBUG)
340        {
341            System.out.println("CodeAttributeComposer: putting results in ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
342        }
343
344        if (level != -1)
345        {
346            throw new IllegalArgumentException("Code fragment not ended ["+level+"]");
347        }
348
349        level++;
350
351        // Make sure the code attribute has sufficient space for the composed
352        // code.
353        if (codeAttribute.u4codeLength < codeLength)
354        {
355            codeAttribute.code = new byte[codeLength];
356        }
357
358        // Copy the composed code over into the code attribute.
359        codeAttribute.u4codeLength = codeLength;
360        System.arraycopy(code, 0, codeAttribute.code, 0, codeLength);
361
362        // Remove exceptions with empty code blocks (done before).
363        //exceptionTableLength =
364        //    removeEmptyExceptions(exceptionTable, exceptionTableLength);
365
366        // Make sure the exception table has sufficient space for the composed
367        // exceptions.
368        if (codeAttribute.exceptionTable.length < exceptionTableLength)
369        {
370            codeAttribute.exceptionTable = new ExceptionInfo[exceptionTableLength];
371        }
372
373        // Copy the exception table.
374        codeAttribute.u2exceptionTableLength = exceptionTableLength;
375        System.arraycopy(exceptionTable, 0, codeAttribute.exceptionTable, 0, exceptionTableLength);
376
377        // Update the maximum stack size and local variable frame size.
378        stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
379        variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
380
381        // Remap  the line number table and the local variable table.
382        codeAttribute.attributesAccept(clazz, method, this);
383
384        // Remap the exception table.
385        //codeAttribute.exceptionsAccept(clazz, method, this);
386
387        // Remove exceptions with empty code blocks (done before).
388        //codeAttribute.u2exceptionTableLength =
389        //    removeEmptyExceptions(codeAttribute.exceptionTable,
390        //                          codeAttribute.u2exceptionTableLength);
391
392//        // Make sure instructions are widened if necessary.
393//        instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
394
395        level--;
396    }
397
398
399    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
400    {
401        // Remap all stack map entries.
402        expectedStackMapFrameOffset = -1;
403        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
404    }
405
406
407    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
408    {
409        // Remap all stack map table entries.
410        expectedStackMapFrameOffset = 0;
411        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
412    }
413
414
415    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
416    {
417        // Remap all line number table entries.
418        lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
419
420        // Remove line numbers with empty code blocks.
421        lineNumberTableAttribute.u2lineNumberTableLength =
422           removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
423                                  lineNumberTableAttribute.u2lineNumberTableLength,
424                                  codeAttribute.u4codeLength);
425    }
426
427
428    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
429    {
430        // Remap all local variable table entries.
431        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
432
433        // Remove local variables with empty code blocks.
434        localVariableTableAttribute.u2localVariableTableLength =
435            removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
436                                      localVariableTableAttribute.u2localVariableTableLength,
437                                      codeAttribute.u2maxLocals);
438    }
439
440
441    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
442    {
443        // Remap all local variable table entries.
444        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
445
446        // Remove local variables with empty code blocks.
447        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
448            removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
449                                          localVariableTypeTableAttribute.u2localVariableTypeTableLength,
450                                          codeAttribute.u2maxLocals);
451    }
452
453
454    // Implementations for InstructionVisitor.
455
456    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
457
458
459    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
460    {
461        // Adjust the branch offset.
462        branchInstruction.branchOffset = remapBranchOffset(offset,
463                                                           branchInstruction.branchOffset);
464    }
465
466
467    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
468    {
469        // Adjust the default jump offset.
470        switchInstruction.defaultOffset = remapBranchOffset(offset,
471                                                            switchInstruction.defaultOffset);
472
473        // Adjust the jump offsets.
474        remapJumpOffsets(offset,
475                         switchInstruction.jumpOffsets);
476    }
477
478
479    // Implementations for ExceptionInfoVisitor.
480
481    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
482    {
483        // Remap the code offsets. Note that the instruction offset map also has
484        // an entry for the first offset after the code, for u2endPC.
485        exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
486        exceptionInfo.u2endPC   = remapInstructionOffset(exceptionInfo.u2endPC);
487
488        // See if we can remap the handler right away. Unmapped exception
489        // handlers are negated, in order to mark them as external.
490        int handlerPC = exceptionInfo.u2handlerPC;
491        exceptionInfo.u2handlerPC =
492            !allowExternalExceptionHandlers ||
493            remappableInstructionOffset(handlerPC) ?
494                remapInstructionOffset(handlerPC) :
495                -handlerPC;
496    }
497
498
499    // Implementations for StackMapFrameVisitor.
500
501    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
502    {
503        // Remap the stack map frame offset.
504        int stackMapFrameOffset = remapInstructionOffset(offset);
505
506        int offsetDelta = stackMapFrameOffset;
507
508        // Compute the offset delta if the frame is part of a stack map frame
509        // table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
510        if (expectedStackMapFrameOffset >= 0)
511        {
512            offsetDelta -= expectedStackMapFrameOffset;
513
514            expectedStackMapFrameOffset = stackMapFrameOffset + 1;
515        }
516
517        stackMapFrame.u2offsetDelta = offsetDelta;
518    }
519
520
521    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
522    {
523        // Remap the stack map frame offset.
524        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
525
526        // Remap the verification type offset.
527        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
528    }
529
530
531    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
532    {
533        // Remap the stack map frame offset.
534        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
535
536        // Remap the verification type offsets.
537        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
538    }
539
540
541    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
542    {
543        // Remap the stack map frame offset.
544        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
545
546        // Remap the verification type offsets.
547        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
548        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
549    }
550
551
552    // Implementations for VerificationTypeVisitor.
553
554    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
555
556
557    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
558    {
559        // Remap the offset of the 'new' instruction.
560        uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
561    }
562
563
564    // Implementations for LineNumberInfoVisitor.
565
566    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
567    {
568        // Remap the code offset.
569        lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
570    }
571
572
573    // Implementations for LocalVariableInfoVisitor.
574
575    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
576    {
577        // Remap the code offset and length.
578        // TODO: The local variable frame might not be strictly preserved.
579        int startPC = remapInstructionOffset(localVariableInfo.u2startPC);
580        int endPC   = remapInstructionOffset(localVariableInfo.u2startPC + localVariableInfo.u2length);
581
582        localVariableInfo.u2startPC = startPC;
583        localVariableInfo.u2length  = endPC - startPC;
584    }
585
586    // Implementations for LocalVariableTypeInfoVisitor.
587
588    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
589    {
590        // Remap the code offset and length.
591        // TODO: The local variable frame might not be strictly preserved.
592        int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
593        int endPC   = remapInstructionOffset(localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length);
594
595        localVariableTypeInfo.u2startPC = startPC;
596        localVariableTypeInfo.u2length  = endPC - startPC;
597    }
598
599
600    // Small utility methods.
601
602    /**
603     * Make sure the code arrays have at least the given size.
604     */
605    private void ensureCodeLength(int newCodeLength)
606    {
607        if (code.length < newCodeLength)
608        {
609            // Add 20% to avoid extending the arrays too often.
610            newCodeLength = newCodeLength * 6 / 5;
611
612            byte[] newCode = new byte[newCodeLength];
613            System.arraycopy(code, 0, newCode, 0, codeLength);
614            code = newCode;
615
616            int[] newOldInstructionOffsets = new int[newCodeLength];
617            System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength);
618            oldInstructionOffsets = newOldInstructionOffsets;
619        }
620    }
621
622
623    /**
624     * Adjusts the given jump offsets for the instruction at the given offset.
625     */
626    private void remapJumpOffsets(int offset, int[] jumpOffsets)
627    {
628        for (int index = 0; index < jumpOffsets.length; index++)
629        {
630            jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
631        }
632    }
633
634
635    /**
636     * Computes the new branch offset for the instruction at the given new offset
637     * with the given old branch offset.
638     */
639    private int remapBranchOffset(int newInstructionOffset, int branchOffset)
640    {
641        if (newInstructionOffset < 0 ||
642            newInstructionOffset > codeLength)
643        {
644            throw new IllegalArgumentException("Invalid instruction offset ["+newInstructionOffset +"] in code with length ["+codeLength+"]");
645        }
646
647        int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset];
648
649        return remapInstructionOffset(oldInstructionOffset + branchOffset) -
650               remapInstructionOffset(oldInstructionOffset);
651    }
652
653
654    /**
655     * Computes the new instruction offset for the instruction at the given old
656     * offset.
657     */
658    private int remapInstructionOffset(int oldInstructionOffset)
659    {
660        if (oldInstructionOffset < 0 ||
661            oldInstructionOffset > codeFragmentLengths[level])
662        {
663            throw new IllegalArgumentException("Instruction offset ["+oldInstructionOffset +"] out of range in code fragment with length ["+codeFragmentLengths[level]+"] at level "+level);
664        }
665
666        int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset];
667        if (newInstructionOffset == INVALID)
668        {
669            throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment at level "+level);
670        }
671
672        return newInstructionOffset;
673    }
674
675
676    /**
677     * Returns whether the given old instruction offset can be remapped at the
678     */
679    private boolean remappableInstructionOffset(int oldInstructionOffset)
680    {
681        return
682            oldInstructionOffset <= codeFragmentLengths[level] &&
683            instructionOffsetMap[level][oldInstructionOffset] > INVALID;
684    }
685
686
687    /**
688     * Returns the given list of exceptions, without the ones that have empty
689     * code blocks.
690     */
691    private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
692                                      int             exceptionInfoCount)
693    {
694        // Overwrite all empty exceptions.
695        int newIndex = 0;
696        for (int index = 0; index < exceptionInfoCount; index++)
697        {
698            ExceptionInfo exceptionInfo = exceptionInfos[index];
699            if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
700            {
701                exceptionInfos[newIndex++] = exceptionInfo;
702            }
703        }
704
705        // Clear the unused array entries.
706        for (int index = newIndex; index < exceptionInfoCount; index++)
707        {
708            exceptionInfos[index] = null;
709        }
710
711        return newIndex;
712    }
713
714
715    /**
716     * Returns the given list of line numbers, without the ones that have empty
717     * code blocks or that exceed the code size.
718     */
719    private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
720                                       int              lineNumberInfoCount,
721                                       int              codeLength)
722    {
723        // Overwrite all empty line number entries.
724        int newIndex = 0;
725        for (int index = 0; index < lineNumberInfoCount; index++)
726        {
727            LineNumberInfo lineNumberInfo = lineNumberInfos[index];
728            int startPC = lineNumberInfo.u2startPC;
729            if (startPC < codeLength &&
730                (index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
731            {
732                lineNumberInfos[newIndex++] = lineNumberInfo;
733            }
734        }
735
736        // Clear the unused array entries.
737        for (int index = newIndex; index < lineNumberInfoCount; index++)
738        {
739            lineNumberInfos[index] = null;
740        }
741
742        return newIndex;
743    }
744
745
746    /**
747     * Returns the given list of local variables, without the ones that have empty
748     * code blocks or that exceed the actual number of local variables.
749     */
750    private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
751                                          int                 localVariableInfoCount,
752                                          int                 maxLocals)
753    {
754        // Overwrite all empty local variable entries.
755        int newIndex = 0;
756        for (int index = 0; index < localVariableInfoCount; index++)
757        {
758            LocalVariableInfo localVariableInfo = localVariableInfos[index];
759            if (localVariableInfo.u2length > 0 &&
760                localVariableInfo.u2index < maxLocals)
761            {
762                localVariableInfos[newIndex++] = localVariableInfo;
763            }
764        }
765
766        // Clear the unused array entries.
767        for (int index = newIndex; index < localVariableInfoCount; index++)
768        {
769            localVariableInfos[index] = null;
770        }
771
772        return newIndex;
773    }
774
775
776    /**
777     * Returns the given list of local variable types, without the ones that
778     * have empty code blocks or that exceed the actual number of local variables.
779     */
780    private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
781                                              int                     localVariableTypeInfoCount,
782                                              int                     maxLocals)
783    {
784        // Overwrite all empty local variable type entries.
785        int newIndex = 0;
786        for (int index = 0; index < localVariableTypeInfoCount; index++)
787        {
788            LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
789            if (localVariableTypeInfo.u2length > 0 &&
790                localVariableTypeInfo.u2index < maxLocals)
791            {
792                localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
793            }
794        }
795
796        // Clear the unused array entries.
797        for (int index = newIndex; index < localVariableTypeInfoCount; index++)
798        {
799            localVariableTypeInfos[index] = null;
800        }
801
802        return newIndex;
803    }
804
805
806    private void println(String string1, String string2)
807    {
808        print(string1, string2);
809
810        System.out.println();
811    }
812
813    private void print(String string1, String string2)
814    {
815        System.out.print(string1);
816
817        for (int index = 0; index < level; index++)
818        {
819            System.out.print("  ");
820        }
821
822        System.out.print(string2);
823    }
824
825
826    public static void main(String[] args)
827    {
828        CodeAttributeComposer composer = new CodeAttributeComposer();
829
830        composer.beginCodeFragment(4);
831        composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0));
832        composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0));
833        composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1));
834
835        composer.beginCodeFragment(4);
836        composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1));
837        composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0));
838        composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5));
839        composer.appendInstruction(3, new BranchInstruction(InstructionConstants.OP_IFICMPLT, -3));
840        composer.endCodeFragment();
841
842        composer.appendInstruction(3, new SimpleInstruction(InstructionConstants.OP_RETURN));
843        composer.endCodeFragment();
844    }
845}
846