Javassist - 如何为方法添加行号

5

我是一个Java字节码和Javassist的新手。我使用Javassist创建了一个新的类文件。尽管我添加了字段和方法,但我无法添加到方法中的行号。通过我的研究结果,我了解到我需要将linenumberattribute添加到方法信息的codeattribute中。此外,linenumberattribute由linenumbertable组成。我不知道如何使用Javassist创建一个新的linenumberattribute。


2
我认为这是一个有趣的问题,而且投票者应该解释他/她的理由。 - AlexR
类中的LineNumberTable结构将类中的字节码映射到源文件中的行。由于您直接注入字节码,因此源文件中没有行可供映射。正因为如此,javassist不允许您创建LineNumberAttribute对象(它没有公共构造函数)...如果您真的有某种情况需要注入行号属性,请告诉我,我很好奇。此外,如果确实需要,我认为我有一些技巧可以解决问题。 - pabrantes
其实,我并不是真的需要那个 :) 但我很好奇如何做到。我的目标是在运行时创建新的节点实体和图形存储库类(我们可以假设实体和dao类似)。我需要使用javassist创建一个新类,并使用aspectj重新编译。为了做到这一点,我检查了使用spring-data-neo4j aspectj编译的java类,并意识到aspectj可以添加新的行号。但是,我无法使用javassist做到这一点。 - nsylmz
@pabrantes:Javassist允许您创建新方法甚至新类。那么,为什么您认为从某种源文件中创建或操作的描述是不可能的呢?Apache Xalan将XSLT文档编译成字节码,因此您有一个场景,其中将字节码与源文件和行号相关联是有意义的。但是他们使用BCEL... - Holger
@Holger:我觉得你误解了我的意思。我只是在说LineNumberTable通常包含源代码文件中行的信息。因此,如果您使用javassist创建新方法,那么LineNumberTable中可能会有什么有意义的信息呢?你提到的Xalan是回答这个问题的好例子。 - pabrantes
1个回答

3
我是一名编写生成JVM代码的编译器的开发者。我需要在输出中包含行号。我的做法是这样的:
我建立了一个类似于以下对象的列表:
public class MyLineNum {
    public final short pc;
    public final short lineNum;
}

然后我添加了行号表:
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 );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接