new variant of ConstantPoolChecker called ConstantPoolScanner

This commit is contained in:
Andy Clement
2016-04-11 12:45:31 -07:00
parent cfb3a22b8c
commit a2d9606b15
4 changed files with 609 additions and 299 deletions

View File

@@ -0,0 +1,400 @@
/*
* 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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// TODO try to recall why I created ConstantPoolChecker2, what was up with ConstantPoolChecker?
// http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
/**
* Enables us to check things quickly in the constant pool. This version accumulates the class references and the method
* references, for classes that start with 'j' (we want to catch: java/lang). It skips everything it can and the end
* result is a list of class references and a list of method references. The former look like this 'a/b/C' whilst the
* latter look like this 'java/lang/Foo.bar' (the descriptor for the method is not included). Interface methods are
* skipped.
*
* @author Andy Clement
* @since 0.7.3
*/
public class ConstantPoolChecker2 {
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;
private final static byte CONSTANT_MethodHandle = 15;
private final static byte CONSTANT_MethodType = 16;
private final static byte CONSTANT_InvokeDynamic = 18;
// 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();
// File[] fs = new File("/Users/aclement/grailsreload/foo/target/classes").listFiles();
checkThemAll(fs);
System.out.println("total=" + total / 1000000d);
}
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();
References refs = getReferences(data);
total += (System.nanoTime() - stime);
System.out.println(refs.referencedClasses);
System.out.println(refs.referencedMethods);
}
}
}
static long total = 0;
// 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 References getReferences(byte[] bytes) {
ConstantPoolChecker2 cpc2 = new ConstantPoolChecker2(bytes);
return new References(cpc2.slashedclassname, cpc2.referencedClasses, cpc2.referencedMethods);
}
static class References {
String slashedClassName;
List<String> referencedClasses;
List<String> referencedMethods;
References(String slashedClassName, List<String> rc, List<String> rm) {
this.slashedClassName = slashedClassName;
this.referencedClasses = rc;
this.referencedMethods = rm;
}
}
// 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<String> referencedClasses = new ArrayList<String>();
private List<String> referencedMethods = new ArrayList<String>();
private String slashedclassname;
private ConstantPoolChecker2(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;
case CONSTANT_Methodref:
int[] indexes = (int[]) cpdata[i];
int classindex2 = indexes[0];
int nameAndTypeIndex = indexes[1];
StringBuilder s = new StringBuilder();
String theClassName = (String) cpdata[(Integer) cpdata[classindex2]];
if (theClassName.charAt(0) == 'j') {
s.append(theClassName);
s.append(".");
s.append((String) cpdata[(Integer) cpdata[nameAndTypeIndex]]);
referencedMethods.add(s.toString());
}
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_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); // skip minor and major versions
cpsize = dis.readShort();
if (DEBUG) {
System.out.println("Constant Pool Size =" + cpsize);
}
cpdata = new Object[cpsize];
type = new int[cpsize];
for (int cpentry = 1; cpentry < cpsize; cpentry++) {
boolean doubleSlot = processConstantPoolEntry(cpentry, dis);
if (doubleSlot) {
cpentry++;
}
}
dis.skip(2); // access flags
int thisclassname = dis.readShort();
int classindex = ((Integer) cpdata[thisclassname]);
slashedclassname = (String) cpdata[classindex];
}
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();
switch (b) {
case CONSTANT_Utf8:
// CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
cpdata[index] = dis.readUTF();
// type[index] = b;
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; }
type[index] = b;
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; }
type[index] = b;
//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; }
// type[index] = b;
cpdata[index] = (int) dis.readShort();// new int[] { dis.readShort(), dis.readShort() };
dis.skip(2); // skip the descriptor for now
if (DEBUG) {
System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0]
+ ",descriptor_index="
+ ((int[]) cpdata[index])[1] + "]");
}
break;
case CONSTANT_InvokeDynamic:
//CONSTANT_InvokeDynamic_info {
// u1 tag;
// u2 bootstrap_method_attr_index;
// u2 name_and_type_index;
//}
dis.skipBytes(4);
break;
case CONSTANT_MethodHandle:
//CONSTANT_MethodHandle_info {
// u1 tag;
// u1 reference_kind;
// u2 reference_index;
//}
dis.skipBytes(3);
break;
case CONSTANT_MethodType:
//CONSTANT_MethodType_info {
// u1 tag;
// u2 descriptor_index;
//}
dis.skipBytes(2);
break;
default:
throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
}
return false;
}
}

View File

@@ -16,28 +16,24 @@
package org.springsource.loaded;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
// TODO try to recall why I created ConstantPoolChecker2, what was up with ConstantPoolChecker?
// http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
/**
* Enables us to check things quickly in the constant pool. This version accumulates the class references and the method
* references, for classes that start with 'j' (we want to catch: java/lang). It skips everything it can and the end
* result is a list of class references and a list of method references. The former look like this 'a/b/C' whilst the
* latter look like this 'java/lang/Foo.bar' (the descriptor for the method is not included). Interface methods are
* skipped.
*
*
* Useful reference: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
*
* @author Andy Clement
* @since 0.7.3
*/
public class ConstantPoolChecker2 {
public class ConstantPoolScanner {
private static final boolean DEBUG = false;
@@ -69,35 +65,37 @@ public class ConstantPoolChecker2 {
private final static byte CONSTANT_InvokeDynamic = 18;
// 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();
// File[] fs = new File("/Users/aclement/grailsreload/foo/target/classes").listFiles();
private byte[] classbytes;
checkThemAll(fs);
System.out.println("total=" + total / 1000000d);
// Used during the parse step
private int ptr;
// 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<String> referencedClasses = new ArrayList<String>();
private List<String> referencedMethods = new ArrayList<String>();
private String slashedclassname;
public static References getReferences(byte[] classbytes) {
ConstantPoolScanner cpScanner = new ConstantPoolScanner(classbytes);
return new References(cpScanner.slashedclassname, cpScanner.referencedClasses, cpScanner.referencedMethods);
}
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();
References refs = getReferences(data);
total += (System.nanoTime() - stime);
System.out.println(refs.referencedClasses);
System.out.println(refs.referencedMethods);
}
}
private ConstantPoolScanner(byte[] bytes) {
parseClass(bytes);
computeReferences();
}
static long total = 0;
// Format of a classfile:
// ClassFile {
// u4 magic;
// u2 minor_version;
@@ -116,52 +114,137 @@ public class ConstantPoolChecker2 {
// u2 attributes_count;
// attribute_info attributes[attributes_count];
// }
static References getReferences(byte[] bytes) {
ConstantPoolChecker2 cpc2 = new ConstantPoolChecker2(bytes);
return new References(cpc2.slashedclassname, cpc2.referencedClasses, cpc2.referencedMethods);
}
static class References {
String slashedClassName;
List<String> referencedClasses;
List<String> referencedMethods;
References(String slashedClassName, List<String> rc, List<String> rm) {
this.slashedClassName = slashedClassName;
this.referencedClasses = rc;
this.referencedMethods = rm;
private void parseClass(byte[] bytes) {
try {
this.classbytes = bytes;
this.ptr = 0;
int magic = readInt(); // magic 0xCAFEBABE
if (magic != 0xCAFEBABE) {
throw new IllegalStateException("not bytecode, magic was 0x" + Integer.toString(magic, 16));
}
ptr += 4; // skip minor and major versions
cpsize = readUnsignedShort();
if (DEBUG) {
System.out.println("Constant Pool Size =" + cpsize);
}
cpdata = new Object[cpsize];
type = new int[cpsize];
for (int cpentry = 1; cpentry < cpsize; cpentry++) {
boolean wasDoubleSlotItem = processConstantPoolEntry(cpentry);
if (wasDoubleSlotItem) {
cpentry++;
}
}
ptr += 2; // access flags
int thisclassname = readUnsignedShort();
int classindex = ((Integer) cpdata[thisclassname]);
slashedclassname = accessUtf8(classindex);
}
catch (Exception e) {
throw new IllegalStateException("Unexpected problem processing bytes for class", e);
}
}
// 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<String> referencedClasses = new ArrayList<String>();
private List<String> referencedMethods = new ArrayList<String>();
private String slashedclassname;
private ConstantPoolChecker2(byte[] bytes) {
readConstantPool(bytes);
computeReferences();
/**
* Return the UTF8 at the specified index in the constant pool. The data found at the constant pool for that index
* may not have been unpacked yet if this is the first access of the string. If not unpacked the constant pool entry
* is a pair of ints in an array representing the offset and length within the classbytes where the UTF8 string is
* encoded. Once decoded the constant pool entry is flipped from an int array to a String for future fast access.
*
* @param cpIndex constant pool index
* @return UTF8 string at that constant pool index
*/
private String accessUtf8(int cpIndex) {
Object object = cpdata[cpIndex];
if (object instanceof String) {
return (String) object;
}
int[] ptrAndLen = (int[]) object;
String value;
try {
value = new String(classbytes, ptrAndLen[0], ptrAndLen[1], "UTF8");
}
catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Bad data found at constant pool position " + cpIndex + " offset="
+ ptrAndLen[0] + " length=" + ptrAndLen[1], e);
}
cpdata[cpIndex] = value;
return value;
}
public void computeReferences() {
/**
* @return an int constructed from the next four bytes to be processed
*/
private final int readInt() {
return ((classbytes[ptr++] & 0xFF) << 24) + ((classbytes[ptr++] & 0xFF) << 16)
+ ((classbytes[ptr++] & 0xFF) << 8)
+ (classbytes[ptr++] & 0xFF);
}
/**
* @return an unsigned short constructed from the next two bytes to be processed
*/
private final int readUnsignedShort() {
return ((classbytes[ptr++] & 0xff) << 8) + (classbytes[ptr++] & 0xff);
}
private boolean processConstantPoolEntry(int index) throws IOException {
byte b = classbytes[ptr++];
switch (b) {
case CONSTANT_Integer: // CONSTANT_Integer_info { u1 tag; u4 bytes; }
case CONSTANT_Float: // CONSTANT_Float_info { u1 tag; u4 bytes; }
case CONSTANT_Fieldref: // CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
case CONSTANT_InterfaceMethodref: // CONSTANT_InterfaceMethodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
case CONSTANT_InvokeDynamic: // CONSTANT_InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }
ptr += 4;
break;
case CONSTANT_Utf8:
// CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
// Cache just the index and length - do not unpack it now
int len = readUnsignedShort();
cpdata[index] = new int[] { ptr, len };
ptr += len;
break;
case CONSTANT_Long: // CONSTANT_Long_info { u1 tag; u4 high_bytes; u4 low_bytes; }
case CONSTANT_Double: // CONSTANT_Double_info { u1 tag; u4 high_bytes; u4 low_bytes; }
ptr += 8;
return true;
case CONSTANT_Class: // CONSTANT_Class_info { u1 tag; u2 name_index; }
type[index] = b;
cpdata[index] = readUnsignedShort();
break;
case CONSTANT_Methodref:
// CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
type[index] = b;
cpdata[index] = new int[] { readUnsignedShort(), readUnsignedShort() };
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; }
// type[index] = b;
cpdata[index] = readUnsignedShort();
ptr += 2; // skip the descriptor for now
break;
case CONSTANT_MethodHandle:
// CONSTANT_MethodHandle_info { u1 tag; u1 reference_kind; u2 reference_index; }
ptr += 3;
break;
case CONSTANT_String: // CONSTANT_String_info { u1 tag; u2 string_index; }
case CONSTANT_MethodType: // CONSTANT_MethodType_info { u1 tag; u2 descriptor_index; }
ptr += 2;
break;
default:
throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
}
return false;
}
private 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];
String classname = accessUtf8(classindex);
if (classname == null) {
throw new IllegalStateException();
}
@@ -172,229 +255,50 @@ public class ConstantPoolChecker2 {
int classindex2 = indexes[0];
int nameAndTypeIndex = indexes[1];
StringBuilder s = new StringBuilder();
String theClassName = (String) cpdata[(Integer) cpdata[classindex2]];
String theClassName = accessUtf8((Integer) cpdata[classindex2]);
if (theClassName.charAt(0) == 'j') {
s.append(theClassName);
s.append(".");
s.append((String) cpdata[(Integer) cpdata[nameAndTypeIndex]]);
s.append(accessUtf8((Integer) cpdata[nameAndTypeIndex]));
referencedMethods.add(s.toString());
}
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_InterfaceMethodref = 11;
// private final static byte CONSTANT_NameAndType = 12;
// 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_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); // skip minor and major versions
cpsize = dis.readShort();
if (DEBUG) {
System.out.println("Constant Pool Size =" + cpsize);
}
cpdata = new Object[cpsize];
type = new int[cpsize];
for (int cpentry = 1; cpentry < cpsize; cpentry++) {
boolean doubleSlot = processConstantPoolEntry(cpentry, dis);
if (doubleSlot) {
cpentry++;
}
}
dis.skip(2); // access flags
int thisclassname = dis.readShort();
int classindex = ((Integer) cpdata[thisclassname]);
slashedclassname = (String) cpdata[classindex];
public static class References {
public final String slashedClassName;
public final List<String> referencedClasses;
public final List<String> referencedMethods;
References(String slashedClassName, List<String> rc, List<String> rm) {
this.slashedClassName = slashedClassName;
this.referencedClasses = rc;
this.referencedMethods = rm;
}
catch (Exception e) {
throw new IllegalStateException("Unexpected problem processing bytes for class", e);
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("Class=").append(slashedClassName).append("\n");
s.append("ReferencedClasses=#").append(referencedClasses.size()).append("\n");
s.append("ReferencedMethods=#").append(referencedMethods.size()).append("\n");
return s.toString();
}
}
private boolean processConstantPoolEntry(int index, DataInputStream dis) throws IOException {
byte b = dis.readByte();
switch (b) {
case CONSTANT_Utf8:
// CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
cpdata[index] = dis.readUTF();
// type[index] = b;
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; }
type[index] = b;
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; }
type[index] = b;
//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; }
// type[index] = b;
cpdata[index] = (int) dis.readShort();// new int[] { dis.readShort(), dis.readShort() };
dis.skip(2); // skip the descriptor for now
if (DEBUG) {
System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0]
+ ",descriptor_index="
+ ((int[]) cpdata[index])[1] + "]");
}
break;
case CONSTANT_InvokeDynamic:
//CONSTANT_InvokeDynamic_info {
// u1 tag;
// u2 bootstrap_method_attr_index;
// u2 name_and_type_index;
//}
dis.skipBytes(4);
break;
case CONSTANT_MethodHandle:
//CONSTANT_MethodHandle_info {
// u1 tag;
// u1 reference_kind;
// u2 reference_index;
//}
dis.skipBytes(3);
break;
case CONSTANT_MethodType:
//CONSTANT_MethodType_info {
// u1 tag;
// u2 descriptor_index;
//}
dis.skipBytes(2);
break;
default:
throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
}
return false;
}
}

View File

@@ -26,7 +26,7 @@ import org.objectweb.asm.tree.MethodNode;
/**
* Captures the information about the reloaded parts of a type that vary each time a new version is loaded.
*
*
* @author Andy Clement
* @since 0.5.0
*/
@@ -101,6 +101,7 @@ public class CurrentLiveVersion {
&& GlobalConfiguration.classesToDump.contains(reloadableType.getSlashedName())) {
Utils.dump(Utils.getExecutorName(reloadableType.getName(), versionstamp).replace('.', '/'), this.executor);
}
// DEFAULT METHODS - REMOVE THE IF
if (!typeDescriptor.isInterface()) {
this.dispatcherName = Utils.getDispatcherName(reloadableType.getName(), versionstamp);
this.executorName = Utils.getExecutorName(reloadableType.getName(), versionstamp);
@@ -117,6 +118,7 @@ public class CurrentLiveVersion {
public void define() {
staticInitializer = null;
haveLookedForStaticInitializer = false;
// DEFAULT METHODS - remove the if
if (!typeDescriptor.isInterface()) {
try {
dispatcherClass = reloadableType.typeRegistry.defineClass(dispatcherName, dispatcher, false);
@@ -143,6 +145,7 @@ public class CurrentLiveVersion {
t.printStackTrace();
}
}
// DEFAULT METHODS - remove the if
if (!typeDescriptor.isInterface()) {
try {
dispatcherInstance = dispatcherClass.newInstance();

View File

@@ -60,7 +60,7 @@ import org.springsource.loaded.ri.ReflectiveInterceptor;
* </ul>
* The cache is for types that are *only* getting reflection interception done, not for types touching anything
* reloadable.
*
*
* @author Andy Clement
* @since 0.5.0
*/
@@ -83,7 +83,7 @@ public class MethodInvokerRewriter {
* Rewrite regular operations on reloadable types and any reflective calls.
* <p>
* Note: no caching is done here (the cache is not read or written to)
*
*
* @param typeRegistry the registry for which the rewriting is being done.
* @param bytes the bytes for the type to modify.
* @param skipReferencesCheck do we need to do a quick check to see if there is anything worth rewriting?
@@ -126,9 +126,9 @@ public class MethodInvokerRewriter {
if (b != null) {
if (b.booleanValue()) { // the type was modified on an earlier run, there should be cached code around
String cacheFileName = new StringBuilder(slashedClassName.replace('/', '_')).append("_").append(
bytes.length)
.append(".bytes").toString();
File cacheFile = new File(GlobalConfiguration.cacheDir, ".slcache" + File.separator + cacheFileName);
bytes.length).append(".bytes").toString();
File cacheFile = new File(GlobalConfiguration.cacheDir,
".slcache" + File.separator + cacheFileName);
if (DEBUG_CACHING) {
System.out.println("Checking for cache file " + cacheFile);
}
@@ -210,7 +210,7 @@ public class MethodInvokerRewriter {
/**
* Load the cache index from the file '&lt;cacheDir&gt;/.index'.
*
*
*/
private static void ensureCacheIndexLoaded() {
if (cacheIndex == null) {
@@ -286,9 +286,10 @@ public class MethodInvokerRewriter {
}
}
private static byte[] rewrite(boolean canCache, TypeRegistry typeRegistry, byte[] bytes, boolean skipReferencesCheck) {
private static byte[] rewrite(boolean canCache, TypeRegistry typeRegistry, byte[] bytes,
boolean skipReferencesCheck) {
// v1 - just looks at classes, if it sees jlClass or a jlr type it has to be cautious and assume a
// v1 - just looks at classes, if it sees jlClass or a jlr type it has to be cautious and assume a
// rewrite is necessary:
// List<String> classes = ConstantPoolChecker.getReferencedClasses(bytes);
// // System.out.println(classes);
@@ -664,10 +665,10 @@ public class MethodInvokerRewriter {
* Call this method to declare that a certain method is 'interceptable'. An interceptable method should have a
* corresponding interceptor method in {@link ReflectiveInterceptor}. The name and signature of the interceptor
* will be derived from the interceptable method.
*
*
* For example, java.lang.Class.getMethod(Class[] params) ==> ReflectiveInterceptor.jlClassGetMethod(Class thiz,
* Class[] params)
*
*
* @param owner Slashed class name of the declaring type.
* @param methodName Name of the interceptable method.
*/
@@ -720,7 +721,8 @@ public class MethodInvokerRewriter {
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.slashedclassname = name;
@@ -737,6 +739,7 @@ public class MethodInvokerRewriter {
}
}
@Override
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature,
final Object value) {
fieldcount++;
@@ -837,7 +840,7 @@ public class MethodInvokerRewriter {
// TODO write up how the code looks for these in a comment
/**
* code:
*
*
* <code>
* <pre>
* boolean b = TypeRegistry.instanceFieldInterceptionRequired(regId|classId,name)
@@ -971,14 +974,14 @@ public class MethodInvokerRewriter {
* <li>whether to rewrite it
* <li>what method should be called instead
* </ul>
*
*
* @return true if the call was modified/intercepted
*/
private boolean interceptReflection(String owner, String name, String desc) {
if (isInterceptable(owner, name)) {
//TODO: [...] this is probably a lot slower than unfolding this check into
//TODO: [...] this is probably a lot slower than unfolding this check into
// bunch of optimised if cases, but it is also much easier to manage.
// It should be possible to write something to generate the optimised
// It should be possible to write something to generate the optimised
// if's from the contents of the 'interceptable' HashSet. Measure before optimizing.
callReflectiveInterceptor(owner, name, desc, mv);
return true;
@@ -1021,7 +1024,7 @@ public class MethodInvokerRewriter {
/**
* Generate bytecode to convert parameters on the stack into an array (based on the descriptor). If the
* descriptor shows there are no parameters then null is stacked.
*
*
* @param descriptor MethodType descriptor showing parameters and return value
*/
private void stackParameters(String descriptor) {
@@ -1153,7 +1156,7 @@ public class MethodInvokerRewriter {
/**
* Determine if a method call is a reflective call and an attempt should be made to rewrite it.
*
*
* @return true if the call was rewritten
*/
private boolean rewriteReflectiveCall(int opcode, String owner, String name, String desc) {
@@ -1269,7 +1272,7 @@ public class MethodInvokerRewriter {
* Invokeinterface rewriting is done by calling the type registry to see if what we are about to do is OK.
* The method we call returns a boolean indicating whether it can be called directly or if we must direct it
* through the dynamic dispatch method.
*
*
*/
private void rewriteINVOKEINTERFACE(final int opcode, final String owner, final String name,
final String desc,