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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.