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