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 自行从流读取。
相关条目
- ModClassVisitorV2 - 调用 beginNewTypeName
- V2ConstructionReplacerTransformer - 替换
new ModClassVisitor()为 V2 - ASMDataTableV2 - 接收 addASMData 的 table 类型