001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200.bytecode;
020
021import java.io.DataOutputStream;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.List;
026
027import org.apache.commons.compress.harmony.pack200.Pack200Exception;
028
029/**
030 * Local variable table
031 */
032public class LocalVariableTableAttribute extends BCIRenumberedAttribute {
033
034    private static CPUTF8 attributeName;
035
036    public static void setAttributeName(final CPUTF8 cpUTF8Value) {
037        attributeName = cpUTF8Value;
038    }
039
040    private final int localVariableTableLength;
041    private final int[] startPcs;
042    private final int[] lengths;
043    private int[] nameIndexes;
044    private int[] descriptorIndexes;
045    private final int[] indexes;
046    private final CPUTF8[] names;
047    private final CPUTF8[] descriptors;
048
049    private int codeLength;
050
051    public LocalVariableTableAttribute(final int localVariableTableLength, final int[] startPcs, final int[] lengths, final CPUTF8[] names,
052            final CPUTF8[] descriptors, final int[] indexes) {
053        super(attributeName);
054        this.localVariableTableLength = localVariableTableLength;
055        this.startPcs = startPcs;
056        this.lengths = lengths;
057        this.names = names;
058        this.descriptors = descriptors;
059        this.indexes = indexes;
060    }
061
062    @Override
063    protected int getLength() {
064        return 2 + 10 * localVariableTableLength;
065    }
066
067    @Override
068    protected ClassFileEntry[] getNestedClassFileEntries() {
069        final List<CPUTF8> nestedEntries = new ArrayList<>();
070        nestedEntries.add(getAttributeName());
071        for (int i = 0; i < localVariableTableLength; i++) {
072            nestedEntries.add(names[i]);
073            nestedEntries.add(descriptors[i]);
074        }
075        return nestedEntries.toArray(NONE);
076    }
077
078    @Override
079    protected int[] getStartPCs() {
080        return startPcs;
081    }
082
083    /*
084     * (non-Javadoc)
085     *
086     * @see org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute#renumber(java.util.List)
087     */
088    @Override
089    public void renumber(final List<Integer> byteCodeOffsets) throws Pack200Exception {
090        // Remember the unrenumbered startPcs, since that's used later
091        // to calculate end position.
092        final int[] unrenumberedStartPcs = Arrays.copyOf(startPcs, startPcs.length);
093
094        // Next renumber startPcs in place
095        super.renumber(byteCodeOffsets);
096
097        // lengths are BRANCH5 encoded, not BCI-encoded.
098        // In other words:
099        // startPc is BCI5 startPc
100        // endPc is byteCodeOffset[(index of startPc in byteCodeOffset) +
101        // (encoded length)]
102        // real length = endPc - startPc
103        // special case if endPc is beyond end of bytecode array
104
105        final int maxSize = codeLength;
106
107        // Iterate through the lengths and update each in turn.
108        // This is done in place in the lengths array.
109        for (int index = 0; index < lengths.length; index++) {
110            final int startPc = startPcs[index];
111            int revisedLength = -1;
112            final int encodedLength = lengths[index];
113
114            // First get the index of the startPc in the byteCodeOffsets
115            final int indexOfStartPC = unrenumberedStartPcs[index];
116            // Given the index of the startPc, we can now add
117            // the encodedLength to it to get the stop index.
118            final int stopIndex = indexOfStartPC + encodedLength;
119            if (stopIndex < 0) {
120                throw new Pack200Exception("Error renumbering bytecode indexes");
121            }
122            // Length can either be an index into the byte code offsets, or one
123            // beyond the
124            // end of the byte code offsets. Need to determine which this is.
125            if (stopIndex == byteCodeOffsets.size()) {
126                // Pointing to one past the end of the byte code array
127                revisedLength = maxSize - startPc;
128            } else {
129                // We're indexed into the byte code array
130                final int stopValue = byteCodeOffsets.get(stopIndex).intValue();
131                revisedLength = stopValue - startPc;
132            }
133            lengths[index] = revisedLength;
134        }
135    }
136
137    @Override
138    protected void resolve(final ClassConstantPool pool) {
139        super.resolve(pool);
140        nameIndexes = new int[localVariableTableLength];
141        descriptorIndexes = new int[localVariableTableLength];
142        for (int i = 0; i < localVariableTableLength; i++) {
143            names[i].resolve(pool);
144            descriptors[i].resolve(pool);
145            nameIndexes[i] = pool.indexOf(names[i]);
146            descriptorIndexes[i] = pool.indexOf(descriptors[i]);
147        }
148    }
149
150    public void setCodeLength(final int length) {
151        codeLength = length;
152    }
153
154    @Override
155    public String toString() {
156        return "LocalVariableTable: " + +localVariableTableLength + " variables";
157    }
158
159    @Override
160    protected void writeBody(final DataOutputStream dos) throws IOException {
161        dos.writeShort(localVariableTableLength);
162        for (int i = 0; i < localVariableTableLength; i++) {
163            dos.writeShort(startPcs[i]);
164            dos.writeShort(lengths[i]);
165            dos.writeShort(nameIndexes[i]);
166            dos.writeShort(descriptorIndexes[i]);
167            dos.writeShort(indexes[i]);
168        }
169    }
170}