org.apache.bcel.classfile.ClassParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcel Show documentation
Show all versions of bcel Show documentation
Apache Commons Bytecode Engineering Library, with type annotations
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.bcel.classfile;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.bcel.Const;
/**
* Wrapper class that parses a given Java .class file. The method parse returns a
* JavaClass object on success. When an I/O error or an
* inconsistency occurs an appropiate exception is propagated back to
* the caller.
*
* The structure and the names comply, except for a few conveniences,
* exactly with the
* JVM specification 1.0. See this paper for
* further details about the structure of a bytecode file.
*
* @version $Id$
*/
public final class ClassParser {
private DataInputStream dataInputStream;
private final boolean fileOwned;
private final String file_name;
private String zip_file;
private int class_name_index;
private int superclass_name_index;
private int major; // Compiler version
private int minor; // Compiler version
private int access_flags; // Access rights of parsed class
private int[] interfaces; // Names of implemented interfaces
private ConstantPool constant_pool; // collection of constants
private Field[] fields; // class fields, i.e., its variables
private Method[] methods; // methods defined in the class
private Attribute[] attributes; // attributes defined in the class
private final boolean is_zip; // Loaded from zip file
private static final int BUFSIZE = 8192;
/**
* Parse class from the given stream.
*
* @param inputStream Input stream
* @param file_name File name
*/
public ClassParser(final InputStream inputStream, final String file_name) {
this.file_name = file_name;
fileOwned = false;
final String clazz = inputStream.getClass().getName(); // Not a very clean solution ...
is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
if (inputStream instanceof DataInputStream) {
this.dataInputStream = (DataInputStream) inputStream;
} else {
this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE));
}
}
/** Parse class from given .class file.
*
* @param file_name file name
*/
public ClassParser(final String file_name) {
is_zip = false;
this.file_name = file_name;
fileOwned = true;
}
/** Parse class from given .class file in a ZIP-archive
*
* @param zip_file zip file name
* @param file_name file name
*/
public ClassParser(final String zip_file, final String file_name) {
is_zip = true;
fileOwned = true;
this.zip_file = zip_file;
this.file_name = file_name;
}
/**
* Parse the given Java class file and return an object that represents
* the contained data, i.e., constants, methods, fields and commands.
* A ClassFormatException is raised, if the file is not a valid
* .class file. (This does not include verification of the byte code as it
* is performed by the java interpreter).
*
* @return Class object representing the parsed class file
* @throws IOException
* @throws ClassFormatException
*/
public JavaClass parse() throws IOException, ClassFormatException {
ZipFile zip = null;
try {
if (fileOwned) {
if (is_zip) {
zip = new ZipFile(zip_file);
final ZipEntry entry = zip.getEntry(file_name);
if (entry == null) {
throw new IOException("File " + file_name + " not found");
}
dataInputStream = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
BUFSIZE));
} else {
dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(
file_name), BUFSIZE));
}
}
/****************** Read headers ********************************/
// Check magic tag of class file
readID();
// Get compiler version
readVersion();
/****************** Read constant pool and related **************/
// Read constant pool entries
readConstantPool();
// Get class information
readClassInfo();
// Get interface information, i.e., implemented interfaces
readInterfaces();
/****************** Read class fields and methods ***************/
// Read class fields, i.e., the variables of the class
readFields();
// Read class methods, i.e., the functions in the class
readMethods();
// Read class attributes
readAttributes();
// Check for unknown variables
//Unknown[] u = Unknown.getUnknownAttributes();
//for (int i=0; i < u.length; i++)
// System.err.println("WARNING: " + u[i]);
// Everything should have been read now
// if(file.available() > 0) {
// int bytes = file.available();
// byte[] buf = new byte[bytes];
// file.read(buf);
// if(!(is_zip && (buf.length == 1))) {
// System.err.println("WARNING: Trailing garbage at end of " + file_name);
// System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
// }
// }
} finally {
// Read everything of interest, so close the file
if (fileOwned) {
try {
if (dataInputStream != null) {
dataInputStream.close();
}
} catch (final IOException ioe) {
//ignore close exceptions
}
}
try {
if (zip != null) {
zip.close();
}
} catch (final IOException ioe) {
//ignore close exceptions
}
}
// Return the information we have gathered in a new object
return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
access_flags, constant_pool, interfaces, fields, methods, attributes, is_zip
? JavaClass.ZIP
: JavaClass.FILE);
}
/**
* Read information about the attributes of the class.
* @throws IOException
* @throws ClassFormatException
*/
private void readAttributes() throws IOException, ClassFormatException {
final int attributes_count = dataInputStream.readUnsignedShort();
attributes = new Attribute[attributes_count];
for (int i = 0; i < attributes_count; i++) {
attributes[i] = Attribute.readAttribute(dataInputStream, constant_pool);
}
}
/**
* Read information about the class and its super class.
* @throws IOException
* @throws ClassFormatException
*/
private void readClassInfo() throws IOException, ClassFormatException {
access_flags = dataInputStream.readUnsignedShort();
/* Interfaces are implicitely abstract, the flag should be set
* according to the JVM specification.
*/
if ((access_flags & Const.ACC_INTERFACE) != 0) {
access_flags |= Const.ACC_ABSTRACT;
}
if (((access_flags & Const.ACC_ABSTRACT) != 0)
&& ((access_flags & Const.ACC_FINAL) != 0)) {
throw new ClassFormatException("Class " + file_name + " can't be both final and abstract");
}
class_name_index = dataInputStream.readUnsignedShort();
superclass_name_index = dataInputStream.readUnsignedShort();
}
/**
* Read constant pool entries.
* @throws IOException
* @throws ClassFormatException
*/
private void readConstantPool() throws IOException, ClassFormatException {
constant_pool = new ConstantPool(dataInputStream);
}
/**
* Read information about the fields of the class, i.e., its variables.
* @throws IOException
* @throws ClassFormatException
*/
private void readFields() throws IOException, ClassFormatException {
final int fields_count = dataInputStream.readUnsignedShort();
fields = new Field[fields_count];
for (int i = 0; i < fields_count; i++) {
fields[i] = new Field(dataInputStream, constant_pool);
}
}
/******************** Private utility methods **********************/
/**
* Check whether the header of the file is ok.
* Of course, this has to be the first action on successive file reads.
* @throws IOException
* @throws ClassFormatException
*/
private void readID() throws IOException, ClassFormatException {
if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) {
throw new ClassFormatException(file_name + " is not a Java .class file");
}
}
/**
* Read information about the interfaces implemented by this class.
* @throws IOException
* @throws ClassFormatException
*/
private void readInterfaces() throws IOException, ClassFormatException {
final int interfaces_count = dataInputStream.readUnsignedShort();
interfaces = new int[interfaces_count];
for (int i = 0; i < interfaces_count; i++) {
interfaces[i] = dataInputStream.readUnsignedShort();
}
}
/**
* Read information about the methods of the class.
* @throws IOException
* @throws ClassFormatException
*/
private void readMethods() throws IOException, ClassFormatException {
final int methods_count = dataInputStream.readUnsignedShort();
methods = new Method[methods_count];
for (int i = 0; i < methods_count; i++) {
methods[i] = new Method(dataInputStream, constant_pool);
}
}
/**
* Read major and minor version of compiler which created the file.
* @throws IOException
* @throws ClassFormatException
*/
private void readVersion() throws IOException, ClassFormatException {
minor = dataInputStream.readUnsignedShort();
major = dataInputStream.readUnsignedShort();
}
}