Syntax error when adding Runnable in static initializer with javassist

519 views Asked by At

I want to use javassist (version 3.19-GA) to generate bytecode of a static initializer of a class that starts a thread. For some reason I cannot understand javassist expects a ";" somewhere even though I believe the code I provide is syntactically correct. Does someone see more than I do? Here is the code. What is the problem?

ClassPool pool = ClassPool.getDefault();
final CtClass clazz = pool.get(somename);
clazz.makeClassInitializer().insertAfter(
   "try{Runnable r=new Runnable () {public void run () { System.out.println (\"hello!!!!\"); }}; " +
   "new Thread(r).start(); } catch(Exception e){}");

I'm getting the following exception:

javassist.CannotCompileException: [source error] ; is missing
    at javassist.CtBehavior.insertAfter(CtBehavior.java:877)
    at javassist.CtBehavior.insertAfter(CtBehavior.java:792)
    at my.code(myclass.java:111)

Thanks for any hint.

2

There are 2 answers

1
davidxxx On

From the official documentation of void insertAfter(String src):

Parameters:

src - the source code representing the inserted bytecode. It must be a single statement or block.

In your src String parameter, you don't provide a single statement or a block.
A block is "{}".

Try insertBefore(String src) method with global enclosing "{}":

ClassPool pool = ClassPool.getDefault();
final CtClass clazz = pool.get(somename);
clazz.makeClassInitializer().insertBefore(
   "{try{Runnable r = new Runnable () {public void run () { System.out.println (\"hello!!!!\"); }}; " +
   "new Thread(r).start(); } catch(Exception e){}}");
0
light_keeper On

Most probably javassist compiler does not support anonymous inner classes like new Runnable () {...}

You have to create new class, inherit it from Runnable, implement method run and create object of this class in your constructor.

package hello;
import javassist.*;

class Test{
}

class Main {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        final CtClass clazz = pool.get(Test.class.getCanonicalName());

        CtClass runnable = pool.makeClass("my.custom.RunnableImpl");
        runnable.addInterface(pool.get("java.lang.Runnable"));
        CtMethod method = CtNewMethod.make("public void run() { System.out.println(\"hello!!!!\"); }", runnable);
        runnable.addMethod(method);

        // load class
        runnable.toClass();

        clazz.setName("newTest");
        CtConstructor ctConstructor = clazz.makeClassInitializer();
        ctConstructor.insertAfter("try{ new Thread( new my.custom.RunnableImpl() ).start(); } catch(Exception e){}");
        Class aClass = clazz.toClass();

        // call initializer
        Class.forName(aClass.getCanonicalName());
        Thread.sleep(1000);
    }
}