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