FieldLevelTessellatorTransformer
基本信息
| 属性 | 值 |
|---|---|
| 类 | ...loading.fml.compat.transformers.generic.FieldLevelTessellatorTransformer |
| 路径 | src/main/java/com/gtnewhorizons/angelica/loading/fml/compat/transformers/generic/FieldLevelTessellatorTransformer.java |
| 行数 | 88 |
| 类型 | public class(:17),不实现 IClassTransformer |
| 入口签名 | public static void transform(ClassNode cn, List<String> patchMethods)(:19) |
| 数据来源 | ICompatHandler.getFieldLevelTessellator() |
它是什么 / 挂在哪个阶段
不是 IClassTransformer,是被调用的静态工具。唯一调用点是 GenericCompatTransformer.transform() :72-74,而 GenericCompatTransformer 由 CompatHandlers.getTransformers() 在 handler 非空时追加进 AngelicaClientTweaker.getASMTransformerClass()。
所以它跑在 LaunchWrapper 类加载期(IClassTransformer#transform 内部),比 RFB 路径更晚、更可控。RFB 路径不提供它(见 AngelicaRfbPlugin)。
它改写什么
transform 对 patchMethods 列出的每个方法调用 injectLocalTessellatorAndReplaceFieldUsage(:26),做两件事:
第 1 步:找出「非 Minecraft 自有」的 Tessellator 字段(:28-41)
扫全量指令,收集 GETSTATIC/GETFIELD 且描述符为 Lnet/minecraft/client/renderer/Tessellator; 的指令,但排除 owner 为 net/minecraft/client/renderer/Tessellator 本身的(即排除 Tessellator.instance 的直接调用),记为 owner;name 形式的哈希串。
源码注释 :28-30 说清了意图:抓的是「任何人把它缓存进字段」的情况。
第 2 步:注入局部变量并重定向(:45-82)
若 fields 为空则立即 return(:43)——不动该方法。否则:
| 步骤 | 行 | 动作 |
|---|---|---|
| 分配局部槽 | :45 |
localIndex = mn.maxLocals |
登记 LocalVariableNode |
:47-54 |
名字 tes,范围 [startLabel, endLabel) |
| 插入初始化 | :56-67 |
GETSTATIC Tessellator.<obf("instance","field_78398_a")> → ASTORE localIndex |
| 重写引用 | :71-81 |
对第 1 步收集的每个哈希,在原 GETSTATIC/GETFIELD 之前插入 ALOAD localIndex,再删除原指令 |
混淆映射
:61 使用 AngelicaClientTweaker.obf("instance", "field_78398_a") —— 依赖 AngelicaClientTweaker 的 OBF_ENV 静态标志(由 injectData 从黑板 runtimeDeobfuscationEnabled 读入,:143)。这条路径与 AngelicaTweaker :15-21 注释里自陈的「index 0 导致看到 obfuscated 而非 SRG」缺陷同源。
已知问题
startLabel被插到方法最开头,endLabel追加到方法末尾(:57、:81)——局部变量tes的作用域因此覆盖整个方法,而不是首次使用点到方法末。功能上无害(只是调试信息),但在超大方法里会误导调试器。- 注入是无条件的:只要方法里出现一个缓存的 Tessellator 字段,就在方法第一条指令前插入
GETSTATIC+ASTORE。若该字段实际只在某个分支里被用到,方法开头仍会执行一次Tessellator.instance取值——每次调用都有微小开销。 - 只处理
GETSTATIC/GETFIELD:若某 mod 用PUTFIELD写入缓存的 Tessellator,第 1 步不会收集它,替换也不会发生(静默漏改)。 LocalVariableNode的index/desc顺序参数依赖 ASM 5+ 的LocalVariableNode(String, String, String, LabelNode, LabelNode, int)构造函数——该重载在更高 ASM 版本中已废弃。当前编译目标可用,但升级 ASM 时会断。
相关条目
- ICompatHandler -
getFieldLevelTessellator()定义 - GenericCompatTransformer - 唯一调用点
- StacksOnStacksCompatHandler - 唯一提供数据的 handler
- AngelicaTweaker - 排序索引缺陷的源头
- FML Transformers - 4 个
generic/transformer 的分派表