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 034 public static void setAttributeName(final CPUTF8 cpUTF8Value) { 035 attributeName = cpUTF8Value; 036 } 037 038 private final int localVariableTableLength; 039 private final int[] startPcs; 040 private final int[] lengths; 041 private int[] nameIndexes; 042 private int[] descriptorIndexes; 043 private final int[] indexes; 044 private final CPUTF8[] names; 045 private final CPUTF8[] descriptors; 046 047 private int codeLength; 048 049 public LocalVariableTableAttribute(final int localVariableTableLength, final int[] startPcs, final int[] lengths, final CPUTF8[] names, 050 final CPUTF8[] descriptors, final int[] indexes) { 051 super(attributeName); 052 this.localVariableTableLength = localVariableTableLength; 053 this.startPcs = startPcs; 054 this.lengths = lengths; 055 this.names = names; 056 this.descriptors = descriptors; 057 this.indexes = indexes; 058 } 059 060 @Override 061 protected int getLength() { 062 return 2 + 10 * localVariableTableLength; 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 < localVariableTableLength; i++) { 070 nestedEntries.add(names[i]); 071 nestedEntries.add(descriptors[i]); 072 } 073 return nestedEntries.toArray(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[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}