    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}