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 }