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