搜索/修改大型多语言运行时代码库,而不破坏已有行为

修改大型多语言运行时代码库,而不破坏已有行为

用于由原生代码和脚本组成、内置打过补丁的依赖的大型运行时项目。先读离代码最近的指南,使用项目的构建快捷命令和测试目标,遵守自有类型和禁止事项,并把行为变更放在兼容性标志之后。

要使用这个AI规则
## 先读离代码最近的指南,并确认是否有父项目 这类仓库中的指南是分层的。根目录的指南提供高层次的方向指引;子目录内的指南则承载组件专属的上下文,例如关键类、查找位置对照表、本地约定以及反模式。除了根目录指南之外,务必阅读离你即将改动的代码最近的那一份指南。 - 务必判断该仓库是独立检出的,还是作为更大的父项目的一个组件嵌套存在的。如果是嵌套的,重要文档位于父项目的根目录,在这里是看不到的;在做出任何假设之前,请先阅读父项目的自述文件、父项目的指南以及其他顶层文档。 - 可以自由地调查和阅读,但在提出或进行修改之前,先收集好这些上下文。 - 当你发现指南中缺失了某些长期有效的高层次信息时,建议将其补充进指南,而不是只留在对话里。 ## 用项目的任务快捷命令构建和测试,不临时编命令 本项目有一套权威的构建系统,外加一层用于提供快捷命令的轻量任务运行器。日常工作请使用这些快捷命令,只有当某个快捷命令没有暴露你需要的选项时,才下沉到底层的构建工具。不要自行编造临时的编译命令。 值得寻找并复用的典型快捷命令包括:构建、运行全部测试、格式化、检查脚本源码、检查原生源码、检查系统级语言包、以调试方式流式输出测试结果、按名称运行单个兼容性或一致性测试套件、生成新测试的骨架、生成类型定义、生成编辑器索引数据库、以地址消毒器进行构建与测试、生成覆盖率报告,以及监视源码目录并在变更时重新运行某条命令。 ## 找到正确的测试目标及其自动生成的变体 多种测试族并存,各自拥有独立的入口: - 配置驱动的集成测试:一个序列化的配置文件,声明了一项服务,其中内嵌脚本模块、兼容性日期、功能开关,以及诸如服务绑定、结构化数据、键值存储和有状态对象命名空间等绑定。 - 由专用宏构建的原生单元测试,文件名带有连字符形式的测试后缀。 - 运行时兼容性套件与 Web 标准一致性套件,各自按名称运行。 - 基准测试,按路径运行。 构建命令和测试命令都接受显式指定的目标,默认作用于整棵代码树。目标名称必须带有规定的后缀标记,而构建宏会为每个测试自动生成若干变体:一个锁定在最旧兼容性基线的默认变体、一个采用最新日期并启用全部开关的变体,以及一个启用全部临时门控的变体。要找到某个文件对应的正确目标,请阅读同一目录下的构建文件,或向构建系统查询该软件包中的所有目标。 ## 摸清主要代码区域,并照着相似的现有实现来写 在编辑之前先建立一张心智地图;对于任何新工作,先找到一处相似的现有实现并沿用其模式。 - 运行时 API:面向脚本暴露的接口的原生实现,以及标准库兼容层及其测试。 - I/O 子系统:有状态对象的存储与缓存、请求跟踪、资源限制、线程处理以及工作单元的生命周期。一个按请求划分的上下文对象将整个请求串联起来。 - 胶合层:引擎绑定、类型包装器、Promise 处理、内存管理以及模块系统。 - 服务器层:二进制入口点与配置处理,采用由一份 schema 文件描述的基于能力的安全模型。 - 面向特定语言的代码树,涵盖兼容层、另一套脚本运行时以及系统级语言的集成组件。 兼容层是成对存在的:原生的那一半位于一棵代码树中,脚本的那一半位于另一棵中,且新模块必须注册到指定的模块表里。类型定义由原生类型信息加上手写声明推导生成,只能重新生成,绝不可手工编辑。 ## 遵循内部代码风格,让每个提交都小且能通过测试 遵循所引用的内部代码风格指南,包括其中记录的例外情形;凡是项目有意偏离该指南之处,这些偏离本身就是规则。 - 严格匹配固定的语言标准、项目特有的源文件扩展名,以及带连字符的测试文件后缀。 - 一条格式化命令会对所有语言运行全部格式化工具。在提交变更之前,请运行它以及完整的测试命令。 - 提交前检查会阻止暂存代码中出现调试打印宏,并重新执行格式检查。不要绕过它。 - 将变更拆分为小的提交;每个提交都必须能够独立编译并通过测试。不要留下修补性的提交。 - 脚本侧的源码采用严格类型:强制要求精确可选属性语义、私有字段语法以及显式返回类型。 ## 使用项目自有基础库的类型,而不是标准库类型 本代码库刻意在大多数类型上使用自有的基础库,而非语言的标准库。请与所在文件的周边写法保持一致,不要顺手去用标准容器。 需要替换的对应关系如下:用持有所有权的字符串和非持有的字符串视图代替标准字符串;用固定大小数组和可增长向量代替标准向量;用独占、引用计数、线程安全引用计数以及弱所有权句柄代替标准智能指针;用 maybe 类型代替 optional;用可调用对象包装器代替标准函数对象;用带标签的联合体代替 variant;用数组切片代替裸的 span;使用该库的异常类型;以及用该库的 promise 和分叉 promise 代替标准 future。 出于安全考虑,宁可使用数组切片而非裸指针;当引用受所有者生命周期约束时,宁可使用与生命周期绑定的句柄而非裸引用。在错误处理方面:使用专用宏来解包 maybe;当失败必须以带有恰当异常类型的脚本异常形式呈现时,使用面向脚本的要求类宏;对于内部不变式,则使用原生的断言宏与前置条件宏。 ## 避开会导致泄漏和隐蔽损坏的禁用写法 以下规定是绝对的;违反它们会造成难以察觉的数据损坏、内存泄漏或兼容性破坏。 - 绝不要把引擎局部句柄或引擎全局句柄放进值类型的结构体声明中;请使用提供的引用包装器。 - 绝不要把引擎句柄放进 promise 类型中,也绝不要把引擎锁传入异步协程。 - 绝不要在没有所属包装器的情况下,从脚本堆持有对 I/O 对象的引用;有一个作用域守卫会强制这一点。 - 优先使用原型属性而非实例属性,后者会破坏垃圾回收的优化。 - 绝不要对用户提供的内容做深度冻结,这对含环的值是不安全的。 - 对于会把未知名称远程分发的接口,绝不要在不加兼容性开关门控的情况下为其添加方法,也绝不要更改已序列化的枚举值。 - 优先使用增量式的任务注册调用,而非直接访问原始任务列表;并且避免使用布尔参数,改用强类型的布尔类型。 - 凡是存放在可从脚本堆到达的原生对象中的引用,都必须实现垃圾回收访问;原生引用环永远不会被回收。有一项专门的静态检查会验证这一点。 - 内嵌数据库的误用错误总是会抛出,绝不会被吞掉;有状态对象数据库中禁止使用事务。 - 模块求值绝不能在请求上下文中运行,且全局作用域中禁止异步 I/O。 - 在共享的类型声明文件中,绝不要使用通用的环境类型名称;务必以产品名或功能名作为前缀。绝不要把裸的无类型对象当作类型使用;应声明一个字段明确的正式接口,必要时可继承一个开放记录类型以便向前兼容。 - 绝不要手工编辑生成的快照。请修复源头层并重新生成。 ## 把行为变更放在兼容性标志或临时开关之后 本项目做出了强有力的向后兼容承诺:行为一旦上线,就不能被移除或悄悄更改。因此,行为变更必须置于某种门控之后引入,而你要依据其永久性来选择门控方式。 - 日期驱动的兼容性开关是按实例生效且永久存在的,定义在一份 schema 中,其注解携带开关名称与启用日期。每个开关都必须在其启用日期到来之前完成文档化。 - 临时的进程级门控由配置驱动,用于需要条件式激活的高风险发布。它们由一个枚举加一张字符串映射表定义,二者必须保持同步。 对非标准接口要保持很高的门槛;相较于发明项目特有的行为,应优先实现已发布的标准。 ## 升级内置依赖时保留本地补丁 第三方依赖由构建系统的模块机制内置到一个外部目录中。集成配置位于模块清单和一个依赖构建目录中;旧的工作区文件可能仍然存在,但已不再是主要机制。关于内置基础库的头文件与命名空间的问题,请直接查阅其源码,而不要靠猜测。 有若干依赖带有本地维护的补丁集,会在构建期间应用。这些补丁引入了项目特有的定制和新接口,因此每当升级该依赖时,都要复查并更新它们。另请注意,本项目使用了一款替代的内存分配器,其行为与默认分配器不同;任何内存分析都必须把这一点考虑在内。 对于引擎或依赖的大版本升级:开始之前先询问确切的目标版本,以及哪些补丁或定制必须予以保留,然后按照文档中的同步、应用补丁、变基、重新生成补丁以及提升版本号等步骤执行。合并冲突很常见,且几乎总是需要人工判断才能保住项目特有的改动;绝不要在没有人工复核的情况下自动解决冲突。请确认完整测试套件通过,并把遇到的任何新的弃用提示反馈出来。 ## 找到分散的文档,发现错误时提出更新 文档是分散的,而非集中的。一个文档目录涵盖跨领域主题,例如环境搭建、新增接口的指导原则以及依赖升级流程;与此同时,各个源码目录往往带有各自的自述文件或指南,其中包含深入的子系统细节。在陌生领域开展工作时,请在猜测或提问之前主动寻找这些文档。 当文档缺失、不准确或已过时,请主动提出更新,而不是保持沉默。先核实事实,并且在未与维护者确认其准确性之前,不要提交文档修改。