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