


修改托管包装器及其原生库而不破坏 ABI
在修改封装原生 C 库的跨平台托管包装器时使用。它会沿着每一层追踪改动,从源码构建原生二进制文件而不是使用陈旧的下载,保持公开 ABI 稳定,正确处理指针所有权和线程,并且绝不跳过失败的测试。
要使用这个AI规则
## 把每个改动从托管包装器一直追踪到原生代码
将库视为一个分层系统:托管wrapper、互操作声明、稳定的C兼容API以及原生实现。
- 在托管wrapper中验证公开输入。
- 保持C兼容bridge精简且可预测;它应负责转换和转发,而不是重复实现策略。
- 确保托管声明、导出的C符号、参数类型、返回类型和原生实现完全一致。
- 编辑代码前,先确定每项验证规则、内存分配、错误转换和生命周期转移由哪一层负责。
- 沿所有受影响的层追踪变更,不要只修复遇到的第一个编译错误。
即使变更能够在托管层编译通过,如果原生二进制文件缺少新符号或采用不同签名,仍可能在运行时失败。
## 按变更类型选择预构建或从源码构建的原生二进制文件
托管wrapper构建前必须存在原生二进制文件,但这些二进制文件的正确来源取决于变更内容。
- 仅修改托管代码时,使用代码库批准的、对应当前已发布里程碑的预构建原生产物。
- 修改原生实现、C API、依赖项、submodule、版本或里程碑时,必须针对目标平台和architecture从源代码构建原生二进制文件。
- 任何影响原生侧的变更完成后,在这些变更发布到产物源之前,禁止下载并使用当前预构建二进制文件。
- 如果原生源代码构建失败,应诊断toolchain、依赖项、路径或网络前提条件。绝不能仅为了让托管构建通过而退回陈旧的下载产物。
- 如果平台cache可能保留旧原生module并造成错误测试结果,应清理这些cache。
API变更后使用陈旧原生二进制文件,可能生成表面成功但实际错误的构建,随后在运行时出现entry point缺失。
## 重新生成绑定而不是手改,并保持 ABI 稳定
不要手工编辑生成的binding、生成文档、已render的release page或其他声明为生成输出的内容。修改可编辑source,并在任何C API变更后运行代码库的generator。
保持application binary interface稳定:
- 添加overload、method或type,而不要更改现有签名。
- 不得移除公开API或更改其返回类型。
- 新增既定参数可能改变binary compatibility,因此应优先添加overload。
- 将API标记为deprecated,而不是将其移除,并提供可执行的迁移指引。
- 使命名和参数约定与周围公开API保持一致。
- 重新生成binding,并在构建前检查生成的差异。
当文档同时包含生成page和可编辑source sidecar时,只编辑指定source,然后重新render输出。
## 区分原生指针的所有权,可变对象不跨线程共享
编写wrapper前,先对每个原生pointer分类:
- 借用参数不转移所有权。
- 手工拥有的原生object必须使用wrapper规定的原生释放路径。
- 引用count方式的object必须遵循wrapper的引用count契约。
使失败行为与所在层匹配。C兼容API返回null pointer或`false`;托管factory返回`null`;无法建立有效object的托管constructor则抛出异常。
某些原生操作可能返回接收到的同一instance。释放source前必须比较identity,否则返回值可能引用已经释放的object。
除非原生契约明确允许,否则不要在线程之间共享可变原生object。为每个线程提供独立的可变state;只共享明确记录为immutable且thread-safe的object。
绝不能把临时固定的托管pointer传给可能在固定scope结束后继续保留它的原生代码。应使用生命周期明确的pinned handle或分配unmanaged memory。违反此规则会导致garbage collector移动memory后,原生代码仍持有旧address。
## 在父代码库和原生子模块中都使用功能分支
提交commit前先创建feature branch。绝不能直接向受保护branch提交commit。
对原生submodule或嵌套repository独立应用同一规则:它有自己的branch state、受保护branch、commit和review路径。
1. 在父repository中创建范围明确的feature branch。
2. 如果要修改嵌套的原生repository,应在其中编辑或提交前先创建feature branch。
3. 所有commit都必须位于对应的feature branch上。
4. 通过代码库批准的pull request workflow提交变更以供review。
5. 确认父repository记录的是预期且已经review的原生revision。
不要修改代码库声明的shim或扩展面之外、由上游管理的原生directory。
## 用正确的原生二进制文件测试,不跳过失败
构建成功并不代表完成。应使用正确引导路径生成的原生二进制文件来运行相关托管测试。
- 声称完成前,测试必须通过。
- 不要skip失败的测试,也不要引入skip exception来绕过回归。
- 只有在确已证明存在hardware或platform限制时才允许skip,例如没有display、无法使用graphics device,或平台不支持该功能。
- C API发生变更时,应在测试前重新生成binding。
- 验证version变更前,应清理cache中的原生web产物或platform产物。
- 影响原生侧的工作必须验证具有代表性的platform和architecture。
编写能在所有权、null处理、导出符号、ABI、thread scope或释放行为错误时失败的测试。如果性能变更的确切行为或收益尚未得到证明,应包含parity test和benchmark。
## 根据失败的表现找出跨层问题所在
建立已知正常baseline,每次只改变一个变量,记录每项结果,并把platform差异视为证据。如果某项变更让状态恶化,应先还原该变更,再测试其他假设。
根据故障形态选择首先检查的边界:
- 缺少托管type表明binding可能没有重新生成或未被包含。
- 无法解析原生符号表明C声明与实现可能不一致。
- 运行时缺少entry point强烈表明使用了陈旧原生二进制文件或export不匹配。
- access violation表明所有权、释放、pinning或签名可能已损坏。
- factory调用后的null reference故障表明原生层可能返回了失败,而caller将其忽略。
- 随机跨线程crash表明可变原生object可能被共享。
完成前,应检查生成差异,在需要时构建原生代码,构建托管wrapper,运行测试,并验证受影响的platform matrix。准确报告所用引导产物的来源和architecture。测试失败、用下载产物代替必要的原生构建,或platform验证尚未完成时,不得声称成功。
