Bug: 蓝图重编译后刷新缓存的废弃 UFunction - #2358
Open
Monocluar wants to merge 2 commits into
Open
Conversation
在 Unreal Editor 中重新编译 Blueprint 或 `UTypeScriptGeneratedClass` 后,从 JavaScript 再次调用重编译前已经访问过的函数,可能发生崩溃。 崩溃时,Puerts 调用的 `UFunction` 已经被 Unreal Engine 蓝图编译器迁移到 `TRASHCLASS_` 临时类中,但该函数尚未被 GC,因此对应的 `TWeakObjectPtr<UFunction>` 仍然有效。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题说明
Blueprint 重编译时,Unreal Engine 会生成新的 Class,并将旧 Class 标记为:
旧 Class 中的 UFunction 会被迁移到临时类下,并等待后续 GC 回收。
在 GC 执行前,旧函数仍然满足:
Function.IsValid() == trueFFunctionTranslator::Call()当前仅在函数弱指针失效时重新查找函数:因此,旧函数被迁移到临时类后,如果尚未被 GC,FFunctionTranslator 仍会继续调用该函数。
对于被 Puerts 重定向的函数,旧函数可能进一步进入:
此时函数所属 Class 已经不是原来的 UTypeScriptGeneratedClass,可能导致无效类型转换或空指针访问。
此外,原有函数刷新逻辑位于参数缓冲区分配之后。如果重编译修改了函数参数或返回值,刷新后的 ParamsBufferSize 与已经分配的参数缓冲区可能不一致。
修改内容
识别重编译产生的旧函数
在 Editor 环境中,调用缓存函数前检查其 Owner Class 是否带有:
如果函数所属 Class 已经被新版本替代,则使用初始化时保存的 FunctionName,从当前调用对象的 Class 中重新查找对应函数。
这里使用 ClassFlags 判断,不进行 TRASHCLASS_、REINST_ 等名称字符串检查,降低正常调用路径的额外开销。
调整函数刷新顺序
函数调用顺序调整为:
参数缓冲区在函数刷新之后分配,确保使用新函数签名对应的
ParamsBufferSize。使用刷新后的函数执行调用
FastCall 和 SlowCall 的选择及实际调用统一使用刷新后的 CallFunctionPtr,不再读取可能已经属于旧 Class 的缓存函数。
清理旧函数签名状态
FFunctionTranslator::Init()可能在蓝图重编译后被再次调用,因此重新初始化前清理旧函数对应的:同时在构造函数中将默认参数缓存指针初始化为 nullptr,保证首次初始化和后续刷新路径的内存状态一致。
修改范围
本次修改仅涉及:
未修改公开 API,也不依赖具体项目类型、资产或业务逻辑。
性能影响
正常 Editor 调用路径只增加:
只有检测到 Class 已被重编译替换时,才会执行 FindFunctionByName() 和 Init()。
非 Editor 构建不会执行旧 Class 检查。