RFC-0086:RFC-0050:FIDL 属性语法更新

RFC-0086:RFC-0050 更新:FIDL 属性语法
状态已接受
领域
  • FIDL
说明

以 FIDL 语言为属性指定新语法。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-03-09
审核日期(年-月-日)2021-04-07

摘要

本文档介绍了在 API 中使用 FIDL 语言。

另见:

设计初衷

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_areq_b), 可选关键字(opt_aopt_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,该属性接受可选的 参数 platformsinceremoveddeprecatednote。例如:

@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;
};

值得注意的是,引用已编号的平台版本的参数 (即 sinceremoveddeprecated)也可以接受特殊字符串 "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 的注释架构 可作为参考点,为您找出可能更 本文档中建议的语法。