Q 包混淆类 / Velocity 扩展 / Kotlin 扩展 — 完整反编译分析
目录
1. Q 包混淆类分析
Q 包位于 Q/(默认包根目录),包含 3 个混淆类,它们是 iFlyCode 内联代码提示系统的核心数据模型和交互处理类。
1.1 Q.q — CodeTipType 数据模型(CodeEditorInlay 实现)
源文件: cg (混淆前)
完整类名: Q.q
继承关系: public final class Q.q implements com.aicode.service.CodeEditorInlay
字段列表
| 混淆名 | 类型 | 语义名 | 说明 |
|---|---|---|---|
float | int | editorOffset | 编辑器中的偏移位置 |
byte | List<String> | lines | 代码提示的文本行列表 |
enum | com.aicode.enums.CodeTipType | type | 代码提示类型 |
方法列表
| 方法签名 | 说明 |
|---|---|
Q.q(CodeTipType, int, List<String>) | 构造函数,初始化 type/offset/lines |
CodeTipType getType() | 获取提示类型,null 检查后调用 enum() |
void setType(CodeTipType) | 设置提示类型 |
int getEditorOffset() | 获取编辑器偏移 |
void setEditorOffset(int) | 设置编辑器偏移 |
List<String> getLines() | 获取文本行,null 检查后调用 enum() |
List<String> ze() | 获取文本行(混淆名),与 getLines() 逻辑相同 |
void setLines(List<String>) | 设置文本行 |
String toString() | 格式化输出 type/offset/lines |
private static void enum(int) | 空指针/参数校验失败时的异常生成器 |
CodeTipType 映射关系
CodeTipType 枚举定义了 3 个值:
CodeTipType.AfterLineEnd -> ordinal 0
CodeTipType.Block -> ordinal 1
CodeTipType.Inline -> ordinal 2enum(int) 方法中的 tableswitch 使用 0-4 的范围(包含额外占位),但实际只有 3 个枚举值被使用。该方法根据参数值生成不同的混淆错误消息字符串,然后通过 String.format 构造异常消息,最终抛出 IllegalStateException 或 IllegalArgumentException。
与 CodeEditorInlay 接口的映射
CodeEditorInlay 接口定义了以下抽象方法,Q.q 全部实现:
| 接口方法 | Q.q 实现 |
|---|---|
List<String> getLines() | getLines() |
int getEditorOffset() | getEditorOffset() |
void setEditorOffset(int) | setEditorOffset(int) |
void setType(CodeTipType) | setType(CodeTipType) |
void setLines(List<String>) | setLines(List<String>) |
CodeTipType getType() | getType() |
boolean isEmptyTip() | 默认实现(接口 default) |
引用方式
Q.q作为CodeEditorInlay的唯一实现,被EditorManagerService等服务层广泛使用- 构造函数中的 null 检查使用混淆字符串,通过
ConditionalActionConfiguration.H()和ChatInputController.H()解码 ze()方法是混淆后的 getter,功能与getLines()完全相同
1.2 Q.sa — 内联提示循环动作(抽象 Action)
源文件: dh (混淆前)
完整类名: Q.sa
继承关系: public abstract class Q.sa extends com.aicode.action.click.PluginAnAction
字段列表
无实例字段(继承自 PluginAnAction)。
方法列表
| 方法签名 | 说明 |
|---|---|
Q.sa() | 构造函数,调用 PluginAnAction.<init>() |
abstract boolean doCycleAction(Editor) | 抽象方法,子类实现循环动作逻辑 |
String getWarningHintText() | 获取警告提示文本,从 BasicActionsBundle 获取 |
void actionPerformed(AnActionEvent) | 动作执行入口,获取 Editor 后调用 doCycleAction |
void update(AnActionEvent) | 更新动作状态,检查 Editor 和 hasTipInlays |
private static void Nd(Editor, String) | 显示错误提示(HintManager.showErrorHint) |
private static void enum(int) | 异常生成器 |
核心逻辑分析
actionPerformed 流程:
- 从
AnActionEvent获取Editor实例(通过CommonDataKeys.EDITOR) - 调用
doCycleAction(editor)— 抽象方法,由子类实现具体循环逻辑 - 如果
doCycleAction返回false,调用Nd(editor, getWarningHintText())显示错误提示
update 流程:
- 获取当前 Editor
- 检查
EditorManagerService.getInstance().hasTipInlays(editor)是否有活动的提示 - 根据结果设置
Presentation.setEnabled()
Nd 方法(私有静态):
- 使用
HintManager.getInstance().showErrorHint()显示错误提示 - 参数:
errorHintType = 1033,hideFlags = 1500 - 在当前 caret offset 处显示
CodeTipType 映射关系
Q.sa 不直接持有 CodeTipType,但通过 EditorManagerService.hasTipInlays() 间接关联。doCycleAction 的子类实现会操作 Q.q(CodeEditorInlay)实例,其中包含 CodeTipType 类型信息。
1.3 Q.ua — 编辑器动作拦截器(EditorActionHandler 包装器)
源文件: yl (混淆前)
完整类名: Q.ua
继承关系: public class Q.ua extends com.intellij.openapi.editor.actionSystem.EditorActionHandler
字段列表
| 混淆名 | 类型 | 语义名 | 说明 |
|---|---|---|---|
enum | EditorActionHandler (final) | delegate | 被包装的原始动作处理器 |
方法列表
| 方法签名 | 说明 |
|---|---|
Q.ua(EditorActionHandler) | 构造函数,保存原始 handler |
boolean isEnabledForCaret(Editor, Caret, DataContext) | 判断动作是否启用 |
void doExecute(Editor, Caret, DataContext) | 执行动作 |
boolean executeInCommand(Editor, DataContext) | 是否在命令中执行 |
static boolean Yf(Editor) | 判断编辑器是否有活动的内联提示 |
private static void enum(int) | 异常生成器 |
核心逻辑分析
Yf(Editor) — 关键静态方法:
1. EditorManagerService.getInstance().isAvailable(editor) — 服务是否可用
2. EditorManagerService.getInstance().hasTipInlays(editor) — 是否有提示
3. LookupManager.getActiveLookup(editor) == null — 没有活动的代码补全
4. 三个条件同时满足返回 trueisEnabledForCaret 逻辑:
if (Yf(editor)) {
return false; // 有内联提示时,禁用原始动作
} else if (delegate != null && delegate.isEnabled(...)) {
return true; // 无提示时,委托给原始 handler
} else {
return false;
}doExecute 逻辑:
if (Yf(editor)) {
// 有内联提示时,执行 EscReject 操作
EditorManagerService.getInstance().disposeTips(editor, OperateActionEnum.EscReject);
}
// 然后委托给原始 handler 执行(如果存在且 enabled)
if (delegate != null && delegate.isEnabled(...)) {
delegate.execute(editor, caret, dataContext);
}OperateActionEnum 映射
Q.ua 使用 OperateActionEnum.EscReject,该枚举完整值:
| 枚举值 | 说明 |
|---|---|
Applied | 用户已应用提示 |
Typing | 用户正在输入 |
IdeCompletion | IDE 自动补全 |
Cycling | 循环切换提示 |
SettingsChange | 设置变更 |
EscReject | ESC 键拒绝提示 |
UserOperate | 用户操作 |
CaretChange | 光标位置变更 |
TypingAsSuggested | 按建议输入 |
设计模式
Q.ua 实现了 装饰器模式(Decorator Pattern):
- 包装原始
EditorActionHandler - 在有内联提示时拦截动作(如 ESC 键),触发
disposeTips - 在无提示时透传给原始 handler
- 与
ConditionalEditorActionHandler互补:后者处理条件性按键绑定,前者处理通用编辑器动作拦截
2. Velocity 扩展类分析
iFlyCode 对 Apache Velocity 模板引擎进行了深度定制,包含 5 个扩展类,用于模板配置管理和属性键的废弃迁移。
2.1 DeprecationAwareExtProperties — 废弃键感知属性表
源文件: ia (混淆前)
完整类名: org.apache.velocity.util.DeprecationAwareExtProperties
继承关系: public class DeprecationAwareExtProperties extends Hashtable<String, Object>
字段列表
| 混淆名 | 类型 | 语义名 | 说明 |
|---|---|---|---|
byte | Map<String, String> (static) | deprecatedKeyMap | 废弃键到新键的映射 |
enum | Set<String> | warnedKeys | 已警告过的键集合 |
logger | Logger (static) | logger | SLF4J 日志器 |
方法列表
| 方法签名 | 说明 |
|---|---|
DeprecationAwareExtProperties() | 构造函数,初始化 warnedKeys HashSet |
Object put(Object, Object) | 代理 put(String, Object) |
Object put(String, Object) | 重写,先 translateKey 再存入 Hashtable |
Object get(String) | 重写,先 translateKey 再从 Hashtable 获取 |
String translateKey(String) | 核心方法:将废弃键翻译为新键 |
boolean containsKey(String) | 重写,先 translateKey 再检查 |
void warnDeprecated(String, String) | 记录废弃警告(仅首次) |
static {} | 静态初始化块 |
核心逻辑:translateKey
1. 查找 deprecatedKeyMap(byte 字段),如果找到映射,返回新键并警告
2. 检查键是否包含 "." 分隔符(混淆字符串解码后),如果是,取最后一段
3. 检查键是否以特定后缀结尾(22 字符长度),如果是,去掉后缀
4. 都不匹配,返回原始键静态初始化块逻辑
1. 初始化 logger(通过 LoggerFactory.getLogger)
2. 创建 deprecatedKeyMap(HashMap)
3. 反射读取 DeprecatedRuntimeConstants 的所有字段
4. 验证字段名以 "OLD_" 前缀开头(混淆后为 2 字符前缀)
5. 验证字段类型为 String
6. 验证字段值非空
7. 截取字段名前 4 个字符后的部分
8. 在 RuntimeConstants 中查找对应的新字段
9. 如果新旧值不同,将旧值 -> 新值映射加入 deprecatedKeyMap
10. 任何异常包装为 VelocityException与模板引擎的集成
DeprecationAwareExtProperties 是 Velocity 配置属性的基础容器,提供透明的键名迁移:
- 所有属性访问(get/put/containsKey)自动经过
translateKey - 废弃键被静默映射到新键,仅首次使用时发出 SLF4J 警告
- 继承
Hashtable<String, Object>,保持与 Velocity 原生 API 的兼容性
2.2 ExtProperties — 扩展属性配置
源文件: xa (混淆前)
完整类名: org.apache.velocity.util.ExtProperties
继承关系: public class ExtProperties extends DeprecationAwareExtProperties
字段列表
| 混淆名 | 类型 | 语义名 | 说明 |
|---|---|---|---|
fileSeparator | String | fileSeparator | 文件路径分隔符 |
isInitialized | boolean | isInitialized | 是否已初始化 |
file | String | file | 配置文件路径 |
keysAsListed | ArrayList<String> | keysAsListed | 按插入顺序排列的键列表 |
enum | ExtProperties (private) | defaults | 默认属性集 |
include | String (static) | include | include 指令名称 |
basePath | String | basePath | 基础路径 |
END_TOKEN | String (static final) | END_TOKEN | 变量插值结束标记 |
START_TOKEN | String (static final) | START_TOKEN | 变量插值开始标记 |
方法列表(完整)
| 方法签名 | 说明 |
|---|---|
ExtProperties() | 默认构造 |
ExtProperties(String) | 从文件路径构造 |
ExtProperties(String, String) | 从文件路径和编码构造 |
boolean getBoolean(String) | 获取布尔值 |
boolean getBoolean(String, boolean) | 获取布尔值(带默认) |
Boolean getBoolean(String, Boolean) | 获取布尔值(包装类型) |
byte getByte(String) | 获取字节值 |
byte getByte(String, byte) | 获取字节值(带默认) |
Byte getByte(String, Byte) | 获取字节值(包装类型) |
short getShort(String) | 获取短整型 |
short getShort(String, short) | 获取短整型(带默认) |
Short getShort(String, Short) | 获取短整型(包装类型) |
int getInt(String) | 获取整型 |
int getInt(String, int) | 获取整型(带默认) |
Integer getInteger(String) | 获取整型(包装类型) |
Integer getInteger(String, Integer) | 获取整型(包装类型,带默认) |
int getInteger(String, int) | 获取整型(带默认) |
long getLong(String) | 获取长整型 |
long getLong(String, long) | 获取长整型(带默认) |
Long getLong(String, Long) | 获取长整型(包装类型) |
float getFloat(String) | 获取浮点型 |
float getFloat(String, float) | 获取浮点型(带默认) |
Float getFloat(String, Float) | 获取浮点型(包装类型) |
double getDouble(String) | 获取双精度型 |
double getDouble(String, double) | 获取双精度型(带默认) |
Double getDouble(String, Double) | 获取双精度型(包装类型) |
String getString(String) | 获取字符串 |
String getString(String, String) | 获取字符串(带默认) |
String[] getStringArray(String) | 获取字符串数组 |
Vector getVector(String) | 获取向量 |
Vector getVector(String, Vector) | 获取向量(带默认) |
List getList(String) | 获取列表 |
List getList(String, List) | 获取列表(带默认) |
Properties getProperties(String) | 获取属性子集 |
Properties getProperties(String, Properties) | 获取属性子集(带默认) |
Object getProperty(String) | 获取属性 |
void setProperty(String, Object) | 设置属性 |
void addProperty(String, Object) | 添加属性 |
void clearProperty(String) | 清除属性 |
void combine(ExtProperties) | 合并属性 |
ExtProperties subset(String) | 获取子集 |
Iterator<String> getKeys() | 获取键迭代器 |
Iterator<String> getKeys(String) | 获取带前缀的键迭代器 |
void load(InputStream) | 从流加载 |
synchronized void load(InputStream, String) | 从流加载(带编码) |
synchronized void save(OutputStream, String) | 保存到流 |
String interpolate(String) | 变量插值 |
String interpolateHelper(String, List<String>) | 插值辅助 |
void display() | 显示所有属性 |
boolean isInitialized() | 是否已初始化 |
String getInclude() | 获取 include 指令名 |
void setInclude(String) | 设置 include 指令名 |
static ExtProperties convertProperties(Map) | 从 Map 转换 |
static ExtProperties convertProperties(Properties) | 从 Properties 转换 |
private void char() | 内部初始化 |
private void int(String, Object) | 内部属性设置 |
private void case(String, Object) | 内部属性处理 |
private static String catch(String) | 内部字符串处理 |
private static String break() | 内部字符串生成 |
private static String short(String) | 内部字符串处理 |
private static boolean float(String) | 判断字符串是否为变量引用 |
private static int long(String, int, char) | 内部整数解析 |
模板变量注入机制
ExtProperties 实现了 Velocity 的变量插值系统:
- START_TOKEN / END_TOKEN: 定义变量引用的边界标记(如
${和}) - interpolate(String): 扫描字符串中的变量引用并替换为实际值
- interpolateHelper(String, List<String>): 递归处理嵌套变量引用,防止循环引用
- float(String): 检测字符串是否包含变量引用标记
- subset(String): 支持命名空间隔离的属性子集
2.3 DeprecatedRuntimeConstants — 废弃运行时常量接口
源文件: u (混淆前)
完整类名: org.apache.velocity.runtime.DeprecatedRuntimeConstants
类型: public interface
常量列表(39 个)
| 常量名 | 说明 |
|---|---|
OLD_RUNTIME_LOG_REFERENCE_LOG_INVALID | 无效引用日志记录 |
OLD_MAX_NUMBER_LOOPS | 最大循环次数 |
OLD_SKIP_INVALID_ITERATOR | 跳过无效迭代器 |
OLD_CHECK_EMPTY_OBJECTS | 检查空对象 |
OLD_ERRORMSG_START | 错误消息开始标记 |
OLD_ERRORMSG_END | 错误消息结束标记 |
OLD_PARSE_DIRECTIVE_MAXDEPTH | 解析指令最大深度 |
OLD_DEFINE_DIRECTIVE_MAXDEPTH | 定义指令最大深度 |
OLD_CUSTOM_DIRECTIVES | 自定义指令 |
OLD_RESOURCE_MANAGER_DEFAULTCACHE_SIZE | 资源管理器默认缓存大小 |
OLD_RESOURCE_MANAGER_LOGWHENFOUND | 资源管理器发现时日志 |
OLD_RESOURCE_LOADERS | 资源加载器 |
OLD_FILE_RESOURCE_LOADER_PATH | 文件资源加载器路径 |
OLD_FILE_RESOURCE_LOADER_CACHE | 文件资源加载器缓存 |
OLD_RESOURCE_LOADER_CHECK_INTERVAL | 资源加载器检查间隔 |
OLD_DS_RESOURCE_LOADER_DATASOURCE | 数据源资源加载器 |
OLD_DS_RESOURCE_LOADER_KEY_COLUMN | 数据源键列 |
OLD_DS_RESOURCE_LOADER_TEMPLATE_COLUMN | 数据源模板列 |
OLD_DS_RESOURCE_LOADER_TIMESTAMP_COLUMN | 数据源时间戳列 |
OLD_INPUT_ENCODING | 输入编码 |
OLD_EVENTHANDLER_REFERENCEINSERTION | 引用插入事件处理器 |
OLD_EVENTHANDLER_METHODEXCEPTION | 方法异常事件处理器 |
OLD_EVENTHANDLER_INCLUDE | Include 事件处理器 |
OLD_EVENTHANDLER_INVALIDREFERENCES | 无效引用事件处理器 |
OLD_VM_LIBRARY | Velocimacro 库 |
OLD_VM_LIBRARY_DEFAULT | 默认 Velocimacro 库 |
OLD_VM_PERM_ALLOW_INLINE | 允许内联宏 |
OLD_VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL | 允许内联宏替换全局 |
OLD_VM_PERM_INLINE_LOCAL | 本地内联宏 |
OLD_VM_MAX_DEPTH | 宏最大深度 |
OLD_VM_BODY_REFERENCE | 宏体引用 |
OLD_RUNTIME_REFERENCES_STRICT | 严格引用模式 |
OLD_RUNTIME_REFERENCES_STRICT_ESCAPE | 严格引用转义 |
OLD_UBERSPECT_CLASSNAME | Uberspect 类名 |
OLD_CONVERSION_HANDLER_CLASS | 转换处理器类 |
OLD_INTERPOLATE_STRINGLITERALS | 字符串字面量插值 |
OLD_STRICT_MATH | 严格数学模式 |
OLD_CONTEXT_AUTOREFERENCE_KEY | 上下文自动引用键 |
OLD_SPACE_GOBBLING | 空格吞噬模式 |
OLD_VM_ENABLE_BC_MODE | 宏向后兼容模式 |
初始化机制
所有常量值通过混淆字符串 + 解码函数初始化:
- 偶数索引常量使用
NewFileUtils.H()解码 - 奇数索引常量使用
FontKt.H()解码 - 这种交替使用两个不同解码器的模式增加了逆向难度
2.4 ExtProperties$C — 属性键分割器
源文件: xa (与 ExtProperties 同源)
完整类名: org.apache.velocity.util.ExtProperties$C
继承关系: public class ExtProperties$C extends StringTokenizer
字段列表
| 混淆名 | 类型 | 语义名 | 说明 |
|---|---|---|---|
enum | String (static final) | delimiter | 分隔符(解码后为 ",") |
方法列表
| 方法签名 | 说明 |
|---|---|
ExtProperties$C(String) | 构造函数,使用分隔符创建 StringTokenizer |
boolean hasMoreTokens() | 是否有更多 token |
String nextToken() | 获取下一个 token,处理转义分隔符 |
核心逻辑
nextToken() 方法重写了 StringTokenizer 的行为:
- 循环读取 token
- 如果 token 以转义字符结尾(
float()检测),去掉末尾转义符,追加分隔符,继续读取 - 否则直接返回 token
- 最终 trim 结果
这允许属性值中包含转义的分隔符(如 value1\,value2 被视为单个值)。
2.5 ExtProperties$L — 属性文件行读取器
源文件: xa (与 ExtProperties 同源)
完整类名: org.apache.velocity.util.ExtProperties$L
继承关系: public class ExtProperties$L extends LineNumberReader
方法列表
| 方法签名 | 说明 |
|---|---|
ExtProperties$L(Reader) | 构造函数,包装 Reader |
String false() throws IOException | 读取逻辑行(合并续行) |
核心逻辑
false() 方法(混淆后的方法名,语义为 readLogicalLine):
- 创建 StringBuilder
- 循环读取物理行
- 跳过空行和以
#开头的注释行 - 如果行以续行符结尾(
float()检测),去掉续行符,追加到 StringBuilder,继续读取下一行 - 否则直接返回该行
- 到达文件末尾返回 null
这实现了属性文件中的续行功能(行末 \ 表示续行)。
3. Kotlin 扩展类分析
iFlyCode 使用 Kotlin 扩展函数为 IntelliJ Platform API 和 Swing 组件添加便利方法。所有扩展类都包含 H(Object) 静态方法,这是混淆字符串解码器。
3.1 EditorKt — Editor 扩展函数集
源文件: vb (混淆前)
完整类名: com.aicode.util.EditorKt
静态字段
| 字段名 | 类型 | 说明 |
|---|---|---|
FILE_LANG | Map<String, String> | 文件扩展名到语言名的映射 |
categoryRendererCollection | Map<String, InlineChatCategoryPanelRenderer> | 分类面板渲染器集合 |
stopRendererCollection | Map<String, InlineChatStopPanelRenderer> | 停止面板渲染器集合 |
rendererCollection | Map<String, Object> | 通用渲染器集合 |
commentSymbols | Map<String, String> | 语言到注释符号的映射 |
inlineChatCacheData | Map<String, InlineChatInfo> | 内联聊天缓存数据(按文件路径索引) |
inlineChatBtnCache | Map<Editor, Inlay<?>> | 内联聊天按钮缓存(按 Editor 索引) |
inlineChatVersion | AtomicInteger | 内联聊天版本计数器 |
endSymbols | Map<String, String> | 语言到结束符号的映射 |
扩展函数列表
| 函数签名 | 扩展接收者 | 说明 |
|---|---|---|
void addInfoByEditor(Editor, InlineChatInfo) | Editor | 按编辑器添加内联聊天信息到缓存 |
void addCursorListener(Editor, CaretListener) | Editor | 为编辑器添加光标监听器 |
String getErrorRendererTip(Editor, EditorRequestService) | Editor | 获取错误渲染器提示文本 |
String getFileType(Editor) | Editor | 获取编辑器当前文件类型(扩展名) |
void addSelectionListener(Editor, SelectionListener) | Editor | 为编辑器添加选区监听器 |
boolean getHasSelection(Editor) | Editor | 检查编辑器是否有选中文本 |
void closeCategoryPanel(Editor) | Editor | 关闭分类面板 |
void closeStopPanel(Editor) | Editor | 关闭停止面板 |
void closeButtonPanel(Editor) | Editor | 关闭按钮面板 |
VirtualFile getFile(Editor) | Editor | 获取编辑器关联的虚拟文件 |
InlineChatInfo getInfoByVirtualFile(VirtualFile) | VirtualFile | 按虚拟文件获取内联聊天信息 |
InlineChatInfo getInfoByEditor(Editor) | Editor | 按编辑器获取内联聊天信息 |
void removeEditor(Editor) | Editor | 从缓存中移除编辑器 |
boolean containEditor(Editor) | Editor | 检查编辑器是否在缓存中 |
功能分析
EditorKt 是内联聊天系统的核心工具类,提供:
- 缓存管理:
inlineChatCacheData和inlineChatBtnCache维护编辑器与内联提示的映射关系 - 语言支持:
FILE_LANG、commentSymbols、endSymbols提供语言特定的注释和结束符号 - 渲染器注册:
categoryRendererCollection、stopRendererCollection、rendererCollection管理不同类型的面板渲染器 - 监听器管理: 便捷方法添加光标和选区监听器
- 面板控制: 关闭各类内联聊天面板
3.2 FontKt — 字体扩展函数集
源文件: ob (混淆前)
完整类名: com.aicode.ui.FontKt
扩展函数列表
| 函数签名 | 扩展接收者 | 返回类型 | 说明 |
|---|---|---|---|
italic(JBFont) | JBFont | JBFont | 设置字体为斜体(调用 asItalic) |
bold(JBFont) | JBFont | JBFont | 设置字体为粗体(调用 asBold) |
plain(JBFont) | JBFont | JBFont | 设置字体为常规(调用 asPlain) |
textWidthForFont(String, Font) | String + Font | int | 计算文本在指定字体下的像素宽度 |
widthForFont(String, Font) | String + Font | int | 计算文本宽度(带 null 检查的包装) |
H(Object) | — | String | 混淆字符串解码器 |
功能分析
FontKt 提供 IntelliJ 平台字体操作的 Kotlin 扩展:
- 字体样式链:
italic()、bold()、plain()返回JBFont,支持链式调用 - 文本测量:
textWidthForFont()使用JLabel.getFontMetrics().stringWidth()精确计算文本像素宽度 - 混淆解码器:
H(Object)方法是全局混淆字符串解码器之一,被DeprecatedRuntimeConstants等类引用
H() 解码算法
FontKt.H() 实现了与 JComponentKt.H() 类似的解码算法:
- 获取调用栈的类名和方法名,拼接为密钥字符串
- 对密钥字符串进行位运算变换(位移 + 异或)
- 使用变换后的密钥对输入字符串进行 XOR 解码
- 双向遍历(从两端向中间)解码字符数组
3.3 JComponentKt — Swing 组件扩展函数集
源文件: rb (混淆前)
完整类名: com.aicode.util.JComponentKt
扩展函数列表
| 函数签名 | 扩展接收者 | 返回类型 | 说明 |
|---|---|---|---|
font(JComponent, Font) | JComponent | JComponent | 设置字体并返回自身(链式调用) |
findComponent(JComponent) | JComponent | T | 在组件树中查找指定类型子组件 |
findComponent(KClass<T>, Component) | KClass<T> + Component | T | 按类型在组件树中查找子组件 |
isChildFocused(JComponent) | JComponent | boolean | 递归检查子组件是否获得焦点 |
minimumSize(JComponent, int, int) | JComponent | JComponent | 设置最小尺寸 |
minimumSize$default(...) | JComponent | JComponent | minimumSize 默认参数版本 |
maximumSize(JComponent, int, int) | JComponent | JComponent | 设置最大尺寸 |
maximumSize$default(...) | JComponent | JComponent | maximumSize 默认参数版本 |
preferredSize(JComponent, int, int) | JComponent | JComponent | 设置首选尺寸 |
preferredSize$default(...) | JComponent | JComponent | preferredSize 默认参数版本 |
opaque(JComponent, boolean) | JComponent | JComponent | 设置不透明度 |
border(JComponent, Border) | JComponent | JComponent | 设置边框 |
removeInsets(JComponent) | JComponent | JComponent | 移除内边距 |
update(JComponent) | JComponent | JComponent | 刷新组件 UI |
lockMouseInteractions(JComponent) | JComponent | JComponent | 锁定鼠标交互 |
inAllChildren(JComponent, Function1) | JComponent | void | 递归遍历所有子组件执行操作 |
H(Object) | — | String | 混淆字符串解码器 |
功能分析
JComponentKt 实现了 Kotlin DSL 风格 的 Swing 组件构建器:
- 链式配置: 所有 setter 方法返回
JComponent自身,支持component.font(f).border(b).opaque(true)风格 - 尺寸快捷方法:
minimumSize/maximumSize/preferredSize封装Dimension创建 - 组件查找:
findComponent使用 Kotlin 反射按类型递归查找 - 递归遍历:
inAllChildren对组件树执行深度优先遍历 - 焦点检测:
isChildFocused递归检查焦点状态 - 交互锁定:
lockMouseInteractions禁用鼠标事件(用于只读 UI 元素)
3.4 InlineChatStatusServiceKt — 内联聊天状态服务扩展
源文件: hk (混淆前)
完整类名: com.aicode.inline.status.InlineChatStatusServiceKt
扩展函数列表
| 函数签名 | 说明 |
|---|---|
InlineChatStatusService InlineChatStatusService() | 顶级属性访问器,获取 InlineChatStatusService 单例 |
H(Object) | 混淆字符串解码器 |
private enum(int) | 异常生成器 |
功能分析
这是一个 Kotlin 顶级属性扩展,提供便捷的服务访问:
// Kotlin 源码等价
val InlineChatStatusService: InlineChatStatusService
get() = InlineChatStatusServiceProvider.INSTANCE.get()- 通过
InlineChatStatusServiceProvider单例获取服务实例 - 简化其他类对状态服务的访问,避免重复调用
InlineChatStatusServiceProvider.INSTANCE.get() - null 安全检查:如果服务为 null,抛出
IllegalStateException
3.5 IdeEditorActionRouterKt — 编辑器动作路由扩展
源文件: ai (混淆前)
完整类名: com.aicode.inline.ide.IdeEditorActionRouterKt
字段列表
| 混淆名 | 类型 | 语义名 | 说明 |
|---|---|---|---|
enum | Logger (private static final) | logger | IntelliJ 诊断日志器 |
扩展函数列表
| 函数签名 | 说明 |
|---|---|
void replaceWithConditionalAction(EditorActionManager, String, ConditionalActionConfiguration, InlineChatService) | 替换编辑器动作为条件动作 |
private enum(int) | 异常生成器 |
核心逻辑:replaceWithConditionalAction
1. 从 ActionManager 获取指定 actionId 的 AnAction
2. 如果 action 不存在,logger.debug 记录并返回
3. 从 EditorActionManager 获取当前 action handler
4. 如果 handler 不存在,logger.warn 记录并返回
5. 创建 ConditionalEditorActionHandler 包装原始 handler
6. 调用 EditorActionManager.setActionHandler() 替换
7. ClassCastException 处理:logger.warn 记录功能分析
IdeEditorActionRouterKt 是内联聊天系统的 动作拦截注册器:
- 将指定的 IDE 编辑器动作(如 Enter、Tab、Escape)替换为
ConditionalEditorActionHandler ConditionalEditorActionHandler包装原始 handler,在内联聊天激活时拦截按键- 与
Q.ua(通用编辑器动作拦截器)互补:Q.ua处理所有编辑器动作的通用拦截IdeEditorActionRouterKt处理特定按键的条件性拦截
4. 三个子系统之间的交互关系
4.1 整体架构
+-------------------------------------------------------+
| iFlyCode 插件 |
| |
| +-- Q 包混淆类 (数据模型 + 动作处理) ----------------+ |
| | | |
| | Q.q (CodeEditorInlay) <--- 数据模型 | |
| | Q.sa (CycleAction) <--- 用户交互 | |
| | Q.ua (ActionHandler) <--- 动作拦截 | |
| | | |
| +----------------------------------------------------+ |
| |
| +-- Velocity 扩展 (模板配置) -------------------------+ |
| | | |
| | DeprecationAwareExtProperties <--- 属性基类 | |
| | ExtProperties <--- 完整属性系统 | |
| | DeprecatedRuntimeConstants <--- 废弃常量定义 | |
| | ExtProperties$C <--- 键分割器 | |
| | ExtProperties$L <--- 行读取器 | |
| | | |
| +----------------------------------------------------+ |
| |
| +-- Kotlin 扩展 (便利 API) --------------------------+ |
| | | |
| | EditorKt <--- 编辑器工具 | |
| | FontKt <--- 字体工具 | |
| | JComponentKt <--- UI 组件工具 | |
| | InlineChatStatusServiceKt <--- 状态服务访问 | |
| | IdeEditorActionRouterKt <--- 动作路由 | |
| | | |
| +----------------------------------------------------+ |
+-------------------------------------------------------+4.2 交互关系详解
Q 包 <-> Kotlin 扩展
| 交互路径 | 说明 |
|---|---|
Q.q <-> EditorKt | EditorKt 管理的 inlineChatCacheData 缓存存储 InlineChatInfo,与 Q.q 的 CodeTipType 类型信息关联 |
Q.sa <-> EditorKt | Q.sa.update() 调用 EditorManagerService.hasTipInlays(),该服务使用 EditorKt 管理的缓存 |
Q.ua <-> IdeEditorActionRouterKt | Q.ua 是通用动作拦截器,IdeEditorActionRouterKt 注册特定条件动作,两者共同构成按键拦截体系 |
Q.ua <-> EditorKt | Q.ua.Yf() 调用 EditorManagerService.isAvailable() 和 hasTipInlays(),依赖 EditorKt 的缓存数据 |
Q.sa <-> InlineChatStatusServiceKt | Q.sa 的 update() 方法可能通过 InlineChatStatusService 检查内联聊天状态 |
Velocity 扩展 <-> Kotlin 扩展
| 交互路径 | 说明 |
|---|---|
DeprecatedRuntimeConstants <-> FontKt | 废弃常量的奇数索引值使用 FontKt.H() 解码 |
DeprecatedRuntimeConstants <-> NewFileUtils | 废弃常量的偶数索引值使用 NewFileUtils.H() 解码 |
DeprecationAwareExtProperties <-> GenericUtils | 属性键翻译中的字符串常量使用 GenericUtils.H() 解码 |
ExtProperties$C <-> Application.H() | 分隔符字符串使用 Application.H() 解码 |
Q 包 <-> Velocity 扩展
| 交互路径 | 说明 |
|---|---|
| 间接关联 | Velocity 模板系统用于生成代码提示内容,Q.q 的 lines 字段可能包含模板渲染结果 |
| 配置驱动 | Velocity 的 ExtProperties 配置可能影响模板中变量插值的行为,进而影响 Q.q 的内容生成 |
4.3 混淆字符串解码器网络
三个子系统共享一个混淆字符串解码器网络,每个 H(Object) 方法使用不同的密钥派生算法:
| 解码器 | 所在类 | 密钥来源 | 被引用者 |
|---|---|---|---|
ConditionalActionConfiguration.H() | inline.ide | 调用栈类名+方法名 | Q.q, DeprecationAwareExtProperties |
ChatInputController.H() | inline.controller | 调用栈类名+方法名 | Q.q |
Application.H() | util | 调用栈类名+方法名 | Q.sa, ExtProperties$C, FontKt |
RequestCancelException.H() | exception | 调用栈类名+方法名 | Q.sa, FontKt |
PropertyUtils.H() | util | 调用栈类名+方法名 | Q.ua, InlineChatStatusServiceKt |
GitReviewService.H() | agent.service | 调用栈类名+方法名 | Q.ua |
GenericUtils.H() | diff | 调用栈类名+方法名 | DeprecationAwareExtProperties |
FontKt.H() | ui | 调用栈类名+方法名 | DeprecatedRuntimeConstants |
NewFileUtils.H() | util | 调用栈类名+方法名 | DeprecatedRuntimeConstants |
Maps.H() | util | 调用栈类名+方法名 | EditorKt |
PositionUtil.H() | util | 调用栈类名+方法名 | InlineChatStatusServiceKt |
FileInfo.H() | diff | 调用栈类名+方法名 | IdeEditorActionRouterKt |
LanguageFileExtensionDetails.H() | content.util.file | 调用栈类名+方法名 | IdeEditorActionRouterKt |
ActionButton.H() | ui | 调用栈类名+方法名 | ExtProperties$C |
4.4 按键拦截体系
Q 包和 Kotlin 扩展共同构建了一个分层的按键拦截体系:
用户按键
|
v
EditorActionManager
|
+-- IdeEditorActionRouterKt.replaceWithConditionalAction()
| |
| v
| ConditionalEditorActionHandler (特定按键条件拦截)
| - 检查 ConditionalActionConfiguration
| - 检查 ActionScope
| - 检查 KeyStrokeExecutorProvider
| |
| v
+-- Q.ua (通用编辑器动作拦截)
| |
| v
| Yf(editor) 检查:
| - EditorManagerService.isAvailable()
| - EditorManagerService.hasTipInlays()
| - LookupManager.getActiveLookup() == null
| |
| +-- 有内联提示: disposeTips(EscReject)
| +-- 无内联提示: 委托给原始 handler
|
+-- Q.sa (循环动作)
|
v
doCycleAction(editor)
|
+-- 返回 true: 动作已处理
+-- 返回 false: 显示 HintManager 错误提示4.5 数据流
Velocity 模板引擎
|
v (模板渲染 + 变量插值)
ExtProperties (配置管理)
|
v (配置参数)
代码提示生成服务
|
v
Q.q (CodeEditorInlay 实现)
| - type: CodeTipType (AfterLineEnd/Block/Inline)
| - editorOffset: int
| - lines: List<String>
|
v
EditorKt (缓存管理)
| - inlineChatCacheData
| - inlineChatBtnCache
| - categoryRendererCollection
| - stopRendererCollection
|
v
渲染器 (InlineChatCategoryPanelRenderer, etc.)
|
v
用户界面显示
|
v (用户交互)
Q.sa / Q.ua / IdeEditorActionRouterKt (动作处理)
|
v
EditorManagerService.disposeTips() / InlineChatStatusService4.6 关键发现总结
- Q 包是内联提示的核心:
Q.q是唯一实现CodeEditorInlay接口的类,Q.sa和Q.ua分别处理用户交互和动作拦截 - Velocity 扩展是配置基础设施: 为模板引擎提供属性管理和废弃键迁移,间接支持代码提示内容生成
- Kotlin 扩展是便利层: 提供类型安全的 API 和 DSL 风格的组件构建,简化 Q 包和 Velocity 的使用
- 混淆系统是跨子系统的: 三个子系统共享 14+ 个不同的字符串解码器,每个解码器使用调用栈信息派生密钥
- 按键拦截是多层协作的:
IdeEditorActionRouterKt注册条件拦截,Q.ua提供通用拦截,Q.sa处理循环动作 - 缓存管理集中在 EditorKt: 所有编辑器相关的缓存(聊天数据、按钮、渲染器)都由
EditorKt管理