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