001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.bytecode;
017:
018: /**
019: * An iterator for editing a code attribute.
020: *
021: * <p>If there are multiple <code>CodeIterator</code>s referring to the
022: * same <code>Code_attribute</code>, then inserting a gap by one
023: * <code>CodeIterator</code> will break the other
024: * <code>CodeIterator</code>.
025: *
026: * <p>This iterator does not provide <code>remove()</code>.
027: * If a piece of code in a <code>Code_attribute</code> is unnecessary,
028: * it should be overwritten with <code>NOP</code>.
029: *
030: * @see CodeAttribute#iterator()
031: */
032: public class CodeIterator implements Opcode {
033: protected CodeAttribute codeAttr;
034: protected byte[] bytecode;
035: protected int endPos;
036: protected int currentPos;
037:
038: protected CodeIterator(CodeAttribute ca) {
039: codeAttr = ca;
040: bytecode = ca.getCode();
041: begin();
042: }
043:
044: /**
045: * Moves to the first instruction.
046: */
047: public void begin() {
048: currentPos = 0;
049: endPos = getCodeLength();
050: }
051:
052: /**
053: * Moves to the given index.
054: *
055: * <p>The index of the next instruction is set to the given index.
056: * The successive call to <code>next()</code>
057: * returns the index that has been given to <code>move()</code>.
058: *
059: * <p>Note that the index is into the byte array returned by
060: * <code>get().getCode()</code>.
061: *
062: * @see CodeAttribute#getCode()
063: */
064: public void move(int index) {
065: currentPos = index;
066: }
067:
068: /**
069: * Returns a Code attribute read with this iterator.
070: */
071: public CodeAttribute get() {
072: return codeAttr;
073: }
074:
075: /**
076: * Returns <code>code_length</code> of <code>Code_attribute</code>.
077: */
078: public int getCodeLength() {
079: return bytecode.length;
080: }
081:
082: /**
083: * Returns the unsigned 8bit value at the given index.
084: */
085: public int byteAt(int index) {
086: return bytecode[index] & 0xff;
087: }
088:
089: /**
090: * Writes an 8bit value at the given index.
091: */
092: public void writeByte(int value, int index) {
093: bytecode[index] = (byte) value;
094: }
095:
096: /**
097: * Returns the unsigned 16bit value at the given index.
098: */
099: public int u16bitAt(int index) {
100: return ByteArray.readU16bit(bytecode, index);
101: }
102:
103: /**
104: * Returns the signed 16bit value at the given index.
105: */
106: public int s16bitAt(int index) {
107: return ByteArray.readS16bit(bytecode, index);
108: }
109:
110: /**
111: * Writes a 16 bit integer at the index.
112: */
113: public void write16bit(int value, int index) {
114: ByteArray.write16bit(value, bytecode, index);
115: }
116:
117: /**
118: * Returns the signed 32bit value at the given index.
119: */
120: public int s32bitAt(int index) {
121: return ByteArray.read32bit(bytecode, index);
122: }
123:
124: /**
125: * Writes a 32bit integer at the index.
126: */
127: public void write32bit(int value, int index) {
128: ByteArray.write32bit(value, bytecode, index);
129: }
130:
131: /**
132: * Writes a byte array at the index.
133: *
134: * @param code may be a zero-length array.
135: */
136: public void write(byte[] code, int index) {
137: int len = code.length;
138: for (int j = 0; j < len; ++j)
139: bytecode[index++] = code[j];
140: }
141:
142: /**
143: * Returns true if there is more instructions.
144: */
145: public boolean hasNext() {
146: return currentPos < endPos;
147: }
148:
149: /**
150: * Returns the index of the next instruction
151: * (not the operand following the current opcode).
152: *
153: * <p>Note that the index is into the byte array returned by
154: * <code>get().getCode()</code>.
155: *
156: * @see CodeAttribute#getCode()
157: * @see CodeIterator#byteAt(int)
158: */
159: public int next() throws BadBytecode {
160: int pos = currentPos;
161: currentPos = nextOpcode(bytecode, pos);
162: return pos;
163: }
164:
165: /**
166: * Obtains the value that the next call
167: * to <code>next()</code> will return.
168: *
169: * <p>This method is side-effects free.
170: * Successive calls to <code>lookAhead()</code> return the
171: * same value until <code>next()</code> is called.
172: */
173: public int lookAhead() {
174: return currentPos;
175: }
176:
177: /**
178: * Moves to the instruction for
179: * either <code>super()</code> or <code>this()</code>.
180: *
181: * <p>This method skips all the instructions for computing arguments
182: * to <code>super()</code> or <code>this()</code>, which should be
183: * placed at the beginning of a constructor body.
184: *
185: * <p>This method returns the index of INVOKESPECIAL instruction
186: * executing <code>super()</code> or <code>this()</code>.
187: * A successive call to <code>next()</code> returns the
188: * index of the next instruction following that INVOKESPECIAL.
189: *
190: * <p>This method works only for a constructor.
191: *
192: * @return the index of the INVOKESPECIAL instruction, or -1
193: * if a constructor invocation is not found.
194: */
195: public int skipConstructor() throws BadBytecode {
196: return skipSuperConstructor0(-1);
197: }
198:
199: /**
200: * Moves to the instruction for <code>super()</code>.
201: *
202: * <p>This method skips all the instructions for computing arguments to
203: * <code>super()</code>, which should be
204: * placed at the beginning of a constructor body.
205: *
206: * <p>This method returns the index of INVOKESPECIAL instruction
207: * executing <code>super()</code>.
208: * A successive call to <code>next()</code> returns the
209: * index of the next instruction following that INVOKESPECIAL.
210: *
211: * <p>This method works only for a constructor.
212: *
213: * @return the index of the INVOKESPECIAL instruction, or -1
214: * if a super constructor invocation is not found
215: * but <code>this()</code> is found.
216: */
217: public int skipSuperConstructor() throws BadBytecode {
218: return skipSuperConstructor0(0);
219: }
220:
221: /**
222: * Moves to the instruction for <code>this()</code>.
223: *
224: * <p>This method skips all the instructions for computing arguments to
225: * <code>this()</code>, which should be
226: * placed at the beginning of a constructor body.
227: *
228: * <p>This method returns the index of INVOKESPECIAL instruction
229: * executing <code>this()</code>.
230: * A successive call to <code>next()</code> returns the
231: * index of the next instruction following that INVOKESPECIAL.
232: *
233: * <p>This method works only for a constructor.
234: *
235: * @return the index of the INVOKESPECIAL instruction, or -1
236: * if a explicit constructor invocation is not found
237: * but <code>super()</code> is found.
238: */
239: public int skipThisConstructor() throws BadBytecode {
240: return skipSuperConstructor0(1);
241: }
242:
243: /* skipSuper 1: this(), 0: super(), -1: both.
244: */
245: private int skipSuperConstructor0(int skipThis) throws BadBytecode {
246: begin();
247: ConstPool cp = codeAttr.getConstPool();
248: String this ClassName = codeAttr.getDeclaringClass();
249: int nested = 0;
250: while (hasNext()) {
251: int index = next();
252: int c = byteAt(index);
253: if (c == NEW)
254: ++nested;
255: else if (c == INVOKESPECIAL) {
256: int mref = ByteArray.readU16bit(bytecode, index + 1);
257: if (cp.getMethodrefName(mref).equals(
258: MethodInfo.nameInit))
259: if (--nested < 0) {
260: if (skipThis < 0)
261: return index;
262:
263: String cname = cp.getMethodrefClassName(mref);
264: if (cname.equals(this ClassName) == (skipThis > 0))
265: return index;
266: else
267: break;
268: }
269: }
270: }
271:
272: begin();
273: return -1;
274: }
275:
276: /**
277: * Inserts the given bytecode sequence
278: * before the next instruction that would be returned by
279: * <code>next()</code> (not before the instruction returned
280: * by tha last call to <code>next()</code>).
281: * Branch offsets and the exception table are also updated.
282: *
283: * <p>If the next instruction is at the beginning of a block statement,
284: * then the bytecode is inserted within that block.
285: *
286: * <p>An extra gap may be inserted at the end of the inserted
287: * bytecode sequence for adjusting alignment if the code attribute
288: * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
289: *
290: * @param code inserted bytecode sequence.
291: * @return the index indicating the first byte of the
292: * inserted byte sequence.
293: */
294: public int insert(byte[] code) throws BadBytecode {
295: int pos = currentPos;
296: insert0(currentPos, code, false);
297: return pos;
298: }
299:
300: /**
301: * Inserts the given bytecode sequence
302: * before the instruction at the given index <code>pos</code>.
303: * Branch offsets and the exception table are also updated.
304: *
305: * <p>If the instruction at the given index is at the beginning
306: * of a block statement,
307: * then the bytecode is inserted within that block.
308: *
309: * <p>An extra gap may be inserted at the end of the inserted
310: * bytecode sequence for adjusting alignment if the code attribute
311: * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
312: *
313: * @param pos the index at which a byte sequence is inserted.
314: * @param code inserted bytecode sequence.
315: */
316: public void insert(int pos, byte[] code) throws BadBytecode {
317: insert0(pos, code, false);
318: }
319:
320: /**
321: * Inserts the given bytecode sequence exclusively
322: * before the next instruction that would be returned by
323: * <code>next()</code> (not before the instruction returned
324: * by tha last call to <code>next()</code>).
325: * Branch offsets and the exception table are also updated.
326: *
327: * <p>If the next instruction is at the beginning of a block statement,
328: * then the bytecode is excluded from that block.
329: *
330: * <p>An extra gap may be inserted at the end of the inserted
331: * bytecode sequence for adjusting alignment if the code attribute
332: * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
333: *
334: * @param code inserted bytecode sequence.
335: * @return the index indicating the first byte of the
336: * inserted byte sequence.
337: */
338: public int insertEx(byte[] code) throws BadBytecode {
339: int pos = currentPos;
340: insert0(currentPos, code, true);
341: return pos;
342: }
343:
344: /**
345: * Inserts the given bytecode sequence exclusively
346: * before the instruction at the given index <code>pos</code>.
347: * Branch offsets and the exception table are also updated.
348: *
349: * <p>If the instruction at the given index is at the beginning
350: * of a block statement,
351: * then the bytecode is excluded from that block.
352: *
353: * <p>An extra gap may be inserted at the end of the inserted
354: * bytecode sequence for adjusting alignment if the code attribute
355: * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
356: *
357: * @param pos the index at which a byte sequence is inserted.
358: * @param code inserted bytecode sequence.
359: */
360: public void insertEx(int pos, byte[] code) throws BadBytecode {
361: insert0(pos, code, true);
362: }
363:
364: private void insert0(int pos, byte[] code, boolean exclusive)
365: throws BadBytecode {
366: int len = code.length;
367: if (len <= 0)
368: return;
369:
370: insertGapCore(pos, len, exclusive); // currentPos will change.
371: for (int j = 0; j < len; ++j)
372: bytecode[pos++] = code[j];
373: }
374:
375: /**
376: * Inserts a gap
377: * before the next instruction that would be returned by
378: * <code>next()</code> (not before the instruction returned
379: * by tha last call to <code>next()</code>).
380: * Branch offsets and the exception table are also updated.
381: * The inserted gap is filled with NOP. The gap length may be
382: * extended to a multiple of 4.
383: *
384: * <p>If the next instruction is at the beginning of a block statement,
385: * then the gap is inserted within that block.
386: *
387: * @param length gap length
388: * @return the index indicating the first byte of the inserted gap.
389: */
390: public int insertGap(int length) throws BadBytecode {
391: int pos = currentPos;
392: insertGapCore(currentPos, length, false);
393: return pos;
394: }
395:
396: /**
397: * Inserts a gap in front of the instruction at the given
398: * index <code>pos</code>.
399: * Branch offsets and the exception table are also updated.
400: * The inserted gap is filled with NOP. The gap length may be
401: * extended to a multiple of 4.
402: *
403: * <p>If the instruction at the given index is at the beginning
404: * of a block statement,
405: * then the gap is inserted within that block.
406: *
407: * @param pos the index at which a gap is inserted.
408: * @param length gap length.
409: * @return the length of the inserted gap.
410: * It might be bigger than <code>length</code>.
411: */
412: public int insertGap(int pos, int length) throws BadBytecode {
413: return insertGapCore(pos, length, false);
414: }
415:
416: /**
417: * Inserts an exclusive gap
418: * before the next instruction that would be returned by
419: * <code>next()</code> (not before the instruction returned
420: * by tha last call to <code>next()</code>).
421: * Branch offsets and the exception table are also updated.
422: * The inserted gap is filled with NOP. The gap length may be
423: * extended to a multiple of 4.
424: *
425: * <p>If the next instruction is at the beginning of a block statement,
426: * then the gap is excluded from that block.
427: *
428: * @param length gap length
429: * @return the index indicating the first byte of the inserted gap.
430: */
431: public int insertExGap(int length) throws BadBytecode {
432: int pos = currentPos;
433: insertGapCore(currentPos, length, true);
434: return pos;
435: }
436:
437: /**
438: * Inserts an exclusive gap in front of the instruction at the given
439: * index <code>pos</code>.
440: * Branch offsets and the exception table are also updated.
441: * The inserted gap is filled with NOP. The gap length may be
442: * extended to a multiple of 4.
443: *
444: * <p>If the instruction at the given index is at the beginning
445: * of a block statement,
446: * then the gap is excluded from that block.
447: *
448: * @param pos the index at which a gap is inserted.
449: * @param length gap length.
450: * @return the length of the inserted gap.
451: * It might be bigger than <code>length</code>.
452: */
453: public int insertExGap(int pos, int length) throws BadBytecode {
454: return insertGapCore(pos, length, true);
455: }
456:
457: /**
458: * @return the length of the really inserted gap.
459: */
460: private int insertGapCore(int pos, int length, boolean exclusive)
461: throws BadBytecode {
462: if (length <= 0)
463: return 0;
464:
465: int cur = currentPos;
466: byte[] c = insertGap(bytecode, pos, length, exclusive, get()
467: .getExceptionTable(), codeAttr);
468: int length2 = c.length - bytecode.length;
469: if (cur >= pos)
470: currentPos = cur + length2;
471:
472: codeAttr.setCode(c);
473: bytecode = c;
474: endPos = getCodeLength();
475: updateCursors(pos, length2);
476: return length2;
477: }
478:
479: /**
480: * Is called when a gap is inserted. The default implementation is empty.
481: * A subclass can override this method so that cursors will be updated.
482: *
483: * @param pos the position where a gap is inserted.
484: * @param length the length of the gap.
485: */
486: protected void updateCursors(int pos, int length) {
487: // empty
488: }
489:
490: /**
491: * Copies and inserts the entries in the given exception table
492: * at the beginning of the exception table in the code attribute
493: * edited by this object.
494: *
495: * @param offset the value added to the code positions included
496: * in the entries.
497: */
498: public void insert(ExceptionTable et, int offset) {
499: codeAttr.getExceptionTable().add(0, et, offset);
500: }
501:
502: /**
503: * Appends the given bytecode sequence at the end.
504: *
505: * @param code the bytecode appended.
506: * @return the position of the first byte of the appended bytecode.
507: */
508: public int append(byte[] code) {
509: int size = getCodeLength();
510: int len = code.length;
511: if (len <= 0)
512: return size;
513:
514: appendGap(len);
515: byte[] dest = bytecode;
516: for (int i = 0; i < len; ++i)
517: dest[i + size] = code[i];
518:
519: return size;
520: }
521:
522: /**
523: * Appends a gap at the end of the bytecode sequence.
524: *
525: * @param gapLength gap length
526: */
527: public void appendGap(int gapLength) {
528: byte[] code = bytecode;
529: int codeLength = code.length;
530: byte[] newcode = new byte[codeLength + gapLength];
531:
532: int i;
533: for (i = 0; i < codeLength; ++i)
534: newcode[i] = code[i];
535:
536: for (i = codeLength; i < codeLength + gapLength; ++i)
537: newcode[i] = NOP;
538:
539: codeAttr.setCode(newcode);
540: bytecode = newcode;
541: endPos = getCodeLength();
542: }
543:
544: /**
545: * Copies and appends the entries in the given exception table
546: * at the end of the exception table in the code attribute
547: * edited by this object.
548: *
549: * @param offset the value added to the code positions included
550: * in the entries.
551: */
552: public void append(ExceptionTable et, int offset) {
553: ExceptionTable table = codeAttr.getExceptionTable();
554: table.add(table.size(), et, offset);
555: }
556:
557: /* opcodeLegth is used for implementing nextOpcode().
558: */
559: private static final int opcodeLength[] = { 1, 1, 1, 1, 1, 1, 1, 1,
560: 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 1, 1,
561: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
562: 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
563: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
564: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
565: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
566: 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
567: 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
568: 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 0, 3,
569: 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, 5, 5 };
570:
571: // 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE
572:
573: /**
574: * Calculates the index of the next opcode.
575: */
576: static int nextOpcode(byte[] code, int index) throws BadBytecode {
577: int opcode;
578: try {
579: opcode = code[index] & 0xff;
580: } catch (IndexOutOfBoundsException e) {
581: throw new BadBytecode("invalid opcode address");
582: }
583:
584: try {
585: int len = opcodeLength[opcode];
586: if (len > 0)
587: return index + len;
588: else if (opcode == WIDE)
589: if (code[index + 1] == (byte) IINC) // WIDE IINC
590: return index + 6;
591: else
592: return index + 4; // WIDE ...
593: else {
594: int index2 = (index & ~3) + 8;
595: if (opcode == LOOKUPSWITCH) {
596: int npairs = ByteArray.read32bit(code, index2);
597: return index2 + npairs * 8 + 4;
598: } else if (opcode == TABLESWITCH) {
599: int low = ByteArray.read32bit(code, index2);
600: int high = ByteArray.read32bit(code, index2 + 4);
601: return index2 + (high - low + 1) * 4 + 8;
602: }
603: // else
604: // throw new BadBytecode(opcode);
605: }
606: } catch (IndexOutOfBoundsException e) {
607: }
608:
609: // opcode is UNUSED or an IndexOutOfBoundsException was thrown.
610: throw new BadBytecode(opcode);
611: }
612:
613: // methods for implementing insertGap().
614:
615: /* If "where" is the beginning of a block statement, then the inserted
616: * gap is also included in the block statement.
617: * "where" must indicate the first byte of an opcode.
618: * The inserted gap is filled with NOP. gapLength may be extended to
619: * a multiple of 4.
620: */
621: static byte[] insertGap(byte[] code, int where, int gapLength,
622: boolean exclusive, ExceptionTable etable, CodeAttribute ca)
623: throws BadBytecode {
624: if (gapLength <= 0)
625: return code;
626:
627: try {
628: return insertGap0(code, where, gapLength, exclusive,
629: etable, ca);
630: } catch (AlignmentException e) {
631: try {
632: return insertGap0(code, where, (gapLength + 3) & ~3,
633: exclusive, etable, ca);
634: } catch (AlignmentException e2) {
635: throw new RuntimeException("fatal error?");
636: }
637: }
638: }
639:
640: private static byte[] insertGap0(byte[] code, int where,
641: int gapLength, boolean exclusive, ExceptionTable etable,
642: CodeAttribute ca) throws BadBytecode, AlignmentException {
643: int codeLength = code.length;
644: byte[] newcode = new byte[codeLength + gapLength];
645: insertGap2(code, where, gapLength, codeLength, newcode,
646: exclusive);
647: etable.shiftPc(where, gapLength, exclusive);
648: LineNumberAttribute na = (LineNumberAttribute) ca
649: .getAttribute(LineNumberAttribute.tag);
650: if (na != null)
651: na.shiftPc(where, gapLength, exclusive);
652:
653: LocalVariableAttribute va = (LocalVariableAttribute) ca
654: .getAttribute(LocalVariableAttribute.tag);
655: if (va != null)
656: va.shiftPc(where, gapLength, exclusive);
657:
658: LocalVariableAttribute vta = (LocalVariableAttribute) ca
659: .getAttribute(LocalVariableAttribute.typeTag);
660: if (vta != null)
661: vta.shiftPc(where, gapLength, exclusive);
662:
663: return newcode;
664: }
665:
666: private static void insertGap2(byte[] code, int where,
667: int gapLength, int endPos, byte[] newcode, boolean exclusive)
668: throws BadBytecode, AlignmentException {
669: int nextPos;
670: int i = 0;
671: int j = 0;
672: for (; i < endPos; i = nextPos) {
673: if (i == where) {
674: int j2 = j + gapLength;
675: while (j < j2)
676: newcode[j++] = NOP;
677: }
678:
679: nextPos = nextOpcode(code, i);
680: int inst = code[i] & 0xff;
681: // if<cond>, if_icmp<cond>, if_acmp<cond>, goto, jsr
682: if ((153 <= inst && inst <= 168) || inst == IFNULL
683: || inst == IFNONNULL) {
684: /* 2bytes *signed* offset */
685: int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff);
686: offset = newOffset(i, offset, where, gapLength,
687: exclusive);
688: newcode[j] = code[i];
689: ByteArray.write16bit(offset, newcode, j + 1);
690: j += 3;
691: } else if (inst == GOTO_W || inst == JSR_W) {
692: /* 4bytes offset */
693: int offset = ByteArray.read32bit(code, i + 1);
694: offset = newOffset(i, offset, where, gapLength,
695: exclusive);
696: newcode[j++] = code[i];
697: ByteArray.write32bit(offset, newcode, j);
698: j += 4;
699: } else if (inst == TABLESWITCH) {
700: if (i != j && (gapLength & 3) != 0)
701: throw new AlignmentException();
702:
703: int i0 = i;
704: int i2 = (i & ~3) + 4; // 0-3 byte padding
705: while (i0 < i2)
706: newcode[j++] = code[i0++];
707:
708: int defaultbyte = newOffset(i, ByteArray.read32bit(
709: code, i2), where, gapLength, exclusive);
710: ByteArray.write32bit(defaultbyte, newcode, j);
711: int lowbyte = ByteArray.read32bit(code, i2 + 4);
712: ByteArray.write32bit(lowbyte, newcode, j + 4);
713: int highbyte = ByteArray.read32bit(code, i2 + 8);
714: ByteArray.write32bit(highbyte, newcode, j + 8);
715: j += 12;
716: i0 = i2 + 12;
717: i2 = i0 + (highbyte - lowbyte + 1) * 4;
718: while (i0 < i2) {
719: int offset = newOffset(i, ByteArray.read32bit(code,
720: i0), where, gapLength, exclusive);
721: ByteArray.write32bit(offset, newcode, j);
722: j += 4;
723: i0 += 4;
724: }
725: } else if (inst == LOOKUPSWITCH) {
726: if (i != j && (gapLength & 3) != 0)
727: throw new AlignmentException();
728:
729: int i0 = i;
730: int i2 = (i & ~3) + 4; // 0-3 byte padding
731: while (i0 < i2)
732: newcode[j++] = code[i0++];
733:
734: int defaultbyte = newOffset(i, ByteArray.read32bit(
735: code, i2), where, gapLength, exclusive);
736: ByteArray.write32bit(defaultbyte, newcode, j);
737: int npairs = ByteArray.read32bit(code, i2 + 4);
738: ByteArray.write32bit(npairs, newcode, j + 4);
739: j += 8;
740: i0 = i2 + 8;
741: i2 = i0 + npairs * 8;
742: while (i0 < i2) {
743: ByteArray.copy32bit(code, i0, newcode, j);
744: int offset = newOffset(i, ByteArray.read32bit(code,
745: i0 + 4), where, gapLength, exclusive);
746: ByteArray.write32bit(offset, newcode, j + 4);
747: j += 8;
748: i0 += 8;
749: }
750: } else
751: while (i < nextPos)
752: newcode[j++] = code[i++];
753: }
754: }
755:
756: private static int newOffset(int i, int offset, int where,
757: int gapLength, boolean exclusive) {
758: int target = i + offset;
759: if (i < where) {
760: if (where < target || (exclusive && where == target))
761: offset += gapLength;
762: } else if (target < where || (!exclusive && where == target))
763: offset -= gapLength;
764:
765: return offset;
766: }
767: }
768:
769: class AlignmentException extends Exception {
770: }
|