jakarta-bcel-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Dmitri Colebatch" <...@colebatch.com>
Subject two questions: classloading, and NoSuchMethodFoundErrors on on-the-fly generated classes
Date Sun, 13 Oct 2002 15:05:19 GMT
Hey all,

I'm hoping this isn't too much of a newbie post - I have done a search
through the archives, and read the manual... if there's something else I
should have looked at before writing this, then please point me there (o:

First things first.  My requirements are to dynamically implement an
abstract class.  The test case I have (see end of email for my hacky src
code) is one where I create a class, and then load it using my classloader
(subclasses bcel.util.ClassLoader), and then two tests - try to call a
method through reflection, and try to cast it to the superclass.

reflection does all sorts of weird things:

add method = public void
com.colebatch.$$BCEL$$GeneratedObject.add(java.lang.Object)
java.lang.reflect.InvocationTargetException: java.lang.NoSuchMethodError
 at com.colebatch.$$BCEL$$GeneratedObject.add(<generated>)

which comes from this bit of code:

    java.lang.reflect.Method method = o.getClass().getMethod("add", new
Class[] { Object.class } );
    System.out.println("add method = " + method);
    method.invoke(o, new Object[] {"foo"});

which I find _really_ weird, I would have expected getMethod(..) line to
fail, but we get passed that, and then apparently the method doesn't exist.

casting behaves a little more understandably.... in that, it does't cast.
Simple though, because the superclass is loaded by two different class
loaders:

TestSuperClass.class = sun.misc.Launcher$AppClassLoader@71732b
clazz.getSuperclass() = com.colebatch.TestBCEL$1@70eed6

(from code:)

      System.out.println("TestSuperClass.class = " +
TestSuperClass.class.getClassLoader());
      System.out.println("clazz.getSuperclass() = " +
clazz.getSuperclass().getClassLoader());

I assume what happes here is that the bcel classloader loads the child
class, and all its superclasses....  looking through the code, modifyClass
(whilst not doing anything) ensures that the class is loaded by the bcel cl.
Am I being naive/stupid thinking that I can use on-the-fly generated classes
without using JavaWrapper?  At this stage there's no real reason why I
couldn't use it, but I'd prefer to be as hands-off as I can be at this
point.

any feedback on either of the above issues would be greatly appreciated.

cheers
dim

------------------- src code here -----------------

package com.colebatch;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.RETURN;
import org.apache.bcel.generic.Type;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestBCEL extends TestCase implements Constants
{
  public TestBCEL(String name)
  {
    super(name);
  }

  public static Test suite()
  {
    return new TestSuite(TestBCEL.class);
  }

  public static void main(String[] args) throws Exception
  {
    new TestBCEL("foo").testBCEL();
  }


//-------------------------------------------------------------------------
  // tests

  public void testBCEL()
    throws Exception
  {
    final String superClassName = "com.colebatch.TestSuperClass";
    final String className = "com.colebatch.$$BCEL$$GeneratedObject";

    ClassGen cg = new ClassGen(className, superClassName,
                               "<generated>", ACC_PUBLIC | ACC_SUPER,
                               null);
    ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool

    String ctxFieldName = "list";
    ObjectType ctxType = new ObjectType("java.util.List");

    addField(ctxType, ctxFieldName, cp, cg);
    addConstructor(cg, cp, superClassName, className, ctxFieldName,
ctxType);
    addMethod(cg, cp, className, ctxFieldName, ctxType);

    final Map classes = new HashMap();

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    cg.getJavaClass().dump(baos);
    classes.put(className, baos.toByteArray());

    org.apache.bcel.util.ClassLoader cl = new
org.apache.bcel.util.ClassLoader()
    {
      protected JavaClass createClass(String class_name)
      {
        byte[]      bytes  = (byte[]) classes.get(class_name);
        ClassParser parser = new ClassParser(new
ByteArrayInputStream(bytes), "<generated>");

        JavaClass clazz = null;

        try
        {
          clazz = parser.parse();

          dump(clazz, null, class_name);

          // Adapt the class name to the passed value
          ConstantPool cp = clazz.getConstantPool();

          ConstantClass cl =
(ConstantClass)cp.getConstant(clazz.getClassNameIndex(),
                       Constants.CONSTANT_Class);
          ConstantUtf8 name =
(ConstantUtf8)cp.getConstant(cl.getNameIndex(),
                       Constants.CONSTANT_Utf8);
          name.setBytes(class_name.replace('.', '/'));

          return clazz;
        }
        catch (IOException e)
        {
          throw new RuntimeException(e.getMessage());
        }
      }
    };
    Class clazz = cl.loadClass(className);

    for (int i = 0; i < clazz.getMethods().length; i++)
    {
      java.lang.reflect.Method method = clazz.getMethods()[i];
      System.out.println("method = " + method);
    }

    List list = new ArrayList();
    Constructor constructor = clazz.getConstructor(new Class[]
{List.class});
    Object o = constructor.newInstance(new Object[] {list});
    if (o instanceof TestSuperClass)
    {
      ((TestSuperClass) o).add("foo");
      assertEquals("length not 1", 1, list.size());
      assertEquals("first value not foo", "foo", list.get(0));
      list.remove(0);
    }
    else
    {
      System.out.println("not TestSuperClass");
      System.out.println("clazz.getSuperclass() = " +
clazz.getSuperclass());
      for (int i = 0; i < clazz.getInterfaces().length; i++)
      {
        Class aClass = clazz.getInterfaces()[i];
        System.out.println("implements " + aClass);
      }

      System.out.println("-------------");
      System.out.println("TestSuperClass.class = " +
TestSuperClass.class.getClassLoader());
      System.out.println("clazz.getSuperclass() = " +
clazz.getSuperclass().getClassLoader());
    }

    java.lang.reflect.Method method = o.getClass().getMethod("add", new
Class[] { Object.class } );
    System.out.println("add method = " + method);
    method.invoke(o, new Object[] {"foo"});
    assertEquals("length not 1", 1, list.size());
    assertEquals("first value not foo", "foo", list.get(0));

//    dump(className);
  }

  private void addField(ObjectType ctxType, String ctxFieldName,
ConstantPoolGen cp, ClassGen cg)
  {
    FieldGen fieldGen = new FieldGen(Constants.ACC_PUBLIC, // |
Constants.ACC_FINAL,
                                     ctxType,
                                     ctxFieldName,
                                     cp);
    Field field = fieldGen.getField();
    cg.addField(field);
  }

  public static void dump(String className, String methodName)
  {
    JavaClass clazz = Repository.lookupClass(className);
    dump(clazz, methodName, className);
  }

  private static void dump(JavaClass clazz, String methodName, String
className)
  {
    Method[] methods = clazz.getMethods();
    for (int i = 0; i < methods.length; i++)
    {
      Method method = methods[i];
      if (methodName != null && !method.getName().equals(methodName))
        continue;

      System.out.println("method = " + method);
      ConstantPoolGen cp = new ConstantPoolGen();
      MethodGen methodGen = new MethodGen(method, className, cp);
      InstructionList instructionList = methodGen.getInstructionList();
      Instruction[] instructions = instructionList.getInstructions();
      for (int j = 0; j < instructions.length; j++)
      {
        Instruction instruction = instructions[j];
        System.out.println("instruction = " + instruction);
      }

      System.out.println("");
      System.out.println("-----------------------------------");
      System.out.println("");
    }
  }

  private void addConstructor(ClassGen cg, ConstantPoolGen cp, final String
superClassName, final String className, String ctxFieldName, ObjectType
ctxType)
  {
    InstructionList il = new InstructionList();
    InstructionFactory factory = new InstructionFactory(cg, cp);
    il.append(new ALOAD(0));
    il.append(factory.createInvoke(superClassName, "<init>", Type.VOID, new
Type[0], Constants.INVOKESPECIAL));
    il.append(new ALOAD(0)); // load ctx
    il.append(new ALOAD(1)); // load ctx
    il.append(factory.createPutField(className, ctxFieldName, ctxType));
    il.append(new RETURN());
    MethodGen constructorGen = new MethodGen(Constants.ACC_PUBLIC,
                                             Type.VOID,
                                             new Type[]{ctxType},
                                             new String[]{"list"},
                                             "<init>",
                                             className,
                                             il,
                                             cp);
    constructorGen.setMaxStack();
    cg.addMethod(constructorGen.getMethod());

    il.dispose();
  }


  private void addMethod(ClassGen cg, ConstantPoolGen cp, String className,
String ctxFieldName, ObjectType ctxType)
  {
//    generated:
//    method = public void add(Object o)
//    instruction = aload_0[42](1)
//    instruction = getfield[180](3) 14
//    instruction = aload_1[43](1)
//    instruction = invokeinterface[185](5) 25
//    instruction = pop[87](1)
//    instruction = return[177](1)
//
//    -----------------------------------
//
//    coded:
//    method = public void add(Object o)
//    instruction = aload_0[42](1)
//    instruction = getfield[180](3) 109
//    instruction = aload_1[43](1)
//    instruction = invokeinterface[185](5) 110
//    instruction = pop[87](1)
//    instruction = return[177](1)

    InstructionList il = new InstructionList();
    InstructionFactory factory = new InstructionFactory(cg, cp);
    il.append(new ALOAD(0));
    il.append(factory.createGetField(className, ctxFieldName, ctxType));
    il.append(new ALOAD(1));
    il.append(factory.createInvoke("java.util.List", "add", Type.VOID, new
Type[] {Type.OBJECT}, Constants.INVOKEINTERFACE));
//    il.append(new POP());
    il.append(new RETURN());
    MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
                                             Type.VOID,
                                             new Type[] {Type.OBJECT},
                                             new String[]{"o"},
                                             "add",
                                             className,
                                             il,
                                             cp);
    mg.setMaxStack();
    cg.addMethod(mg.getMethod());

    il.dispose();
  }

}


package com.colebatch;

public abstract class TestSuperClass
{
  public TestSuperClass()
  {
    System.out.println("TestSuperClass.<init>()");
  }

  public abstract void add(Object o);
}


--
To unsubscribe, e-mail:   <mailto:bcel-user-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:bcel-user-help@jakarta.apache.org>


Mime
View raw message