转换器注册表(Transformer Registry)
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 字节码转换器注册表 |
| 实现类 | net.vfyjxf.nechar.transform.TransformerRegistry |
| 来源 | 基于 Towdium 的 JustEnoughCharacters 1.12.0 版 TransformerRegistry.java 改写(MIT 协议) |
| 转换器总数 | 2 个(均由 static 块注册) |
注册顺序(static 块)
configurables.add(new TransformerString());
configurables.add(new TransformerRegExp());
transformers.addAll(configurables);
| 顺序 | 转换器 | 类型 | getName() |
|---|---|---|---|
| 1 | TransformerString |
Transformer.Configurable |
string contains |
| 2 | TransformerRegExp |
Transformer.Configurable |
regular expression |
configurables与transformers是两个公开静态列表;两者内容当前完全相同,transformers由configurables一次性拷贝而来。- 顺序有意义:
NechClassTransformer#transform按transformers的迭代顺序依次应用转换器。TransformerString先于TransformerRegExp,因为源码注释明确写着匹配到方法名时break(每个方法每类只收集首个命中),而两个转换器的目标方法集合存在重叠类。
查询接口
getTransformer(String name):遍历 transformers,收集所有 accepts(name) 为 true 的转换器,返回新 ArrayList(可能为空,也可能含多个)。参数 name 是类名(内部名或全限定名,由调用方传入),不是方法名。
Transformer.Configurable 的工作流
两个转换器的共同基类,定义了"配置驱动"的方法改写流程:
| 阶段 | 实现 | 说明 |
|---|---|---|
| 构造 | TransformerString() / TransformerRegExp() 各自在构造器中调用 reload() |
保证注册表静态块执行时方法表已就绪 |
reload() |
新建 MethodDecoder → addAll(getDefault()) → addAll(getAdditional()) → removeAll(NechConfig.transformerMethodBlackList) → 赋值给 md |
默认列表 + 附加列表 − 黑名单 |
accepts(name) |
md.contains(name) |
仅当该类名在方法表中出现才返回 true |
transform(ClassNode c) |
md.getMethodsForClass(c.name.replace('/', '.')) 取出该类需改写的方法名集合;非空则对 c.methods 中名字命中的方法逐个 transform(MethodNode);为空则打印 No function matched in class ... |
方法名匹配不含描述符过滤 |
transform(byte[])(继承自 Transformer.Default)用 ASM ClassReader 解析字节码 → transform(ClassNode) → ClassWriter(COMPUTE_MAXS) 写回。
MethodDecoder(方法表结构)
基于 Guava HashMultimap<String, String>:类全限定名 → 方法名集合。
| 方法 | 行为 |
|---|---|
addAll(String[] names) |
每项按 ":" 分割;必须恰好 2 段(类名:方法名)才 put,否则调 logError 打印 Invalid config syntax: ... 并跳过 |
removeAll(String[] names) |
同上格式校验后 remove(类名, 方法名) |
getMethodsForClass(String c) |
methods.get(c) |
contains(String s) |
methods.containsKey(s) |
因为是 HashMultimap(set 语义),列表中的重复项会被自动去重(见 默认转换目标列表)。
ASM 改写原语(Transformer 接口静态方法)
| 方法 | 作用 |
|---|---|
findMethod(ClassNode, name) / findMethod(ClassNode, name, desc) |
按名字 / 名字+描述符查找方法并打印日志;本仓两个转换器未直接使用 |
transformInvoke(MethodNode, owner, name, newOwner, newName, id, isInterface, op, arg1, arg2) |
遍历方法指令流,把 INVOKEVIRTUAL / INVOKESPECIAL / INVOKESTATIC 中 owner+name 命中的调用替换为新的 MethodInsnNode;另外处理 INVOKEDYNAMIC——当 arg1/arg2 均非 null 且 bsmArgs[1] 是 Handle 且其 owner/name 命中时,用新的 Handle(6, newOwner, newName, id) 重建 InvokeDynamicInsnNode |
transformConstruct(MethodNode, desc, destNew) |
改写 NEW 与 INVOKESPECIAL 的类型/属主,用于替换 new X() 的目标类 |
transformHook(MethodNode, owner, name, id) |
在方法内每条 RETURN 指令前插入一次静态调用 |
所有原语都会把改写动作写入日志(Transforming invoke of ...、Transformed N occurrences. 等),可在开启 verbose 后观察。
相关条目
- TransformerString - 改写
String.contains - TransformerRegExp - 改写
Pattern.matcher与String.matches - 默认转换目标列表 - 默认 + 附加列表的全部条目
- 配置系统 -
transformerMethodBlackList等配置项 - Coremod 引导 ClassTransformer - 转换器的调用方