/Users/lyon/j4p/src/javassist/bytecode/CodeIterator.java

1    /* 
2     * Javassist, a Java-bytecode translator toolkit. 
3     * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. 
4     * 
5     * The contents of this file are subject to the Mozilla Public License Version 
6     * 1.1 (the "License"); you may not use this file except in compliance with 
7     * the License.  Alternatively, the contents of this file may be used under 
8     * the terms of the GNU Lesser General Public License Version 2.1 or later. 
9     * 
10    * Software distributed under the License is distributed on an "AS IS" basis, 
11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
12    * for the specific language governing rights and limitations under the 
13    * License. 
14    */ 
15    
16   package javassist.bytecode; 
17    
18   /** 
19    * An iterator for editing a code attribute. 
20    * 
21    * <p>This iterator does not provide <code>remove()</code>. 
22    * If a piece of code in a <code>Code_attribute</code> is unnecessary, 
23    * it should be overwritten with <code>NOP</code>. 
24    * 
25    * @see CodeAttribute#iterator() 
26    */ 
27   public class CodeIterator implements Opcode { 
28       protected CodeAttribute codeAttr; 
29       protected byte[] bytecode; 
30       protected int endPos; 
31       protected int currentPos; 
32    
33       CodeIterator(CodeAttribute ca) { 
34           codeAttr = ca; 
35           bytecode = ca.getCode(); 
36           begin(); 
37       } 
38    
39       /** 
40        * Moves to the first instruction. 
41        */ 
42       public void begin() { 
43           currentPos = 0; 
44           endPos = getCodeLength(); 
45       } 
46    
47       /** 
48        * Moves to the given index. 
49        * 
50        * <p>The index of the next instruction is set to the given index. 
51        * The successive call to <code>next()</code> 
52        * returns the index that has been given to <code>move()</code>. 
53        * 
54        * <p>Note that the index is into the byte array returned by 
55        * <code>get().getCode()</code>. 
56        * 
57        * @see CodeAttribute#getCode() 
58        */ 
59       public void move(int index) { 
60           currentPos = index; 
61       } 
62    
63       /** 
64        * Returns a Code attribute read with this iterator. 
65        */ 
66       public CodeAttribute get() { 
67           return codeAttr; 
68       } 
69    
70       /** 
71        * Returns <code>code_length</code> of <code>Code_attribute</code>. 
72        */ 
73       public int getCodeLength() { 
74           return bytecode.length; 
75       } 
76    
77       /** 
78        * Returns the unsigned 8bit value at the given index. 
79        */ 
80       public int byteAt(int index) { 
81           return bytecode[index] & 0xff; 
82       } 
83    
84       /** 
85        * Writes an 8bit value at the given index. 
86        */ 
87       public void writeByte(int value, int index) { 
88           bytecode[index] = (byte) value; 
89       } 
90    
91       /** 
92        * Returns the unsigned 16bit value at the given index. 
93        */ 
94       public int u16bitAt(int index) { 
95           return ByteArray.readU16bit(bytecode, index); 
96       } 
97    
98       /** 
99        * Returns the signed 16bit value at the given index. 
100       */ 
101      public int s16bitAt(int index) { 
102          return ByteArray.readS16bit(bytecode, index); 
103      } 
104   
105      /** 
106       * Writes a 16 bit integer at the index. 
107       */ 
108      public void write16bit(int value, int index) { 
109          ByteArray.write16bit(value, bytecode, index); 
110      } 
111   
112      /** 
113       * Returns the signed 32bit value at the given index. 
114       */ 
115      public int s32bitAt(int index) { 
116          return ByteArray.read32bit(bytecode, index); 
117      } 
118   
119      /** 
120       * Writes a 32bit integer at the index. 
121       */ 
122      public void write32bit(int value, int index) { 
123          ByteArray.write32bit(value, bytecode, index); 
124      } 
125   
126      /** 
127       * Writes a byte array at the index. 
128       * 
129       * @param code  may be a zero-length array. 
130       */ 
131      public void write(byte[] code, int index) { 
132          int len = code.length; 
133          for (int j = 0; j < len; ++j) 
134              bytecode[index++] = code[j]; 
135      } 
136   
137      /** 
138       * Returns true if there is more instructions. 
139       */ 
140      public boolean hasNext() { 
141          return currentPos < endPos; 
142      } 
143   
144      /** 
145       * Returns the index of the next instruction 
146       * (not the operand following the current opcode). 
147       * 
148       * <p>Note that the index is into the byte array returned by 
149       * <code>get().getCode()</code>. 
150       * 
151       * @see CodeAttribute#getCode() 
152       * @see CodeIterator#byteAt(int) 
153       */ 
154      public int next() throws BadBytecode { 
155          int pos = currentPos; 
156          currentPos = nextOpcode(bytecode, pos); 
157          return pos; 
158      } 
159   
160      /** 
161       * Moves to the first instruction following 
162       * constructor invocation <code>super()</code> or <code>this()</code>. 
163       * 
164       * <p>This method skips all the instructions for executing 
165       * <code>super()</code> or <code>this()</code>, which should be 
166       * placed at the beginning of a constructor body. 
167       * 
168       * <p>This method returns the index of INVOKESPECIAL instruction 
169       * executing <code>super()</code> or <code>this()</code>. 
170       * A successive call to <code>next()</code> returns the 
171       * index of the next instruction following that INVOKESPECIAL. 
172       * 
173       * <p>This method works only for a constructor. 
174       * 
175       * @return  the index of the INVOKESPECIAL instruction, or -1 
176       *          if a constructor invocation is not found. 
177       */ 
178      public int skipConstructor() throws BadBytecode { 
179          return skipSuperConstructor0(-1); 
180      } 
181   
182      /** 
183       * Moves to the first instruction following super 
184       * constructor invocation <code>super()</code>. 
185       * 
186       * <p>This method skips all the instructions for executing 
187       * <code>super()</code>, which should be 
188       * placed at the beginning of a constructor body. 
189       * 
190       * <p>This method returns the index of INVOKESPECIAL instruction 
191       * executing <code>super()</code>. 
192       * A successive call to <code>next()</code> returns the 
193       * index of the next instruction following that INVOKESPECIAL. 
194       * 
195       * <p>This method works only for a constructor. 
196       * 
197       * @return  the index of the INVOKESPECIAL instruction, or -1 
198       *          if a super constructor invocation is not found 
199       *          but <code>this()</code> is found. 
200       */ 
201      public int skipSuperConstructor() throws BadBytecode { 
202          return skipSuperConstructor0(0); 
203      } 
204   
205      /** 
206       * Moves to the first instruction following explicit 
207       * constructor invocation <code>this()</code>. 
208       * 
209       * <p>This method skips all the instructions for executing 
210       * <code>this()</code>, which should be 
211       * placed at the beginning of a constructor body. 
212       * 
213       * <p>This method returns the index of INVOKESPECIAL instruction 
214       * executing <code>this()</code>. 
215       * A successive call to <code>next()</code> returns the 
216       * index of the next instruction following that INVOKESPECIAL. 
217       * 
218       * <p>This method works only for a constructor. 
219       * 
220       * @return  the index of the INVOKESPECIAL instruction, or -1 
221       *          if a explicit constructor invocation is not found 
222       *          but <code>super()</code> is found. 
223       */ 
224      public int skipThisConstructor() throws BadBytecode { 
225          return skipSuperConstructor0(1); 
226      } 
227   
228      /* skipSuper        1: this(), 0: super(), -1: both. 
229       */ 
230      private int skipSuperConstructor0(int skipThis) throws BadBytecode { 
231          begin(); 
232          ConstPool cp = codeAttr.getConstPool(); 
233          String thisClassName = codeAttr.getDeclaringClass(); 
234          int nested = 0; 
235          while (hasNext()) { 
236              int index = next(); 
237              int c = byteAt(index); 
238              if (c == NEW) 
239                  ++nested; 
240              else if (c == INVOKESPECIAL) { 
241                  int mref = ByteArray.readU16bit(bytecode, index + 1); 
242                  if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit)) 
243                      if (--nested < 0) { 
244                          if (skipThis < 0) 
245                              return index; 
246   
247                          String cname = cp.getMethodrefClassName(mref); 
248                          if (cname.equals(thisClassName) == (skipThis > 0)) 
249                              return index; 
250                          else 
251                              break; 
252                      } 
253              } 
254          } 
255   
256          begin(); 
257          return -1; 
258      } 
259   
260      /** 
261       * Inserts the given bytecode sequence 
262       * before the next instruction that would be returned by 
263       * <code>next()</code> (not before the instruction returned 
264       * by tha last call to <code>next()</code>). 
265       * Branch offsets and the exception table are also updated. 
266       * 
267       * <p>If the next instruction is at the beginning of a block statement, 
268       * then the bytecode is inserted within that block. 
269       * 
270       * <p>An extra gap may be inserted at the end of the inserted 
271       * bytecode sequence for adjusting alignment if the code attribute 
272       * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>. 
273       * 
274       * @param code      inserted bytecode sequence. 
275       * @return          the index indicating the first byte of the 
276       *                  inserted byte sequence. 
277       */ 
278      public int insert(byte[] code) 
279              throws BadBytecode { 
280          int pos = currentPos; 
281          insert0(currentPos, code, false); 
282          return pos; 
283      } 
284   
285      /** 
286       * Inserts the given bytecode sequence 
287       * before the instruction at the given index <code>pos</code>. 
288       * Branch offsets and the exception table are also updated. 
289       * 
290       * <p>If the instruction at the given index is at the beginning 
291       * of a block statement, 
292       * then the bytecode is inserted within that block. 
293       * 
294       * <p>An extra gap may be inserted at the end of the inserted 
295       * bytecode sequence for adjusting alignment if the code attribute 
296       * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>. 
297       * 
298       * @param pos       the index at which a byte sequence is inserted. 
299       * @param code      inserted bytecode sequence. 
300       */ 
301      public void insert(int pos, byte[] code) throws BadBytecode { 
302          insert0(pos, code, false); 
303      } 
304   
305      /** 
306       * Inserts the given bytecode sequence exclusively 
307       * before the next instruction that would be returned by 
308       * <code>next()</code> (not before the instruction returned 
309       * by tha last call to <code>next()</code>). 
310       * Branch offsets and the exception table are also updated. 
311       * 
312       * <p>If the next instruction is at the beginning of a block statement, 
313       * then the bytecode is excluded from that block. 
314       * 
315       * <p>An extra gap may be inserted at the end of the inserted 
316       * bytecode sequence for adjusting alignment if the code attribute 
317       * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>. 
318       * 
319       * @param code      inserted bytecode sequence. 
320       * @return          the index indicating the first byte of the 
321       *                  inserted byte sequence. 
322       */ 
323      public int insertEx(byte[] code) 
324              throws BadBytecode { 
325          int pos = currentPos; 
326          insert0(currentPos, code, true); 
327          return pos; 
328      } 
329   
330      /** 
331       * Inserts the given bytecode sequence exclusively 
332       * before the instruction at the given index <code>pos</code>. 
333       * Branch offsets and the exception table are also updated. 
334       * 
335       * <p>If the instruction at the given index is at the beginning 
336       * of a block statement, 
337       * then the bytecode is excluded from that block. 
338       * 
339       * <p>An extra gap may be inserted at the end of the inserted 
340       * bytecode sequence for adjusting alignment if the code attribute 
341       * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>. 
342       * 
343       * @param pos       the index at which a byte sequence is inserted. 
344       * @param code      inserted bytecode sequence. 
345       */ 
346      public void insertEx(int pos, byte[] code) throws BadBytecode { 
347          insert0(pos, code, true); 
348      } 
349   
350      private void insert0(int pos, byte[] code, boolean exclusive) 
351              throws BadBytecode { 
352          int len = code.length; 
353          if (len <= 0) 
354              return; 
355   
356          insertGapCore(pos, len, exclusive);     // currentPos will change. 
357          for (int j = 0; j < len; ++j) 
358              bytecode[pos++] = code[j]; 
359      } 
360   
361      /** 
362       * Inserts a gap 
363       * before the next instruction that would be returned by 
364       * <code>next()</code> (not before the instruction returned 
365       * by tha last call to <code>next()</code>). 
366       * Branch offsets and the exception table are also updated. 
367       * The inserted gap is filled with NOP.  The gap length may be 
368       * extended to a multiple of 4. 
369       * 
370       * <p>If the next instruction is at the beginning of a block statement, 
371       * then the gap is inserted within that block. 
372       * 
373       * @param length            gap length 
374       * @return  the index indicating the first byte of the inserted gap. 
375       */ 
376      public int insertGap(int length) throws BadBytecode { 
377          int pos = currentPos; 
378          insertGapCore(currentPos, length, false); 
379          return pos; 
380      } 
381   
382      /** 
383       * Inserts a gap in front of the instruction at the given 
384       * index <code>pos</code>. 
385       * Branch offsets and the exception table are also updated. 
386       * The inserted gap is filled with NOP.  The gap length may be 
387       * extended to a multiple of 4. 
388       * 
389       * <p>If the instruction at the given index is at the beginning 
390       * of a block statement, 
391       * then the gap is inserted within that block. 
392       * 
393       * @param pos               the index at which a gap is inserted. 
394       * @param length            gap length. 
395       * @return the length of the inserted gap. 
396       *          It might be bigger than <code>length</code>. 
397       */ 
398      public int insertGap(int pos, int length) throws BadBytecode { 
399          return insertGapCore(pos, length, false); 
400      } 
401   
402      /** 
403       * Inserts an exclusive gap 
404       * before the next instruction that would be returned by 
405       * <code>next()</code> (not before the instruction returned 
406       * by tha last call to <code>next()</code>). 
407       * Branch offsets and the exception table are also updated. 
408       * The inserted gap is filled with NOP.  The gap length may be 
409       * extended to a multiple of 4. 
410       * 
411       * <p>If the next instruction is at the beginning of a block statement, 
412       * then the gap is excluded from that block. 
413       * 
414       * @param length            gap length 
415       * @return  the index indicating the first byte of the inserted gap. 
416       */ 
417      public int insertExGap(int length) throws BadBytecode { 
418          int pos = currentPos; 
419          insertGapCore(currentPos, length, true); 
420          return pos; 
421      } 
422   
423      /** 
424       * Inserts an exclusive gap in front of the instruction at the given 
425       * index <code>pos</code>. 
426       * Branch offsets and the exception table are also updated. 
427       * The inserted gap is filled with NOP.  The gap length may be 
428       * extended to a multiple of 4. 
429       * 
430       * <p>If the instruction at the given index is at the beginning 
431       * of a block statement, 
432       * then the gap is excluded from that block. 
433       * 
434       * @param pos               the index at which a gap is inserted. 
435       * @param length            gap length. 
436       * @return the length of the inserted gap. 
437       *          It might be bigger than <code>length</code>. 
438       */ 
439      public int insertExGap(int pos, int length) throws BadBytecode { 
440          return insertGapCore(pos, length, true); 
441      } 
442   
443      /** 
444       * @return the length of the really inserted gap. 
445       */ 
446      private int insertGapCore(int pos, int length, boolean exclusive) 
447              throws BadBytecode { 
448          if (length <= 0) 
449              return 0; 
450   
451          int cur = currentPos; 
452          byte[] c = insertGap(bytecode, pos, length, exclusive, 
453                  get().getExceptionTable()); 
454          int length2 = c.length - bytecode.length; 
455          if (cur >= pos) 
456              currentPos = cur + length2; 
457   
458          codeAttr.setCode(c); 
459          bytecode = c; 
460          endPos = getCodeLength(); 
461          return length2; 
462      } 
463   
464      /** 
465       * Copies and inserts the entries in the given exception table 
466       * at the beginning of the exception table in the code attribute 
467       * edited by this object. 
468       * 
469       * @param offset    the value added to the code positions included 
470       *                          in the entries. 
471       */ 
472      public void insert(ExceptionTable et, int offset) { 
473          codeAttr.getExceptionTable().add(0, et, offset); 
474      } 
475   
476      /** 
477       * Appends the given bytecode sequence at the end. 
478       * 
479       * @param code      the bytecode appended. 
480       * @return  the position of the first byte of the appended bytecode. 
481       */ 
482      public int append(byte[] code) { 
483          int size = getCodeLength(); 
484          int len = code.length; 
485          if (len <= 0) 
486              return size; 
487   
488          appendGap(len); 
489          byte[] dest = bytecode; 
490          for (int i = 0; i < len; ++i) 
491              dest[i + size] = code[i]; 
492   
493          return size; 
494      } 
495   
496      /** 
497       * Appends a gap at the end of the bytecode sequence. 
498       * 
499       * @param length            gap length 
500       */ 
501      public void appendGap(int gapLength) { 
502          byte[] code = bytecode; 
503          int codeLength = code.length; 
504          byte[] newcode = new byte[codeLength + gapLength]; 
505   
506          int i; 
507          for (i = 0; i < codeLength; ++i) 
508              newcode[i] = code[i]; 
509   
510          for (i = codeLength; i < codeLength + gapLength; ++i) 
511              newcode[i] = NOP; 
512   
513          codeAttr.setCode(newcode); 
514          bytecode = newcode; 
515          endPos = getCodeLength(); 
516      } 
517   
518      /** 
519       * Copies and appends the entries in the given exception table 
520       * at the end of the exception table in the code attribute 
521       * edited by this object. 
522       * 
523       * @param offset    the value added to the code positions included 
524       *                          in the entries. 
525       */ 
526      public void append(ExceptionTable et, int offset) { 
527          ExceptionTable table = codeAttr.getExceptionTable(); 
528          table.add(table.size(), et, offset); 
529      } 
530   
531      /* opcodeLegth is used for implementing nextOpcode(). 
532       */ 
533      private static final int opcodeLength[] = { 
534          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 
535          3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
536          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 
537          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
538          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
539          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
540          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 
541          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 
542          3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, 
543          3, 3, 3, 3, 3, 5, 0, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, 
544          5, 5 
545      }; 
546      // 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE 
547   
548      /** 
549       * Calculates the index of the next opcode. 
550       */ 
551      static int nextOpcode(byte[] code, int index) 
552              throws BadBytecode { 
553          int opcode; 
554          try { 
555              opcode = code[index] & 0xff; 
556          } catch (IndexOutOfBoundsException e) { 
557              throw new BadBytecode("invalid opcode address"); 
558          } 
559   
560          try { 
561              int len = opcodeLength[opcode]; 
562              if (len > 0) 
563                  return index + len; 
564              else if (opcode == WIDE) 
565                  if (code[index + 1] == (byte) IINC)      // WIDE IINC 
566                      return index + 6; 
567                  else 
568                      return index + 4;           // WIDE ... 
569              else { 
570                  int index2 = (index & ~3) + 8; 
571                  if (opcode == LOOKUPSWITCH) { 
572                      int npairs = ByteArray.read32bit(code, index2); 
573                      return index2 + npairs * 8 + 4; 
574                  } else if (opcode == TABLESWITCH) { 
575                      int low = ByteArray.read32bit(code, index2); 
576                      int high = ByteArray.read32bit(code, index2 + 4); 
577                      return index2 + (high - low + 1) * 4 + 8; 
578                  } 
579                  // else 
580                  //     throw new BadBytecode(opcode); 
581              } 
582          } catch (IndexOutOfBoundsException e) { 
583          } 
584   
585          // opcode is UNUSED or an IndexOutOfBoundsException was thrown. 
586          throw new BadBytecode(opcode); 
587      } 
588   
589      // methods for implementing insertGap(). 
590   
591      /* If "where" is the beginning of a block statement, then the inserted 
592       * gap is also included in the block statement. 
593       * "where" must indicate the first byte of an opcode. 
594       * The inserted gap is filled with NOP.  gapLength may be extended to 
595       * a multiple of 4. 
596       */ 
597      static byte[] insertGap(byte[] code, int where, int gapLength, 
598                              boolean exclusive, ExceptionTable etable) 
599              throws BadBytecode { 
600          if (gapLength <= 0) 
601              return code; 
602   
603          try { 
604              return insertGap0(code, where, gapLength, exclusive, etable); 
605          } catch (AlignmentException e) { 
606              try { 
607                  return insertGap0(code, where, (gapLength + 3) & ~3, 
608                          exclusive, etable); 
609              } catch (AlignmentException e2) { 
610                  throw new RuntimeException("fatal error?"); 
611              } 
612          } 
613      } 
614   
615      private static byte[] insertGap0(byte[] code, int where, int gapLength, 
616                                       boolean exclusive, ExceptionTable etable) 
617              throws BadBytecode, AlignmentException { 
618          int codeLength = code.length; 
619          byte[] newcode = new byte[codeLength + gapLength]; 
620          insertGap2(code, where, gapLength, codeLength, newcode, exclusive); 
621          etable.shiftPc(where, gapLength, exclusive); 
622          return newcode; 
623      } 
624   
625      private static void insertGap2(byte[] code, int where, int gapLength, 
626                                     int endPos, byte[] newcode, boolean exclusive) 
627              throws BadBytecode, AlignmentException { 
628          int nextPos; 
629          int i = 0; 
630          int j = 0; 
631          for (; i < endPos; i = nextPos) { 
632              if (i == where) { 
633                  int j2 = j + gapLength; 
634                  while (j < j2) 
635                      newcode[j++] = NOP; 
636              } 
637   
638              nextPos = nextOpcode(code, i); 
639              int inst = code[i] & 0xff; 
640              // if<cond>, if_icmp<cond>, if_acmp<cond>, goto, jsr 
641              if ((153 <= inst && inst <= 168) 
642                      || inst == IFNULL || inst == IFNONNULL) { 
643                  /* 2bytes *signed* offset */ 
644                  int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); 
645                  offset = newOffset(i, offset, where, gapLength, exclusive); 
646                  newcode[j] = code[i]; 
647                  ByteArray.write16bit(offset, newcode, j + 1); 
648                  j += 3; 
649              } else if (inst == GOTO_W || inst == JSR_W) { 
650                  /* 4bytes offset */ 
651                  int offset = ByteArray.read32bit(code, i + 1); 
652                  offset = newOffset(i, offset, where, gapLength, exclusive); 
653                  newcode[j++] = code[i]; 
654                  ByteArray.write32bit(offset, newcode, j); 
655                  j += 4; 
656              } else if (inst == TABLESWITCH) { 
657                  if (i != j && (gapLength & 3) != 0) 
658                      throw new AlignmentException(); 
659   
660                  int i0 = i; 
661                  int i2 = (i & ~3) + 4;  // 0-3 byte padding 
662                  while (i0 < i2) 
663                      newcode[j++] = code[i0++]; 
664   
665                  int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), 
666                          where, gapLength, exclusive); 
667                  ByteArray.write32bit(defaultbyte, newcode, j); 
668                  int lowbyte = ByteArray.read32bit(code, i2 + 4); 
669                  ByteArray.write32bit(lowbyte, newcode, j + 4); 
670                  int highbyte = ByteArray.read32bit(code, i2 + 8); 
671                  ByteArray.write32bit(highbyte, newcode, j + 8); 
672                  j += 12; 
673                  i0 = i2 + 12; 
674                  i2 = i0 + (highbyte - lowbyte + 1) * 4; 
675                  while (i0 < i2) { 
676                      int offset = newOffset(i, ByteArray.read32bit(code, i0), 
677                              where, gapLength, exclusive); 
678                      ByteArray.write32bit(offset, newcode, j); 
679                      j += 4; 
680                      i0 += 4; 
681                  } 
682              } else if (inst == LOOKUPSWITCH) { 
683                  if (i != j && (gapLength & 3) != 0) 
684                      throw new AlignmentException(); 
685   
686                  int i0 = i; 
687                  int i2 = (i & ~3) + 4;  // 0-3 byte padding 
688                  while (i0 < i2) 
689                      newcode[j++] = code[i0++]; 
690   
691                  int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), 
692                          where, gapLength, exclusive); 
693                  ByteArray.write32bit(defaultbyte, newcode, j); 
694                  int npairs = ByteArray.read32bit(code, i2 + 4); 
695                  ByteArray.write32bit(npairs, newcode, j + 4); 
696                  j += 8; 
697                  i0 = i2 + 8; 
698                  i2 = i0 + npairs * 8; 
699                  while (i0 < i2) { 
700                      ByteArray.copy32bit(code, i0, newcode, j); 
701                      int offset = newOffset(i, 
702                              ByteArray.read32bit(code, i0 + 4), 
703                              where, gapLength, exclusive); 
704                      ByteArray.write32bit(offset, newcode, j + 4); 
705                      j += 8; 
706                      i0 += 8; 
707                  } 
708              } else 
709                  while (i < nextPos) 
710                      newcode[j++] = code[i++]; 
711          } 
712      } 
713   
714      private static int newOffset(int i, int offset, int where, 
715                                   int gapLength, boolean exclusive) { 
716          int target = i + offset; 
717          if (i < where) { 
718              if (where < target || (exclusive && where == target)) 
719                  offset += gapLength; 
720          } else if (target < where || (!exclusive && where == target)) 
721              offset -= gapLength; 
722   
723          return offset; 
724      } 
725  } 
726   
727   
728  class AlignmentException extends Exception { 
729  } 
730