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.
332 lines
12 KiB
332 lines
12 KiB
/*
|
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
package com.sun.org.apache.bcel.internal.classfile;
|
|
|
|
/* ====================================================================
|
|
* The Apache Software License, Version 1.1
|
|
*
|
|
* Copyright (c) 2001 The Apache Software Foundation. All rights
|
|
* reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. The end-user documentation included with the redistribution,
|
|
* if any, must include the following acknowledgment:
|
|
* "This product includes software developed by the
|
|
* Apache Software Foundation (http://www.apache.org/)."
|
|
* Alternately, this acknowledgment may appear in the software itself,
|
|
* if and wherever such third-party acknowledgments normally appear.
|
|
*
|
|
* 4. The names "Apache" and "Apache Software Foundation" and
|
|
* "Apache BCEL" must not be used to endorse or promote products
|
|
* derived from this software without prior written permission. For
|
|
* written permission, please contact apache@apache.org.
|
|
*
|
|
* 5. Products derived from this software may not be called "Apache",
|
|
* "Apache BCEL", nor may "Apache" appear in their name, without
|
|
* prior written permission of the Apache Software Foundation.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
* ====================================================================
|
|
*
|
|
* This software consists of voluntary contributions made by many
|
|
* individuals on behalf of the Apache Software Foundation. For more
|
|
* information on the Apache Software Foundation, please see
|
|
* <http://www.apache.org/>.
|
|
*/
|
|
|
|
import com.sun.org.apache.bcel.internal.Constants;
|
|
import java.io.*;
|
|
import java.util.zip.*;
|
|
|
|
/**
|
|
* Wrapper class that parses a given Java .class file. The method <A
|
|
* href ="#parse">parse</A> returns a <A href ="JavaClass.html">
|
|
* JavaClass</A> 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 <A href="ftp://java.sun.com/docs/specs/vmspec.ps">
|
|
* JVM specification 1.0</a>. See this paper for
|
|
* further details about the structure of a bytecode file.
|
|
*
|
|
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
|
|
*/
|
|
public final class ClassParser {
|
|
private DataInputStream file;
|
|
private ZipFile zip;
|
|
private String file_name;
|
|
private int class_name_index, superclass_name_index;
|
|
private int major, 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 boolean is_zip; // Loaded from zip file
|
|
|
|
private static final int BUFSIZE = 8192;
|
|
|
|
/**
|
|
* Parse class from the given stream.
|
|
*
|
|
* @param file Input stream
|
|
* @param file_name File name
|
|
*/
|
|
public ClassParser(InputStream file, String file_name) {
|
|
this.file_name = file_name;
|
|
|
|
String clazz = file.getClass().getName(); // Not a very clean solution ...
|
|
is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
|
|
|
|
if(file instanceof DataInputStream) // Is already a data stream
|
|
this.file = (DataInputStream)file;
|
|
else
|
|
this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
|
|
}
|
|
|
|
/** Parse class from given .class file.
|
|
*
|
|
* @param file_name file name
|
|
* @throws IOException
|
|
*/
|
|
public ClassParser(String file_name) throws IOException
|
|
{
|
|
is_zip = false;
|
|
this.file_name = file_name;
|
|
file = new DataInputStream(new BufferedInputStream
|
|
(new FileInputStream(file_name), BUFSIZE));
|
|
}
|
|
|
|
/** Parse class from given .class file in a ZIP-archive
|
|
*
|
|
* @param file_name file name
|
|
* @throws IOException
|
|
*/
|
|
public ClassParser(String zip_file, String file_name) throws IOException
|
|
{
|
|
is_zip = true;
|
|
zip = new ZipFile(zip_file);
|
|
ZipEntry entry = zip.getEntry(file_name);
|
|
|
|
this.file_name = file_name;
|
|
|
|
file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
|
|
BUFSIZE));
|
|
}
|
|
|
|
/**
|
|
* Parse the given Java class file and return an object that represents
|
|
* the contained data, i.e., constants, methods, fields and commands.
|
|
* A <em>ClassFormatException</em> 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
|
|
{
|
|
/****************** 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));
|
|
// }
|
|
// }
|
|
|
|
// Read everything of interest, so close the file
|
|
file.close();
|
|
if(zip != null)
|
|
zip.close();
|
|
|
|
// 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 final void readAttributes() throws IOException, ClassFormatException
|
|
{
|
|
int attributes_count;
|
|
|
|
attributes_count = file.readUnsignedShort();
|
|
attributes = new Attribute[attributes_count];
|
|
|
|
for(int i=0; i < attributes_count; i++)
|
|
attributes[i] = Attribute.readAttribute(file, constant_pool);
|
|
}
|
|
|
|
/**
|
|
* Read information about the class and its super class.
|
|
* @throws IOException
|
|
* @throws ClassFormatException
|
|
*/
|
|
private final void readClassInfo() throws IOException, ClassFormatException
|
|
{
|
|
access_flags = file.readUnsignedShort();
|
|
|
|
/* Interfaces are implicitely abstract, the flag should be set
|
|
* according to the JVM specification.
|
|
*/
|
|
if((access_flags & Constants.ACC_INTERFACE) != 0)
|
|
access_flags |= Constants.ACC_ABSTRACT;
|
|
|
|
if(((access_flags & Constants.ACC_ABSTRACT) != 0) &&
|
|
((access_flags & Constants.ACC_FINAL) != 0 ))
|
|
throw new ClassFormatException("Class can't be both final and abstract");
|
|
|
|
class_name_index = file.readUnsignedShort();
|
|
superclass_name_index = file.readUnsignedShort();
|
|
}
|
|
/**
|
|
* Read constant pool entries.
|
|
* @throws IOException
|
|
* @throws ClassFormatException
|
|
*/
|
|
private final void readConstantPool() throws IOException, ClassFormatException
|
|
{
|
|
constant_pool = new ConstantPool(file);
|
|
}
|
|
|
|
/**
|
|
* Read information about the fields of the class, i.e., its variables.
|
|
* @throws IOException
|
|
* @throws ClassFormatException
|
|
*/
|
|
private final void readFields() throws IOException, ClassFormatException
|
|
{
|
|
int fields_count;
|
|
|
|
fields_count = file.readUnsignedShort();
|
|
fields = new Field[fields_count];
|
|
|
|
for(int i=0; i < fields_count; i++)
|
|
fields[i] = new Field(file, 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 final void readID() throws IOException, ClassFormatException
|
|
{
|
|
int magic = 0xCAFEBABE;
|
|
|
|
if(file.readInt() != 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 final void readInterfaces() throws IOException, ClassFormatException
|
|
{
|
|
int interfaces_count;
|
|
|
|
interfaces_count = file.readUnsignedShort();
|
|
interfaces = new int[interfaces_count];
|
|
|
|
for(int i=0; i < interfaces_count; i++)
|
|
interfaces[i] = file.readUnsignedShort();
|
|
}
|
|
/**
|
|
* Read information about the methods of the class.
|
|
* @throws IOException
|
|
* @throws ClassFormatException
|
|
*/
|
|
private final void readMethods() throws IOException, ClassFormatException
|
|
{
|
|
int methods_count;
|
|
|
|
methods_count = file.readUnsignedShort();
|
|
methods = new Method[methods_count];
|
|
|
|
for(int i=0; i < methods_count; i++)
|
|
methods[i] = new Method(file, constant_pool);
|
|
}
|
|
/**
|
|
* Read major and minor version of compiler which created the file.
|
|
* @throws IOException
|
|
* @throws ClassFormatException
|
|
*/
|
|
private final void readVersion() throws IOException, ClassFormatException
|
|
{
|
|
minor = file.readUnsignedShort();
|
|
major = file.readUnsignedShort();
|
|
}
|
|
}
|