1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
/*
* 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.tomcat.util.bcel.classfile;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import org.apache.tomcat.util.bcel.Constants;
/**
* 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:m.dahm@gmx.de">M. Dahm</A>
*/
public final class ClassParser {
private static final int MAGIC = 0xCAFEBABE;
private final DataInput file;
private String class_name, superclass_name;
private int access_flags; // Access rights of parsed class
private String[] interface_names; // Names of implemented interfaces
private ConstantPool constant_pool; // collection of constants
private Annotations runtimeVisibleAnnotations; // "RuntimeVisibleAnnotations" attribute defined in the class
private static final int BUFSIZE = 8192;
private static final String[] INTERFACES_EMPTY_ARRAY = new String[0];
/**
* Parse class from the given stream.
*
* @param file Input stream
*/
public ClassParser(InputStream file) {
this.file = new FastDataInputStream(file, 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();
// Return the information we have gathered in a new object
return new JavaClass(class_name, superclass_name,
access_flags, constant_pool, interface_names,
runtimeVisibleAnnotations);
}
/**
* Read information about the attributes of the class.
* @throws IOException
* @throws ClassFormatException
*/
private void readAttributes() throws IOException, ClassFormatException {
int attributes_count;
attributes_count = file.readUnsignedShort();
for (int i = 0; i < attributes_count; i++) {
ConstantUtf8 c;
String name;
int name_index;
int length;
// Get class name from constant pool via `name_index' indirection
name_index = file.readUnsignedShort();
c = (ConstantUtf8) constant_pool.getConstant(name_index,
Constants.CONSTANT_Utf8);
name = c.getBytes();
// Length of data in bytes
length = file.readInt();
if (name.equals("RuntimeVisibleAnnotations")) {
if (runtimeVisibleAnnotations != null) {
throw new ClassFormatException(
"RuntimeVisibleAnnotations attribute is not allowed more than once in a class file");
}
runtimeVisibleAnnotations = new Annotations(file, constant_pool);
} else {
// All other attributes are skipped
Utility.skipFully(file, length);
}
}
}
/**
* Read information about the class and its super class.
* @throws IOException
* @throws ClassFormatException
*/
private 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");
}
int class_name_index = file.readUnsignedShort();
class_name = Utility.getClassName(constant_pool, class_name_index);
int superclass_name_index = file.readUnsignedShort();
if (superclass_name_index > 0) {
// May be zero -> class is java.lang.Object
superclass_name = Utility.getClassName(constant_pool, superclass_name_index);
} else {
superclass_name = "java.lang.Object";
}
}
/**
* Read constant pool entries.
* @throws IOException
* @throws ClassFormatException
*/
private 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 void readFields() throws IOException, ClassFormatException {
int fields_count = file.readUnsignedShort();
for (int i = 0; i < fields_count; i++) {
Utility.swallowFieldOrMethod(file);
}
}
/******************** 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 (file.readInt() != MAGIC) {
throw new ClassFormatException("It 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 {
int interfaces_count;
interfaces_count = file.readUnsignedShort();
if (interfaces_count > 0) {
interface_names = new String[interfaces_count];
for (int i = 0; i < interfaces_count; i++) {
int index = file.readUnsignedShort();
interface_names[i] = Utility.getClassName(constant_pool, index);
}
} else {
interface_names = INTERFACES_EMPTY_ARRAY;
}
}
/**
* Read information about the methods of the class.
* @throws IOException
* @throws ClassFormatException
*/
private void readMethods() throws IOException, ClassFormatException {
int methods_count;
methods_count = file.readUnsignedShort();
for (int i = 0; i < methods_count; i++) {
Utility.swallowFieldOrMethod(file);
}
}
/**
* Read major and minor version of compiler which created the file.
* @throws IOException
* @throws ClassFormatException
*/
private void readVersion() throws IOException, ClassFormatException {
// file.readUnsignedShort(); // Unused minor
// file.readUnsignedShort(); // Unused major
Utility.skipFully(file, 4);
}
}
|