RFC-0086:RFC-0050 更新:FIDL 属性语法 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 以 FIDL 语言为属性指定新语法。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-03-09 |
审核日期(年-月-日) | 2021-04-07 |
摘要
另见:
设计初衷
FIDL 属性提供了一种将编译时元数据附加到 FIDL 的清晰方法 声明,允许作者向 FIDL 编译器传递额外信息 相关文档(文档、 NoDoc)编译时验证 (ForDeprecatedCBindings)、 代码生成 (过渡性、 传输),所需的 API 可用性 (可发现)等。除了这些 “官方”属性,FIDL 作者可以定义自己的“自定义” 属性,这些属性不影响编译,但仍附加到 生成的 FIDL JSON 中间表示法, 下游使用。
现有属性语法缺少三个属性:
- 多个属性应以单个逗号分隔声明的形式编写,
如下所示:
[Discoverable, NoDoc, Transport = "Channel"]
。这种方法不够灵活 可能无法清楚属性是相互独立的。 - 属性目前只能接受 0 个或 1 个参数,但 而不是更多。
- 如果属性带有参数,则值始终是字符串。例如:
MaxBytes 属性使用字符串化数字作为其参数,如下所示:
[MaxBytes="128"]
。这会让人感到困惑,尤其是当 FIDL 的其余部分 您可以预测输入。
FIDL 语法目前正在进行一项重大迁移, RFC-0050:语法改版工作。这就为 对属性实施语法更改。
设计
语法
每个属性都是一个声明,其中包含属性的名称
(按照惯例,lower_snake_cased)后跟 @
符号。对于
带有多个属性的声明,则不存在首选排序方式
不同属性之间的映射。例如,属性声明 [Discoverable,
NoDoc, Transport = "Channel"]
现在可编写为:
@discoverable
@no_doc
@transport("Channel")
protocol P {};
如有必要,可以在属性名称中后跟一对圆括号, 包含一个或多个参数的逗号分隔列表。参数可以是 任何有效的 FIDL 常量。例如:
const DEFAULT_TRANSPORT string = "Channel";
@transport(DEFAULT_TRANSPORT)
protocol P {};
参数必须使用 lower_snake_cased “keyword”标记语法类似于
(在 Python 中找到的属性除外)
使用一个参数,该参数必须省略关键字。参数输入和存在
的必需实参仅验证原始属性。对于
因此编译器默认会忽略
预定值。请考虑以下模拟 @native
的用法示例
属性,该属性接受两个必需的参数(req_a
和 req_b
),
可选关键字(opt_a
和 opt_b
):
const C bool = true;
@native(req_a="Foo",req_b=3) // OK: neither opt arg is set
@native(req_a="Foo",req_b=3,opt_c=C) // OK: only opt_a is set
@native(req_a="Foo",req_b=3,opt_d=-4) // OK: only opt_b is set
@native(req_a="Foo",req_b=3,opt_c=C,opt_d=-4) // OK: both opt args are set
@native(req_a="Foo",req_b=3,opt_d=-4,opt_c=C) // OK: arg order is irrelevant
@native(opt_d=-4,req_a="Foo",req_b=3) // OK: arg order is irrelevant
@native(req_b=3) // Error: missing req_a
@native(req_a="Foo") // Error: missing req_b
type S = struct {};
作者定义的自定义属性无架构,仅由
它们是否存在于 FIDL 文件中。编译器无法验证
参数的数量,是否为必需参数,以及参数的正确类型
(不过,如果我们选择实现“In-FIDL”属性,
如
替代方案部分)。因此,只有
强制执行属性签名的语法正确性。示例
作者定义的属性 @custom
中如下所示:
@custom(a="Bar",b=true) // OK: grammatically correct
@custom("Bar",true) // Error: bad grammar - multiple args require keywords
@custom("Bar") // OK: correct grammar (though signature now unclear)
@custom(true) // OK: correct grammar (signature even more unclear)
@custom() // Error: bad grammar - cannot have empty arguments list
@custom // OK: grammatically correct
type S = struct {};
正式地说,新 FIDL 属性的修改后的 BNF 语法 语法如下:
attribute = "@", IDENTIFIER , ( "(" , constant | attribute-args, ")" ) ;
attribute-args = attribute-arg | attribute-arg, "," attribute-args;
attribute-arg = IDENTIFIER , "=" , constant;
RFC-0040:标识符唯一性适用于属性名称。这个 表示属性名称中的不同大小写形式和连续下划线 在规范名称解析期间简化为单个通用名称。以下 因此会导致范围冲突,在编译时生成错误:
@foo_bar
@FooBar // Error: re-used attribute name "foo_bar"
@fooBar // Error: re-used attribute name "foo_bar"
@Foo_Bar // Error: re-used attribute name "foo_bar"
@foo__bar // Error: re-used attribute name "foo_bar"
@FOOBar // Error: re-used attribute name "foo_bar"
type S = struct {};
JSON IR
属性的 FIDL JSON 中间表示法架构
新语法。架构的 属性
定义现在有一个 location
字段,用于跟踪
属性声明在文件中的位置。value
字段
取而代之的是 arguments
字段,该字段将每个参数的值存储为
name
/value
对,后者采用
#/definitions/constant
。对于只接受一个参数的属性,
来源中没有关键字 name
,唯一参数的名称默认为
"value"
。
此外,还有 compose
个节和 reserved
个表/联合成员
之前无法包含属性。此 RFC 规范纠正了这些疏漏,
将新的 #/definitions/compose
定义添加为
#/definitions/interface
中,并正式将 makebe_attributes 属性附加到
#/definitions/table-member
。
总之,此 RFC 规范引入了三个新的架构定义
(#/definitions/attribute-arg
、#/definitions/attribute-args
和
#/definitions/compose
)并修改了另外三个(#/definitions/attribute
、
#/definitions/interface
和 #/definitions/table-member
)。新属性
则会显示如下定义:
"attribute-arg": {
{
"description": "Definition of an attribute argument",
"type": "object",
"required": [
"name",
"value",
],
"properties": {
"name": {
"description": "Name of the attribute argument",
"type": "string",
},
"value": {
"description": "Value of the attribute argument",
"$ref": "#/definitions/constant",
},
"location": {
"description": "Source location of the attribute argument",
"$ref": "#/definitions/location"
},
},
},
},
"attribute-args": {
{
"description": "Definition of an attributes argument list",
"type": "array",
"items": {
"description": "List of arguments",
"$ref": "#/definitions/attribute-arg",
},
},
},
"attribute": {
{
"description": "Definition of an attribute",
"type": "object",
"required": [
"name",
"arguments",
"location",
],
"properties": {
"name": {
"description": "Attribute name",
"type": "string",
},
"arguments": {
"description": "Attribute arguments",
"$ref": "#/definitions/attribute-args",
},
"location": {
"description": "Source location of the declaration",
"$ref": "#/definitions/location",
},
},
},
},
"compose": {
{
"description": "Compose member of an interface declaration",
"type": "object",
"required": [
"name",
"location",
],
"properties": {
"name": {
"$ref": "#/definitions/compound-identifier",
"description": "Name of the composed interface"
},
"maybe_attributes": {
"description": "Optional list of attributes of the compose declaration",
"$ref": "#/definitions/attributes-list",
},
"location": {
"description": "Source location of the compose declaration",
"$ref": "#/definitions/location",
},
},
},
},
继续前面部分中的 @native
示例,
其 type S
的 JSON IR 输出的 maybe_attributes
字段将如下所示:
"maybe_attributes": [
{
"name": "native",
"arguments": [
// Note: the omitted opt_d is not included in the IR
{
"name": "req_a",
"value": {
"kind": "literal",
"value": "Foo",
"expression": "\"Foo\"",
"value": {
"kind": "string",
"value": "Foo",
"expression": "\"Foo\"",
},
},
},
{
"name": "req_b",
"value": {
"kind": "literal",
"value": "3",
"expression": "3",
"literal": {
"kind": "numeric",
"value": "3",
"expression": "3",
},
},
},
{
"name": "opt_c",
"value": {
"kind": "identifier",
"value": "true",
"expression": "C",
"identifier": "example/C",
},
},
],
"location": {
"filename": "/path/to/src/fidl/file/example.fidl",
"line": 4,
"column": 0,
"length": 36,
},
},
],
案例研究:FIDL 版本控制
RFC-0083:FIDL 版本控制介绍了基于属性的语法,
将版本控制元数据附加到 FIDL 声明及其成员。在
具体而言,它定义了一个新属性 @available
,该属性接受可选的
参数 platform
、since
、removed
、deprecated
和 note
。例如:
@available(since=2)
type User = table {
// Was created with struct at version 2, so no @available attribute is needed.
1: is_admin bool;
// Deprecated in favor of, and eventually replaced by, the name field.
@available(deprecated=3,removed=4,note="use UTF-8 `name` instead")
2: uid vector<uint8>;
// The current (unreleased) version constrains this definition.
// Placing "removed" before "since" is discouraged, but won't fail compilation.
@available(removed="HEAD",since=3)
3: name string;
@available(since="HEAD")
3: name string:60;
};
值得注意的是,引用已编号的平台版本的参数
(即 since
、removed
和 deprecated
)也可以接受特殊字符串
"HEAD"
。这意味着,这些参数并没有一个
可解析类型,例如 uint8
。允许使用此类构造,因为
特定类型的验证规则属性(如 @available
)
硬编码到编译器本身。其他更细微的规则,例如
限制只有 @available
属性附加到 library
声明
可以带有 platform
参数,同样按照这种定制方式进行处理。
实现
此提案将作为更广泛的 RFC-0050 的一部分实施 FIDL 语法转换。以“new”格式写入的所有 FIDL 文件,语法为 以及正式的 FIDL 语法将会更新,以便与其他 RFC-0050。
此外,架构定义将更新为 适应本文档中指定的 JSON IR 更改。不过, 在实际迁移到 语法,以确保迁移前和迁移后 语法生成的 IR 将成为验证 新编译器的准确性。因此,对 属性的更新 定义 然后再进行任何 RFC-0050 迁移,以确保 JSON IR 不会更改 。
性能
这些语法更改不太可能对性能产生影响。
安全注意事项
这些语法更改不太可能对安全性产生重大影响。它们确实有 只不过是保留当前潜在的安全验证属性 更容易撰写和推理。
隐私保护注意事项
这些语法变更不太可能对隐私权产生重大影响。它们确实有 使用未来可能用于隐私验证的属性 更容易撰写和推理。
测试
这些语法变更不太可能对测试产生重大影响。它们确实有 只不过是让未来潜在的测试插桩属性 更容易撰写和推理。
文档
所有相关文档和示例都将更新,以体现新的 语法(涵盖在更广泛的 RFC-0050 文档更新中)。在 尤其是官方 FIDL 的参考文档 属性将会更新以反映此设计规定的规则,并且 注意每个属性的有效参数类型和隐含默认值。
缺点、替代方案和未知之处
In-FIDL 属性架构
在设计空间里,可能用到的语法几乎是无限的,这个部分 不会尝试解决所有问题然而,有一个选项非常严重, 是允许定义注解函数的接口 “使用 FIDL”。此备用语法及其遭拒的理由如下 如下所述。
考虑以下 FIDL 文件及其自定义属性的接口 以内联方式定义:
type MyAttrArgs = table {
1: foo string;
2: bar int32;
3: baz bool;
};
@myAttr(MyAttrArgs{foo: "abc", bar: 1, baz: false})
type MyStruct = struct {...};
此设计的优势在于“吃我们自己的 dogfood”:FIDL 是一个接口, 定义语言,为什么不为我们的内置 API 定义接口, 编译器感知属性函数?此外,这使得 自定义、用户定义的属性, 编译器会如何将此类用户定义的元信息附加到 生成的绑定代码仍是一个开放式问题。
此设计路线最终因尝试过多而被拒绝。以上均不是 属性用例中的语言在可预见的未来内必不可少, 定义功能,甚至还不清楚启用用户定义的 属性是可取的未来目标。本文档建议的语法为 对于其他热门语言(例如 Rust 和 Python。
当前设计中没有明确阻止实现 In-FIDL 的因素。 属性架构,所以此替代方案仍是可行的方案,以备将来使用 对属性语法的扩展。
为属性参数使用字符串字面量
对于本文档中指定的设计,可以进行以下更改: 而不再允许类型化参数,而是保留要求 所有参数值均为字符串字面量。请参考以下示例 图示:
const MAX_STR string = "123";
const MAX_NUM uint64 = 123;
@max_handles(123) // Error: cannot use non-string types
@max_handles(MAX_STR) // Error: cannot use const identifiers
@max_handles(MAX_NUM) // Error: cannot use non-string types or const identifiers
@max_handles("123") // OK: only string literals are allowed
type S = struct {};
这种设计的优点是实现起来很简单。接收者
因此后端需要
复杂的提取逻辑,可正确解释各种可能的类型
每个属性的名称参数。而以前,
将字符串 "123"
传递到整数 123
应该已经足够,后端现在需要处理
整个 #/definitions/constant
架构。这个额外的
实现费用乘以支持的后端数量。
允许使用类型化属性的好处是,它可以集中这种类型
投射逻辑。例如,请考虑属性声明
@my_custom_attr("3.")
。如果每个后端都需要执行自己的类型转换,
可将 "3."
视为要转换为整数的有效值,而将另一个
不能。我们很难捕捉这类东西的所有细微差别,
在属性实现方面不可避免地与后端有明显差异。美化一个
对属性类型在保真度中的行为方式的规范化理解可消除
不一致性。
遭拒的次要更改
本文档中介绍的属性语法明确指出, 附加到单个声明的多个属性之间的排序为 不相关。另一种方法是强制按字母顺序排列 订单。由于作者定义的自定义属性以及 它们可能会以可能 最好按特定顺序排列。请考虑以下两种 由作者定义的自定义属性,其顺序可能会造成混淆 如果需要按字母顺序排序,则将其反过来:
@this_attr("Foo")
@test_for_this_attr(false)
protocol P {};
另外,camelCase 被视为推荐使用的大小写格式, 属性语法。此建议最终被拒绝,因为没有任何 FIDL 样式指南中的其他大小写建议 实现驼峰命名法,并认为向库中添加一个新的 大小写样式。
早期艺术作品和参考资料
此 RFC 是 RFC-0050:语法 修改,从而修改正式的 FIDL 语法。
该方案借鉴了一些现有的“类似于属性”的在 其他语言,具体来说:
- Python 的装饰器语法和
关键字参数的设计灵感,
本文档中介绍的一些设计选择,例如使用
@
符号,并按关键字引用参数。 - Rust 的属性规范,虽然表面上 在某些语法选择上有所不同,在概念上也类似于 提案。
- Cap'n Proto 的注释架构 可作为参考点,为您找出可能更 本文档中建议的语法。