ASMModParserV2

基本信息

属性 值
类 com.mitchej123.jarjar.fml.common.discovery.asm.ASMModParserV2
父类 cpw.mods.fml.common.discovery.asm.ASMModParser
来源 Minecraft Forge + gtnhlib
关键扩展 捕获 binaryInterfaceNames,作为 ASMData 注入 table

ASMModParserV2 是 FML 原版 ASMModParser 的扩展。它不仅解析 @Mod/@SidedProxy 等已知注解,还把类实现的所有接口记录下来,注入 ASMDataTable,模拟新版 Forge 的 ASMDataTable 接口捕获行为。

字节码预扫

private static final ClassConstantPoolParser PRESCAN = new ClassConstantPoolParser("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations");
private static final String DUMMY_MARKER_INTERNAL_NAME = "$JarJarDummy";
private static final byte[] DUMMY_CLASS_BYTES = buildDummyClassBytes();

public ASMModParserV2(byte[] classBytes, String classEntry) throws IOException {
    super(new ByteArrayInputStream(PRESCAN.find(classBytes, false) ? classBytes : DUMMY_CLASS_BYTES));
    this.classEntry = classEntry;
    if (DUMMY_MARKER_INTERNAL_NAME.equals(getASMType().getInternalName())) {
        try {
            final ClassHeaderMetadata header = new ClassHeaderMetadata(classBytes);
            final List<String> ifaceNames = header.binaryInterfaceNames;
            beginNewTypeName(
                header.binaryThisName,
                header.majorVersion,
                header.binarySuperName,
                ifaceNames.toArray(new String[0]));
        } catch (IllegalArgumentException ignored) {}
    }
}

PRESCAN.find(classBytes, false) 快速判断 .class 是否有任何 annotation 段:

  • 有 → 直接喂给 ASMModParser 完整字节
  • 无 → 喂 8 字节 $JarJarDummy 占位类(绕过 ASMModParser 的字节消费);后续用 ClassHeaderMetadata 只读常量池取出 this/super/interfaces,调用 beginNewTypeName 把这些字段喂给 ASMModParser

ClassHeaderMetadata 是 rfb 库提供的字节码头解析器(无 ASM 树构建,开销极低),只解析常量池获取 binaryInterfaceNames 等字段。

发送 ASMData

@Override
public void sendToTable(ASMDataTable table, ModCandidate candidate) {
    super.sendToTable(table, candidate);
    if (interfaces == null) return;
    for (String intf : interfaces) {
        table.addASMData(candidate, intf, getASMType().getInternalName(), null, null);
    }
}

super.sendToTable 把 @Mod/@SidedProxy 等已知注解加入 table;V2 追加:把每条 interface 作为"伪注解"也加入 table。第二参数是注解描述符,此处用接口的 internal name(如 cpw/mods/fml/common/IFMLLoadingPlugin)。

这样 ASMDataTable.getAll("cpw/mods/fml/common/IFMLLoadingPlugin") 就能查出所有实现该接口的类——新版 Forge 提供的功能。

调用于 ModClassVisitorV2

beginNewTypeName 在 ASMModParserV2 中由 ModClassVisitorV2.visit() 触发:

public void beginNewTypeName(String typeQName, int classVersion, String superClassQName, String[] interfaces) {
    super.beginNewTypeName(typeQName, classVersion, superClassQName);
    if (this.interfaces == null) this.interfaces = Sets.newHashSet();
    Collections.addAll(this.interfaces, interfaces);
}

与 ClassConstantPoolParser 配合

ParallellModDiscoverer.ModScanTask.computeJarFile 优先用 entrySize 一次性读:

final long entrySize = entry.getSize();
if (entrySize >= 0) {
    final byte[] classBytes = new byte[(int) entrySize];
    classStream.readNBytes(classBytes, 0, classBytes.length);
    asmData = new ASMModParserV2(classBytes, entryName);
} else {
    asmData = new ASMModParserV2(classStream, entryName);
}

ASMModParserV2(InputStream) 走另一条构造函数,内部委托给 ASMModParser 自行从流读取。

相关条目