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.forms;
018
019import java.util.Arrays;
020
021import org.apache.commons.compress.harmony.unpack200.bytecode.ByteCode;
022import org.apache.commons.compress.harmony.unpack200.bytecode.OperandManager;
023
024public class LookupSwitchForm extends SwitchForm {
025
026    public LookupSwitchForm(final int opcode, final String name) {
027        super(opcode, name);
028    }
029
030    @Override
031    public void setByteCodeOperands(final ByteCode byteCode, final OperandManager operandManager, final int codeLength) {
032        final int caseCount = operandManager.nextCaseCount();
033        final int defaultPc = operandManager.nextLabel();
034        final int[] caseValues = new int[caseCount];
035        Arrays.setAll(caseValues, i -> operandManager.nextCaseValues());
036        final int[] casePcs = new int[caseCount];
037        Arrays.setAll(casePcs, i -> operandManager.nextLabel());
038
039        final int[] labelsArray = new int[caseCount + 1];
040        labelsArray[0] = defaultPc;
041        System.arraycopy(casePcs, 0, labelsArray, 1, caseCount + 1 - 1);
042        byteCode.setByteCodeTargets(labelsArray);
043
044        // All this gets dumped into the rewrite bytes of the
045        // poor bytecode.
046
047        // Unlike most byte codes, the LookupSwitch is a
048        // variable-sized bytecode. Because of this, the
049        // rewrite array has to be defined here individually
050        // for each bytecode, rather than in the ByteCodeForm
051        // class.
052
053        // First, there's the bytecode. Then there are 0-3
054        // bytes of padding so that the first (default)
055        // label is on a 4-byte offset.
056        final int padLength = 3 - codeLength % 4;
057        final int rewriteSize = 1 + padLength + 4 // defaultbytes
058                + 4 // npairs
059                + 4 * caseValues.length + 4 * casePcs.length;
060
061        final int[] newRewrite = new int[rewriteSize];
062        int rewriteIndex = 0;
063
064        // Fill in what we can now
065        // opcode
066        newRewrite[rewriteIndex++] = byteCode.getOpcode();
067
068        // padding
069        for (int index = 0; index < padLength; index++) {
070            newRewrite[rewriteIndex++] = 0;
071        }
072
073        // defaultbyte
074        // This gets overwritten by fixUpByteCodeTargets
075        newRewrite[rewriteIndex++] = -1;
076        newRewrite[rewriteIndex++] = -1;
077        newRewrite[rewriteIndex++] = -1;
078        newRewrite[rewriteIndex++] = -1;
079
080        // npairs
081        final int npairsIndex = rewriteIndex;
082        setRewrite4Bytes(caseValues.length, npairsIndex, newRewrite);
083        rewriteIndex += 4;
084
085        // match-offset pairs
086        // The caseValues aren't overwritten, but the
087        // casePcs will get overwritten by fixUpByteCodeTargets
088        for (final int caseValue : caseValues) {
089            // match
090            setRewrite4Bytes(caseValue, rewriteIndex, newRewrite);
091            rewriteIndex += 4;
092            // offset
093            newRewrite[rewriteIndex++] = -1;
094            newRewrite[rewriteIndex++] = -1;
095            newRewrite[rewriteIndex++] = -1;
096            newRewrite[rewriteIndex++] = -1;
097        }
098        byteCode.setRewrite(newRewrite);
099    }
100}