001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.commons.compress.harmony.unpack200.bytecode;
018
019import java.io.DataOutputStream;
020import java.io.IOException;
021
022import org.apache.commons.compress.harmony.unpack200.Segment;
023import org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm;
024
025/**
026 * A bytecode class file entry.
027 */
028public class ByteCode extends ClassFileEntry {
029
030    private static ByteCode[] noArgByteCodes = new ByteCode[255];
031
032    public static ByteCode getByteCode(final int opcode) {
033        final int byteOpcode = 0xFF & opcode;
034        if (ByteCodeForm.get(byteOpcode).hasNoOperand()) {
035            if (null == noArgByteCodes[byteOpcode]) {
036                noArgByteCodes[byteOpcode] = new ByteCode(byteOpcode);
037            }
038            return noArgByteCodes[byteOpcode];
039        }
040        return new ByteCode(byteOpcode);
041    }
042
043    private final ByteCodeForm byteCodeForm;
044
045    private ClassFileEntry[] nested;
046    private int[][] nestedPositions;
047    private int[] rewrite;
048
049    private int byteCodeOffset = -1;
050    private int[] byteCodeTargets;
051
052    protected ByteCode(final int opcode) {
053        this(opcode, ClassFileEntry.NONE);
054    }
055
056    protected ByteCode(final int opcode, final ClassFileEntry[] nested) {
057        this.byteCodeForm = ByteCodeForm.get(opcode);
058        this.rewrite = byteCodeForm.getRewriteCopy();
059        this.nested = nested;
060    }
061
062    /**
063     * Some ByteCodes (in particular, those with labels need to be fixed up after all the bytecodes in the CodeAttribute
064     * have been added. (This can't be done beforehand because the CodeAttribute needs to be complete before targets can
065     * be assigned.)
066     *
067     * @param codeAttribute the code attribute
068     */
069    public void applyByteCodeTargetFixup(final CodeAttribute codeAttribute) {
070        getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute);
071    }
072
073    @Override
074    protected void doWrite(final DataOutputStream dos) throws IOException {
075        for (final int element : rewrite) {
076            dos.writeByte(element);
077        }
078    }
079
080    @Override
081    public boolean equals(final Object obj) {
082        return this == obj;
083    }
084
085    public void extractOperands(final OperandManager operandManager, final Segment segment, final int codeLength) {
086        // Given an OperandTable, figure out which operands
087        // the receiver needs and stuff them in operands.
088        // Later on the operands can be rewritten (But that's
089        // later, not now).
090        final ByteCodeForm currentByteCodeForm = getByteCodeForm();
091        currentByteCodeForm.setByteCodeOperands(this, operandManager, codeLength);
092    }
093
094    protected ByteCodeForm getByteCodeForm() {
095        return byteCodeForm;
096    }
097
098    public int getByteCodeIndex() {
099        return byteCodeOffset;
100    }
101
102    public int[] getByteCodeTargets() {
103        return byteCodeTargets;
104    }
105
106    public int getLength() {
107        return rewrite.length;
108    }
109
110    public String getName() {
111        return getByteCodeForm().getName();
112    }
113
114    @Override
115    public ClassFileEntry[] getNestedClassFileEntries() {
116        return nested;
117    }
118
119    public int[] getNestedPosition(final int index) {
120        return getNestedPositions()[index];
121    }
122
123    public int[][] getNestedPositions() {
124        return nestedPositions;
125    }
126
127    public int getOpcode() {
128        return getByteCodeForm().getOpcode();
129    }
130
131    /**
132     * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to
133     * update the array. This method permits their associated bytecode formst to query their rewrite array.
134     *
135     * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm
136     * 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);
151     * otherwise, it will answer false.
152     *
153     * @return boolean true if multibytecode, false otherwise
154     */
155    public boolean hasMultipleByteCodes() {
156        return getByteCodeForm().hasMultipleByteCodes();
157    }
158
159    public boolean nestedMustStartClassPool() {
160        return byteCodeForm.nestedMustStartClassPool();
161    }
162
163    /*
164     * (non-Javadoc)
165     *
166     * @see
167     * org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#resolve(org.apache.commons.compress.harmony
168     * .unpack200.bytecode.ClassConstantPool)
169     */
170    @Override
171    protected void resolve(final ClassConstantPool pool) {
172        super.resolve(pool);
173        if (nested.length > 0) {
174            // Update the bytecode rewrite array so that it points
175            // to the elements of the nested array.
176            for (int index = 0; index < nested.length; index++) {
177                final int argLength = getNestedPosition(index)[1];
178                switch (argLength) {
179
180                case 1:
181                    setOperandByte(pool.indexOf(nested[index]), getNestedPosition(index)[0]);
182                    break;
183
184                case 2:
185                    setOperand2Bytes(pool.indexOf(nested[index]), getNestedPosition(index)[0]);
186                    break;
187
188                default:
189                    throw new Error("Unhandled resolve " + this);
190                }
191            }
192        }
193    }
194
195    /**
196     * ByteCodes may need to know their position in the code array (in particular, label byte codes need to know where
197     * they are in order to calculate their targets). This method lets the CodeAttribute specify where the byte code is.
198     *
199     * Since there are no aload0+label instructions, this method doesn't worry about multioperation bytecodes.
200     *
201     * @param byteCodeOffset int position in code array.
202     */
203    public void setByteCodeIndex(final int byteCodeOffset) {
204        this.byteCodeOffset = byteCodeOffset;
205    }
206
207    /**
208     * Some ByteCodes (in particular, those with labels) have to keep track of byteCodeTargets. These are initially
209     * offsets in the CodeAttribute array relative to the byteCodeOffset, but later get fixed up to point to the
210     * absolute position in the CodeAttribute array. This method sets the targets.
211     *
212     * @param byteCodeTargets int index in array
213     */
214    public void setByteCodeTargets(final int[] byteCodeTargets) {
215        this.byteCodeTargets = byteCodeTargets;
216    }
217
218    public void setNested(final ClassFileEntry[] nested) {
219        this.nested = nested;
220    }
221
222    /**
223     * nestedPositions is an array of arrays of ints. Each subarray specifies a position of a nested element (from the
224     * nested[] array) and the length of that element.
225     *
226     * For instance, one might have a nested of: {CPClass java/lang/Foo, CPFloat 3.14} The nestedPositions would then
227     * be: {{0,2},{2,2}} In other words, when the bytecode is resolved, the CPClass will be resolved to an int and
228     * inserted at position 0 and 1 of the rewrite arguments (the first occurrences of -1). The CPFloat will be resolved
229     * to an int position and inserted at positions 2 and 3 of the rewrite arguments.
230     *
231     * @param nestedPositions Each subarray specifies a position of a nested element (from the nested[] array) and the
232     *        length of that element.
233     */
234    public void setNestedPositions(final int[][] nestedPositions) {
235        this.nestedPositions = nestedPositions;
236    }
237
238    /**
239     * Given an int operand, set the rewrite bytes for that position and the one immediately following it to a
240     * high-byte, low-byte encoding of the operand.
241     *
242     * @param operand int to set the rewrite bytes to
243     * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1}
244     *        position 0 is the first -1, position 1 is the second -1, etc.
245     */
246    public void setOperand2Bytes(final int operand, final int position) {
247        final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
248        final int byteCodeFormLength = getByteCodeForm().getRewrite().length;
249        if (firstOperandIndex < 1) {
250            // No operand rewriting permitted for this bytecode
251            throw new Error("Trying to rewrite " + this + " that has no rewrite");
252        }
253
254        if (firstOperandIndex + position + 1 > byteCodeFormLength) {
255            throw new Error("Trying to rewrite " + this + " with an int at position " + position
256                + " but this won't fit in the rewrite array");
257        }
258
259        rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8;
260        rewrite[firstOperandIndex + position + 1] = operand & 0xFF;
261    }
262
263    /**
264     * Given an int operand, treat it as a byte and set the rewrite byte for that position to that value. Mask of
265     * anything beyond 0xFF.
266     *
267     * @param operand int to set the rewrite byte to (unsigned)
268     * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1}
269     *        position 0 is the first -1, position 1 is the second -1, etc.
270     */
271    public void setOperandByte(final int operand, final int position) {
272        final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
273        final int byteCodeFormLength = getByteCodeForm().operandLength();
274        if (firstOperandIndex < 1) {
275            // No operand rewriting permitted for this bytecode
276            throw new Error("Trying to rewrite " + this + " that has no rewrite");
277        }
278
279        if (firstOperandIndex + position > byteCodeFormLength) {
280            throw new Error("Trying to rewrite " + this + " with an byte at position " + position
281                + " but this won't fit in the rewrite array");
282        }
283
284        rewrite[firstOperandIndex + position] = operand & 0xFF;
285    }
286
287    /**
288     * Given an array of ints which correspond to bytes in the operand of the bytecode, set the rewrite bytes of the
289     * operand to be the appropriate values. All values in operands[] will be masked with 0xFF so they fit into a byte.
290     *
291     * @param operands int[] rewrite operand bytes
292     */
293    public void setOperandBytes(final int[] operands) {
294        final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
295        final int byteCodeFormLength = getByteCodeForm().operandLength();
296        if (firstOperandIndex < 1) {
297            // No operand rewriting permitted for this bytecode
298            throw new Error("Trying to rewrite " + this + " that has no rewrite");
299        }
300
301        if (byteCodeFormLength != operands.length) {
302            throw new Error("Trying to rewrite " + this + " with " + operands.length + " but bytecode has length "
303                + byteCodeForm.operandLength());
304        }
305
306        for (int index = 0; index < byteCodeFormLength; index++) {
307            rewrite[index + firstOperandIndex] = operands[index] & 0xFF;
308        }
309    }
310
311    /**
312     * This is just like setOperandInt, but takes special care when the operand is less than 0 to make sure it's written
313     * correctly.
314     *
315     * @param operand int to set the rewrite bytes to
316     * @param position int position of the operands in the rewrite bytes
317     */
318    public void setOperandSigned2Bytes(final int operand, final int position) {
319        if (operand >= 0) {
320            setOperand2Bytes(operand, position);
321        } else {
322            final int twosComplementOperand = 0x10000 + operand;
323            setOperand2Bytes(twosComplementOperand, position);
324        }
325    }
326
327    /**
328     * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to
329     * update the array. This method permits that.
330     *
331     * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm
332     * instead to specify those rewrites.
333     *
334     * @param rewrite Some bytecodes.
335     */
336    public void setRewrite(final int[] rewrite) {
337        this.rewrite = rewrite;
338    }
339
340    @Override
341    public String toString() {
342        return getByteCodeForm().getName();
343    }
344}