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}