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