org.springsource.loaded.ConstantPoolChecker Maven / Gradle / Ivy
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed 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.springsource.loaded;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// TODO does not yet support the new constant pool entry types that come with Java 7
// http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
/**
* Quickly checks the constant pool for class references, it skips everything else as fast as it can. The class
* references are then available for checking.
*
* @author Andy Clement
* @since 0.7.3
*/
public class ConstantPoolChecker {
private static final boolean DEBUG = false;
private final static byte CONSTANT_Utf8 = 1;
private final static byte CONSTANT_Integer = 3;
private final static byte CONSTANT_Float = 4;
private final static byte CONSTANT_Long = 5;
private final static byte CONSTANT_Double = 6;
private final static byte CONSTANT_Class = 7;
private final static byte CONSTANT_String = 8;
private final static byte CONSTANT_Fieldref = 9;
private final static byte CONSTANT_Methodref = 10;
private final static byte CONSTANT_InterfaceMethodref = 11;
private final static byte CONSTANT_NameAndType = 12;
// Test entry point just goes through all the code in the bin folder
public static void main(String[] args) throws Exception {
// File[] fs = new File("./bin").listFiles();
// File[] fs = new File("../testdata-groovy/bin").listFiles();
// checkThemAll(fs);
// System.out.println("total=" + total / 1000000d);
}
// static long total = 0;
// private static void checkThemAll(File[] fs) throws Exception {
// for (File f : fs) {
// if (f.isDirectory()) {
// checkThemAll(f.listFiles());
// } else if (f.getName().endsWith(".class")) {
// System.out.println(f);
// byte[] data = Utils.loadFromStream(new FileInputStream(f));
// long stime = System.nanoTime();
// List ls = getReferencedClasses(data);
// // total += (System.nanoTime() - stime);
// System.out.println(ls);
// }
// }
// }
// ClassFile {
// u4 magic;
// u2 minor_version;
// u2 major_version;
// u2 constant_pool_count;
// cp_info constant_pool[constant_pool_count-1];
// u2 access_flags;
// u2 this_class;
// u2 super_class;
// u2 interfaces_count;
// u2 interfaces[interfaces_count];
// u2 fields_count;
// field_info fields[fields_count];
// u2 methods_count;
// method_info methods[methods_count];
// u2 attributes_count;
// attribute_info attributes[attributes_count];
// }
static List getReferencedClasses(byte[] bytes) {
return new ConstantPoolChecker(bytes).referencedClasses;
}
// Filled with strings and int[]
private Object[] cpdata;
private int cpsize;
private int[] type;
// Does not need to be a set as there are no dups in the ConstantPool (for a class from a decent compiler...)
private List referencedClasses = new ArrayList();
private ConstantPoolChecker(byte[] bytes) {
readConstantPool(bytes);
computeReferences();
}
public void computeReferences() {
for (int i = 0; i < cpsize; i++) {
switch (type[i]) {
case CONSTANT_Class:
int classindex = ((Integer) cpdata[i]);
String classname = (String) cpdata[classindex];
if (classname == null) {
throw new IllegalStateException();
}
referencedClasses.add(classname);
break;
// private final static byte CONSTANT_Utf8 = 1;
// private final static byte CONSTANT_Integer = 3;
// private final static byte CONSTANT_Float = 4;
// private final static byte CONSTANT_Long = 5;
// private final static byte CONSTANT_Double = 6;
// private final static byte CONSTANT_String = 8;
// private final static byte CONSTANT_Fieldref = 9;
// private final static byte CONSTANT_Methodref = 10;
// private final static byte CONSTANT_InterfaceMethodref = 11;
// private final static byte CONSTANT_NameAndType = 12;
}
}
}
public void readConstantPool(byte[] bytes) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
DataInputStream dis = new DataInputStream(bais);
int magic = dis.readInt(); // magic 0xCAFEBABE
if (magic != 0xCAFEBABE) {
throw new IllegalStateException("not bytecode, magic was 0x" + Integer.toString(magic, 16));
}
dis.skip(4);
// dis.readShort(); // minor
// dis.readShort(); // major
cpsize = dis.readShort();
if (DEBUG) {
System.out.println("Constant Pool Size =" + cpsize);
}
cpdata = new Object[cpsize];
type = new int[cpsize];
// int max = cpsize - 1;
for (int cpentry = 1; cpentry < cpsize; cpentry++) {
boolean doubleSlot = processConstantPoolEntry(cpentry, dis);
if (doubleSlot) {
cpentry++;
}
}
}
catch (Exception e) {
throw new IllegalStateException("Unexpected problem processing bytes for class", e);
}
}
private boolean processConstantPoolEntry(int index, DataInputStream dis) throws IOException {
byte b = dis.readByte();
type[index] = b;
switch (b) {
case CONSTANT_Utf8:
// CONSTANT_Utf8_info {
// u1 tag;
// u2 length;
// u1 bytes[length];
// }
cpdata[index] = dis.readUTF();
if (DEBUG) {
System.out.println(index + ":UTF8[" + cpdata[index] + "]");
}
break;
case CONSTANT_Integer:
// CONSTANT_Integer_info {
// u1 tag;
// u4 bytes;
// }
if (DEBUG) {
int i = dis.readInt();
if (DEBUG) {
System.out.println(index + ":INTEGER[" + i + "]");
}
}
else {
dis.skip(4);
}
break;
case CONSTANT_Float:
// CONSTANT_Float_info {
// u1 tag;
// u4 bytes;
// }
if (DEBUG) {
float f = dis.readFloat();
if (DEBUG) {
System.out.println(index + ":FLOAT[" + f + "]");
}
}
else {
dis.skip(4);
}
break;
case CONSTANT_Long:
// CONSTANT_Long_info {
// u1 tag;
// u4 high_bytes;
// u4 low_bytes;
// }
if (DEBUG) {
long l = dis.readLong();
if (DEBUG) {
System.out.println(index + ":LONG[" + l + "]");
}
}
else {
dis.skip(8);
}
return true;
case CONSTANT_Double:
// CONSTANT_Double_info {
// u1 tag;
// u4 high_bytes;
// u4 low_bytes;
// }
if (DEBUG) {
double d = dis.readDouble();
if (DEBUG) {
System.out.println(index + ":DOUBLE[" + d + "]");
}
}
else {
dis.skip(8);
}
return true;
case CONSTANT_Class:
// CONSTANT_Class_info {
// u1 tag;
// u2 name_index;
// }
cpdata[index] = (int) dis.readShort();
if (DEBUG) {
System.out.println(index + ":CLASS[name_index=" + cpdata[index] + "]");
}
break;
case CONSTANT_String:
// CONSTANT_String_info {
// u1 tag;
// u2 string_index;
// }
if (DEBUG) {
cpdata[index] = (int) dis.readShort();
if (DEBUG) {
System.out.println(index + ":STRING[string_index=" + cpdata[index] + "]");
}
}
else {
dis.skip(2);
}
break;
case CONSTANT_Fieldref:
// CONSTANT_Fieldref_info {
// u1 tag;
// u2 class_index;
// u2 name_and_type_index;
// }
if (DEBUG) {
cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
if (DEBUG) {
System.out.println(index + ":FIELDREF[class_index=" + ((int[]) cpdata[index])[0]
+ ",name_and_type_index="
+ ((int[]) cpdata[index])[1] + "]");
}
}
else {
dis.skip(4);
}
break;
case CONSTANT_Methodref:
// CONSTANT_Methodref_info {
// u1 tag;
// u2 class_index;
// u2 name_and_type_index;
// }
if (DEBUG) {
cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
if (DEBUG) {
System.out.println(index + ":METHODREF[class_index=" + ((int[]) cpdata[index])[0]
+ ",name_and_type_index="
+ ((int[]) cpdata[index])[1] + "]");
}
}
else {
dis.skip(4);
}
break;
case CONSTANT_InterfaceMethodref:
// CONSTANT_InterfaceMethodref_info {
// u1 tag;
// u2 class_index;
// u2 name_and_type_index;
// }
if (DEBUG) {
cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
if (DEBUG) {
System.out.println(index + ":INTERFACEMETHODREF[class_index=" + ((int[]) cpdata[index])[0]
+ ",name_and_type_index=" + ((int[]) cpdata[index])[1] + "]");
}
}
else {
dis.skip(4);
}
break;
case CONSTANT_NameAndType:
// The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:
// CONSTANT_NameAndType_info {
// u1 tag;
// u2 name_index;
// u2 descriptor_index;
// }
if (DEBUG) {
cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
if (DEBUG) {
System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0]
+ ",descriptor_index="
+ ((int[]) cpdata[index])[1] + "]");
}
}
else {
dis.skip(4);
}
break;
default:
throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
}
return false;
}
}