Re: ASM4 / Java 7
Jeremy Manson <[email protected]>
| Newsgroups | gmane.comp.java.objectweb.asm |
|---|---|
| Message-ID | <CAHF5=kQfRMBi6B+mfOZ9o9AvGvJWjuQONgvCQySYUbM=LnRu_A@mail.gmail.com> |
Thanks, Eliot. That is what I was talking about (sorry if I was unclear), and I already have very similar code. My question was whether such code belongs in the core ASM libraries, so that people don't need to rewrite it constantly. Specifically, I found that with Java 7, I couldn't rewrite any meaningful applications without it. Find my version, mostly copied from the ASM test, attached. I don't really want to redistribute something that is mostly copied from the ASM test with my project if I can avoid it... Jeremy On Fri, Aug 26, 2011 at 4:54 PM, Eliot Moss <[email protected]> wrote: > Jeremy -- I think you're talking about the need to compute > the common superclass of two classes. I wrote some code > to do that. It uses a static table to remember some facts > about classes that our rewriting agent sees, and so will > have what it needs to know about the superclasses. It does > not need to *load* the classes, only to have seen them > come by the transformer, and superclasses are always seen > before the subclasses. > > Most of the code of the class I attach is relevant; the > visitMethod method may not be for you. > > Maybe there are more elegant ways to do this, but it seems > to work for the cases we have run so far. > > Best wishes -- Eliot Moss >
StaticClassWriter.java
(text/x-java, 6.6 KB)
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.io.InputStream;
import java.io.IOException;
/**
* A {@link ClassWriter} that looks for static class data in the
* classpath when the classes are not available at runtime.
*
* <p>ClassWriter uses class hierarchy information, which it gets by
* looking at loaded classes, to make some decisions about the best
* way to write classes. StaticClassWriter fails over to looking for
* the class hierarchy information in the ClassLoader's resources
* (usually the classpath) if the class it needs hasn't been loaded
* yet.
*
* <p>This class was heavily influenced by ASM's
* ClassWriterComputeFramesTest, which does almost exactly the same
* thing.
*/
class StaticClassWriter extends ClassWriter {
/* The classloader that we use to look for the unloaded class */
private final ClassLoader classLoader;
/*
* {@inheritDoc}
* @param classLoader the class loader that loaded this class
*/
public StaticClassWriter(
ClassReader classReader, int flags, ClassLoader classLoader) {
super(classReader, flags);
this.classLoader = classLoader;
}
/**
* {@inheritDoc}
*/
@Override protected String getCommonSuperClass(
final String type1, final String type2) {
try {
return super.getCommonSuperClass(type1, type2);
} catch (RuntimeException e) {
// Try something else...
}
// Exactly the same as in ClassWriter, but gets the superclass
// from the class file.
ClassInfo ci1, ci2;
try {
ci1 = new ClassInfo(type1, classLoader);
ci2 = new ClassInfo(type2, classLoader);
} catch (Throwable e) {
throw new RuntimeException(e);
}
if (ci1.isAssignableFrom(ci2)) {
return type1;
}
if (ci2.isAssignableFrom(ci1)) {
return type2;
}
if (ci1.isInterface() || ci2.isInterface()) {
return "java/lang/Object";
} else {
do {
// Should never be null, because if ci1 were the Object class
// or an interface, it would have been caught above.
ci1 = ci1.getSuperclass();
} while (!ci1.isAssignableFrom(ci2));
return ci1.getType().getInternalName();
}
}
/**
* For a given class, this stores the information needed by the
* getCommonSuperClass test. This determines if the class is
* available at runtime, and then, if it isn't, it tries to get the
* class file, and extract the appropriate information from that.
*/
static class ClassInfo {
private final Type type;
private final ClassLoader loader;
private final boolean isInterface;
private final String superClass;
private final String[] interfaces;
public ClassInfo(String type, ClassLoader loader) {
Class cls = null;
// First, see if we can extract the information from the class...
try {
cls = Class.forName(type);
} catch (Exception e) {
// failover...
}
if (cls != null) {
this.type = Type.getType(cls);
this.loader = loader;
this.isInterface = cls.isInterface();
this.superClass = cls.getSuperclass().getName();
Class[] ifs = cls.getInterfaces();
this.interfaces = new String[ifs.length];
for (int i = 0; i < ifs.length; i++) {
this.interfaces[i] = ifs[i].getName();
}
return;
}
// The class isn't loaded. Try to get the class file, and
// extract the information from that.
this.loader = loader;
this.type = Type.getObjectType(type);
String fileName = type.replace('.', '/') + ".class";
InputStream is = null;
ClassReader cr;
try {
is = (loader == null) ?
ClassLoader.getSystemResourceAsStream(fileName) :
loader.getResourceAsStream(fileName);
cr = new ClassReader(is);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
}
}
}
int offset = cr.header;
isInterface = (cr.readUnsignedShort(offset) & Opcodes.ACC_INTERFACE) != 0;
char[] buf = new char[2048];
// Read the superclass
offset += 4;
superClass = readConstantPoolString(cr, offset, buf);
// Read the interfaces
offset += 2;
int numInterfaces = cr.readUnsignedShort(offset);
interfaces = new String[numInterfaces];
offset += 2;
for (int i = 0; i < numInterfaces; i++) {
interfaces[i] = readConstantPoolString(cr, offset, buf);
offset += 2;
}
}
String readConstantPoolString(ClassReader cr, int offset, char[] buf) {
int cpIndex = cr.getItem(cr.readUnsignedShort(offset));
return (cpIndex == 0) ? null : cr.readUTF8(cpIndex, buf);
}
Type getType() {
return type;
}
ClassInfo getSuperclass() {
if (superClass == null) {
return null;
}
return new ClassInfo(superClass, loader);
}
/**
* Same as {@link Class#getInterfaces()}
*/
ClassInfo[] getInterfaces() {
if (interfaces == null) {
return new ClassInfo[0];
}
ClassInfo[] result = new ClassInfo[interfaces.length];
for (int i = 0; i < result.length; ++i) {
result[i] = new ClassInfo(interfaces[i], loader);
}
return result;
}
/**
* Same as {@link Class#isInterface}
*/
boolean isInterface() {
return isInterface;
}
private boolean implementsInterface(ClassInfo that) {
for (ClassInfo c = this; c != null; c = c.getSuperclass()) {
ClassInfo[] interfaces = c.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
ClassInfo iface = interfaces[i];
if (iface.type.equals(that.type) ||
iface.implementsInterface(that)) {
return true;
}
}
}
return false;
}
private boolean isSubclassOf(ClassInfo that) {
for (ClassInfo ci = this; ci != null; ci = ci.getSuperclass()) {
if (ci.getSuperclass() != null &&
ci.getSuperclass().type.equals(that.type)) {
return true;
}
}
return false;
}
/**
* Same as {@link Class#isAssignableFrom(Class)}
*/
boolean isAssignableFrom(ClassInfo that) {
if (this == that ||
that.isSubclassOf(this) ||
that.implementsInterface(this) ||
(that.isInterface()
&& getType().getDescriptor().equals("Ljava/lang/Object;"))) {
return true;
}
return false;
}
}
}
message-footer.txt
(text/plain, 238 B)
-- You receive this message as a subscriber of the [email protected] mailing list. To unsubscribe: mailto:[email protected] For general help: mailto:[email protected]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws