You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
11 KiB
328 lines
11 KiB
/* BinaryInfo Copyright (C) 1998-1999 Jochen Hoenicke.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
package net.sf.jode.bytecode;
|
|
import java.io.DataInputStream;
|
|
import java.io.DataOutputStream;
|
|
import java.io.EOFException;
|
|
import java.io.FilterInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import net.sf.jode.util.SimpleMap;
|
|
|
|
///#def COLLECTIONS java.util
|
|
import java.util.Map;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
///#enddef
|
|
|
|
|
|
/**
|
|
* <p>Represents a container for user specified attributes.</p>
|
|
*
|
|
* <p>Java bytecode is extensible: Classes, Methods and Fields may
|
|
* have any number of attributes. Every attribute has a name and some
|
|
* unformatted data.</p>
|
|
*
|
|
* <p>There are some predefined attributes, even the Code of a Method
|
|
* is an attribute. These predefined attributes are all handled by
|
|
* this package as appropriate. This methods are only useful for non
|
|
* standard attributes.</p>
|
|
*
|
|
* <p>You can provide new attributes by overriding the protected
|
|
* methods of this class. This makes it possible to use constant pool
|
|
* entries in the attributes.</p>
|
|
*
|
|
* <p>Another possibility is to add the attributes with the public
|
|
* method. This way you don't need to extend the classes, but you
|
|
* can't use a constant pool for the contents of the attributes. One
|
|
* possible application of this are installation classes. These
|
|
* classes have a special attribute containing a zip archive of the
|
|
* files that should be installed. There are other possible uses,
|
|
* e.g. putting native machine code for some architectures into the
|
|
* class.</p>
|
|
*
|
|
* @author Jochen Hoenicke
|
|
*/
|
|
public class BinaryInfo {
|
|
private Map unknownAttributes = null;
|
|
|
|
void skipAttributes(DataInputStream input) throws IOException {
|
|
int count = input.readUnsignedShort();
|
|
for (int i=0; i< count; i++) {
|
|
input.readUnsignedShort(); // the name index
|
|
long length = input.readInt();
|
|
while (length > 0) {
|
|
long skipped = input.skip(length);
|
|
if (skipped == 0)
|
|
throw new EOFException("Can't skip. EOF?");
|
|
length -= skipped;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads in an attributes of this class. Overwrite this method if
|
|
* you want to handle your own attributes. If you don't know how
|
|
* to handle an attribute call this method for the super class.
|
|
* @param name the attribute name.
|
|
* @param length the length of the attribute.
|
|
* @param constantPool the constant pool of the class.
|
|
* @param input a data input stream where you can read the attribute
|
|
* from. It will protect you to read more over the attribute boundary.
|
|
* @param howMuch the constant that was given to the {@link
|
|
* ClassInfo#load} function when loading this class.
|
|
*/
|
|
protected void readAttribute(String name, int length,
|
|
ConstantPool constantPool,
|
|
DataInputStream input,
|
|
int howMuch) throws IOException {
|
|
byte[] data = new byte[length];
|
|
input.readFully(data);
|
|
if (howMuch >= ClassInfo.ALL) {
|
|
if (unknownAttributes == null)
|
|
unknownAttributes = new SimpleMap();
|
|
unknownAttributes.put(name, data);
|
|
}
|
|
}
|
|
|
|
static class ConstrainedInputStream extends FilterInputStream {
|
|
int length;
|
|
|
|
public ConstrainedInputStream(int attrLength, InputStream input) {
|
|
super(input);
|
|
length = attrLength;
|
|
}
|
|
|
|
public int read() throws IOException {
|
|
if (length > 0) {
|
|
int data = super.read();
|
|
length--;
|
|
return data;
|
|
}
|
|
throw new EOFException();
|
|
}
|
|
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
if (length < len) {
|
|
len = length;
|
|
}
|
|
if (len == 0)
|
|
return -1;
|
|
int count = super.read(b, off, len);
|
|
length -= count;
|
|
return count;
|
|
}
|
|
|
|
public int read(byte[] b) throws IOException {
|
|
return read(b, 0, b.length);
|
|
}
|
|
|
|
public long skip(long count) throws IOException {
|
|
if (length < count) {
|
|
count = length;
|
|
}
|
|
count = super.skip(count);
|
|
length -= (int) count;
|
|
return count;
|
|
}
|
|
|
|
public void skipRemaining() throws IOException {
|
|
while (length > 0) {
|
|
int skipped = (int) skip(length);
|
|
if (skipped == 0)
|
|
throw new EOFException();
|
|
length -= skipped;
|
|
}
|
|
}
|
|
}
|
|
|
|
void readAttributes(ConstantPool constantPool,
|
|
DataInputStream input,
|
|
int howMuch) throws IOException {
|
|
int count = input.readUnsignedShort();
|
|
unknownAttributes = null;
|
|
for (int i=0; i< count; i++) {
|
|
String attrName =
|
|
constantPool.getUTF8(input.readUnsignedShort());
|
|
final int attrLength = input.readInt();
|
|
ConstrainedInputStream constrInput =
|
|
new ConstrainedInputStream(attrLength, input);
|
|
readAttribute(attrName, attrLength,
|
|
constantPool, new DataInputStream(constrInput),
|
|
howMuch);
|
|
constrInput.skipRemaining();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Drops information from this info. Override this to drop your
|
|
* own info and don't forget to call the method of the super class.
|
|
* @param howMuch the constant that was given to the {@link
|
|
* ClassInfo#drop} function when loading this class.
|
|
*/
|
|
protected void drop(int keep) {
|
|
if (keep < ClassInfo.ALL)
|
|
unknownAttributes = null;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of attributes of this class. Overwrite this
|
|
* method if you want to add your own attributes by providing a
|
|
* writeAttributes method. You should call this method for the
|
|
* super class and add the number of your own attributes to the
|
|
* returned value.
|
|
* @return the number of attributes of this class.
|
|
*/
|
|
protected int getAttributeCount() {
|
|
return unknownAttributes != null ? unknownAttributes.size() : 0;
|
|
}
|
|
|
|
/**
|
|
* Prepare writing your attributes. Overwrite this method if you
|
|
* want to add your own attributes, which need constants on the
|
|
* class pool. Add the necessary constants to the constant pool
|
|
* and call this method for the super class.
|
|
* @param gcp The growable constant pool.
|
|
*/
|
|
protected void prepareAttributes(GrowableConstantPool gcp) {
|
|
if (unknownAttributes == null)
|
|
return;
|
|
Iterator i = unknownAttributes.keySet().iterator();
|
|
while (i.hasNext())
|
|
gcp.putUTF8((String) i.next());
|
|
}
|
|
|
|
/**
|
|
* <p>Writes the attributes to the output stream.
|
|
* Overwrite this method if you want to add your own attributes.
|
|
* All constants you need from the growable constant pool must
|
|
* have been previously registered by the {@link #prepareAttributes}
|
|
* method.</p>
|
|
*
|
|
* First call the method of the super class. Afterwrites write
|
|
* each of your own attributes including the attribute header
|
|
* (name and length entry).
|
|
*
|
|
* @param gcp The growable constant pool, which is not growable anymore.
|
|
* @param output the data output stream. You must write exactly
|
|
* as many bytes to it as you have told with the {@link
|
|
* #getAttributeSize} method.
|
|
*/
|
|
protected void writeAttributes
|
|
(GrowableConstantPool constantPool,
|
|
DataOutputStream output) throws IOException {
|
|
int count = getAttributeCount();
|
|
output.writeShort(count);
|
|
if (unknownAttributes != null) {
|
|
Iterator i = unknownAttributes.entrySet().iterator();
|
|
while (i.hasNext()) {
|
|
Map.Entry e = (Map.Entry) i.next();
|
|
String name = (String) e.getKey();
|
|
byte[] data = (byte[]) e.getValue();
|
|
output.writeShort(constantPool.putUTF8(name));
|
|
output.writeInt(data.length);
|
|
output.write(data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the total length of all attributes in this binary info.
|
|
* Overwrite this method if you want to add your own attributes
|
|
* and add the size of your attributes to the value returned by
|
|
* the super class.<br>
|
|
*
|
|
* Currently you only need to write this if you extend
|
|
* BasicBlocks.
|
|
*
|
|
* @return the total length of all attributes, including their
|
|
* headers and the number of attributes field.
|
|
*/
|
|
protected int getAttributeSize() {
|
|
int size = 2; /* attribute count */
|
|
if (unknownAttributes != null) {
|
|
Iterator i = unknownAttributes.values().iterator();
|
|
while (i.hasNext())
|
|
size += 2 + 4 + ((byte[]) i.next()).length;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Finds a non standard attribute with the given name. You don't
|
|
* have access to the constant pool. Instead extend this class
|
|
* and override readAttribute method if you need the pool.
|
|
* @param name the name of the attribute.
|
|
* @return the contents of the attribute, null if not found.
|
|
* @see #readAttribute
|
|
*/
|
|
public byte[] findAttribute(String name) {
|
|
if (unknownAttributes != null)
|
|
return (byte[]) unknownAttributes.get(name);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets all non standard attributes.
|
|
* @return an iterator for all attributes. The values returned by
|
|
* the next() method of the iterator are of byte[] type.
|
|
* @see #findAttribute
|
|
*/
|
|
public Iterator getAttributes() {
|
|
if (unknownAttributes != null)
|
|
return unknownAttributes.values().iterator();
|
|
return Collections.EMPTY_SET.iterator();
|
|
}
|
|
|
|
/**
|
|
* Adds a new non standard attribute or replaces an old one with
|
|
* the same name. If it already exists, it will be overwritten.
|
|
* Note that there's now way to correlate the contents with a
|
|
* constant pool. If you need that extend this class and override
|
|
* the methods {@link #getAttributeCount}, {@link
|
|
* #prepareAttributes}, {@link #writeAttributes}, and {@link
|
|
* #getAttributeSize}.
|
|
* @param name the name of the attribute.
|
|
* @param contents the new contens.
|
|
*/
|
|
public void addAttribute(String name, byte[] contents) {
|
|
if (unknownAttributes == null)
|
|
unknownAttributes = new SimpleMap();
|
|
unknownAttributes.put(name, contents);
|
|
}
|
|
|
|
/**
|
|
* Removes a non standard attributes.
|
|
* @param name the name of the attribute.
|
|
* @return the old contents of the attribute.
|
|
*/
|
|
public byte[] removeAttribute(String name) {
|
|
if (unknownAttributes != null)
|
|
return (byte[]) unknownAttributes.remove(name);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Removes all non standard attributes.
|
|
*/
|
|
public void removeAllAttributes() {
|
|
unknownAttributes = null;
|
|
}
|
|
}
|
|
|