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

I support grappa . This package generates parsers at runtime from Java code, using ASM to create a class that extends the parser class.

I have already migrated from ASM 4 to ASM 5 and from the JVM 1.5 bytecode generator to generate JVM 1.6 bytecode, and now I just managed to create JVM 1.7 bytecode instead ... except that I don’t know why does it work.

Basically, I did the following:

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

Now, why I do not understand why it works for two reasons:

  • I have several MethodVisitor calls that read:

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

    Does this mean that these instructions can be deleted?

  • At first, I just tried and added the COMPUTE_FRAMES argument to the COMPUTE_FRAMES 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 this is an instance field, I believe this is due to the fact that this particular argument is initialized in the constructor?

In any case, all my tests now work, I'm in the process of testing more severe tests, etc. But since my goal is to go further, I would like to at least understand a little why my modifications worked all ...

+7
java java-bytecode-asm jvm bytecode-manipulation
source share
1 answer

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 method code. This map describes the stack layout at key points (jump targets) during the execution of the method. In previous versions of the JVM, you would have to compute this information, which is an expensive computing machine. By requesting this information, the JVM can simply check if frames work, which is much simpler than recalculating everything.

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

Now ClassWriter.COMPUTE_MAXS does something similar: the JVM required the class files to indicate the maximum stack size and the number of variables that each method uses, so that it can just check this, instead of calculating them themselves. This is useful for the same reason as for stack frames: it is a less expensive calculation.

So you really want to ! But, as you said, it failed when you tried to add them. The likely answer is the documentation for ClassWriter.COMPUTE_FRAMES (this is the first place you should watch when embarrassed): it says "computeFrames means computeMaxs" . So just specify ClassWriter.COMPUTE_FRAMES .

First question

With MethodVisitor s, MethodVisitor calls are still required. It is at this point that ASM recounts frames and maximum values. He will simply ignore the arguments you give him. So no, you cannot delete them. (Also note that this is not actually an instruction.)

Second question

I explained above why using COMPUTE_FRAMES , which is the key part here. I don’t know exactly why specifying both flags will break your tests. If you can provide the exact code for what you did there, perhaps it will be possible. I did some source diving in the ClassWriter / MethodWriter , and there seems to be no reason why both pointers could break your code.

+7
source share

All Articles