001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200.bytecode;
020
021import java.io.DataOutputStream;
022import java.io.IOException;
023
024import org.apache.commons.compress.harmony.pack200.Pack200Exception;
025import org.apache.commons.compress.harmony.unpack200.Segment;
026import org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm;
027
028/**
029 * A bytecode class file entry.
030 */
031public class ByteCode extends ClassFileEntry {
032
033    private static ByteCode[] noArgByteCodes = new ByteCode[255];
034
035    public static ByteCode getByteCode(final int opcode) {
036        final int byteOpcode = 0xFF & opcode;
037        if (ByteCodeForm.get(byteOpcode).hasNoOperand()) {
038            if (null == noArgByteCodes[byteOpcode]) {
039                noArgByteCodes[byteOpcode] = new ByteCode(byteOpcode);
040            }
041            return noArgByteCodes[byteOpcode];
042        }
043        return new ByteCode(byteOpcode);
044    }
045
046    private final ByteCodeForm byteCodeForm;
047
048    private ClassFileEntry[] nested;
049    private int[][] nestedPositions;
050    private int[] rewrite;
051
052    private int byteCodeOffset = -1;
053    private int[] byteCodeTargets;
054
055    protected ByteCode(final int opcode) {
056        this(opcode, NONE);
057    }
058
059    protected ByteCode(final int opcode, final ClassFileEntry[] nested) {
060        this.byteCodeForm = ByteCodeForm.get(opcode);
061        this.rewrite = byteCodeForm.getRewriteCopy();
062        this.nested = nested;
063    }
064
065    /**
066     * 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
067     * beforehand because the CodeAttribute needs to be complete before targets can be assigned.)
068     *
069     * @param codeAttribute the code attribute
070     */
071    public void applyByteCodeTargetFixup(final CodeAttribute codeAttribute) {
072        getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute);
073    }
074
075    @Override
076    protected void doWrite(final DataOutputStream dos) throws IOException {
077        for (final int element : rewrite) {
078            dos.writeByte(element);
079        }
080    }
081
082    @Override
083    public boolean equals(final Object obj) {
084        return this == obj;
085    }
086
087    public void extractOperands(final OperandManager operandManager, final Segment segment, final int codeLength) throws Pack200Exception {
088        // Given an OperandTable, figure out which operands
089        // the receiver needs and stuff them in operands.
090        // Later on the operands can be rewritten (But that's
091        // later, not now).
092        getByteCodeForm().setByteCodeOperands(this, operandManager, codeLength);
093    }
094
095    protected ByteCodeForm getByteCodeForm() {
096        return byteCodeForm;
097    }
098
099    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}