/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.classfile; import static com.sun.max.vm.classfile.ErrorContext.*; import java.io.*; import java.util.*; import com.sun.max.vm.bytecode.graft.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.type.*; /** * "LocalVariableTable" attributes in class files, see #4.7.9. */ public final class LocalVariableTable { /** * Represents a merged entry from one or more LocalVariableTables and LocalVariableTypeTables. */ public static final class Entry { private final char startBCI; private final char length; private final char slot; private final char nameIndex; private final char descriptorIndex; private char signatureIndex; public Entry(ClassfileStream classfileStream, boolean forLocalVariableTypeTables) { startBCI = (char) classfileStream.readUnsigned2(); length = (char) classfileStream.readUnsigned2(); nameIndex = (char) classfileStream.readUnsigned2(); if (forLocalVariableTypeTables) { signatureIndex = (char) classfileStream.readUnsigned2(); descriptorIndex = 0; } else { descriptorIndex = (char) classfileStream.readUnsigned2(); } slot = (char) classfileStream.readUnsigned2(); } public Entry(char startBCI, char length, char slot, char nameIndex, char descriptorIndex, char signatureIndex) { this.startBCI = startBCI; this.length = length; this.slot = slot; this.nameIndex = nameIndex; this.descriptorIndex = descriptorIndex; this.signatureIndex = signatureIndex; } public int startBCI() { return startBCI; } public int length() { return length; } public int slot() { return slot; } public int nameIndex() { return nameIndex; } public int descriptorIndex() { return descriptorIndex; } public int signatureIndex() { return signatureIndex; } public Utf8Constant name(ConstantPool constantPool) { return constantPool.utf8At(nameIndex, "local variable name"); } public TypeDescriptor descriptor(ConstantPool constantPool) { return JavaTypeDescriptor.parseTypeDescriptor(constantPool.utf8At(descriptorIndex, "local variable type").toString()); } public Utf8Constant signature(ConstantPool constantPool) { return constantPool.utf8At(signatureIndex); } public void copySignatureIndex(Entry lvttEntry) { signatureIndex = lvttEntry.signatureIndex; } @Override public boolean equals(Object object) { if (object instanceof Entry) { final Entry otherEntry = (Entry) object; return otherEntry.startBCI == startBCI && otherEntry.length == length && otherEntry.slot == slot; } return false; } @Override public int hashCode() { return startBCI + (length * 37) + (slot * 37); } public void verify(ConstantPool constantPool, int codeLength, int maxLocals, boolean forLVTT) { name(constantPool); if (startBCI >= codeLength) { throw classFormatError("Invalid start_pc (" + startBCI + ") in LocalVariableTable"); } final int endPC = startBCI + length; if (endPC > codeLength) { throw classFormatError("Invalid length (" + length + ") in LocalVariableTable"); } if (!forLVTT) { final TypeDescriptor descriptor = descriptor(constantPool); final int index = (!descriptor.toKind().isCategory1) ? slot + 1 : slot; if (index >= maxLocals) { throw classFormatError("Invalid local variable index (" + slot + ") in LocalVariableTable"); } } } @Override public String toString() { return (int) slot + "@" + (int) startBCI + "+" + (int) length + "{name=" + (int) nameIndex + ",descriptor=" + (int) descriptorIndex + ",signature=" + (int) signatureIndex + "}"; } } public static final LocalVariableTable EMPTY = new LocalVariableTable(new char[] {0}); /** * An encoded local variable table. The format is as follows: * <p> * * <pre> * encoded_entries { * u2 number_of_entries_with_a_signature; * { * u2 start_bci; * u2 length; * u2 slot; * u2 name_index; * u2 descriptor_index; * u2 signature_index; * } entries[number_of_entries] * } * </pre> */ private final char[] encodedEntries; private static final int ENCODED_START_BCI = 0; private static final int ENCODED_LENGTH = 1; private static final int ENCODED_SLOT = 2; private static final int ENCODED_NAME_INDEX = 3; private static final int ENCODED_DESCRIPTOR_INDEX = 4; private static final int ENCODED_SIGNATURE_INDEX = 5; LocalVariableTable(char[] encodedEntries) { assert encodedEntries.length >= 1; this.encodedEntries = encodedEntries; } public LocalVariableTable(Collection<Entry> entries) { encodedEntries = new char[(entries.size() * 6) + 1]; int i = 1; for (Entry entry : entries) { encodedEntries[i + ENCODED_START_BCI] = entry.startBCI; encodedEntries[i + ENCODED_LENGTH] = entry.length; encodedEntries[i + ENCODED_SLOT] = entry.slot; encodedEntries[i + ENCODED_NAME_INDEX] = entry.nameIndex; encodedEntries[i + ENCODED_DESCRIPTOR_INDEX] = entry.descriptorIndex; encodedEntries[i + ENCODED_SIGNATURE_INDEX] = entry.signatureIndex; i += 6; if (entry.signatureIndex != 0) { encodedEntries[0]++; } } } public LocalVariableTable relocate(OpcodeBCIRelocator relocator) { if (encodedEntries.length == 1) { return this; } final char[] relocEncodedEntries = new char[this.encodedEntries.length]; relocEncodedEntries[0] = relocEncodedEntries[0]; for (int i = 1; i != relocEncodedEntries.length; i += 6) { final char startBCI = relocEncodedEntries[i + ENCODED_START_BCI]; final char length = relocEncodedEntries[i + ENCODED_LENGTH]; final char relocatedEndBCI = (char) relocator.relocate(startBCI + length); // Special case for start address 0: only parameters can be defined at 0 final char relocatedStartBCI = startBCI == 0 ? 0 : (char) relocator.relocate(startBCI); final char relocatedLength = (char) (relocatedEndBCI - relocatedStartBCI); relocEncodedEntries[i + ENCODED_START_BCI] = relocatedStartBCI; relocEncodedEntries[i + ENCODED_LENGTH] = relocatedLength; relocEncodedEntries[i + ENCODED_SLOT] = relocEncodedEntries[i + ENCODED_SLOT]; relocEncodedEntries[i + ENCODED_NAME_INDEX] = relocEncodedEntries[i + ENCODED_NAME_INDEX]; relocEncodedEntries[i + ENCODED_DESCRIPTOR_INDEX] = relocEncodedEntries[i + ENCODED_DESCRIPTOR_INDEX]; relocEncodedEntries[i + ENCODED_SIGNATURE_INDEX] = relocEncodedEntries[i + ENCODED_SIGNATURE_INDEX]; } return new LocalVariableTable(relocEncodedEntries); } public int numberOfEntries() { return (encodedEntries.length - 1) / 6; } /** * Gets the number of {@linkplain #entries() entries} whose {@linkplain Entry#signatureIndex() signature index} is not 0. * That is, how many entries in this table are derived from a LocalVariableTypeTable class file attribute. */ public int numberOfEntriesWithSignature() { return encodedEntries[0]; } public boolean isEmpty() { return encodedEntries.length == 1; } /** * Gets an object describing a local variable that is live at a given BCI. * * @param index * the index of the variable in the local variables array * @param bci * a BCI for which the details of the variable are being requested * @return a {@link Entry} object describing the requested variable. If the given local variable index and BCI * do not denote a live local variable, then null is returned. */ public Entry findLocalVariable(int index, int bci) { if (encodedEntries.length != 1) { for (int i = 1; i != encodedEntries.length; i += 6) { final char thisSlot = encodedEntries[i + ENCODED_SLOT]; if (thisSlot == index) { final char thisStartBCI = encodedEntries[i + ENCODED_START_BCI]; final char thisLength = encodedEntries[i + ENCODED_LENGTH]; final char thisEndBCI = (char) (thisStartBCI + thisLength); if (bci >= thisStartBCI && bci <= thisEndBCI) { final char thisNameIndex = encodedEntries[i + ENCODED_NAME_INDEX]; final char thisDescriptorIndex = encodedEntries[i + ENCODED_DESCRIPTOR_INDEX]; final char thisSignatureIndex = encodedEntries[i + ENCODED_SIGNATURE_INDEX]; return new Entry(thisStartBCI, thisLength, thisSlot, thisNameIndex, thisDescriptorIndex, thisSignatureIndex); } } } } return null; } public Entry[] entries() { final Entry[] entries = new Entry[numberOfEntries()]; for (int i = 1; i != encodedEntries.length; i += 6) { final char startBCI = encodedEntries[i + ENCODED_START_BCI]; final char slot = encodedEntries[i + ENCODED_SLOT]; final char length = encodedEntries[i + ENCODED_LENGTH]; final char nameIndex = encodedEntries[i + ENCODED_NAME_INDEX]; final char descriptorIndex = encodedEntries[i + ENCODED_DESCRIPTOR_INDEX]; final char signatureIndex = encodedEntries[i + ENCODED_SIGNATURE_INDEX]; entries[i / 6] = new Entry(startBCI, length, slot, nameIndex, descriptorIndex, signatureIndex); } return entries; } @Override public String toString() { final StringBuilder sb = new StringBuilder("["); for (Entry entry : entries()) { if (sb.length() != 1) { sb.append(", "); } sb.append(entry); } return sb.append(']').toString(); } public void encode(DataOutputStream dataOutputStream) throws IOException { CodeAttribute.writeCharArray(dataOutputStream, encodedEntries); } public static LocalVariableTable decode(DataInputStream dataInputStream) throws IOException { return new LocalVariableTable(CodeAttribute.readCharArray(dataInputStream)); } /** * Writes this local variable table to a given stream as a LocalVariableTable class file attribute. * * @param stream * a data output stream that has just written the 'attribute_name_index' and 'attribute_length' * fields of a class file attribute * @param constantPoolEditor */ public void writeLocalVariableTableAttributeInfo(DataOutputStream stream, ConstantPoolEditor constantPoolEditor) throws IOException { final int numberOfEntries = numberOfEntries(); stream.writeShort(numberOfEntries); for (int i = 1; i != encodedEntries.length; i += 6) { stream.writeShort(encodedEntries[i + ENCODED_START_BCI]); stream.writeShort(encodedEntries[i + ENCODED_LENGTH]); stream.writeShort(encodedEntries[i + ENCODED_NAME_INDEX]); stream.writeShort(encodedEntries[i + ENCODED_DESCRIPTOR_INDEX]); stream.writeShort(encodedEntries[i + ENCODED_SLOT]); } } /** * Writes this local variable table to a given stream as a LocalVariableTypeTable class file attribute. * * @param stream * a data output stream that has just written the 'attribute_name_index' and 'attribute_length' * fields of a class file attribute * @param constantPoolEditor */ public void writeLocalVariableTypeTableAttributeInfo(DataOutputStream stream, ConstantPoolEditor constantPoolEditor) throws IOException { int numberOfEntries = numberOfEntriesWithSignature(); stream.writeShort(numberOfEntries); for (int i = 1; i != encodedEntries.length; i += 6) { final int signatureIndex = encodedEntries[i + ENCODED_SIGNATURE_INDEX]; if (signatureIndex != 0) { stream.writeShort(encodedEntries[i + ENCODED_START_BCI]); stream.writeShort(encodedEntries[i + ENCODED_LENGTH]); stream.writeShort(encodedEntries[i + ENCODED_NAME_INDEX]); stream.writeShort(signatureIndex); stream.writeShort(encodedEntries[i + ENCODED_SLOT]); --numberOfEntries; } } assert numberOfEntries == 0; } }