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