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