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