    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;
    021import java.util.ArrayList;
    022import java.util.Arrays;
    023import java.util.List;
    024
    025import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    026
    027/**
    028 * Local variable table
    029 */
    030public class LocalVariableTableAttribute extends BCIRenumberedAttribute {
    031
    032    private static CPUTF8 attributeName;
    033
    034    public static void setAttributeName(final CPUTF8 cpUTF8Value) {
    035        attributeName = cpUTF8Value;
    036    }
    037
    038    private final int localVariableTableLength;
    039    private final int[] startPcs;
    040    private final int[] lengths;
    041    private int[] nameIndexes;
    042    private int[] descriptorIndexes;
    043    private final int[] indexes;
    044    private final CPUTF8[] names;
    045    private final CPUTF8[] descriptors;
    046
    047    private int codeLength;
    048
    049    public LocalVariableTableAttribute(final int localVariableTableLength, final int[] startPcs, final int[] lengths, final CPUTF8[] names,
    050            final CPUTF8[] descriptors, final int[] indexes) {
    051        super(attributeName);
    052        this.localVariableTableLength = localVariableTableLength;
    053        this.startPcs = startPcs;
    054        this.lengths = lengths;
    055        this.names = names;
    056        this.descriptors = descriptors;
    057        this.indexes = indexes;
    058    }
    059
    060    @Override
    061    protected int getLength() {
    062        return 2 + 10 * localVariableTableLength;
    063    }
    064
    065    @Override
    066    protected ClassFileEntry[] getNestedClassFileEntries() {
    067        final List<CPUTF8> nestedEntries = new ArrayList<>();
    068        nestedEntries.add(getAttributeName());
    069        for (int i = 0; i < localVariableTableLength; i++) {
    070            nestedEntries.add(names[i]);
    071            nestedEntries.add(descriptors[i]);
    072        }
    073        return nestedEntries.toArray(ClassFileEntry.NONE);
    074    }
    075
    076    @Override
    077    protected int[] getStartPCs() {
    078        return startPcs;
    079    }
    080
    081    /*
    082     * (non-Javadoc)
    083     *
    084     * @see org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute#renumber(java.util.List)
    085     */
    086    @Override
    087    public void renumber(final List<Integer> byteCodeOffsets) throws Pack200Exception {
    088        // Remember the unrenumbered startPcs, since that's used later
    089        // to calculate end position.
    090        final int[] unrenumberedStartPcs = Arrays.copyOf(startPcs, startPcs.length);
    091
    092        // Next renumber startPcs in place
    093        super.renumber(byteCodeOffsets);
    094
    095        // lengths are BRANCH5 encoded, not BCI-encoded.
    096        // In other words:
    097        // startPc is BCI5 startPc
    098        // endPc is byteCodeOffset[(index of startPc in byteCodeOffset) +
    099        // (encoded length)]
    100        // real length = endPc - startPc
    101        // special case if endPc is beyond end of bytecode array
    102
    103        final int maxSize = codeLength;
    104
    105        // Iterate through the lengths and update each in turn.
    106        // This is done in place in the lengths array.
    107        for (int index = 0; index < lengths.length; index++) {
    108            final int startPc = startPcs[index];
    109            int revisedLength = -1;
    110            final int encodedLength = lengths[index];
    111
    112            // First get the index of the startPc in the byteCodeOffsets
    113            final int indexOfStartPC = unrenumberedStartPcs[index];
    114            // Given the index of the startPc, we can now add
    115            // the encodedLength to it to get the stop index.
    116            final int stopIndex = indexOfStartPC + encodedLength;
    117            if (stopIndex < 0) {
    118                throw new Pack200Exception("Error renumbering bytecode indexes");
    119            }
    120            // Length can either be an index into the byte code offsets, or one
    121            // beyond the
    122            // end of the byte code offsets. Need to determine which this is.
    123            if (stopIndex == byteCodeOffsets.size()) {
    124                // Pointing to one past the end of the byte code array
    125                revisedLength = maxSize - startPc;
    126            } else {
    127                // We're indexed into the byte code array
    128                final int stopValue = byteCodeOffsets.get(stopIndex).intValue();
    129                revisedLength = stopValue - startPc;
    130            }
    131            lengths[index] = revisedLength;
    132        }
    133    }
    134
    135    @Override
    136    protected void resolve(final ClassConstantPool pool) {
    137        super.resolve(pool);
    138        nameIndexes = new int[localVariableTableLength];
    139        descriptorIndexes = new int[localVariableTableLength];
    140        for (int i = 0; i < localVariableTableLength; i++) {
    141            names[i].resolve(pool);
    142            descriptors[i].resolve(pool);
    143            nameIndexes[i] = pool.indexOf(names[i]);
    144            descriptorIndexes[i] = pool.indexOf(descriptors[i]);
    145        }
    146    }
    147
    148    public void setCodeLength(final int length) {
    149        codeLength = length;
    150    }
    151
    152    @Override
    153    public String toString() {
    154        return "LocalVariableTable: " + +localVariableTableLength + " variables";
    155    }
    156
    157    @Override
    158    protected void writeBody(final DataOutputStream dos) throws IOException {
    159        dos.writeShort(localVariableTableLength);
    160        for (int i = 0; i < localVariableTableLength; i++) {
    161            dos.writeShort(startPcs[i]);
    162            dos.writeShort(lengths[i]);
    163            dos.writeShort(nameIndexes[i]);
    164            dos.writeShort(descriptorIndexes[i]);
    165            dos.writeShort(indexes[i]);
    166        }
    167    }
    168}