Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASM 5: when initializing a ClassWriter, what is the difference between COMPUTE_MAXS and COMPUTE_FRAMES?

I am the maintainer of grappa. This package generates parsers at runtime from Java code by using ASM to generate a class extending your parser class.

I have already migrated from ASM 4 to ASM 5, and from generating JVM 1.5 bytecode to generating JVM 1.6 bytecode, and now I have just succeeded in having it generate JVM 1.7 bytecode instead... except I have no idea why this works.

Basically, I did the following:

  • Changing the argument to the ClassWriter constructors; prior to that it was new ClassWriter(ClassWriter.COMPUTE_MAXS), and it is now new ClassWriter(ClassWriter.COMPUTE_FRAMES)
  • Changing the first argument of each call to the .visit() method from Opcodes.V1_6 to Opcodes.V1_7.

Now, why I don't understand why it works for two reasons:

  • I have several calls to MethodVisitors which read:

    mv.visitMaxs(0, 0); // trigger automatic computing
    

    Does that mean that these instructions can be removed?

  • At first I have only tried and added the COMPUTE_FRAMES argument to the ClassWriter constructors but it failed at one point for one of my tests in which I declare:

    static class TestJoinParser
        extends EventBusParser<Object>
    {
        protected final JoinMatcherBuilder builder
            = join('a').using('b');
    }
    

    And the error was:

    java.lang.ClassFormatError: Arguments can't fit into locals
    

    Given that it is an instance field I suppose it has to do with that particular argument being initialized in the constructor?

Anyway, all my tests now work, I am in the process of trying heavier tests etc... But as my goal is to go further, I'd like to understand at least a little as to why my modifications worked at all...

like image 614
fge Avatar asked Mar 29 '15 17:03

fge


1 Answers

First, some background on COMPUTE_FRAMES and COMPUTE_MAXS

ClassWriter.COMPUTE_MAXS has a different function than ClassWriter.COMPUTE_FRAMES.

In recent versions of the JVM, classes contain a stack map along with the method code. This map describes the layout of the stack at key points (jump targets) during the method's execution. In previous versions, the JVM would have to compute this information, which is computationally expensive. By requiring this information, the JVM can just verify that the frames work, which is significantly easier than recalculating everything.

Of course, then the compiler has to generate these frames. This is also difficult, so ASM contains ClassWriter.COMPUTE_FRAMES to allow this - it will then compute them for you.

Now, ClassWriter.COMPUTE_MAXS does something similar: the JVM has required the class files to specify the maximum stack size and number of variables that each method uses, so that it can just verify this instead of having to calculate these itself. This is useful for a similar reason to stack frames: it's less computationally expensive.

So, actually, you want both! But, as you said, it failed when you tried to add them. The probable answer lies in the documentation for ClassWriter.COMPUTE_FRAMES (which is the first place that you should look when confused about it): it says that "computeFrames implies computeMaxs". Therefore, you just just to specify ClassWriter.COMPUTE_FRAMES.

First question

With the MethodVisitors, the visitMaxs calls ARE still required. It is at this point that ASM will recalculate the frames and max values. It will just ignore the arguments that you give it. So, no, you cannot remove them. (Also, note that it's not actually an instruction.)

Second question

I explained above why it's sufficient to just use COMPUTE_FRAMES, which is the key part here. I'm not sure exactly why specifying both flags would break your tests. If you can provide exact code for what you did there, it might be possible to help. I did some source diving on the ClassWriter/MethodWriter source, and there doesn't seem to be any reason why specifying both would break your code.

like image 58
Cel Skeggs Avatar answered Oct 18 '22 14:10

Cel Skeggs