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