    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}