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