View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.harmony.unpack200.bytecode;
18  
19  import java.io.DataOutputStream;
20  import java.io.IOException;
21  
22  import org.apache.commons.compress.harmony.unpack200.Segment;
23  import org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm;
24  
25  /**
26   * A bytecode class file entry.
27   */
28  public class ByteCode extends ClassFileEntry {
29  
30      private static ByteCode[] noArgByteCodes = new ByteCode[255];
31  
32      public static ByteCode getByteCode(final int opcode) {
33          final int byteOpcode = 0xFF & opcode;
34          if (ByteCodeForm.get(byteOpcode).hasNoOperand()) {
35              if (null == noArgByteCodes[byteOpcode]) {
36                  noArgByteCodes[byteOpcode] = new ByteCode(byteOpcode);
37              }
38              return noArgByteCodes[byteOpcode];
39          }
40          return new ByteCode(byteOpcode);
41      }
42  
43      private final ByteCodeForm byteCodeForm;
44  
45      private ClassFileEntry[] nested;
46      private int[][] nestedPositions;
47      private int[] rewrite;
48  
49      private int byteCodeOffset = -1;
50      private int[] byteCodeTargets;
51  
52      protected ByteCode(final int opcode) {
53          this(opcode, ClassFileEntry.NONE);
54      }
55  
56      protected ByteCode(final int opcode, final ClassFileEntry[] nested) {
57          this.byteCodeForm = ByteCodeForm.get(opcode);
58          this.rewrite = byteCodeForm.getRewriteCopy();
59          this.nested = nested;
60      }
61  
62      /**
63       * Some ByteCodes (in particular, those with labels need to be fixed up after all the bytecodes in the CodeAttribute have been added. (This can't be done
64       * beforehand because the CodeAttribute needs to be complete before targets can be assigned.)
65       *
66       * @param codeAttribute the code attribute
67       */
68      public void applyByteCodeTargetFixup(final CodeAttribute codeAttribute) {
69          getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute);
70      }
71  
72      @Override
73      protected void doWrite(final DataOutputStream dos) throws IOException {
74          for (final int element : rewrite) {
75              dos.writeByte(element);
76          }
77      }
78  
79      @Override
80      public boolean equals(final Object obj) {
81          return this == obj;
82      }
83  
84      public void extractOperands(final OperandManager operandManager, final Segment segment, final int codeLength) {
85          // Given an OperandTable, figure out which operands
86          // the receiver needs and stuff them in operands.
87          // Later on the operands can be rewritten (But that's
88          // later, not now).
89          final ByteCodeForm currentByteCodeForm = getByteCodeForm();
90          currentByteCodeForm.setByteCodeOperands(this, operandManager, codeLength);
91      }
92  
93      protected ByteCodeForm getByteCodeForm() {
94          return byteCodeForm;
95      }
96  
97      public int getByteCodeIndex() {
98          return byteCodeOffset;
99      }
100 
101     public int[] getByteCodeTargets() {
102         return byteCodeTargets;
103     }
104 
105     public int getLength() {
106         return rewrite.length;
107     }
108 
109     public String getName() {
110         return getByteCodeForm().getName();
111     }
112 
113     @Override
114     public ClassFileEntry[] getNestedClassFileEntries() {
115         return nested;
116     }
117 
118     public int[] getNestedPosition(final int index) {
119         return getNestedPositions()[index];
120     }
121 
122     public int[][] getNestedPositions() {
123         return nestedPositions;
124     }
125 
126     public int getOpcode() {
127         return getByteCodeForm().getOpcode();
128     }
129 
130     /**
131      * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to update the array. This method permits their
132      * associated bytecode formst to query their rewrite array.
133      *
134      * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites.
135      *
136      * @return Some bytecodes.
137      */
138     public int[] getRewrite() {
139         return rewrite;
140     }
141 
142     @Override
143     public int hashCode() {
144         return objectHashCode();
145     }
146 
147     /**
148      * This method will answer true if the receiver is a multi-bytecode instruction (such as aload0_putfield_super); otherwise, it will answer false.
149      *
150      * @return boolean true if multibytecode, false otherwise
151      */
152     public boolean hasMultipleByteCodes() {
153         return getByteCodeForm().hasMultipleByteCodes();
154     }
155 
156     public boolean nestedMustStartClassPool() {
157         return byteCodeForm.nestedMustStartClassPool();
158     }
159 
160     /*
161      * (non-Javadoc)
162      *
163      * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#resolve(org.apache.commons.compress.harmony
164      * .unpack200.bytecode.ClassConstantPool)
165      */
166     @Override
167     protected void resolve(final ClassConstantPool pool) {
168         super.resolve(pool);
169         if (nested.length > 0) {
170             // Update the bytecode rewrite array so that it points
171             // to the elements of the nested array.
172             for (int index = 0; index < nested.length; index++) {
173                 final int argLength = getNestedPosition(index)[1];
174                 switch (argLength) {
175 
176                 case 1:
177                     setOperandByte(pool.indexOf(nested[index]), getNestedPosition(index)[0]);
178                     break;
179 
180                 case 2:
181                     setOperand2Bytes(pool.indexOf(nested[index]), getNestedPosition(index)[0]);
182                     break;
183 
184                 default:
185                     throw new Error("Unhandled resolve " + this);
186                 }
187             }
188         }
189     }
190 
191     /**
192      * ByteCodes may need to know their position in the code array (in particular, label byte codes need to know where they are in order to calculate their
193      * targets). This method lets the CodeAttribute specify where the byte code is.
194      *
195      * Since there are no aload0+label instructions, this method doesn't worry about multioperation bytecodes.
196      *
197      * @param byteCodeOffset int position in code array.
198      */
199     public void setByteCodeIndex(final int byteCodeOffset) {
200         this.byteCodeOffset = byteCodeOffset;
201     }
202 
203     /**
204      * Some ByteCodes (in particular, those with labels) have to keep track of byteCodeTargets. These are initially offsets in the CodeAttribute array relative
205      * to the byteCodeOffset, but later get fixed up to point to the absolute position in the CodeAttribute array. This method sets the targets.
206      *
207      * @param byteCodeTargets int index in array
208      */
209     public void setByteCodeTargets(final int[] byteCodeTargets) {
210         this.byteCodeTargets = byteCodeTargets;
211     }
212 
213     public void setNested(final ClassFileEntry[] nested) {
214         this.nested = nested;
215     }
216 
217     /**
218      * nestedPositions is an array of arrays of ints. Each subarray specifies a position of a nested element (from the nested[] array) and the length of that
219      * element.
220      *
221      * For instance, one might have a nested of: {CPClass java/lang/Foo, CPFloat 3.14} The nestedPositions would then be: {{0,2},{2,2}} In other words, when the
222      * bytecode is resolved, the CPClass will be resolved to an int and inserted at position 0 and 1 of the rewrite arguments (the first occurrences of -1). The
223      * CPFloat will be resolved to an int position and inserted at positions 2 and 3 of the rewrite arguments.
224      *
225      * @param nestedPositions Each subarray specifies a position of a nested element (from the nested[] array) and the length of that element.
226      */
227     public void setNestedPositions(final int[][] nestedPositions) {
228         this.nestedPositions = nestedPositions;
229     }
230 
231     /**
232      * Given an int operand, set the rewrite bytes for that position and the one immediately following it to a high-byte, low-byte encoding of the operand.
233      *
234      * @param operand  int to set the rewrite bytes to
235      * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1} position 0 is the first -1, position 1 is the
236      *                 second -1, etc.
237      */
238     public void setOperand2Bytes(final int operand, final int position) {
239         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
240         final int byteCodeFormLength = getByteCodeForm().getRewrite().length;
241         if (firstOperandIndex < 1) {
242             // No operand rewriting permitted for this bytecode
243             throw new Error("Trying to rewrite " + this + " that has no rewrite");
244         }
245 
246         if (firstOperandIndex + position + 1 > byteCodeFormLength) {
247             throw new Error("Trying to rewrite " + this + " with an int at position " + position + " but this won't fit in the rewrite array");
248         }
249 
250         rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8;
251         rewrite[firstOperandIndex + position + 1] = operand & 0xFF;
252     }
253 
254     /**
255      * Given an int operand, treat it as a byte and set the rewrite byte for that position to that value. Mask of anything beyond 0xFF.
256      *
257      * @param operand  int to set the rewrite byte to (unsigned)
258      * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1} position 0 is the first -1, position 1 is the
259      *                 second -1, etc.
260      */
261     public void setOperandByte(final int operand, final int position) {
262         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
263         final int byteCodeFormLength = getByteCodeForm().operandLength();
264         if (firstOperandIndex < 1) {
265             // No operand rewriting permitted for this bytecode
266             throw new Error("Trying to rewrite " + this + " that has no rewrite");
267         }
268 
269         if (firstOperandIndex + position > byteCodeFormLength) {
270             throw new Error("Trying to rewrite " + this + " with an byte at position " + position + " but this won't fit in the rewrite array");
271         }
272 
273         rewrite[firstOperandIndex + position] = operand & 0xFF;
274     }
275 
276     /**
277      * Given an array of ints which correspond to bytes in the operand of the bytecode, set the rewrite bytes of the operand to be the appropriate values. All
278      * values in operands[] will be masked with 0xFF so they fit into a byte.
279      *
280      * @param operands int[] rewrite operand bytes
281      */
282     public void setOperandBytes(final int[] operands) {
283         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
284         final int byteCodeFormLength = getByteCodeForm().operandLength();
285         if (firstOperandIndex < 1) {
286             // No operand rewriting permitted for this bytecode
287             throw new Error("Trying to rewrite " + this + " that has no rewrite");
288         }
289 
290         if (byteCodeFormLength != operands.length) {
291             throw new Error("Trying to rewrite " + this + " with " + operands.length + " but bytecode has length " + byteCodeForm.operandLength());
292         }
293 
294         for (int index = 0; index < byteCodeFormLength; index++) {
295             rewrite[index + firstOperandIndex] = operands[index] & 0xFF;
296         }
297     }
298 
299     /**
300      * This is just like setOperandInt, but takes special care when the operand is less than 0 to make sure it's written correctly.
301      *
302      * @param operand  int to set the rewrite bytes to
303      * @param position int position of the operands in the rewrite bytes
304      */
305     public void setOperandSigned2Bytes(final int operand, final int position) {
306         if (operand >= 0) {
307             setOperand2Bytes(operand, position);
308         } else {
309             final int twosComplementOperand = 0x10000 + operand;
310             setOperand2Bytes(twosComplementOperand, position);
311         }
312     }
313 
314     /**
315      * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to update the array. This method permits that.
316      *
317      * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites.
318      *
319      * @param rewrite Some bytecodes.
320      */
321     public void setRewrite(final int[] rewrite) {
322         this.rewrite = rewrite;
323     }
324 
325     @Override
326     public String toString() {
327         return getByteCodeForm().getName();
328     }
329 }