RFC-0040:标识符唯一性 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 目前,FIDL 规范和前端编译器基于简单的字符串比较将两个标识符视为不同的标识符。我们提出了一种将绑定生成器进行的转换考虑在内的新算法。 |
作者 | |
提交日期(年-月-日) | 2019-04-08 |
审核日期(年-月-日) | 2019-05-14 |
“SnowFlake 与 SNOW_FLAKE”
总结
FIDL 规范和前端编译器目前根据简单的字符串比较,将两个标识符视为不同的标识符。因此,我们提出了一种将绑定生成器进行的转换考虑在内的新算法。
设计初衷
语言绑定生成器会转换标识符,以符合目标语言限制条件和样式(将多个 FIDL 标识符映射到单个目标语言标识符)。这可能会导致意外冲突,这些冲突只有在定位到特定语言后才会显示。
设计
因此,我们提议对任何现有库都没有违反的 FIDL 标识符施加限制。它不会更改 FIDL 语言、IR(目前为 1)、绑定、样式指南或评分准则。
实际上,标识符由一系列连接在一起的字词组成。联接单词的常见方法是 CamelCase
(从小写到大写的转换是单词边界)和 snake_case
(使用一个或多个下划线 (_
) 来分隔单词)。
标识符应转换为规范形式,以便进行比较。这将采用 lower_snake_case
形式,并保留原始形式的单词分隔符。字词被损坏 (1) 有下划线;(2) 从小写或数字转换为大写;(3) 从大写转换为小写之前。
在 FIDL 中,标识符必须以原始形式使用。因此,如果某个类型名为 FooBar
,尝试将其称为 foo_bar
则会出错。
有一种简单的算法可以执行这种转换,详见 Python2:
def canonical(identifier):
last = '_'
out = ''
for i, c in enumerate(identifier):
is_next_lower = i + 1 < len(identifier) and identifier[i+1].islower()
if c == '_':
if last != '_':
out += '_'
elif (((last.islower() or last.isdigit()) and c.isupper())
or (last != '_' and c.isupper() and is_next_lower)):
out += '_' + c.lower()
else:
out += c.lower()
last = c
return out
下面是一些示例及其可能翻译成的各种目标语言的内容:
FIDL 标识符 | 规范形式 | C++ | Rust | Go | Dart |
---|---|---|---|---|---|
foobar | foobar | foobar | foobar | 傻瓜 | foobar_ |
foo_bar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
foo__bar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
FooBar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
fooBar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
FOOBar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
实施策略
前端编译器将会更新,以检查每个新标识符的规范形式是否与任何其他标识符的规范形式不冲突。
FIDL IR 的下一版本应围绕规范名称(而不是原始名称)进行组织,但原始名称将以声明字段的形式提供。如果我们可以避免在生成的绑定中使用未经修改的名称,就可以从 IR 中删除原始名称。
工效学设计
它将现行的 FIDL 语言限制标准化。
文档和示例
我们将更新 FIDL 语言文档以描述此限制条件。 它会进行扩展,以包含上述设计部分的大部分内容。
由于此方案只是对现有做法进行编码,因此示例和教程不会受到影响。
向后兼容性
任何可能会违反此项变更的现有 FIDL 库都违反了我们的样式指南,并且无法与很多语言绑定搭配使用。这不会更改用于计算序数的标识符的形式。
性能
这对前端编译器造成的成本微乎其微。
安全性
无影响。
测试
fidlc
中将会对规范化算法实现进行大量测试。此外,我们还会进行 fidlc
测试,以确保在声明冲突的标识符时可以捕捉到错误,并确保必须使用原始名称来引用声明。
缺点、替代方案和未知情况
一种方案是不进行任何操作。通常,我们会在非 C++ 生成的绑定中发现这些问题,因为构建失败。随着 Rust 在 fuchsia.git
中使用得越来越多,冲突滑到其他花瓣的可能性就会减少。这些问题已经很少见了。
规范化算法很简单,但有一个糟糕的失败情况,即 UPPER_SNAKE_CASE 标识符中的字母数字混合字词可能会损坏。例如,H264_ENCODER
→ h264_encoder
,但为 A2DP_PROFILE
→ a2_dp_profile
。这是因为该算法将数字视为小写字母。
我们不得不中断数字到字母的转换,因为 H264Encoder
应规范化为 h264_encoder
。不含小写字母的标识符可能采用特殊大小写形式(仅破解下划线),但这会增加算法的复杂性,还可能会加剧思维模型的复杂性。
规范形式可以表示为字词列表,而不是 low_camel_case 字符串。它们是等效的,在实践中,作为字符串管理会更简单。
在生成序数时,我们可以使用标识符的规范形式。这会使此项更改成为破坏性更改,而不会带来任何明显的好处。如果将来出现有序标志中断的日子,我们可以考虑进行这种更改。
初步拒绝和第二次审核
在 2019 年 4 月 18 日的首次审核中,此 FTP 遭到拒绝,原因如下。
- 关于解决这类问题的两种观点
- 努力对目标语言的约束条件进行建模,以尽可能保持 FIDL 的灵活性,即使该样式与推荐的样式不同也是如此。
这就是此 FTP 采用的方法。
- 优点:可以灵活地在 Fuchsia 之外最终使用 FIDL,从编程语言的角度来看更纯粹。
- 缺点:范围规则比较复杂,不强制执行样式,但建议采用(例如,通过执行 lint 检查)。可能会导致合作伙伴构建的 API 不符合我们想要的 Fuchsia 式指南(因为它们不需要运行,也不需要执行 lint 检查)。
- 直接在语言中强制执行样式限制,从而消除问题类。
- 优点:系统会强制执行样式,告知开发者应采用的内容,或者内容无法编译。
- 缺点:考虑语言定义中的样式选择,使用 FIDL 的初级开发者要爬得更高。
- → 我们拒绝了该提案,并更倾向于采用一种能直接强制执行相应语言的样式的方法。
- → 接下来是实现上述目标的正式提案,并阐明所有方面(例如,
uint8
是否应为Uint8
,vector<T>
是否为Vector<T>
?)
Google 根据以下观察结果决定推翻这一决定:
内核 API 现在称为 FIDL。这促使我们在去年 10 月重新打开了标识符唯一性问题,我们基本上推翻了这项决定,让“C 样式”和“FIDL 样式”共存。目前,Fildl linter 会检查这一点。
我们看到过其他用例,推动 FIDL 泛化传输,并可能使用局部样式规则来更好地支持这些域。
标识符冲突仍然是一个问题,而建模目标语言限制条件在 FIDL 工具链中是一个明确发现的缺陷。
早期技术和参考资料
在 proto3 中,应用了类似的规则,以便为 JSON 编码生成 lowerCamelCase
名称。