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.forms;
020
021import org.apache.commons.compress.harmony.unpack200.bytecode.ByteCode;
022import org.apache.commons.compress.harmony.unpack200.bytecode.OperandManager;
023
024/**
025 * This class implements the byte code form for the wide instruction. Unlike other instructions, it can take multiple forms, depending on what is being widened.
026 */
027public class WideForm extends VariableInstructionForm {
028
029    /**
030     * Constructs a new instance with the specified opcode, name, operandType and rewrite.
031     *
032     * @param opcode  index corresponding to the opcode's value.
033     * @param name    String printable name of the opcode.
034     */
035    public WideForm(final int opcode, final String name) {
036        super(opcode, name);
037    }
038
039    /*
040     * (non-Javadoc)
041     *
042     * @see org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm#setByteCodeOperands(org.apache.commons.
043     * compress.harmony.unpack200.bytecode.ByteCode, org.apache.commons.compress.harmony.unpack200.bytecode.OperandTable,
044     * org.apache.commons.compress.harmony.unpack200.SegmentConstantPool)
045     */
046    @Override
047    public void setByteCodeOperands(final ByteCode byteCode, final OperandManager operandManager, final int codeLength) {
048        final int instruction = operandManager.nextWideByteCode();
049        if (instruction == 132) {
050            setByteCodeOperandsFormat2(instruction, byteCode, operandManager, codeLength);
051        } else {
052            setByteCodeOperandsFormat1(instruction, byteCode, operandManager, codeLength);
053        }
054    }
055
056    /**
057     * This method sets the rewrite array for the bytecode using Format 1 of the JVM spec: an opcode and two index bytes. This is used for ?load/?store/ret
058     *
059     * @param instruction    should be 132
060     * @param byteCode       the byte code whose rewrite array should be updated
061     * @param operandManager the source of the operands
062     * @param codeLength     ignored
063     */
064    protected void setByteCodeOperandsFormat1(final int instruction, final ByteCode byteCode, final OperandManager operandManager, final int codeLength) {
065
066        // Even though this code is really similar to the
067        // code for setByteCodeOperandsFormat2, I've left it
068        // distinct here. This is so changing one will
069        // not change the other - if there is a need to change,
070        // there's a good chance that the formats will
071        // differ, so an updater will not have to disentangle
072        // it.
073        final int local = operandManager.nextLocal();
074
075        // Unlike most byte codes, the wide bytecode is a
076        // variable-sized bytecode. Because of this, the
077        // rewrite array has to be defined here individually
078        // for each bytecode, rather than in the ByteCodeForm
079        // class.
080
081        final int[] newRewrite = new int[4];
082        int rewriteIndex = 0;
083
084        // Fill in what we can now
085        // wide opcode
086        newRewrite[rewriteIndex++] = byteCode.getOpcode();
087
088        // "real" instruction that is widened
089        newRewrite[rewriteIndex++] = instruction;
090
091        // Index bytes
092        setRewrite2Bytes(local, rewriteIndex, newRewrite);
093        rewriteIndex += 2;
094
095        byteCode.setRewrite(newRewrite);
096    }
097
098    /**
099     * This method sets the rewrite array for the bytecode using Format 2 of the JVM spec: an opcode, two index bytes, and two constant bytes. This is used for
100     * iinc.
101     *
102     * @param instruction    int should be 132
103     * @param byteCode       ByteCode whose rewrite array should be updated
104     * @param operandManager OperandManager source of the operands
105     * @param codeLength     ignored
106     */
107    protected void setByteCodeOperandsFormat2(final int instruction, final ByteCode byteCode, final OperandManager operandManager, final int codeLength) {
108
109        final int local = operandManager.nextLocal();
110        final int constWord = operandManager.nextShort();
111
112        // Unlike most byte codes, the wide bytecode is a
113        // variable-sized bytecode. Because of this, the
114        // rewrite array has to be defined here individually
115        // for each bytecode, rather than in the ByteCodeForm
116        // class.
117
118        final int[] newRewrite = new int[6];
119        int rewriteIndex = 0;
120
121        // Fill in what we can now
122        // wide opcode
123        newRewrite[rewriteIndex++] = byteCode.getOpcode();
124
125        // "real" instruction that is widened
126        newRewrite[rewriteIndex++] = instruction;
127
128        // Index bytes
129        setRewrite2Bytes(local, rewriteIndex, newRewrite);
130        rewriteIndex += 2;
131
132        // constant bytes
133        setRewrite2Bytes(constWord, rewriteIndex, newRewrite);
134        rewriteIndex += 2; // not strictly necessary, but just in case
135        // something comes along later
136
137        byteCode.setRewrite(newRewrite);
138    }
139}