View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.unpack200.bytecode;
20  
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  
27  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
28  
29  /**
30   * Local variable table
31   */
32  public class LocalVariableTableAttribute extends BCIRenumberedAttribute {
33  
34      private static CPUTF8 attributeName;
35  
36      public static void setAttributeName(final CPUTF8 cpUTF8Value) {
37          attributeName = cpUTF8Value;
38      }
39  
40      private final int localVariableTableLength;
41      private final int[] startPcs;
42      private final int[] lengths;
43      private int[] nameIndexes;
44      private int[] descriptorIndexes;
45      private final int[] indexes;
46      private final CPUTF8[] names;
47      private final CPUTF8[] descriptors;
48  
49      private int codeLength;
50  
51      public LocalVariableTableAttribute(final int localVariableTableLength, final int[] startPcs, final int[] lengths, final CPUTF8[] names,
52              final CPUTF8[] descriptors, final int[] indexes) {
53          super(attributeName);
54          this.localVariableTableLength = localVariableTableLength;
55          this.startPcs = startPcs;
56          this.lengths = lengths;
57          this.names = names;
58          this.descriptors = descriptors;
59          this.indexes = indexes;
60      }
61  
62      @Override
63      protected int getLength() {
64          return 2 + 10 * localVariableTableLength;
65      }
66  
67      @Override
68      protected ClassFileEntry[] getNestedClassFileEntries() {
69          final List<CPUTF8> nestedEntries = new ArrayList<>();
70          nestedEntries.add(getAttributeName());
71          for (int i = 0; i < localVariableTableLength; i++) {
72              nestedEntries.add(names[i]);
73              nestedEntries.add(descriptors[i]);
74          }
75          return nestedEntries.toArray(NONE);
76      }
77  
78      @Override
79      protected int[] getStartPCs() {
80          return startPcs;
81      }
82  
83      /*
84       * (non-Javadoc)
85       *
86       * @see org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute#renumber(java.util.List)
87       */
88      @Override
89      public void renumber(final List<Integer> byteCodeOffsets) throws Pack200Exception {
90          // Remember the unrenumbered startPcs, since that's used later
91          // to calculate end position.
92          final int[] unrenumberedStartPcs = Arrays.copyOf(startPcs, startPcs.length);
93  
94          // Next renumber startPcs in place
95          super.renumber(byteCodeOffsets);
96  
97          // lengths are BRANCH5 encoded, not BCI-encoded.
98          // In other words:
99          // 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 }