Search code examples
javabytecodejavassist

Javassist - How to add line number to method


I am a newbie to java bytecode and javassist. I created a new class file with using javassist. Although I added fields and methods, I couldn't achieve to add line number to method. Result of my research, I understand that I need to add linenumberattribute to codeattribute of method info. Moreover, linenumberattribute consists of linenumbertable. I don't know how can I create a new linenumberattribute with javassist.


Solution

  • I am writing a compiler that produces JVM code. I need line numbers in the output. I do it this way.

    I build up a list of objects similar to this:

    public class MyLineNum {
        public final short pc;
        public final short lineNum;
    }
    

    Then I add the line number table:

    final ClassFile  classFile = ...;
    final ConstPool  constPool = classFile.getConstPool();
    final MethodInfo minfo     = new MethodInfo( ... );
    final Bytecode   code      = new Bytecode( constPool );
    ... code that writes to 'code'
    
    final List<MyLineNum> lineNums = new ArrayList<>();
    ... code that adds to 'lineNums'
    
    final CodeAttribute codeAttr = code.toCodeAttribute();
    if ( !lineNums.isEmpty() ) {
        // JVM spec describes method line number table thus:
        //    u2 line_number_table_length;
        //    { u2 start_pc;
        //      u2 line_number;
        //    } line_number_table[ line_number_table_length ];
        final int    numLineNums = lineNums.size();
        final byte[] lineNumTbl  = new byte[ ( numLineNums * 4 ) + 2 ];
    
        // Write line_number_table_length.
        int byteIx = 0;
        ByteArray.write16bit( numLineNums, lineNumTbl, byteIx );
        byteIx += 2;
    
        // Write the individual line number entries.
        for ( final MyLineNum ln : lineNums) {
            // start_pc
            ByteArray.write16bit( ln.pc, lineNumTbl, byteIx );
            byteIx += 2;
            // line_number
            ByteArray.write16bit( ln.lineNum, lineNumTbl, byteIx );
            byteIx += 2;
        }
    
        // Add the line number table to the CodeAttribute.
        @SuppressWarnings("unchecked")
        final List<AttributeInfo> codeAttrAttrs = codeAttr.getAttributes();
        codeAttrAttrs.removeIf( ( ai ) -> ai.getName().equals( "LineNumberTable" ) );       // remove if already present
        codeAttrAttrs.add( new AttributeInfo( constPool, "LineNumberTable", lineNumTbl ) );
    }
    
    // Attach the CodeAttribute to the MethodInfo.
    minfo.setCodeAttribute( codeAttr );
    
    // Attach the MethodInfo to the ClassFile.
    try {
        classFile.addMethod( minfo );
    }
    catch ( final DuplicateMemberException ex ) {
        throw new AssertionError( "Caught " + ex, ex );
    }