001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This library is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This library is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
015: * for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public License
018: * along with this library; if not, write to the Free Software Foundation,
019: * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.classfile.editor;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.*;
025: import proguard.classfile.attribute.preverification.*;
026: import proguard.classfile.attribute.preverification.visitor.*;
027: import proguard.classfile.attribute.visitor.*;
028: import proguard.classfile.instruction.*;
029: import proguard.classfile.instruction.visitor.InstructionVisitor;
030: import proguard.classfile.util.SimplifiedVisitor;
031:
032: /**
033: * This AttributeVisitor accumulates instructions and exceptions, and then
034: * copies them into code attributes that it visits.
035: *
036: * @author Eric Lafortune
037: */
038: public class CodeAttributeComposer extends SimplifiedVisitor implements
039: AttributeVisitor, InstructionVisitor, ExceptionInfoVisitor,
040: StackMapFrameVisitor, VerificationTypeVisitor,
041: LineNumberInfoVisitor, LocalVariableInfoVisitor,
042: LocalVariableTypeInfoVisitor {
043: //*
044: private static final boolean DEBUG = false;
045: /*/
046: public static boolean DEBUG = true;
047: //*/
048:
049: private static final int MAXIMUM_LEVELS = 32;
050: private static final int INVALID = -1;
051:
052: private boolean allowExternalExceptionHandlers;
053:
054: private int maximumCodeLength;
055: private int codeLength;
056: private int exceptionTableLength;
057: private int level = -1;
058:
059: private byte[] code = new byte[ClassConstants.TYPICAL_CODE_LENGTH];
060: private int[] oldInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
061:
062: private final int[] codeFragmentOffsets = new int[MAXIMUM_LEVELS];
063: private final int[] codeFragmentLengths = new int[MAXIMUM_LEVELS];
064: private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH];
065:
066: private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH];
067:
068: private int expectedStackMapFrameOffset;
069:
070: private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater();
071: private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
072:
073: /**
074: * Creates a new CodeAttributeComposer that doesn't allow external exception
075: * handlers.
076: */
077: public CodeAttributeComposer() {
078: this (false);
079: }
080:
081: /**
082: * Creates a new CodeAttributeComposer that optionally allows external
083: * exception handlers.
084: */
085: public CodeAttributeComposer(boolean allowExternalExceptionHandlers) {
086: this .allowExternalExceptionHandlers = allowExternalExceptionHandlers;
087: }
088:
089: /**
090: * Starts a new code definition.
091: */
092: public void reset() {
093: maximumCodeLength = 0;
094: codeLength = 0;
095: exceptionTableLength = 0;
096: level = -1;
097: }
098:
099: /**
100: * Starts a new code fragment. Branch instructions that are added are
101: * assumed to be relative within such code fragments.
102: * @param maximumCodeFragmentLength the maximum length of the code that will
103: * be added as part of this fragment.
104: */
105: public void beginCodeFragment(int maximumCodeFragmentLength) {
106: level++;
107:
108: if (level >= MAXIMUM_LEVELS) {
109: throw new IllegalArgumentException(
110: "Maximum number of code fragment levels exceeded ["
111: + level + "]");
112: }
113:
114: // Make sure there is sufficient space for adding the code fragment.
115: maximumCodeLength += maximumCodeFragmentLength;
116: if (code.length < maximumCodeLength) {
117: byte[] newCode = new byte[maximumCodeLength];
118: System.arraycopy(code, 0, newCode, 0, codeLength);
119: code = newCode;
120:
121: int[] newOldInstructionOffsets = new int[maximumCodeLength];
122: System.arraycopy(oldInstructionOffsets, 0,
123: newOldInstructionOffsets, 0, codeLength);
124: oldInstructionOffsets = newOldInstructionOffsets;
125: }
126:
127: // Try to reuse the previous array for this code fragment.
128: if (instructionOffsetMap[level].length <= maximumCodeFragmentLength) {
129: instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1];
130: }
131:
132: // Initialize the offset map.
133: for (int index = 0; index <= maximumCodeFragmentLength; index++) {
134: instructionOffsetMap[level][index] = INVALID;
135: }
136:
137: // Remember the location of the code fragment.
138: codeFragmentOffsets[level] = codeLength;
139: codeFragmentLengths[level] = maximumCodeFragmentLength;
140: }
141:
142: /**
143: * Appends the given instruction with the given old offset.
144: * @param oldInstructionOffset the old offset of the instruction, to which
145: * branches and other references in the current
146: * code fragment are pointing.
147: * @param instruction the instruction to be appended.
148: */
149: public void appendInstruction(int oldInstructionOffset,
150: Instruction instruction) {
151: if (DEBUG) {
152: println("[" + codeLength + "] <- ", instruction
153: .toString(oldInstructionOffset));
154: }
155:
156: // Remember the old offset of the appended instruction.
157: oldInstructionOffsets[codeLength] = oldInstructionOffset;
158:
159: // Write the instruction.
160: instruction.write(code, codeLength);
161:
162: // Fill out the new offset of the appended instruction.
163: instructionOffsetMap[level][oldInstructionOffset] = codeLength;
164:
165: // Continue appending at the next instruction offset.
166: codeLength += instruction.length(codeLength);
167: }
168:
169: /**
170: * Appends the given label with the given old offset.
171: * @param oldInstructionOffset the old offset of the label, to which
172: * branches and other references in the current
173: * code fragment are pointing.
174: */
175: public void appendLabel(int oldInstructionOffset) {
176: if (DEBUG) {
177: println("[" + codeLength + "] <- ", "["
178: + oldInstructionOffset + "] (label)");
179: }
180:
181: // Fill out the new offset of the appended instruction.
182: instructionOffsetMap[level][oldInstructionOffset] = codeLength;
183: }
184:
185: /**
186: * Appends the given exception to the exception table.
187: * @param exceptionInfo the exception to be appended.
188: */
189: public void appendException(ExceptionInfo exceptionInfo) {
190: if (DEBUG) {
191: print(" ", "Exception [" + exceptionInfo.u2startPC
192: + " -> " + exceptionInfo.u2endPC + ": "
193: + exceptionInfo.u2handlerPC + "]");
194: }
195:
196: // Remap the exception right away.
197: visitExceptionInfo(null, null, null, exceptionInfo);
198:
199: if (DEBUG) {
200: System.out.println(" -> [" + exceptionInfo.u2startPC
201: + " -> " + exceptionInfo.u2endPC + ": "
202: + exceptionInfo.u2handlerPC + "]");
203: }
204:
205: // Don't add the exception if its instruction range is empty.
206: if (exceptionInfo.u2startPC == exceptionInfo.u2endPC) {
207: if (DEBUG) {
208: println(" ",
209: " (not added because of empty instruction range)");
210: }
211:
212: return;
213: }
214:
215: // Make sure there is sufficient space in the exception table.
216: if (exceptionTable.length <= exceptionTableLength) {
217: ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength + 1];
218: System.arraycopy(exceptionTable, 0, newExceptionTable, 0,
219: exceptionTableLength);
220: exceptionTable = newExceptionTable;
221: }
222:
223: // Add the exception.
224: exceptionTable[exceptionTableLength++] = exceptionInfo;
225: }
226:
227: /**
228: * Wraps up the current code fragment, continuing with the previous one on
229: * the stack.
230: */
231: public void endCodeFragment() {
232: if (level < 0) {
233: throw new IllegalArgumentException(
234: "Code fragment not begun [" + level + "]");
235: }
236:
237: // Remap the instructions of the code fragment.
238: int instructionOffset = codeFragmentOffsets[level];
239: while (instructionOffset < codeLength) {
240: // Get the next instruction.
241: Instruction instruction = InstructionFactory.create(code,
242: instructionOffset);
243:
244: // Does this instruction still have to be remapped?
245: if (oldInstructionOffsets[instructionOffset] >= 0) {
246: // Adapt the instruction for its new offset.
247: instruction.accept(null, null, null, instructionOffset,
248: this );
249:
250: // Write the instruction back.
251: instruction.write(code, instructionOffset);
252:
253: // Don't remap this instruction again.
254: oldInstructionOffsets[instructionOffset] = -1;
255: }
256:
257: // Continue remapping at the next instruction offset.
258: instructionOffset += instruction.length(instructionOffset);
259: }
260:
261: // Correct the estimated maximum code length, now that we know the
262: // actual length of this code fragment.
263: maximumCodeLength += codeLength - codeFragmentOffsets[level]
264: - codeFragmentLengths[level];
265:
266: // Try to remap the exception handlers that couldn't be remapped before.
267: if (allowExternalExceptionHandlers) {
268: for (int index = 0; index < exceptionTableLength; index++) {
269: ExceptionInfo exceptionInfo = exceptionTable[index];
270:
271: // Unmapped exception handlers are still negated.
272: int handlerPC = -exceptionInfo.u2handlerPC;
273: if (handlerPC > 0) {
274: if (instructionOffsetMap[level][handlerPC] < codeLength) {
275: exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC);
276: } else if (level == 0) {
277: throw new IllegalStateException(
278: "Couldn't remap exception handler offset ["
279: + handlerPC + "]");
280: }
281: }
282: }
283: }
284:
285: level--;
286: }
287:
288: // Implementations for AttributeVisitor.
289:
290: public void visitAnyAttribute(Clazz clazz, Attribute attribute) {
291: }
292:
293: public void visitCodeAttribute(Clazz clazz, Method method,
294: CodeAttribute codeAttribute) {
295: if (DEBUG) {
296: System.out
297: .println("CodeAttributeComposer: putting results in ["
298: + clazz.getName()
299: + "."
300: + method.getName(clazz)
301: + method.getDescriptor(clazz) + "]");
302: }
303:
304: if (level != -1) {
305: throw new IllegalArgumentException(
306: "Code fragment not ended [" + level + "]");
307: }
308:
309: level++;
310:
311: // Make sure the code attribute has sufficient space for the composed
312: // code.
313: if (codeAttribute.u4codeLength < codeLength) {
314: codeAttribute.code = new byte[codeLength];
315: }
316:
317: // Copy the composed code over into the code attribute.
318: codeAttribute.u4codeLength = codeLength;
319: System.arraycopy(code, 0, codeAttribute.code, 0, codeLength);
320:
321: // Remove exceptions with empty code blocks (done before).
322: //exceptionTableLength =
323: // removeEmptyExceptions(exceptionTable, exceptionTableLength);
324:
325: // Make sure the exception table has sufficient space for the composed
326: // exceptions.
327: if (codeAttribute.exceptionTable.length < exceptionTableLength) {
328: codeAttribute.exceptionTable = new ExceptionInfo[exceptionTableLength];
329: }
330:
331: // Copy the exception table.
332: codeAttribute.u2exceptionTableLength = exceptionTableLength;
333: System.arraycopy(exceptionTable, 0,
334: codeAttribute.exceptionTable, 0, exceptionTableLength);
335:
336: // Update the maximum stack size and local variable frame size.
337: stackSizeUpdater.visitCodeAttribute(clazz, method,
338: codeAttribute);
339: variableSizeUpdater.visitCodeAttribute(clazz, method,
340: codeAttribute);
341:
342: // Remap the line number table and the local variable table.
343: codeAttribute.attributesAccept(clazz, method, this );
344:
345: // Remap the exception table.
346: //codeAttribute.exceptionsAccept(clazz, method, this);
347:
348: // Remove exceptions with empty code blocks (done before).
349: //codeAttribute.u2exceptionTableLength =
350: // removeEmptyExceptions(codeAttribute.exceptionTable,
351: // codeAttribute.u2exceptionTableLength);
352:
353: level--;
354: }
355:
356: public void visitStackMapAttribute(Clazz clazz, Method method,
357: CodeAttribute codeAttribute,
358: StackMapAttribute stackMapAttribute) {
359: // Remap all stack map entries.
360: expectedStackMapFrameOffset = -1;
361: stackMapAttribute.stackMapFramesAccept(clazz, method,
362: codeAttribute, this );
363: }
364:
365: public void visitStackMapTableAttribute(Clazz clazz, Method method,
366: CodeAttribute codeAttribute,
367: StackMapTableAttribute stackMapTableAttribute) {
368: // Remap all stack map table entries.
369: expectedStackMapFrameOffset = 0;
370: stackMapTableAttribute.stackMapFramesAccept(clazz, method,
371: codeAttribute, this );
372: }
373:
374: public void visitLineNumberTableAttribute(Clazz clazz,
375: Method method, CodeAttribute codeAttribute,
376: LineNumberTableAttribute lineNumberTableAttribute) {
377: // Remap all line number table entries.
378: lineNumberTableAttribute.lineNumbersAccept(clazz, method,
379: codeAttribute, this );
380:
381: // Remove line numbers with empty code blocks.
382: lineNumberTableAttribute.u2lineNumberTableLength = removeEmptyLineNumbers(
383: lineNumberTableAttribute.lineNumberTable,
384: lineNumberTableAttribute.u2lineNumberTableLength,
385: codeAttribute.u4codeLength);
386: }
387:
388: public void visitLocalVariableTableAttribute(Clazz clazz,
389: Method method, CodeAttribute codeAttribute,
390: LocalVariableTableAttribute localVariableTableAttribute) {
391: // Remap all local variable table entries.
392: localVariableTableAttribute.localVariablesAccept(clazz, method,
393: codeAttribute, this );
394:
395: // Remove local variables with empty code blocks.
396: localVariableTableAttribute.u2localVariableTableLength = removeEmptyLocalVariables(
397: localVariableTableAttribute.localVariableTable,
398: localVariableTableAttribute.u2localVariableTableLength,
399: codeAttribute.u2maxLocals);
400: }
401:
402: public void visitLocalVariableTypeTableAttribute(
403: Clazz clazz,
404: Method method,
405: CodeAttribute codeAttribute,
406: LocalVariableTypeTableAttribute localVariableTypeTableAttribute) {
407: // Remap all local variable table entries.
408: localVariableTypeTableAttribute.localVariablesAccept(clazz,
409: method, codeAttribute, this );
410:
411: // Remove local variables with empty code blocks.
412: localVariableTypeTableAttribute.u2localVariableTypeTableLength = removeEmptyLocalVariableTypes(
413: localVariableTypeTableAttribute.localVariableTypeTable,
414: localVariableTypeTableAttribute.u2localVariableTypeTableLength,
415: codeAttribute.u2maxLocals);
416: }
417:
418: // Implementations for InstructionVisitor.
419:
420: public void visitAnyInstruction(Clazz clazz, Method method,
421: CodeAttribute codeAttribute, int offset,
422: Instruction instruction) {
423: }
424:
425: public void visitBranchInstruction(Clazz clazz, Method method,
426: CodeAttribute codeAttribute, int offset,
427: BranchInstruction branchInstruction) {
428: // Adjust the branch offset.
429: branchInstruction.branchOffset = remapBranchOffset(offset,
430: branchInstruction.branchOffset);
431: }
432:
433: public void visitAnySwitchInstruction(Clazz clazz, Method method,
434: CodeAttribute codeAttribute, int offset,
435: SwitchInstruction switchInstruction) {
436: // Adjust the default jump offset.
437: switchInstruction.defaultOffset = remapBranchOffset(offset,
438: switchInstruction.defaultOffset);
439:
440: // Adjust the jump offsets.
441: remapJumpOffsets(offset, switchInstruction.jumpOffsets);
442: }
443:
444: // Implementations for ExceptionInfoVisitor.
445:
446: public void visitExceptionInfo(Clazz clazz, Method method,
447: CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) {
448: // Remap the code offsets. Note that the instruction offset map also has
449: // an entry for the first offset after the code, for u2endPC.
450: exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
451: exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC);
452:
453: // See if we can remap the handler right away. Unmapped exception
454: // handlers are negated, in order to mark them as external.
455: int handlerPC = exceptionInfo.u2handlerPC;
456: exceptionInfo.u2handlerPC = allowExternalExceptionHandlers
457: && instructionOffsetMap[level][handlerPC] >= codeLength ? -handlerPC
458: : remapInstructionOffset(handlerPC);
459: }
460:
461: // Implementations for StackMapFrameVisitor.
462:
463: public void visitAnyStackMapFrame(Clazz clazz, Method method,
464: CodeAttribute codeAttribute, int offset,
465: StackMapFrame stackMapFrame) {
466: // Remap the stack map frame offset.
467: int stackMapFrameOffset = remapInstructionOffset(offset);
468:
469: int offsetDelta = stackMapFrameOffset;
470:
471: // Compute the offset delta if the frame is part of a stack map frame
472: // table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
473: if (expectedStackMapFrameOffset >= 0) {
474: offsetDelta -= expectedStackMapFrameOffset;
475:
476: expectedStackMapFrameOffset = stackMapFrameOffset + 1;
477: }
478:
479: stackMapFrame.u2offsetDelta = offsetDelta;
480: }
481:
482: public void visitSameOneFrame(Clazz clazz, Method method,
483: CodeAttribute codeAttribute, int offset,
484: SameOneFrame sameOneFrame) {
485: // Remap the stack map frame offset.
486: visitAnyStackMapFrame(clazz, method, codeAttribute, offset,
487: sameOneFrame);
488:
489: // Remap the verification type offset.
490: sameOneFrame.stackItemAccept(clazz, method, codeAttribute,
491: offset, this );
492: }
493:
494: public void visitMoreZeroFrame(Clazz clazz, Method method,
495: CodeAttribute codeAttribute, int offset,
496: MoreZeroFrame moreZeroFrame) {
497: // Remap the stack map frame offset.
498: visitAnyStackMapFrame(clazz, method, codeAttribute, offset,
499: moreZeroFrame);
500:
501: // Remap the verification type offsets.
502: moreZeroFrame.additionalVariablesAccept(clazz, method,
503: codeAttribute, offset, this );
504: }
505:
506: public void visitFullFrame(Clazz clazz, Method method,
507: CodeAttribute codeAttribute, int offset, FullFrame fullFrame) {
508: // Remap the stack map frame offset.
509: visitAnyStackMapFrame(clazz, method, codeAttribute, offset,
510: fullFrame);
511:
512: // Remap the verification type offsets.
513: fullFrame.variablesAccept(clazz, method, codeAttribute, offset,
514: this );
515: fullFrame.stackAccept(clazz, method, codeAttribute, offset,
516: this );
517: }
518:
519: // Implementations for VerificationTypeVisitor.
520:
521: public void visitAnyVerificationType(Clazz clazz, Method method,
522: CodeAttribute codeAttribute, int offset,
523: VerificationType verificationType) {
524: }
525:
526: public void visitUninitializedType(Clazz clazz, Method method,
527: CodeAttribute codeAttribute, int offset,
528: UninitializedType uninitializedType) {
529: // Remap the offset of the 'new' instruction.
530: uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
531: }
532:
533: // Implementations for LineNumberInfoVisitor.
534:
535: public void visitLineNumberInfo(Clazz clazz, Method method,
536: CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) {
537: // Remap the code offset.
538: lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
539: }
540:
541: // Implementations for LocalVariableInfoVisitor.
542:
543: public void visitLocalVariableInfo(Clazz clazz, Method method,
544: CodeAttribute codeAttribute,
545: LocalVariableInfo localVariableInfo) {
546: // Remap the code offset and length.
547: // TODO: The local variable frame might not be strictly preserved.
548: int startPC = remapInstructionOffset(localVariableInfo.u2startPC);
549: int endPC = remapInstructionOffset(localVariableInfo.u2startPC
550: + localVariableInfo.u2length);
551:
552: localVariableInfo.u2startPC = startPC;
553: localVariableInfo.u2length = endPC - startPC;
554: }
555:
556: // Implementations for LocalVariableTypeInfoVisitor.
557:
558: public void visitLocalVariableTypeInfo(Clazz clazz, Method method,
559: CodeAttribute codeAttribute,
560: LocalVariableTypeInfo localVariableTypeInfo) {
561: // Remap the code offset and length.
562: // TODO: The local variable frame might not be strictly preserved.
563: int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
564: int endPC = remapInstructionOffset(localVariableTypeInfo.u2startPC
565: + localVariableTypeInfo.u2length);
566:
567: localVariableTypeInfo.u2startPC = startPC;
568: localVariableTypeInfo.u2length = endPC - startPC;
569: }
570:
571: // Small utility methods.
572:
573: /**
574: * Adjusts the given jump offsets for the instruction at the given offset.
575: */
576: private void remapJumpOffsets(int offset, int[] jumpOffsets) {
577: for (int index = 0; index < jumpOffsets.length; index++) {
578: jumpOffsets[index] = remapBranchOffset(offset,
579: jumpOffsets[index]);
580: }
581: }
582:
583: /**
584: * Computes the new branch offset for the instruction at the given new offset
585: * with the given old branch offset.
586: */
587: private int remapBranchOffset(int newInstructionOffset,
588: int branchOffset) {
589: if (newInstructionOffset < 0
590: || newInstructionOffset > codeLength) {
591: throw new IllegalArgumentException(
592: "Invalid instruction offset ["
593: + newInstructionOffset
594: + "] in code with length [" + codeLength
595: + "]");
596: }
597:
598: int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset];
599:
600: return remapInstructionOffset(oldInstructionOffset
601: + branchOffset)
602: - remapInstructionOffset(oldInstructionOffset);
603: }
604:
605: /**
606: * Computes the new instruction offset for the instruction at the given old
607: * offset.
608: */
609: private int remapInstructionOffset(int oldInstructionOffset) {
610: if (oldInstructionOffset < 0
611: || oldInstructionOffset > codeFragmentLengths[level]) {
612: throw new IllegalArgumentException("Instruction offset ["
613: + oldInstructionOffset
614: + "] out of range in code fragment with length ["
615: + codeFragmentLengths[level] + "] at level "
616: + level);
617: }
618:
619: int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset];
620: if (newInstructionOffset == INVALID) {
621: throw new IllegalArgumentException(
622: "Invalid instruction offset ["
623: + oldInstructionOffset
624: + "] in code fragment at level " + level);
625: }
626:
627: return newInstructionOffset;
628: }
629:
630: /**
631: * Returns the given list of exceptions, without the ones that have empty
632: * code blocks.
633: */
634: private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
635: int exceptionInfoCount) {
636: // Overwrite all empty exceptions.
637: int newIndex = 0;
638: for (int index = 0; index < exceptionInfoCount; index++) {
639: ExceptionInfo exceptionInfo = exceptionInfos[index];
640: if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) {
641: exceptionInfos[newIndex++] = exceptionInfo;
642: }
643: }
644:
645: // Clear the unused array entries.
646: for (int index = newIndex; index < exceptionInfoCount; index++) {
647: exceptionInfos[index] = null;
648: }
649:
650: return newIndex;
651: }
652:
653: /**
654: * Returns the given list of line numbers, without the ones that have empty
655: * code blocks or that exceed the code size.
656: */
657: private int removeEmptyLineNumbers(
658: LineNumberInfo[] lineNumberInfos, int lineNumberInfoCount,
659: int codeLength) {
660: // Overwrite all empty line number entries.
661: int newIndex = 0;
662: for (int index = 0; index < lineNumberInfoCount; index++) {
663: LineNumberInfo lineNumberInfo = lineNumberInfos[index];
664: int startPC = lineNumberInfo.u2startPC;
665: if (startPC < codeLength
666: && (index == 0 || startPC > lineNumberInfos[index - 1].u2startPC)) {
667: lineNumberInfos[newIndex++] = lineNumberInfo;
668: }
669: }
670:
671: // Clear the unused array entries.
672: for (int index = newIndex; index < lineNumberInfoCount; index++) {
673: lineNumberInfos[index] = null;
674: }
675:
676: return newIndex;
677: }
678:
679: /**
680: * Returns the given list of local variables, without the ones that have empty
681: * code blocks or that exceed the actual number of local variables.
682: */
683: private int removeEmptyLocalVariables(
684: LocalVariableInfo[] localVariableInfos,
685: int localVariableInfoCount, int maxLocals) {
686: // Overwrite all empty local variable entries.
687: int newIndex = 0;
688: for (int index = 0; index < localVariableInfoCount; index++) {
689: LocalVariableInfo localVariableInfo = localVariableInfos[index];
690: if (localVariableInfo.u2length > 0
691: && localVariableInfo.u2index < maxLocals) {
692: localVariableInfos[newIndex++] = localVariableInfo;
693: }
694: }
695:
696: // Clear the unused array entries.
697: for (int index = newIndex; index < localVariableInfoCount; index++) {
698: localVariableInfos[index] = null;
699: }
700:
701: return newIndex;
702: }
703:
704: /**
705: * Returns the given list of local variable types, without the ones that
706: * have empty code blocks or that exceed the actual number of local variables.
707: */
708: private int removeEmptyLocalVariableTypes(
709: LocalVariableTypeInfo[] localVariableTypeInfos,
710: int localVariableTypeInfoCount, int maxLocals) {
711: // Overwrite all empty local variable type entries.
712: int newIndex = 0;
713: for (int index = 0; index < localVariableTypeInfoCount; index++) {
714: LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
715: if (localVariableTypeInfo.u2length > 0
716: && localVariableTypeInfo.u2index < maxLocals) {
717: localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
718: }
719: }
720:
721: // Clear the unused array entries.
722: for (int index = newIndex; index < localVariableTypeInfoCount; index++) {
723: localVariableTypeInfos[index] = null;
724: }
725:
726: return newIndex;
727: }
728:
729: private void println(String string1, String string2) {
730: print(string1, string2);
731:
732: System.out.println();
733: }
734:
735: private void print(String string1, String string2) {
736: System.out.print(string1);
737:
738: for (int index = 0; index < level; index++) {
739: System.out.print(" ");
740: }
741:
742: System.out.print(string2);
743: }
744:
745: public static void main(String[] args) {
746: CodeAttributeComposer composer = new CodeAttributeComposer();
747:
748: composer.beginCodeFragment(10);
749: composer.appendInstruction(0, new SimpleInstruction(
750: InstructionConstants.OP_ICONST_0));
751: composer.appendInstruction(1, new VariableInstruction(
752: InstructionConstants.OP_ISTORE, 0));
753: composer.appendInstruction(2, new BranchInstruction(
754: InstructionConstants.OP_GOTO, 1));
755:
756: composer.beginCodeFragment(10);
757: composer.appendInstruction(0, new VariableInstruction(
758: InstructionConstants.OP_IINC, 0, 1));
759: composer.appendInstruction(1, new VariableInstruction(
760: InstructionConstants.OP_ILOAD, 0));
761: composer.appendInstruction(2, new SimpleInstruction(
762: InstructionConstants.OP_ICONST_5));
763: composer.appendInstruction(3, new BranchInstruction(
764: InstructionConstants.OP_IFICMPLT, -3));
765: composer.endCodeFragment();
766:
767: composer.appendInstruction(3, new SimpleInstruction(
768: InstructionConstants.OP_RETURN));
769: composer.endCodeFragment();
770: }
771: }
|