CodeChickenCoreTransformer

基本信息

属性 值
类 com.mitchej123.jarjar.rfb.transformer.CodeChickenCoreTransformer
Transformer ID jarjar-codechickencoreTransformer
目标类 codechicken.core.ClassDiscoverer
改写方法 findClasspathMods

CodeChickenCore 的 ClassDiscoverer 在 1.7.10 modpack 里会被多次触发——它会扫描 classpath 上的 jar/zip 并把它们加入 NEI / 其它的 discovery。JarJar 已经处理过 <mc>/mods/,不希望 CCC 又把这些 jar 当作"classpath mod"二次加载。

修改模式

原版 findClasspathMods 形如:

if (!knownLibraries.contains(minecraftSource.getName())) {
    // 把这个 jar 当作 classpath mod 加入
}

JarJar 在第一个 ifne 后注入:

&& Hooks.shouldIgnore(minecraftSource)

使得"已知库"或"JarJar 禁用"的 jar 直接跳过。

ASM 模式匹配

for (final MethodNode mn : cn.methods) {
    if (!mn.name.equals("findClasspathMods")) continue;
    AbstractInsnNode insn = mn.instructions.getFirst();
    while (insn != null) {
        if (insn.getOpcode() == Opcodes.INVOKEVIRTUAL && insn instanceof MethodInsnNode mInsn
            && mInsn.owner.equals("java/io/File") && mInsn.name.equals("getName")) {
            final int fileVar = ((VarInsnNode)(insn.getPrevious())).var;
            insn = insn.getNext();
            if (!(insn instanceof MethodInsnNode)) continue;
            mInsn = (MethodInsnNode) insn;
            if (!mInsn.owner.equals("java/util/List") && mInsn.name.equals("contains")) continue;

            insn = insn.getNext();
            if (!(insn.getOpcode() == Opcodes.IFNE && insn instanceof JumpInsnNode jInsnNode)) continue;
            final LabelNode ifneJumpLabel = jInsnNode.label;

            insn = insn.getNext(); // label
            mn.instructions.insertBefore(insn, new VarInsnNode(Opcodes.ALOAD, fileVar));
            insn = insn.getNext(); // linenumber
            insn = insn.getNext(); // LDC
            mn.instructions.insertBefore(
                insn,
                new MethodInsnNode(Opcodes.INVOKESTATIC, "com/mitchej123/jarjar/util/Hooks", "shouldIgnore", "(Ljava/io/File;)Z", false));
            mn.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.IFEQ, ifneJumpLabel));
            break;
        }
        insn = insn.getNext();
    }
}

按字节码顺序找到 INVOKEVIRTUAL java/io/File.getName() → 紧邻的 INVOKEVIRTUAL java/util/List.contains(...) → 紧邻的 IFNE <label>,把 IFNE <label> 之后插入 ALOAD fileVar + INVOKESTATIC Hooks.shouldIgnore + IFEQ <label>。效果是:

if (!knownLibraries.contains(file.getName()) && !Hooks.shouldIgnore(file)) { ... }

Hooks.shouldIgnore 实现:

public static boolean shouldIgnore(File file) {
    return DefaultLibraries.isDefaultLibrary(file) || DirectoryModCandidateFinder.getDisabledFiles().contains(file.getName());
}

相关条目