Category Archives: Compiler Construction

Compiler Construction, WS1516

Some hints to the homework:

I use class JavaWrapper from the BCEL Library:

   public static void main(String[] args)
   {
      TraceInfo traceInfo = new TraceInfo();
      JavaWrapper javaWrapper = new JavaWrapper(traceInfo);
      
      try
      {
         javaWrapper.runMain("de.uks.Compiler", new String[] {});
      }
      catch (ClassNotFoundException e)
      {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }

In some main method, I create a javaWrapper object.
In the constructor call, the JavaWrapper gets a special BCEL ClassLoader, in our case a TraceInfo object.

Then the javaWrapper is asked to run the main method of de.uks.Compiler, the class under test.

The javaWrapper uses its special classloader to load the target classes.

public class TraceInfo extends org.apache.bcel.util.ClassLoader
{
   @Override
   protected JavaClass modifyClass(JavaClass clazz)
   {
      String className = clazz.getClassName();
      
      if (className.startsWith("de.uks"))
      {
         System.out.println("Instrumenting " + className);

         ClassGen classGen = new ClassGen(clazz);
         
         ...
         return classGen.getJavaClass()
       }
       

The special BCEL classloader loads the byte code and then calls an internal method modifyClass() before passing the byte code to the JVM.
Thus, TraceInfo extends BCEL classloader and overrides method modifyClass()

modifyClass(JavaClass clazz) gets a JavaClass as paramter.
Here you may modify clazz with ClassGen as done by creating new classes earlier.

Ah yes, you need a main method in class Compiler, too:

   public static void main(String[] args)
   {
      Compiler compiler = new Compiler();
      try
      {
         compiler.testIfStat();
      }
      catch (Exception e)
      {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }

This is, where I am currently.

Albert

Update:

java 8 (and 7 and 6) do some more strict byte code verification. Thus, when you change your byte code, you may get LVTT Verify Error or StackMap Verify Error.
To get around this some very dirty hacks are necessary.

First, just before your modifyClass() method returns the modified clazz call:

   clazz.setMajor(50);

on it. This tells the JVM that your byte code conforms to Java 5 and thus most byte code verification is turned off and your instrumented code
will work just fine.

Well, the LVTT problem will still happen:

LVTT stands for LocalVariableTypeTable and is introduced in Java 1.7 for optimization.

The JVM can deal with it, if this information is just removed from your code.

Thus for each method you modify do

               methodGen.setMaxLocals();
               methodGen.setMaxStack();

               methodGen.removeLocalVariables();   // works cooler than the loop proposed earlier. Thanks to Niklas.
               
               Method newMethod = methodGen.getMethod();
              
               methods[i] = newMethod;

Thanks to Phillip for helping me to fix these problems.

Albert