本文档列出了 FIDL 编译器发出的所有错误,
fidlc
。此网域中的错误标识符始终呈现此前缀
fi-
,后跟四位数代码,例如 fi-0123
。
fi-0001:字符无效
词法分析器在 处未能将字符转换为词元 指定位置。
library test.bad.fi0001;
type ßar = struct {
value uint64;
};
无效字符应通过替换或移除进行修复。
library test.good.fi0001;
type Foo = struct {
value uint64;
};
无效字符取决于地理位置。请参阅 FIDL 语言规范以确定 FIDL 语法的各个部分允许使用哪些字符。
fi-0002:意外换行
字符串字面量不得拆分为多行:
library test.bad.fi0002;
const BAD_STRING string:1 = "Hello
World";
请改为使用转义序列 \n
来表示换行符:
library test.good.fi0002;
const GOOD_STRING string:11 = "Hello\nWorld";
fi-0003:转义序列无效
词法分析器在 转义序列。
library test.bad.fi0003;
const UNESCAPED_BACKSLASH string:2 = "\ ";
const BACKSLASH_TYPO string:1 = "\i";
const CODE_POINT_TYPO string:1 = "\Y1F604";
替换有效字符以开始转义序列,或者移除 非预期的反斜杠字符。
library test.good.fi0003;
const ESCAPED_BACKSLASH string:2 = "\\ ";
const REMOVED_BACKSLASH string:1 = "i";
const SMALL_CODE_POINT string:3 = "\u{2604}";
const BIG_CODE_POINT string:4 = "\u{01F604}";
请参阅 FIDL 语法规范 以获取有效的转义序列。
fi-0004:十六进制数字无效
字符串字面量中的 Unicode 转义字符不得包含无效的十六进制数字:
library test.bad.fi0004;
const SMILE string = "\u{1G600}";
您必须指定一个有效的十六进制 Unicode 码位,其范围为 0 到 10FFFF。
每个十六进制数字必须是从 0 到 9 的数字、从 a
到 f
的小写字母,
或从 A
到 F
的大写字母。在本例中,G
是 F
的拼写错误:
library test.good.fi0004;
const SMILE string = "\u{1F600}";
fi-0005
fi-0006:预期声明
当 FIDL 需要声明但查找其他内容时,就会发生此错误。
这通常是由于拼写错误导致的。有效的声明包括:type
、alias
、
const
、using
、protocol
和 service
。
library test.bad.fi0006;
cosnt SPELLED_CONST_WRONG string:2 = ":("; // Expected a declaraction (such as const).
要修正此错误,请检查顶级声明中是否存在拼写错误,并确保 您只使用 FIDL 支持的功能。
library test.good.fi0006;
const SPELLED_CONST_RIGHT string:2 = ":)";
fi-0007:意外令牌
如果在解析过程中遇到意外标记,就会出现此错误。 一般来说,这是由拼写错误导致的:
library test.bad.fi0007;
alias MyType = vector<uint8>:<,256,optional>; // Extra leading comma
对此问题的解决方法通常是移除非预期的令牌, 请提供剩余的语法:
library test.good.fi0007;
alias MyType = vector<uint8>:<256, optional>;
fi-0008:意外令牌
只要 FIDL 解析器遇到语法上无效的
令牌。这种情况可能会通过多种方式发生,例如枚举成员缺少 =
、
一个额外的令牌,如 library = what.is.that.equals.doing.there
,依此类推。
library test.bad.unexpectedtokenofkind;
type Numbers = flexible enum {
ONE; // FIDL enums don't have a default value.
};
通常,解决此问题的方法包括添加缺失的令牌或移除 额外的一个。
library test.good.fi0008;
type Numbers = flexible enum {
ONE = 1;
};
为避免此错误,请对您的 *.fidl
文件执行一次检查,确保它们
语法正确。
fi-0009:非预期的标识符
当标识符在错误位置使用时,通常会出现此错误:
using test.bad.fi0009;
请改用正确的标识符:
library test.good.fi0009;
fi-0010:标识符无效
找到的标识符不符合有效
标识符。FIDL 标识符可以包含字母数字和下划线
(具体是指 A-Z
、a-z
、0-9
和 _
),以及每个标识符
必须以字母开头并以字母或数字结尾。
library test.bad.fi0010a;
// Foo_ is not a valid identifier because it ends with '_'.
type Foo_ = struct {
value uint64;
};
要解决此问题,请更改标识符,确保其仅包含有效的 字符,以字母开头并以字母或数字结尾。
library test.good.fi0010a;
type Foo = struct {
value uint64;
};
如果将多部分(点号)标识符传递到 属性。
library test.bad.fi0010b;
@foo(bar.baz="Bar", zork="Zoom")
type Empty = struct{};
要解决此问题,请更改为在属性中仅使用单部分标识符。
library test.good.fi0010b;
@foo(bar="Bar", zork="Zoom")
type Empty = struct {};
fi-0011:库名称组件无效
库名称只能包含字母和数字(A-Z
、a-z
和 0-9
),
并且必须以字母开头。
library test.bad.fi0011.name_with_underscores;
要解决此问题,请确保所有库名称组件都符合要求。
library test.good.fi0011.namewithoutunderscores;
fi-0012:类型布局类无效
类型声明必须指定 FIDL 已知的布局:
library test.bad.fi00012;
type Foo = invalid {};
有效布局包括 bits
、enum
、struct
、table
和 union
:
library test.good.fi0012;
type Empty = struct {};
布局是 FIDL 的可参数化说明
类型。它表示一系列可接收进一步
以指定其形状。例如,struct
就是一种布局。
定义了特定成员后,该字段将变为具体类型,而
array
是一种在给定重复类型后变得具体的布局
按指定次数。
布局全部内置在 FIDL 语言中,用户没有办法 可以指定自己的布局,也可以创建自己的泛型类型模板。
fi-0013:封装类型无效
当传递到枚举或位声明的值不是 类型的标识符,例如,改为提供字符串值作为 "支持类型":
library test.bad.fi0013;
type TypeDecl = enum : "int32" {
FOO = 1;
BAR = 2;
};
如需修复此错误,请确保枚举或位的后备类型是类型 标识符。
library test.good.fi0013;
type TypeDecl = enum : int32 {
FOO = 1;
BAR = 2;
};
fi-0014:带有空括号的属性
如果属性有括号但没有参数,就会出现此错误。
library test.bad.fi0014;
@discoverable()
protocol MyProtocol {};
要解决此问题,请从不带实参的属性中移除括号,或提供 参数(如果符合预期)。
library test.good.fi0014;
@discoverable
protocol MyProtocol {};
FIDL 禁止在属性上使用空参数列表,主要作为一种样式选择。
fi-0015:属性参数必须全部命名
为清楚起见,特此说明:当一个属性具有多个参数时, 属性的参数必须明确指定。
当属性有多个参数但参数未超出时,就会出现此错误 为参数明确提供名称。
library test.bad.fi0015;
@foo("abc", "def")
type MyStruct = struct {};
如需解决此问题,请使用 name=value
语法为所有参数提供名称。
library test.good.fi0015;
@foo(bar="abc", baz="def")
type MyStruct = struct {};
fi-0016:成员前缺少序号
当联合或表中的字段缺少序数时,会发生此错误。
library test.bad.fi0016a;
type Foo = table {
x int64;
};
library test.bad.fi0016b;
type Bar = union {
foo int64;
bar vector<uint32>:10;
};
如需修复此错误,请明确指定表或联合的序数:
library test.good.fi0016;
type Foo = table {
1: x int64;
};
type Bar = union {
1: foo int64;
2: bar vector<uint32>:10;
};
与结构体不同,表和联合旨在允许向后兼容 对其中的内容进行更改。为了实现这一点, 值(序数)来标识表字段或联合变体。接收者 避免混淆,并增加在更改时不易意外更改序数的概率 表或联合,必须始终明确指定序数。
fi-0017:序数超出限制
表和联合的序数必须是有效的无符号 32 位整数。负面 序数大于 4,294,967,295 会导致此错误。
library test.bad.fi0017a;
type Foo = table {
-1: foo string;
};
library test.bad.fi0017b;
type Bar = union {
-1: foo string;
};
要修正此错误,请确保所有序数都在允许的范围内。
library test.good.fi0017;
type Foo = table {
1: foo string;
};
type Bar = union {
1: foo string;
};
fi-0018:序数必须从 1 开始
table
和 union
成员序数值都不能为 0:
library test.bad.fi0018;
type Foo = strict union {
0: foo uint32;
1: bar uint64;
};
而是应从 1 开始编号:
library test.good.fi0018;
type Foo = strict union {
1: foo uint32;
2: bar uint64;
};
fi-0019:严格位、枚举或联合体不能为空
严格的位、枚举或联合不允许零成员:
library test.bad.fi0019;
type Numbers = strict enum {};
而应至少包含一位成员:
library test.good.fi0019a;
type Numbers = flexible enum {};
或者,您也可以将声明标记为 flexible
,而不是 strict
:
library test.good.fi0019b;
type Numbers = strict enum {
ONE = 1;
};
空位、枚举或并集不携带任何信息,因此通常不应
可在 API 中使用但是,灵活的数据类型是专为进化而设计的,
定义最初为空的灵活位或枚举非常合理,
(预期以后会添加成员)。您应该始终仔细考虑
在定义新数据类型时是使用 strict
还是 flexible
。
fi-0020:协议成员无效
如果协议中的某项内容未被识别为有效内容,就会出现此错误 协议成员,例如当协议中的内容不是协议时 组合、单向方法、双向方法或事件
library test.bad.fi0020;
protocol Example {
NotAMethodOrCompose;
};
如需修正此错误,请移除无效商品或将它们转换为正确的商品 语法。
library test.good.fi0020;
protocol Example {
AMethod();
};
fi-0021
fi-0022:无法将属性附加到标识符
如果将某个属性添加到声明的类型上,就会显示此错误。 该类型是标识符类型时。例如,将属性放置在 字段名称,但在结构体声明中的字段类型之前,会将 属性替换为字段的类型,而不是字段本身。如果 字段类型是通过名称引用其他类型, 属性。
library test.bad.fi0022;
type Foo = struct {
// uint32 is an existing type, extra attributes cannot be added to it just
// for this field.
data @foo uint32;
};
如果意图是将属性应用于字段,则属性应为 已移到字段名称前面。
属性可以应用于声明它们的类型。这意味着,如果 结构体字段或其他类似声明的类型是匿名类型 而不是标识符类型,可以将属性应用于该类型。
library test.good.fi0022;
type Foo = struct {
// The foo attribute is associated with the data1 field, not the uint32
// type.
@foo
data1 uint32;
// The type of data2 is a newly declared anonymous structure, so that new
// type can have an attribute applied to it.
data2 @foo struct {};
};
fi-0023:类型声明内的属性
使用内嵌布局时, 则可以直接将属性放在布局之前但是,声明 类型,则无法:
library test.bad.fi0023;
type Foo = @foo struct {};
相反,您必须将属性放在 type
关键字之前:
library test.good.fi0023;
@foo
type Foo = struct {};
我们之所以强制执行此要求,是因为在两个位置允许属性会造成混淆。
fi-0024:方法参数列表的文档注释
方法参数列表不能包含文档注释:
library test.bad.fi0024;
protocol Example {
Method(/// This is a one-way method.
struct {
b bool;
});
};
目前,请为该方法本身添加文档注释:
library test.good.fi0024;
protocol Example {
/// This is a one-way method.
Method(struct {
b bool;
});
};
解决此错误后,此错误将不再存在。通过 错误,保留迁移以描述方法负载 使用 FIDL 类型,而不是参数列表。
fi-0025:导入组必须位于文件顶部
除了文件顶部的 library
声明外,不能包含任何
在文件的 using
导入之前声明其他声明(如果存在):
library test.bad.fi0025;
alias i16 = int16;
using dependent;
type UsesDependent = struct {
field dependent.Something;
};
如需解决此错误,请将所有 using
导入内容直接放在一个块中
在 library
声明之后:
library test.good.fi0025;
using dependent;
alias i16 = int16;
type UsesDependent = struct {
field dependent.Something;
};
此规则主要反映了 FIDL 团队在审美方面的决定, 合理分组且易于找到的依赖项更易于阅读。
fi-0026:文档注释块内的注释
注释不应放在文档注释块内:
library test.bad.fi0026;
/// start
// middle
/// end
type Empty = struct {};
而应放在文档注释块的前面或后面:
library test.good.fi0026;
// some comments above,
// maybe about the doc comment
/// A
/// multiline
/// comment!
// another comment, maybe about the struct
type Empty = struct {};
一般来说,文档注释块前面的注释是最好的 用于放置有关文档注释本身的评论。
fi-0027:文档注释块中存在空白行
文档注释块中不应有空行:
library test.bad.fi0027;
/// start
/// end
type Empty = struct {};
而应仅将空白行放在文档注释之前或之后 屏蔽:
library test.good.fi0027a;
/// A doc comment
type Empty = struct {};
或者,考虑完全省略空白行:
library test.good.fi0027b;
/// A doc comment
type Empty = struct {};
fi-0028:文档注释必须后跟声明
与常规评论一样,文档评论一律不得自由浮动:
library test.bad.fi0028;
type Empty = struct {};
/// bad
在所有情况下,文档注释都必须直接放在 FIDL 声明的前面:
library test.good.fi0028a;
/// A doc comment
type Empty = struct {};
FIDL“降幅”在编译期间为 @doc
属性添加文档注释。事实上
可根据需要直接编写注释:
library test.good.fi0028b;
@doc("An attribute doc comment")
type Empty = struct {};
从技术角度来看,独立的文档注释是不可编译的, 还会在语义上造成混淆:什么是“记录”什么都没有?取消点赞 将文档注释处理成结构化文档 因此必须明确它们关联到哪个 FIDL 结构。
fi-0029:资源定义必须至少包含一个属性
禁止使用未指定属性的资源定义:
library test.bad.resourcedefinitionnoproperties;
resource_definition SomeResource : uint32 {
properties {};
};
请至少指定一个属性:
library test.good.fi0029;
resource_definition SomeResource : uint32 {
properties {
subtype strict enum : uint32 {
NONE = 0;
};
};
};
这是一个与 FIDL 的内部实现相关的错误,因此应仅 向负责 FIDL 核心库的开发者提供。最终用户 应该不会看到此错误。
它所引用的 resource_definition
声明是 FIDL 的内部方法,
定义资源,并且将来可能会作为
处理泛化工作的一部分。
fi-0030:修饰符无效
每个 FIDL 修饰符都有一组特定的声明供您使用。 不允许在被禁止的声明中使用修饰符:
library test.bad.fi0030;
type MyStruct = strict struct {
i int32;
};
最佳做法是移除违规修饰符:
library test.good.fi0030;
type MyStruct = struct {
i int32;
};
fi-0031:只有位和枚举可以具有子类型
并非所有 FIDL 布局都可以包含子类型:
library test.bad.fi0031;
type Foo = flexible union : uint32 {};
只有 bits
和 enum
布局是在底层类型上定义的。
library test.good.fi0031;
type Foo = flexible enum : uint32 {};
bits
和 enum
布局有些独特,因为它们
集成 FIDL 基元的受限子类型。正因如此,
指定充当此子类型的基础类型是合理的。
相反,struct
、table
和 union
布局可以任意大,
可以包含多个成员,因此全局、布局范围的子类型不会使
。
fi-0032:不允许使用重复的修饰符
禁止在单个声明中指定相同的修饰符:
library test.bad.fi0032;
type MyUnion = strict resource strict union {
1: foo bool;
};
移除重复的修饰符:
library test.good.fi0032;
type MyUnion = resource strict union {
1: foo bool;
};
fi-0033:修饰符冲突
某些修饰符相互排斥,并且不能同时修饰 相同的声明:
library test.bad.conflictingmodifiers;
type StrictFlexibleFoo = strict flexible union {
1: b bool;
};
type FlexibleStrictBar = flexible strict union {
1: b bool;
};
一个 strict
或 flexible
修饰符只能用于单个
声明:
library test.good.fi0033;
type FlexibleFoo = flexible union {
1: i int32;
};
type StrictBar = strict union {
1: i int32;
};
目前,只有 strict
和 flexible
修饰符是互斥的
。resource
修饰符没有倒数修饰符,因此具有
也不会对其应用此类限制
fi-0034:名称冲突
两个声明的名称不能相同:
library test.bad.fi0034;
const COLOR string = "red";
const COLOR string = "blue";
而应为每个声明指定一个唯一的名称:
library test.good.fi0034b;
const COLOR string = "red";
const OTHER_COLOR string = "blue";
或者,移除错误添加的声明之一:
library test.good.fi0034a;
const COLOR string = "red";
如需详细了解如何选择名称,请参阅 FIDL 样式指南。
fi-0035:规范名称冲突
两个声明不能具有相同的规范名称:
library test.bad.fi0035;
const COLOR string = "red";
protocol Color {};
虽然 COLOR
和 Color
看起来不同,但它们都由
规范名称 color
。您可通过将
将原始名称更改为 snake_case
。
如需修正该错误,请为每个声明指定一个唯一的名称, 规范化:
library test.good.fi0035;
const COLOR string = "red";
protocol ColorMixer {};
按照 FIDL 样式指南的命名准则, 以尽可能降低出现此错误的几率。规范名称冲突会导致 永远不会发生在使用相同大小写样式的声明之间, 由于其他原因,使用不同样式的广告之间 要求(例如,协议名称通常应该是以 -er 结尾的名词短语)。
FIDL 强制执行此规则,因为绑定生成器将名称转换为 目标语言的惯用命名样式。确保唯一规范化 名称,我们保证绑定可以执行此操作,而不会产生名称冲突。 如需了解详情,请参阅 RFC-0040:标识符唯一性。
fi-0036:名称重叠
使用相同名称的声明不能有重叠的播出信息:
@available(added=1)
library test.bad.fi0036;
type Color = strict enum {
RED = 1;
};
@available(added=2)
type Color = flexible enum {
RED = 1;
};
而应使用 @available
属性,以确保只有
声明:
@available(added=1)
library test.good.fi0036;
@available(replaced=2)
type Color = strict enum {
RED = 1;
};
@available(added=2)
type Color = flexible enum {
RED = 1;
};
或者,您也可以重命名或移除某个声明,如 fi-0034。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0037:规范名称重叠
具有相同规范名称的声明不能重叠 可用性:
@available(added=1)
library test.bad.fi0037;
const COLOR string = "red";
@available(added=2)
protocol Color {};
虽然 COLOR
和 Color
看起来不同,但它们都由
规范名称 color
。您可通过将
将原始名称更改为 snake_case
。
如需修正该错误,请为每个声明指定一个唯一的名称, 规范化。
@available(added=1)
library test.good.fi0037;
const COLOR string = "red";
@available(added=2)
protocol ColorMixer {};
或者,您也可以按照 fi-0036 或移除该声明。
请参阅 fi-0035,详细了解 FIDL 为何要求进行声明 具有唯一的规范名称。
fi-0038:名称与导入存在冲突
声明的名称不得与使用 using
导入的库同名:
library dependency;
const VALUE uint32 = 1;
library test.bad.fi0038b;
using dependency;
type dependency = struct {};
// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;
而是导入包含 using
... as
的不同名称的库。
语法:
library test.good.fi0038b;
using dependency as dep;
type dependency = struct {};
const USE_VALUE uint32 = dep.VALUE;
或者,您也可以重命名声明以避免冲突:
library test.good.fi0038c;
using dependency;
type OtherName = struct {};
const USE_VALUE uint32 = dependency.VALUE;
您可以在库名称中使用多个组件来避免此问题。对于
例如,Fuchsia SDK 中的 FIDL 库以 fuchsia.
开头,因此具有
且不能与声明名称冲突。
此错误是为了防止产生歧义。例如,如果 dependency
是一个
具有名为 VALUE
的成员的枚举,那么无论是
dependency.VALUE
引用了该枚举成员或
导入代码库。
fi-0039:规范名称与导入冲突
声明的规范名称不能与包含
using
:
library dependency;
const VALUE uint32 = 1;
library test.bad.fi0039b;
using dependency;
type Dependency = struct {};
// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;
虽然 dependency
和 Dependency
看起来不同,但它们都是
以规范名称 dependency
表示。您会获得规范名称
方法是将原始名称转换为 snake_case
。
如需修正该错误,请使用 using
以不同的名称导入库 ...
as
语法:
library test.good.fi0039b;
using dependency as dep;
type Dependency = struct {};
const USE_VALUE uint32 = dep.VALUE;
或者,您也可以重命名声明以避免冲突:
library test.good.fi0039c;
using dependency;
type OtherName = struct {};
const USE_VALUE uint32 = dependency.VALUE;
请参阅 fi-0038,了解出现此错误的原因以及如何避免此错误。
请参阅 fi-0035,详细了解 FIDL 为何要求进行声明 具有唯一的规范名称。
fi-0040:文件在库名称上不一致
库可以由多个文件组成,但每个文件必须具有相同的 名称:
library test.bad.fi0040a;
library test.bad.fi0040b;
确保库使用的所有文件同名:
library test.good.fi0040;
library test.good.fi0040;
对于多文件库,建议创建一个
空 overview.fidl 文件,用作主“条目”
点”放入库overview.fidl
文件也是
用于将库级 @available
平台
规范。
fi-0041:多个同名库
传递给 fidlc
的每个库都必须具有唯一的名称:
library test.bad.fi0041;
library test.bad.fi0041;
确保所有库的名称都是唯一的:
library test.good.fi0041a;
library test.good.fi0041b;
此错误通常是由于在fidlc
不正确。组成每个库的组成要素文件
编译(即正在编译的库及其所有传递的
依赖项)必须以单个空格分隔的文件列表的形式提供,
通过 --files
参数传递,每个库都有一个此类标志。一个常见的错误是
尝试在单个 --files
列表中传递所有库的文件。
fi-0042:重复库导入
依赖项无法多次导入:
library test.bad.fi0042a;
type Bar = struct {};
library test.bad.fi0042b;
using test.bad.fi0042a;
using test.bad.fi0042a; // duplicated
type Foo = struct {
bar test.bad.fi0042a.Bar;
};
确保每个依赖项仅导入一次:
library test.good.fi0042a;
type Bar = struct {};
library test.good.fi0042b;
using test.good.fi0042a;
type Foo = struct {
bar test.good.fi0042a.Bar;
};
值得注意的是,FIDL 不支持导入
同一个库系统针对整个 fidlc
解析了 @available
版本
通过 --available
标志进行编译,这意味着
其所有依赖项对于任何给定的
编译运行。
fi-0043:库导入冲突
禁止将导入的库化为 与其他导入的库的无别名名称冲突:
library test.bad.fi0043a;
type Bar = struct {};
// This library has a one component name to demonstrate the error.
library fi0043b;
type Baz = struct {};
library test.bad.fi0043c;
using test.bad.fi0043a as fi0043b; // conflict
using fi0043b;
type Foo = struct {
a fi0043b.Bar;
b fi0043b.Baz;
};
请选择其他别名来解决名称冲突:
library test.good.fi0043a;
type Bar = struct {};
library fi0043b;
type Baz = struct {};
library test.good.fi0043c;
using test.good.fi0043a as dep;
using fi0043b;
type Foo = struct {
a dep.Bar;
b fi0043b.Baz;
};
fi-0044:库导入别名冲突
禁止将导入的库化为 与分配给其他导入库的别名冲突:
library test.bad.fi0044a;
type Bar = struct {};
library test.bad.fi0044b;
type Baz = struct {};
library test.bad.fi0044c;
using test.bad.fi0044a as dep;
using test.bad.fi0044b as dep; // conflict
type Foo = struct {
a dep.Bar;
b dep.Baz;
};
选择非冲突的别名来解决名称冲突:
library test.good.fi0044a;
type Bar = struct {};
library test.good.fi0044b;
type Baz = struct {};
library test.good.fi0044c;
using test.good.fi0044a as dep1;
using test.good.fi0044b as dep2;
type Foo = struct {
a dep1.Bar;
b dep2.Baz;
};
fi-0045:使用声明时不允许使用的属性
属性无法附加到 using
声明:
library test.bad.fi0045a;
type Bar = struct {};
library test.bad.fi0045b;
/// not allowed
@also_not_allowed
using test.bad.fi0045a;
type Foo = struct {
bar test.bad.fi0045a.Bar;
};
请移除该属性以修正错误:
library test.good.fi0045a;
type Bar = struct {};
library test.good.fi0045b;
using test.good.fi0045a;
type Foo = struct {
bar test.good.fi0045a.Bar;
};
此限制也适用于 /// ...
文档注释,因为这些只是
@doc("...")
属性的语法糖。
fi-0046:未知库
在大多数情况下,此问题是由于依赖项拼写有误或构建系统未提供依赖项所致。 如果相关依赖项故意使用,则必须移除相关的使用行:
library test.bad.fi0046;
using dependent; // unknown using.
type Foo = struct {
dep dependent.Bar;
};
确保所有导入都使用构建系统作为依赖项添加到库中。
library test.good.fi0046;
type Foo = struct {
dep int64;
};
fi-0047
fi-0048:可选的表成员
表成员类型不能为 optional
:
library test.bad.fi0048;
type Foo = table {
// Strings can be optional in general, but not in table member position.
1: t string:optional;
};
从所有成员中移除 optional
限制条件:
library test.good.fi0048;
type Foo = table {
1: t string;
};
表成员始终是可选的,因此在成员的基础类型上指定这一事实是多余的。
表成员始终是可选的,因为在网络上,每个表成员都表示为矢量中的一个条目。 该矢量始终表示表上的所有已知字段,因此表示省略的每个表成员 作为 null 信封 - 与省略的可选类型的表示完全相同。
fi-0049:可选的联盟成员
联合成员不得为可选项:
library test.bad.fi0049;
type Foo = strict union {
// Strings can be optional in general, but not in unions.
1: bar string:optional;
};
移除 optional
限制条件:
library test.good.fi0049;
type Foo = strict union {
1: bar string;
};
FIDL 不允许将联合成员设为可选,因为这可能导致以多种方式表示相同值。
例如,有三个可选成员的联盟将有 6 个州(每个成员 2 个)。相反,应使用
第四个成员,其类型为 struct {}
,或者使用 Foo:optional
使整体联合变为可选。
fi-0050:禁止使用已弃用的结构体默认语法
之前,FIDL 允许为 struct
成员设置默认值:
library test.bad.fi0050;
type MyStruct = struct {
field int64 = 20;
};
从 RFC-0160:移除对 FIDL 结构体默认值的支持,此行为 不允许:
library test.good.fi0050;
type MyStruct = struct {
field int64;
};
系统不再允许 struct
个成员使用默认值。用户应设置
默认应用于应用逻辑
此语法的少数旧版用户可继续使用它 但不会再出现新的例外情况 可添加到此列表。这些用户迁移完成后,这项功能 将从 FIDL 中永久移除。
fi-0051:未知的依赖库
当您使用来自未知库的符号时,就会出现此错误。
library test.bad.fi0051;
type Company = table {
1: employees vector<unknown.dependent.library.Person>;
2: name string;
};
如需解决此问题,请使用 using 声明导入缺失的依赖库。
library known.dependent.library;
type Person = table {
1: age uint8;
2: name string;
};
library test.good.fi0051;
using known.dependent.library;
type Company = table {
1: employees vector<known.dependent.library.Person>;
2: name string;
};
在执行 fidlc
命令行调用时,通常会出现此错误。
格式不当。如果您确信未知库存在,并且
应可解析,请确保您传递的是依赖库的文件
正确传递。--files
fi-0052:未找到名称
如果您使用 FIDL 编译器找不到的名称,就会发生此错误。
library test.bad.fi0052;
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
}) error ParsingError; // ParsingError doesn't exist.
};
要解决此问题,请移除未找到的名称:
library test.good.fi0052a;
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
或定义未找到的名称:
library test.good.fi0052b;
type ParsingError = flexible enum {
UNEXPECTED_EOF = 0;
};
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
}) error ParsingError;
};
fi-0053:无法引用成员
如果引用的成员不是 bits
或 enum
条目,则会出现此错误。
library test.bad.fi0053a;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
const JOHNS_NAME Person.name = "John Johnson"; // Cannot refer to member of struct 'Person'.
library test.bad.fi0053b;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
type Cat = struct {
name string;
age Person.birthday; // Cannot refer to member of struct 'Person'.
};
要修正此错误,请更改为已命名的类型:
library test.good.fi0053a;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
const JOHNS_NAME string = "John Johnson";
或提取成员的类型:
library test.good.fi0053b;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday Date;
};
type Cat = struct {
name string;
age Date;
};
fi-0054:位/枚举成员无效
如果引用了 enum
或 bits
成员而之前未定义,就会出现此错误。
library test.bad.fi0054;
type Enum = enum {
foo_bar = 1;
};
const EXAMPLE Enum = Enum.FOO_BAR;
为避免此错误,请确认您之前已针对引用的 成员值。这些值区分大小写。
library test.good.fi0054;
type Enum = enum {
foo_bar = 1;
};
const EXAMPLE Enum = Enum.foo_bar;
fi-0055:对已弃用项的引用无效
如果对 type
或 const
的引用使用
@available
属性不兼容。这通常发生在使用
废弃了更高版本中的 types
或 consts
。
@available(added=1)
library test.bad.fi0055;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(added=3)
type Config = table {
// RGB is deprecated in version 2.
1: color RGB;
};
如需修正此错误,请使用未废弃的 type
或 const
:
@available(added=1)
library test.good.fi0055;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(added=3)
type Config = table {
// Using a non-deprecated type.
1: color Color;
};
fi-0056:对已弃用的其他平台的引用无效
当您使用对 type
或 const
的引用时,会发生此错误
具有不兼容的 @available
属性的平台。这种情况通常发生
使用更高版本中已废弃的 types
或 consts
时。
@available(platform="foo", added=1)
library test.bad.fi0056a;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(platform="bar", added=2)
library test.bad.fi0056b;
using test.bad.fi0056a;
@available(added=3)
type Config = table {
// RGB is deprecated in version 2.
1: color test.bad.fi0056a.RGB;
};
如需修正此错误,请使用未废弃的 type
或 const
:
@available(platform="foo", added=1)
library test.good.fi0056a;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(platform="bar", added=2)
library test.good.fi0056b;
using test.good.fi0056a;
@available(added=2)
type Config = table {
// Change to use a non-deprecated type.
1: color test.good.fi0056a.Color;
};
fi-0057:包含周期
可能造成该问题的原因多种多样, 归根结底是 FIDL 声明,在 解决方案。此错误的最简单形式是,当某个类型或协议 直接指代其自身:
library test.bad.fi0057c;
type MySelf = struct {
me MySelf;
};
当某个类型或协议以传递方式传递时,可能会发生更复杂的故障情况 至少通过一种层次的间接指代:
library test.bad.fi0057a;
type Yin = struct {
yang Yang;
};
type Yang = struct {
yin Yin;
};
library test.bad.fi0057b;
protocol Yin {
compose Yang;
};
protocol Yang {
compose Yin;
};
要解决此错误,可以在 包含循环,因为这样会导致该循环被“打破”在编码/解码 时间:
library test.good.fi0057;
type MySelf = struct {
me box<MySelf>;
};
library test.bad.fi0057d;
type MySelf = table {
1: me MySelf;
};
不允许使用被信封打开的递归类型,因为它们
无法编码。在上面的第一个示例中,编码 MySelf
为
需要先对 MySelf
的实例进行编码,这反过来需要
对 MySelf
的实例进行编码,即 ad inifitum。要解决这个问题,
添加“break”在这个链中,可以选择
对 MySelf
的另一个嵌套实例进行编码,或以其他方式对 null 信封进行编码
而不再有其他数据
fi-0058:引用编译器生成的载荷名称
FIDL 会自动为匿名方法载荷生成名称 这样,使用生成的后端代码的用户就可以引用 根据需要进行表示在 *.fidl 文件中引用这些类型 但禁止:
library test.bad.fi0058;
protocol MyProtocol {
strict MyInfallible(struct {
in uint8;
}) -> (struct {
out int8;
});
strict MyFallible(struct {
in uint8;
}) -> (struct {
out int8;
}) error flexible enum {};
strict -> MyEvent(struct {
out int8;
});
};
type MyAnonymousReferences = struct {
a MyProtocolMyInfallibleRequest;
b MyProtocolMyInfallibleResponse;
c MyProtocolMyFallibleRequest;
d MyProtocol_MyFallible_Result;
e MyProtocol_MyFallible_Response;
f MyProtocol_MyFallible_Error;
g MyProtocolMyEventRequest;
};
如果您希望直接引用载荷类型,则应将 payload 类型转换为其自有的命名类型声明:
library test.good.fi0058;
type MyRequest = struct {
in uint8;
};
type MyResponse = struct {
out int8;
};
type MyError = flexible enum {};
protocol MyProtocol {
strict MyInfallible(MyRequest) -> (MyResponse);
strict MyFallible(MyRequest) -> (MyResponse) error MyError;
strict -> MyEvent(MyResponse);
};
type MyAnonymousReferences = struct {
a MyRequest;
b MyResponse;
c MyRequest;
// There is no way to explicitly name the error result union.
// d MyProtocol_MyFallible_Result;
e MyResponse;
f MyError;
g MyResponse;
};
所有 FIDL 方法和事件均预留 [PROTOCOL_NAME][METHOD_NAME]Request
为其匿名请求载荷指定名称严格、绝对可靠的双向方法
另外预留 [PROTOCOL_NAME][METHOD_NAME]Response
。双向方法
灵活或易用的备用关键字:
[PROTOCOL_NAME]_[METHOD_NAME]_Result
[PROTOCOL_NAME]_[METHOD_NAME]_Response
[PROTOCOL_NAME]_[METHOD_NAME]_Error
由于历史原因,这些名称使用下划线,与其他名称不同。
fi-0059:常量类型无效
并非所有类型都可以在 const
声明中使用:
library test.bad.fi0059;
const MY_CONST string:optional = "foo";
如果可能,转换为允许的类型:
library test.good.fi0059;
const MY_CONST string = "foo";
仅限 FIDL 基元(bool
、int8
、int16
、int32
、int64
、uint8
、
uint16
、uint32
、uint64
、float32
、float64
和非可选 string
类型可在 const
声明的左侧使用。
fi-0060:无法解析常量值
常量值必须可解析为已知值:
library test.bad.fi0060;
const MY_CONST bool = optional;
请确保使用的常量是有效值:
library test.good.fi0060;
const MY_CONST bool = true;
此错误通常伴随其他错误,这些错误可提供关于 不可解析预期常量的性质。
fi-0061:非基元值的 OR 运算符
二进制或运算符只能用于基元:
library test.bad.fi0061;
const HI string = "hi";
const THERE string = "there";
const OR_OP string = HI | THERE;
请尝试改为将操作的数据表示为 bits
枚举:
library test.good.fi0061;
type MyBits = flexible bits {
HI = 0x1;
THERE = 0x10;
};
const OR_OP MyBits = MyBits.HI | MyBits.THERE;
fi-0062:不允许使用 newtype
不支持 RFC-0052: Type aliasing and new types 中的新类型 已完全实施,尚无法使用:
library test.bad.fi0062;
type Matrix = array<float64, 9>;
在此期间,您可以通过使用 单个元素:
library test.good.fi0062a;
type Matrix = struct {
elements array<float64, 9>;
};
或者,您也可以定义别名,但请注意,与 newtype 不同, 不提供类型安全(也就是说,它可以与 基础类型):
library test.good.fi0062b;
alias Matrix = array<float64, 9>;
fi-0063:预期值,但类型为
const
声明的右侧必须解析为常量值,
不是类型:
library test.bad.fi0063;
type MyType = struct {};
const MY_CONST uint32 = MyType;
确保右侧是一个值:
library test.good.fi0063;
const MY_VALUE uint32 = 8;
const MY_CONST uint32 = MY_VALUE;
fi-0064:位或枚举值类型不正确
将 bits
或 enum
变体用作 const
声明中的值时,
bits
/enum
值的类型必须与左侧的
const 声明:
library test.bad.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST MyEnum = OtherEnum.VALUE;
一种解决方法是更改 const
声明的类型,以匹配
存储的值:
library test.good.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST OtherEnum = OtherEnum.VALUE;
或者,也可以选择其他值来匹配 const
声明的类型:
library test.good.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST MyEnum = MyEnum.VALUE;
fi-0065:无法将值转换为预期的类型
常量值的类型必须适合其所在位置 。
导致此错误的最常见原因是 const
声明的值
与其声明的类型不匹配:
library test.bad.fi0065a;
const MY_CONST bool = "foo";
如果在 v3 中使用正确定义的 const
值,
底层类型无效的位置:
library test.bad.fi0065b;
const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint8 = ONE | TWO_FIFTY_SIX;
此外,FIDL 的官方人员会检查其参数 架构。由于这些参数本身就是常量值, 可能也会出现同种类型不匹配的情况:
library test.bad.fi0065c;
protocol MyProtocol {
@selector(3840912312901827381273)
MyMethod();
};
在所有这些情况下,解决方法是仅使用预期
在接受 const
值的位置类型。上述情况包括
分别为:
library test.good.fi0065a;
const MY_CONST string = "foo";
library test.good.fi0065b;
const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint16 = ONE | TWO_FIFTY_SIX;
library test.good.fi0065c;
protocol MyProtocol {
@selector("MyOldMethod")
MyMethod();
};
fi-0066:常量溢出类型
常数值不能超出其底层所固有的范围 类型:
library test.bad.fi0066;
const NUM uint64 = -42;
可以通过更改值以符合类型的 范围:
library test.good.fi0066a;
const NUM uint64 = 42;
或者,通过更改为 类型以适应当前溢出 值:
library test.good.fi0066b;
const NUM int64 = -42;
此错误仅与 FIDL 的数字类型有关,且所有数字类型都具有
容量上限。范围来自 C++ std::numeric_limits
接口,如下所示:
类型 | 最小值 | 最大 |
---|---|---|
int8 |
-128 | 127 |
int16 |
32768 | 32767 |
int32 |
2147483648 | 2147483647 |
int64 |
9223372036854775808 | 9223372036854775807 |
uint8 |
0 | 255 |
uint16 |
0 | 65536 |
uint32 |
0 | 4294967295 |
uint64 |
0 | 18446744073709551615 |
float32 |
-3.40282e+38 | 3.40282e+38 |
float64 |
-1.79769e+308 | 1.79769e+308 |
fi-0067:位成员必须是 2 的幂
bits
声明中所有成员的值都不能是
不是 2 的幂:
library test.bad.fi0067;
type NonPowerOfTwo = bits : uint64 {
THREE = 3;
};
相反,成员值应始终为 2 的幂:
library test.good.fi0067a;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
避免受此限制影响的一种简单方法是只使用 位成员值使用掩码,而不是十进制数字:
library test.good.fi0067b;
type Life = bits {
A = 0b000010;
B = 0b001000;
C = 0b100000;
};
bits
结构体表示一个位数组。这是
用于表示一系列布尔标志的节省内存的方法。因为每个
bits
声明的成员会映射到其底层的某个特定位
因此用于该映射的值必须明确标识
要分配给的无符号整数。
fi-0068:灵活枚举具有预留的未知值
如果您定义的枚举成员的值与 预留的未知值。
灵活的枚举可能会包含 FIDL 架构未知的值。此外,
灵活枚举始终会预留一些值,该值将被视为未知值。
默认情况下,该值是由
该枚举的基础整数类型(例如,如果为 uint8
,则为 255
)。
library test.bad.fi0068;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
MAX = 255;
};
要修正该错误,您可以移除该成员或更改其值:
library test.good.fi0068a;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
};
library test.good.fi0068b;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
MAX = 254;
};
最后,如果您在将 strict
枚举转换为
flexible
枚举,您可以使用 @unknown
属性来指定
特定成员的值作为未知值。请参阅 @unknown
。
fi-0069:位必须使用无符号整数子类型
使用带符号数字作为 bits
声明的基础类型是
禁止:
library test.bad.fi0069;
type Fruit = bits : int64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
请改用以下任意一种:uint8
、uint16
、uint32
或 uint64
:
library test.good.fi0069;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
与允许有符号和无符号整数的 enum
声明不同(请参阅:
fi-0070),则 bits
声明只允许后者。这是
因为每个 bits
成员都必须代表
bit 数组(这就是导致 fi-0067
存在)。这可以最清晰地表示为单个无符号整数。通过
无符号整数的二进制表示法直接映射到单个位(2 到
索引的次幂),而带符号整数中的负数几乎是
总是选择多位,这是由于二进制补码的机制所致
表示法。
fi-0070:枚举必须使用 integral 子类型
使用非整数数字 float32
或 float64
作为基础类型
enum
声明:
library test.bad.fi0070;
type MyEnum = enum : float64 {
ONE_POINT_FIVE = 1.5;
};
请改用以下任意一种:int8
、int16
、int32
、int64
、uint8
、
uint16
、uint32
或 uint64
:
library test.good.fi0070;
type MyEnum = enum : uint64 {
ONE = 1;
};
fi-0071:不允许对严格枚举成员使用未知属性
strict enum
不得包含任何带有 @unknown
注解的成员
属性:
library test.bad.fi0071;
type MyEnum = strict enum : int8 {
@unknown
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
如需继续使用 @unknown
属性,请更改为 flexible enum
:
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
否则,只需完全移除该属性即可保留 strict enum
:
library test.good.fi0071;
type MyEnum = strict enum : int8 {
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
@unknown
属性的用途是
从具有用户定义的未知值的 strict enum
过渡到平滑过渡,
转换为 flexible enum
,其中包含已知并由 处理的未知值
FIDL。在上例中,它用于从第二个
使用正确的方法。
fi-0072:只有枚举成员可以带有未知属性
禁止使用 @unknown
属性修饰多个 enum
成员:
library test.bad.fi0072;
type MyEnum = flexible enum : uint8 {
@unknown
UNKNOWN = 0;
@unknown
OTHER = 1;
};
仅选择被用作特定域“未知”的成员并对其进行注释 值:
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
OTHER = 1;
};
@unknown
属性的用途是
从具有用户定义的未知值的 strict enum
过渡到过渡时平滑,
转换为 flexible enum
,其中包含已知并由 处理的未知值
FIDL:
library test.good.fi0072;
type MyEnum = strict enum : int8 {
UNKNOWN = 0;
OTHER = 1;
};
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
OTHER = 1;
};
fi-0073:编写非协议
compose
语句中只能使用协议:
library test.bad.fi0073;
type MyStruct = struct {};
protocol MyProtocol {
compose MyStruct;
};
请确保您所指的名称指向协议:
library test.good.fi0073;
protocol MyOtherProtocol {};
protocol MyProtocol {
compose MyOtherProtocol;
};
fi-0074:方法载荷使用的布局无效
只能使用 struct
、table
或 union
布局来描述方法
载荷:
library test.bad.fi0074;
protocol MyProtocol {
MyMethod(enum {
FOO = 1;
});
};
请改用下列布局之一:
library test.good.fi0074;
protocol MyProtocol {
MyMethod(struct {
foo bool;
});
};
fi-0075:方法载荷使用的基元无效
基元不能用作方法载荷:
library test.bad.fi0075;
protocol MyProtocol {
MyMethod(uint32);
};
请改用 struct
、table
或 union
布局的类型:
library test.good.fi0075;
protocol MyProtocol {
MyMethod(struct {
wrapped_in_struct uint32;
});
};
当所需载荷实际上只是一个原始值时,
不必考虑未来的演变,将值封装在 struct
布局中
其大小与所需值本身的大小相同。
fi-0076
fi-0077:互动载荷不能为空结构体
方法或事件中的载荷不得为空结构体:
library test.bad.fi0077a;
protocol Test {
MyMethod(struct {}) -> (struct {});
};
library test.bad.fi0077b;
protocol Test {
-> MyEvent(struct {});
};
如果您想说明特定请求/响应不受任何
删除空结构体,将 ()
保留在该位置:
library test.good.fi0077a;
protocol Test {
MyMethod() -> ();
};
library test.good.fi0077b;
protocol Test {
-> MyEvent();
};
空结构体无法扩展,并且会在线路上占用 1 个字节。从 FIDL 开始 支持无载荷的互动,这样使用空结构体是 多余且效率低下因此,它们是不允许的。
fi-0078
fi-0079
fi-0080:生成零值序数
此错误绝不应发生。如果您能做到这一点 恭喜,您可能破坏了 SHA-256!
开个玩笑,如果 fidlc 编译器生成序数值,就会出现这个错误。 为 0。这种情况绝不应发生,因此如果确实发生了, FIDL 编译器。如果发生这种情况,请向我们的问题跟踪器报告此问题。
fi-0081:重复方法序数
使用 @selector
属性时,通常会出现此错误
使两个方法名称产生相同的序号。
library test.bad.fi0081;
protocol Parser {
ParseLine();
// Multiple methods with the same ordinal...
@selector("ParseLine")
ParseOneLine();
};
若要解决此问题,请更新方法名称或选择器,以确保它们不会发生冲突。
library test.good.fi0081;
protocol Parser {
ParseLine();
@selector("Parse1Line")
ParseOneLine();
};
如果存在 SHA-256 冲突,也会发生此错误,但 因此基本为零如果你确定,你的选择器没有错, 还是会遇到此错误,则表明您可能是在 FIDL 编译器中发现了 bug。 如果发生这种情况,请向我们的问题跟踪器报告此问题。
fi-0082:选择器值无效
如果为 @selector 使用的值无效,就会出现此错误。 这通常是由于拼写错误所致。选择器必须是独立的 方法名称或完全限定的方法名称。
library test.bad.fi0082;
protocol Parser {
@selector("test.old.fi0082.Parser.Parse")
Parse();
};
要解决此问题,请将选择器更新为有效的独立选择器,或 限定方法名称:
library test.good.fi0082;
protocol Parser {
@selector("test.old.fi0082/Parser.Parse")
Parse();
};
fi-0083:fuchsia.io
必须使用显式序数
FIDL 编译器用于将 fuchsia.io
序数自动重命名为
fuchsia.io1
。这种神奇之处在于
fuchsia.io2
,方法是让方法的 io2
版本具有“normal”
序数。不过,这个系统后来变得有点神奇了,所以现在
需要手动为 fuchsia.io
提供序数。
library fuchsia.io;
protocol SomeProtocol {
SomeMethod();
};
要解决此问题,请手动提供一个选择器,使用 fuchsia.io1
作为
库名称以允许将 fuchsia.io
名称用于 io2。
library fuchsia.io;
protocol SomeProtocol {
@selector("fuchsia.io1/SomeProtocol.SomeMethod")
SomeMethod();
};
fi-0084:方法载荷结构体不允许默认成员
用作方法负载的结构体不能指定默认成员:
library test.bad.fi0084;
type MyStruct = struct {
@allow_deprecated_struct_defaults
a bool = false;
};
protocol MyProtocol {
MyMethod(MyStruct) -> (MyStruct);
};
从相关的 struct
声明中移除默认成员:
library test.good.fi0084;
type MyStruct = struct {
a bool;
};
protocol MyProtocol {
MyMethod(MyStruct) -> (MyStruct);
};
fi-0085
fi-0086
fi-0087
fi-0088:服务成员不能是可选项
将服务成员标记为 optional
时会发生此错误。标记
服务成员(因为 optional
)是不允许的,因为服务成员始终是
可选属性。
library test.bad.fi0088;
protocol Sorter {
Sort(struct {
input vector<int32>;
}) -> (struct {
output vector<int32>;
});
};
service SortService {
quicksort client_end:<Sorter, optional>;
mergesort client_end:<Sorter, optional>;
};
要解决此问题,请移除可选子句:
library test.good.fi0088;
protocol Sorter {
Sort(struct {
input vector<int32>;
}) -> (struct {
output vector<int32>;
});
};
service SortService {
quicksort client_end:Sorter;
mergesort client_end:Sorter;
};
fi-0089
fi-0090
fi-0091:结构体成员类型无效
当您尝试为不受支持的对象设置默认结构体值时,就会发生此错误 类型。只允许数字和布尔值类型设置默认结构体值。
library test.bad.fi0091;
type Person = struct {
@allow_deprecated_struct_defaults
name string:optional = "";
};
要解决此问题,请移除默认值:
library test.good.fi0091;
type Person = struct {
@allow_deprecated_struct_defaults
name string:optional;
};
fi-0092:表序数过大
FIDL 表序数不能大于 64:
library test.bad.fi0092;
type Table64thField = table {
1: x int64;
};
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
// The 64th field of a table must be another table, otherwise it will cause
// fi-0093: Max Ordinal In Table Must Be Table.
64: v64 Table64thField;
65: v65 int64;
};
为了支持 64 个序数以上的增长,FIDL 需要 转换为另一个表格。任何超出 64 的表字段都必须放置在一个嵌套层中, 表格。
library test.good.fi0092;
type Table64thField = table {
1: x int64;
// Any fields beyond 64 of table Example must be move to the nested table in
// ordinal 64 of Example.
2: v65 int64;
};
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
64: v64 Table64thField;
};
表中的每个字段都会产生 FIDL 信封的开销, 该字段为选填字段这样一来,表格中的每个字段 缺失字段,您可以通过添加或移除字段来改进表,但 内存开销比结构体大得多。
一般来说,您可以避免此错误并减少开销, 小型的精细字段。相反,您可以将 需要同时添加或移除到结构体中,并使用 用作表格中的字段这样可以减少开销,避免资源耗尽 代价是一些无法持续改进。
这成为 RFC-0132: FIDL 表大小中的错误 限制 旨在防止用户意外导致 大型表格。这种额外的开销在架构中并不明显,尤其是当存在 只有少数字段(序号很大),或者有很多字段,但是 一次只能使用几个
fi-0093:表中的最大序数必须是表
FIDL 表中第 64 个成员的类型本身必须是表:
library test.bad.fi0093;
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
64: v64 int64;
};
如果用户发现自己需要添加第 64 位成员,则应为每个成员分别创建 表存储第 64 位及之后的成员,并将该成员放入表中:
library test.good.fi0093;
type Table64thField = table {
1: x int64;
};
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
64: v64 Table64thField;
};
该要求背后的推理和动机在 RFC-0132:FIDL 表大小限制。简而言之,FIDL 表需要包含 对它们允许的字段数量有相对有限的限制,否则 一次仅使用几个字段的编码表格式将具有 大量不可接受的无效空间(每个省略 16 个字节)。
FIDL 为希望表中超过 64 个字段的用户提供了一种临时解决方案, 强制为“连续表”保留最后一个序数包含 extra 字段。在此位置使用任何其他类型都会呈现表格 且无法扩展
fi-0094:重复的表成员序数
table
声明中用于成员的序数不得重复:
library test.bad.fi0094;
type MyTable = table {
1: my_field string;
1: my_other_field uint32;
};
根据需要递增序数,以确保声明具有唯一的 序号:
library test.good.fi0094a;
type MyTable = table {
1: my_field string;
2: my_other_field uint32;
};
或者,也可以移除具有重复名称的某个成员:
library test.good.fi0094b;
type MyTable = table {
1: my_field string;
};
序数用于标识电线上的字段。如果两位成员共用一个 序数,没有可靠的方法分辨在何时引用了哪个字段 对 FIDL 消息进行解码。
fi-0095
fi-0096
fi-0097:重复的联盟成员序号
union
声明中用于成员的序数不得重复:
library test.bad.fi0097;
type MyUnion = strict union {
1: my_variant string;
1: my_other_variant int32;
};
根据需要递增序数,以确保声明具有唯一的 序号:
library test.good.fi0097a;
type MyUnion = strict union {
1: my_variant string;
2: my_other_variant int32;
};
或者,也可以移除具有重复名称的某个成员:
library test.good.fi0097b;
type MyUnion = strict union {
1: my_variant string;
};
序数用于标识线上的变体。如果两位成员共用一个 没有可靠的方法分辨被引用的是哪个变体 读取 FIDL 消息。
fi-0098
fi-0099
fi-0100
fi-0101:无法解析大小限制
应用于 vector
或 string
类型定义的大小限制必须是
uint32
类型的有效值:
library test.bad.fi0101a;
alias MyBoundedOptionalVector = vector<uint32>:<"255", optional>;
library test.bad.fi0101b;
alias MyBoundedOptionalVector = vector<uint32>:<uint8, optional>;
请确保满足此条件:
library test.good.fi0101;
alias MyBoundedOptionalVector = vector<uint32>:<255, optional>;
fi-0102:成员值无法解析
bits
和 enum
声明的成员必须是
指定的子类型:
library test.bad.fi0102;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = -4;
};
确保所有值都与声明的基础类型匹配:
library test.good.fi0102;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
fi-0103:结构体默认值无法解析
struct
声明的成员的默认值必须与其
相应成员声明的类型:
library test.bad.fi0103;
type MyEnum = enum : int32 {
A = 1;
};
type MyStruct = struct {
@allow_deprecated_struct_defaults
field MyEnum = 1;
};
确保该值与声明的类型匹配:
library test.good.fi0103;
type MyEnum = enum : int32 {
A = 1;
};
type MyStruct = struct {
@allow_deprecated_struct_defaults
field MyEnum = MyEnum.A;
};
fi-0104:属性参数无法解析
官方的参数值 根据属性架构的预期,FIDL 属性不得无效 该参数:
library test.bad.fi0104;
type MyStruct = struct {
my_field @generated_name(true) struct {};
};
确保用作属性参数的值类型正确无误:
library test.good.fi0104;
type MyStruct = struct {
my_field @generated_name("my_inner_type") struct {};
};
fi-0105
fi-0106
fi-0107:重复成员值
bits
和 enum
声明都不能有具有相同值的成员:
library test.bad.fi0107;
type Fruit = flexible enum {
ORANGE = 1;
APPLE = 1;
};
将成员值全部更改为唯一值:
library test.good.fi0107a;
type Fruit = flexible enum {
ORANGE = 1;
APPLE = 2;
};
或者,移除其中一个重复的成员:
library test.good.fi0107b;
type Fruit = flexible enum {
ORANGE = 1;
};
fi-0108
fi-0109
fi-0110:包含类型的资源必须标记为资源
包含句柄的类型,可直接或通过传递
包含另一个包含句柄的类型,否则无法声明该类型
指定为 resource
的类型:
library test.bad.fi0110;
using zx;
type Foo = struct {
handle zx.Handle;
};
有两种可能的解决方案。第一种是为违规内容添加注释 声明:
library test.good.fi0110a;
using zx;
type Foo = resource struct {
handle zx.Handle;
};
或者,您也可以选择彻底移除包含 resource
的类型,
因此无需对所属声明使用修饰符:
library test.good.fi0110b;
type Foo = struct {
value uint32;
};
添加 resource
修饰符的推理和动机
“具有感染力”的可以通过此错误强制执行的使用模式的性质
(在 RFC-0057:默认无句柄中)。
fi-0111:内嵌大小超出限制
不允许使用内嵌大小为 64 KiB 或更大的 FIDL 类型:
library test.bad.fi0111;
type MyStruct = struct {
numbers array<uint8, 65536>;
};
请确保该类型的内嵌大小小于 64 KiB。在此示例中 我们可以调整数组边界:
library test.good.fi0111;
type MyStruct = struct {
numbers array<uint8, 65535>;
};
此限制是出于性能方面的考虑而存在。这意味着编码器和解码器 可以假设大小和偏移量适合无符号的 16 位整数。
除非您使用大型数组或 深度嵌套的结构体。大多数 FIDL 结构(例如字符串、矢量、表、 和联合)使用外行存储空间,这不会计入其 单独的内嵌大小。
fi-0112:服务成员不是 client_end
服务成员只能是客户端,而不能是任何其他类型:
library test.bad.fi0112;
protocol Calculator {};
service Service {
calculator server_end:Calculator;
};
如需修正该错误,请确保成员采用某些协议 P
的 client_end:P
格式:
library test.good.fi0112;
protocol Calculator {};
service Service {
calculator client_end:Calculator;
};
服务是协议实例的集合,而非通用数据 所以允许任意类型就毫无意义。
fi-0113:服务中的传输不匹配
FIDL 服务不得包含使用不同传输的协议:
library test.bad.fi0113;
protocol ChannelProtocol {};
@transport("Driver")
protocol DriverProtocol {};
service SomeService {
a client_end:ChannelProtocol;
b client_end:DriverProtocol;
};
相反,请针对每种传输使用单独的服务:
library test.good.fi0113;
protocol ChannelProtocol {};
@transport("Driver")
protocol DriverProtocol {};
service ChannelService {
protocol client_end:ChannelProtocol;
};
service DriverService {
protocol client_end:DriverProtocol;
};
请注意,服务是 FIDL 中尚未完成的功能。它们最初是 RFC-0041:支持统一 Serviceas 和 设备。 请参阅 https://fxbug.dev/42160684 了解截至 2022 年 10 月的状态。
fi-0114:Composed 协议过于开放
一个协议不能构成比自身更开放的其他协议:
library test.bad.fi0114;
open protocol Composed {};
ajar protocol Composing {
compose Composed;
};
您可以通过提高撰写协议的开放性来解决此问题,即
将其从 closed
更改为 ajar
,或从 ajar
更改为 open
:
library test.good.fi0114a;
open protocol Composed {};
open protocol Composing {
compose Composed;
};
或者,您也可以降低组合协议的开放性,例如将
从 open
更改为 ajar
,或者从 ajar
更改为 closed
:
library test.good.fi0114b;
ajar protocol Composed {};
ajar protocol Composing {
compose Composed;
};
这种规则之所以存在,是因为协议的开放性限制了 方法。例如,Ajar 协议不能包含 灵活的双向方法,但开放协议可以,因此对于 JAR 文件并不安全 构成一个开放式协议
请参阅 RFC-0138:处理未知 互动 ,详细了解协议修饰符。
fi-0115:灵活的双向方法需要开放协议
封闭式和半开式协议不得包含灵活的双向方法:
library test.bad.fi0115;
ajar protocol Protocol {
flexible Method() -> ();
};
请改为将双向方法标记为 strict
,而不是 flexible
:
library test.good.fi0115a;
ajar protocol Protocol {
strict Method() -> ();
};
或者,将协议标记为 open
,而不是 closed
或 ajar
:
library test.good.fi0115b;
open protocol Protocol {
flexible Method() -> ();
};
出现此错误是因为 closed
(或 ajar
)修饰符的作用是
确保某个方法不包含任何灵活的(双向)方法。首次
创建协议时,您应该仔细考虑是否应该使用
闭合、半开或开合。
请参阅 RFC-0138:处理未知 互动 ,详细了解协议修饰符。
fi-0116:灵活的单向方法需要 ajar 或开放协议
封闭协议不得包含灵活的单向方法:
library test.bad.fi0116;
closed protocol Protocol {
flexible Method();
};
请改为将单向方法标记为 strict
,而不是 flexible
:
library test.good.fi0116;
closed protocol Protocol {
strict Method();
};
或者,将协议标记为 ajar
或 open
,而不是 closed
:
library test.good.fi0116;
ajar protocol Protocol {
flexible Method();
};
出现此错误是因为 closed
修饰符的作用是确保
方法不包含任何灵活的方法。首次创建协议时
你应该仔细考虑是应该封闭、半开还是开放
进行微调。
请参阅 RFC-0138:处理未知 互动 ,详细了解协议修饰符。
fi-0117:在不兼容的传输中使用的句柄
协议只能引用与其传输兼容的句柄。 例如,Zircon 信道传输上的协议不能引用 Fuchsia 驱动程序框架的句柄:
library test.bad.fi0117;
using fdf;
protocol Protocol {
Method(resource struct {
h fdf.handle;
});
};
而应使用与协议传输兼容的句柄:
library test.good.fi0117a;
using zx;
protocol Protocol {
Method(resource struct {
h zx.Handle;
});
};
或者,更改协议的传输以匹配标识名:
library test.good.fi0117b;
using fdf;
@transport("Driver")
protocol Protocol {
Method(resource struct {
h fdf.handle;
});
};
fi-0118:在不兼容的传输中使用传输结束
协议只能指代以下项的传输端(client_end
和 server_end
)
共享协议。例如,使用 Syscall 的协议
transport 不能指使用驱动程序传输的协议的客户端端:
library test.bad.fi0118;
@transport("Driver")
protocol DriverProtocol {};
@transport("Syscall")
protocol P {
M(resource struct {
s client_end:DriverProtocol;
});
};
如需修正该错误,请移除传输结束成员:
library test.good.fi0118;
@transport("Driver")
protocol DriverProtocol {};
@transport("Syscall")
protocol Protocol {
M();
};
fi-0119
fi-0120:属性展示位置无效
部分官方属性
允许出现某些情况。例如,@selector
属性只能是
用于:
library test.bad.fi0120a;
@selector("Nonsense")
type MyUnion = union {
1: hello uint8;
};
要修正该错误,请移除此属性:
library test.good.fi0120a;
type MyUnion = union {
1: hello uint8;
};
打算以某种方式使用某个属性时,您也可能会遇到此错误
但将其放在错误的位置。例如,
@generated_name
属性不能直接应用于成员:
library test.bad.fi0120a;
@selector("Nonsense")
type MyUnion = union {
1: hello uint8;
};
而应紧挨着放在成员匿名布局的前面:
library test.good.fi0120a;
type MyUnion = union {
1: hello uint8;
};
fi-0121:属性已弃用
部分官方属性包括 已弃用,不应再使用:
library test.bad.fi0121;
@example_deprecated_attribute
type MyStruct = struct {};
解决方法取决于该属性被弃用的原因。例如,错误消息可能会显示使用其他属性。在本例中,我们只需移除该属性:
library test.good.fi0121;
type MyStruct = struct {};
fi-0122:属性名称重复
一个元素不能有多个同名的属性:
library test.bad.fi0122;
@custom_attribute("first")
@custom_attribute("second")
type Foo = struct {};
正确做法是,每个属性仅指定一次:
library test.good.fi0122;
@custom_attribute("first")
type Foo = struct {};
fi-0123:规范属性名称重复
一个元素不能有多个具有相同规范名称的属性:
library test.bad.fi0123;
@custom_attribute("first")
@CustomAttribute("second")
type Foo = struct {};
虽然 custom_attribute
和 CustomAttribute
看起来有所不同,但它们确实一样
两者均以规范名称 custom_attribute
表示。您将获得
规范名称(具体方法为将原始名称转换为 snake_case
)。
如需修正该错误,请为每个属性指定一个唯一的名称, 规范化。
library test.good.fi0123;
@custom_attribute("first")
@AnotherCustomAttribute("first")
type Foo = struct {};
请参阅 fi-0035,详细了解 FIDL 为何要求进行声明 具有唯一的规范名称。
fi-0124:自定义属性参数必须是 string 或 bool
用户定义的 FIDL 属性的参数仅限于字符串或 布尔值类型:
library test.bad.fi0124;
@my_custom_attr(foo=1, bar=2.3)
type MyStruct = struct {};
library test.good.fi0124;
@my_custom_attr(foo=true, bar="baz")
type MyStruct = struct {};
与官方属性不同,用户定义的架构
属性。因此,
编译器推断任何给定数字参数的类型 - 是 2
是
int8
、uint64
还是 float32
?编译器无从得知。
此问题的一个可能的解决方案是实现一个
numeric
类型。但是,由于
自定义属性参数是目前已知的唯一用例,
功能尚未优先。
fi-0125:Attribute 参数不得命名
使用官方属性时 ,则您不能为该参数命名:
library test.bad.fi0125;
@transport(value="Driver")
protocol Foo {};
正确做法是传递参数而不为其指定名称:
library test.good.fi0125;
@discoverable(name="example.Bar")
protocol Foo {};
FIDL 强制执行此要求以使属性更简洁一致。在
将参数名称推断为 value
(这会显示在
JSON IR),因为这是属性接受的唯一参数。
fi-0126:Attribute 参数必须命名
使用官方属性时 ,则不能传递未命名的实参:
@available(1)
library test.bad.fi0126;
而应指定实参的名称:
@available(added=1)
library test.good.fi0126;
出现此错误的原因是您无法得知想要的参数 属性接受多个参数时设置的值。
fi-0127:缺少必需的属性参数
使用官方属性时 不能省略:
library test.bad.fi0127;
@has_required_arg
type Foo = struct {};
请改为提供所需的实参:
library test.good.fi0127;
@has_required_arg(required="something")
type Foo = struct {};
fi-0128:缺少单个属性参数
使用官方属性时 不能省略:
library test.bad.fi0128;
@transport
protocol Protocol {};
请改为提供参数:
library test.good.fi0128;
@transport("Driver")
protocol Protocol {};
fi-0129:未知属性参数
使用官方属性时, 您不能提供不在其架构中的参数:
@available(added=1, discontinued=2)
library test.bad.fi0129;
如果您想要传递不同的实参,但弄错了名称,请将其更改为使用正确的名称:
@available(added=1, deprecated=2)
library test.good.fi0129a;
或者,移除该参数:
@available(added=1)
library test.good.fi0129b;
出现此错误是因为官方属性是根据架构进行验证的。如果 FIDL 允许任意参数,它们不会产生任何影响,还可能导致 通过遮盖错别字来找出错误。
fi-0130:属性参数重复
属性不能有两个名称相同的参数:
library test.bad.fi0130;
@custom_attribute(custom_arg=true, custom_arg=true)
type Foo = struct {};
请改为仅提供一个具有该名称的参数:
library test.good.fi0130;
@custom_attribute(custom_arg=true)
type Foo = struct {};
fi-0131:规范属性参数重复
属性不能有两个具有相同规范名称的参数:
library test.bad.fi0131;
@custom_attribute(custom_arg=true, CustomArg=true)
type Foo = struct {};
虽然 custom_arg
和 CustomArg
看起来不同,但它们都是
以规范名称 custom_arg
表示。您会获得规范名称
方法是将原始名称转换为 snake_case
。
如需修正该错误,请为每个参数指定一个唯一的名称, 规范化:
library test.good.fi0131a;
@custom_attribute(custom_arg=true, AnotherCustomArg=true)
type Foo = struct {};
或者,移除其中一个参数:
library test.good.fi0131b;
@custom_attribute(custom_arg=true)
type Foo = struct {};
请参阅 fi-0035,详细了解 FIDL 为何要求进行声明 具有唯一的规范名称。
fi-0132:非预期的属性参数
使用官方属性时 ,则您不能提供参数:
library test.bad.fi0132;
type Foo = flexible enum : uint8 {
@unknown("hello")
BAR = 1;
};
请改为移除该参数:
library test.good.fi0132;
type Foo = flexible enum : uint8 {
@unknown
BAR = 1;
};
fi-0133:属性参数必须是字面量
某些官方属性可以 不允许使用是对常量的引用的参数:
library test.bad.fi0133;
const NAME string = "MyTable";
type Foo = struct {
bar @generated_name(NAME) table {};
};
请改为将字面量值作为实参传递:
library test.good.fi0133;
type Foo = struct {
bar @generated_name("MyTable") table {};
};
这些属性需要使用字面量参数,因为它们的值会影响 编译。支持非文字参数将难以实现, 或者在某些情况下是不可行的,因为这会引发矛盾。
fi-0134
fi-0135:可发现名称无效
如果您为 @discoverable
属性使用了错误的名称,就会出现此错误。
@discoverable
属性应该是库名称,后跟 .
,
协议名称。
library test.bad.fi0135;
@discoverable(name="test.bad.fi0135/Parser")
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
如需修正此错误,请使用有效的可发现名称:
library test.good.fi0135;
@discoverable(name="test.good.fi0135.Parser")
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
fi-0136
fi-0137
fi-0138
fi-0139
fi-0140
fi-0141:错误类型无效
方法响应载荷的 error
类型必须是 int32
、uint32
或
其中 enum
个:
library test.bad.fi0141;
protocol MyProtocol {
MyMethod() -> () error float32;
};
将 error
类型更改为某个有效选项即可修复此错误:
library test.good.fi0141;
protocol MyProtocol {
MyMethod() -> () error int32;
};
如需了解详情,请参阅 RFC-0060:错误处理。
fi-0142:协议传输类型无效
protocol
声明中的 @transport(...)
属性不得指定
传输无效:
library test.bad.fi0142;
@transport("Invalid")
protocol MyProtocol {
MyMethod();
};
请改用一种受支持的传输方式:
library test.good.fi0142;
@transport("Syscall")
protocol MyProtocol {
MyMethod();
};
哪些构成支持的传输仍在最终确定。请参阅 FIDL 属性以获取最新信息。
fi-0143
fi-0144
fi-0145:属性拼写错误
属性名称,其拼写与某 FIDL 官方名称的拼写过于相似。 attributes 则会导致编译器警告:
library test.bad.fi0145;
@duc("should be doc")
protocol Example {
Method();
};
在上面的示例中,属性@duc
在拼写上与
官方 FIDL 属性 @doc
。在这种情况下,属性命名方式为
而不是有意将官方 FIDL 属性拼错,
应进行修改,使其足够唯一:
library test.good.fi0145;
@duck("quack")
protocol Example {
Method();
};
除了拼写检查,此警告的目的是阻止使用 与官方 FIDL 属性过于相似的名称。
拼写错误检测算法的工作原理是计算 属性名称与每个官方 FIDL 的距离 属性。名称过于相似,指修改内容过小 触发拼写错误检测器。
fi-0146:生成的名称无效
如果将 @generated_name
属性与无效的
名称。生成的名称必须遵循与所有 FIDL 标识符相同的规则。
library test.bad.fi0146;
type Device = table {
1: kind flexible enum {
DESKTOP = 1;
PHONE = 2;
};
};
type Input = table {
1: kind @generated_name("_kind") flexible enum {
KEYBOARD = 1;
MOUSE = 2;
};
};
若要解决此问题,请将 @generated_name
值更改为有效的标识符。
library test.good.fi0146;
type Device = table {
1: kind flexible enum {
DESKTOP = 1;
PHONE = 2;
};
};
type Input = table {
1: kind @generated_name("input_kind") flexible enum {
KEYBOARD = 1;
MOUSE = 2;
};
};
fi-0147:@available
缺少参数
如果您使用 @available
属性但未提供
必需的参数。@available
至少需要 added
、
deprecated
或 removed
。
@available(added=1)
library test.bad.fi0147;
@available
type Foo = struct {};
如需解决此问题,请添加以下必需参数之一:
@available(added=1)
library test.good.fi0147;
@available(added=2)
type Foo = struct {};
如需了解详情,请参阅 FIDL 版本控制。
fi-0148:未弃用的说明
当您对 @available
使用 note
参数时,会发生此错误
属性,而不使用 deprecated
参数。note
仅适用于
弃用。
@available(added=1, note="My note")
library test.bad.fi0148;
要修正此错误,请移除备注:
@available(added=1)
library test.good.fi0148a;
或添加必要的 deprecated
通知:
@available(added=1, deprecated=2, note="Removed in 2; use X instead.")
library test.good.fi0148b;
如需了解详情,请参阅 FIDL 版本控制。
fi-0149:平台不在库中
当您尝试使用 @available
属性的 platform
时,会发生此错误
参数。platform
参数仅在 library
级别有效。
@available(added=1)
library test.bad.fi0149;
@available(platform="foo")
type Person = struct {
name string;
};
如需解决此问题,请将 platform
参数移到库 @available
中
属性:
@available(added=1, platform="foo")
library test.good.fi0149a;
type Person = struct {
name string;
};
或彻底移除 platform
参数:
@available(added=1)
library test.good.fi0149b;
type Person = struct {
name string;
};
fi-0150:缺少库可用性
如果您在没有添加 @available
属性的情况下将 @available
属性添加到库中,
提供 added
参数。library
需要使用 added
参数
@available
属性。
@available(removed=2)
library test.bad.fi0150a;
@available(platform="foo")
library test.bad.fi0150b;
如需解决此问题,请将 added
参数添加到库的 @available
中
属性:
@available(added=1, removed=2)
library test.good.fi0150a;
@available(added=1, platform="foo")
library test.good.fi0150b;
fi-0151:缺少库可用性
在非 library
上添加 @available
属性时,会发生此错误
library
声明中不包含 @available
属性的声明。
library test.bad.fi0151;
@available(added=1)
type Person = struct {
name string;
};
如需修复此错误,请将 @available
属性添加到 library
声明中:
@available(added=1)
library test.good.fi0151a;
@available(added=1)
type Person = struct {
name string;
};
或者从非 library
声明中移除 @available
属性:
library test.good.fi0151b;
type Person = struct {
name string;
};
fi-0152:平台无效
当您对以下对象的 platform
参数使用无效字符时,就会出现此错误
@available
属性。platform
参数必须是有效的 FIDL 库
标识符。
@available(added=1, platform="Spaces are not allowed")
library test.bad.fi0152;
如需修正此错误,请移除不允许使用的字符:
@available(added=1, platform="foo")
library test.good.fi0152;
fi-0153:版本无效
如果对 added
或 removed
使用无效版本,就会出现此错误
一个 @available
属性。added
和 removed
参数必须
1 到 2^63-1 之间的正整数,或特殊常量 HEAD
。
@available(added=0)
library test.bad.fi0153;
要解决此问题,请将版本更改为有效值:
@available(added=1)
library test.good.fi0153;
fi-0154:可用性订单无效
如果 added
、deprecated
和
@available
属性的 remove
参数。以下限制条件必须
尊重:
added
必须小于或等于deprecated
deprecated
必须小于removed
added
必须小于removed
@available(added=2, removed=2)
library test.bad.fi0154a;
@available(added=2, deprecated=3, removed=3)
library test.bad.fi0154b;
若要解决此问题,请将 added
、deprecated
和 removed
参数更新为
所需的排序:
@available(added=1, removed=2)
library test.good.fi0154a;
@available(added=2, deprecated=2, removed=3)
library test.good.fi0154b;
fi-0155:与父级发布商产生可用性冲突
将 @availability
属性添加到非 library
时,
声明与 library
的声明冲突。
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155a;
@available(added=1)
type Person = struct {
name string;
};
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155b;
@available(added=4)
type Person = struct {
name string;
};
如需修正此错误,请将 @availability
属性更新为必需的属性
限制条件:
@available(added=2, deprecated=3, removed=4)
library test.good.fi0155;
@available(added=2)
type Person = struct {
name string;
};
fi-0156:不能是可选项
当您尝试将不能标记为可选的类型时,就会出现此错误 可选属性。
library test.bad.fi0156;
type Person = struct {
name string;
age int16:optional;
};
如需修复此错误,请移除可选约束条件:
library test.good.fi0156;
type Person = struct {
name string;
age int16;
};
只有可以在不更改线形形状的情况下可选的 FIDL 类型
可以使用 optional
限制条件。请参阅
可选指南或下方的展开内容,了解详情。
FIDL 配方:可选性
您可以将某些 FIDL 类型设置为可选,而无需更改
包含 :optional
约束条件的消息。
此外,table
布局始终是可选的,而 struct
布局从不
。如需将 struct
设为可选,必须将其封装在 box<T>
中,从而
更改其所含消息的导线形状。
基本类型 | 可选版本 | 可选设置会改变电线布局吗? |
---|---|---|
struct {...} |
box<struct {...}> |
是 |
table {...} |
table {...} |
否 |
union {...} |
union {...}:optional |
否 |
vector<T> |
vector<T>:optional |
否 |
string |
string:optional |
否 |
zx.Handle |
zx.Handle:optional |
否 |
client_end:P |
client_end:<P, optional> |
否 |
server_end:P |
server_end:<P, optional> |
否 |
所有其他类型(bits
、enum
、array<T, N>
和基元类型)都不可以
都是可选的
在此变体中,我们允许键值存储区将其他键值存储区视为
成员。简而言之,我们把它变成了一棵树。为此,我们会使用
value
的定义,其中定义使用双成员 union
:一种变体
存储使用与之前相同的 vector<byte>
类型的叶节点,而另一个
以其他嵌套存储区的形式存储分支节点。
推理
在这里,我们可以看到“可选性”的多种用途,通过它可以声明一个类型, 可能会存在。FIDL 中有三种可选性:
- 始终存储的类型
不符合要求
因此有一种内置方式可以描述“缺失”通过
null 信封。正在启用
这些类型的可选性不会影响它们所属的消息的电线形状
它只是更改对特定标签有效的值
类型。
union
、vector<T>
、client_end
、server_end
和zx.Handle
都可以通过添加:optional
约束条件使所有类型都是可选的。 通过将value
union
设置为可选,我们能够引入 null条目,采用不存在的value
的形式。这意味着,空bytes
和不存在/空的store
属性是无效值。 - 与上述类型不同,
struct
布局没有额外的空间, 可以存储 null 标头。因此,需要将其封装在 信封、更改所包含邮件的网线形状 位置为确保这种电线修改效果清晰易读,Item
struct
类型必须封装在box<T>
类型模板中。 - 最后,
table
布局始终是可选的。缺失table
只是一种 且未设置任何成员。
树状结构是自然的自引用数据结构:树中的任何节点都可以
包含具有纯数据(本例中为字符串)的叶,或具有
节点。这需要递归:Item
的定义现在以传递方式传递
需要依赖自身!在 FIDL 中表示递归类型可能有点棘手,
特别是考虑到我们目前获得的支持
受限。只要有
自引用创建的循环中至少有一个可选类型。对于
实例,在这里我们将 items
struct
成员定义为 box<Item>
,
从而打破 include 循环。
这些更改还大量使用了匿名类型,即
声明是内嵌在它们的唯一使用点,而不是直接使用,
它们自己的顶级 type
声明。默认情况下
生成的语言绑定中的类型取自其本地上下文。对于
实例中,新引入的 flexible union
会使用其所属成员的
名称为 Value
,新引入的 struct
将变为 Store
,依此类推。
由于这种启发法有时可能会导致冲突,因此 FIDL 提供了一种逃逸方法。
允许作者手动替换匿名类型生成的
名称。这是通过 @generated_name
属性完成的,该属性允许
更改后端生成的名称在这里,我们可以使用
Store
类型已重命名为 NestedStore
,以防止与
protocol
声明。
实现
FIDL、CML 和 Realm 接口定义修改如下:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
客户端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
服务器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
大区
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
然后,可以使用任何受支持的语言编写客户端和服务器实现:
Rust
客户端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
服务器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++(自然)
客户端
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
服务器
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++(有线)
客户端
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
服务器
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
客户端
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
服务器
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0157:客户端/服务器端限制条件必须是协议
应用于 client_end
或 server_end
的第一个约束条件必须指向
转换为 protocol
定义:
library test.bad.fi0157;
type MyStruct = struct {};
alias ServerEnd = server_end:MyStruct;
将限制条件更改为指向协议:
library test.good.fi0157;
protocol MyProtocol {};
alias ServerEnd = server_end:MyProtocol;
fi-0158:无法绑定两次
alias
声明无法更改已设置的限制的值
其别名的类型:
library test.bad.fi0158;
alias ByteVec256 = vector<uint8>:256;
alias ByteVec512 = ByteVec256:512;
无界限定义应接收自己的 alias
声明,
而每个进一步受限的别名应依次继承:
library test.good.fi0158;
alias AliasOfVectorOfString = vector<string>;
alias AliasOfVectorOfStringSmall = AliasOfVectorOfString:8;
alias AliasOfVectorOfStringLarge = AliasOfVectorOfString:16;
这样做是为了避免混淆和编译器实现复杂性。
fi-0159:结构体不能是可选的
结构体不能具有 optional
约束条件:
library test.bad.fi0159;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday Date:optional;
};
将 T:optional
更改为 box<T>
以解决此问题:
library test.good.fi0159;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday box<Date>;
};
只有可以在不更改线形形状的情况下可选的 FIDL 类型
可以使用 optional
限制条件。请参阅
可选性指南。
fi-0160:类型不能两次标记为可选
当某个类型被设为可选两次时,就会发生此错误。通常 如果该类型在使用时都被标记为可选属性,就会发生这种情况 和声明网站。
library test.bad.fi0160;
alias MyAlias = vector<string>:optional;
type MyStruct = struct {
my_member MyAlias:optional;
};
要修正此错误,只需将类型设置为可选一次即可。
例如,您可以从使用的网站中移除 :optional
。
library test.good.fi0160a;
alias MyAlias = vector<string>:optional;
type MyStruct = struct {
my_member MyAlias;
};
您还可以从别名声明中移除 :optional
。
library test.good.fi0160b;
alias MyAlias = vector<string>;
type MyStruct = struct {
my_member MyAlias:optional;
};
fi-0161:大小不得为零
当您尝试将数组大小约束设置为 0 时,会发生此错误。数组 不能为零。
library test.bad.fi0161;
type Person = struct {
name string;
nicknames array<string, 0>;
};
如需修正此错误,请将大小限制更改为正整数。
library test.good.fi0161;
type Person = struct {
name string;
nicknames array<string, 5>;
};
fi-0162:布局参数数量错误
某些 FIDL 布局(如 vector
和 array
)接受参数。此错误
表示突出显示的类型的参数数量不正确
指定:
library test.bad.fi0162a;
type Foo = struct {
bar array<8>;
};
当不可参数化的类型错误地指定了 附加以下参数:
library test.bad.fi0162b;
type Foo = struct {
bar uint8<8>;
};
解决方法是始终为 问题:
library test.good.fi0162;
type Foo = struct {
bar array<uint8, 8>;
};
FIDL 中仅有的参数化类型是 array<T, N>
、box<T>
和
vector<T>
。client_end
和 server_end
类型曾在
旧版 FIDL 语法,但现在不同了,
但该错误经常会导致出现运行错误。现在,这两种类型的
改为将其协议规范作为(必需)约束条件。
参数始终列在尖括号 <...>
内,
与约束条件的相似点,出现在末尾的 :...
字符之后。
类型。例如,刚看到脸红时,array<T, N>
可能会觉得很奇怪,
其大小以参数形式指定,而 vector<T>:N
则以参数形式指定其大小
限制条件。区别在于参数始终会影响电线布局
相关类型的形状,而约束条件只是改变值的集合
编码/解码时认为可接受的字符,
对电线布局的影响。
如需更全面地讨论以下内容,请参阅 RFC-0050:FIDL 语法改进。 这两个概念之间的区别。
fi-0163:多个约束条件定义
当您尝试使用
多个冒号 (:
)。多个限制条件定义必须使用
尖括号语法 type:<constraint1, constraint2, etc>
。
library test.bad.fi0163;
type Person = struct {
name string;
favorite_color string:30:optional;
};
如需修复此错误,请为约束条件使用尖括号语法:
library test.good.fi0163;
type Person = struct {
name string;
favorite_color string:<30, optional>;
};
fi-0164:限制条件过多
当您尝试为某个类型添加的约束条件时,如果
支持。例如,string
最多支持两个约束条件。
library test.bad.fi0164;
type Person = struct {
name string:<0, optional, 20>;
};
若要解决此问题,请移除额外的约束条件:
library test.good.fi0164;
type Person = struct {
name string:<20, optional>;
};
fi-0165:预期类型
当 FIDL 为 期望某种类型。
library test.bad.fi0165;
type Person = struct {
name string;
nicknames vector<5>;
};
如需修复此错误,请更新您的代码以使用有效的类型:
library test.good.fi0165;
type Person = struct {
name string;
nicknames vector<string>:5;
};
协议不属于 FIDL 类型, 符合预期。
fi-0166:意外约束
当您尝试在非预期位置使用限制条件时,会发生此错误。
这通常是由于命名的 const
位置错误造成的。
library test.bad.fi0166;
const MIN_SIZE uint8 = 1;
const MAX_SIZE uint8 = 5;
type Person = struct {
name string;
nicknames vector<string>:<MIN_SIZE, MAX_SIZE>;
};
如需修复此错误,请移除限制条件:
library test.good.fi0166;
const MAX_SIZE uint8 = 5;
type Person = struct {
name string;
nicknames vector<string>:<MAX_SIZE>;
};
fi-0167:不能限制两次
为符合以下条件的 client_end
或 server_end
重新分配传输的边界:
已通过 alias
声明定义了传输绑定
禁止:
library test.bad.fi0167;
protocol MyOtherProtocol {};
alias ClientEnd = client_end:MyProtocol;
alias ServerEnd = server_end:MyProtocol;
protocol MyProtocol {
MyMethod(resource struct {
my_client ClientEnd:MyOtherProtocol;
}) -> (resource struct {
my_server ServerEnd:MyOtherProtocol;
});
};
应避免 client_end
和 server_end
类型的别名混淆
完全:
library test.good.fi0167;
protocol MyProtocol {
MyMethod(resource struct {
my_client client_end:MyProtocol;
}) -> (resource struct {
my_server server_end:MyProtocol;
});
};
这样做是为了避免混淆和编译器实现复杂性。
fi-0168:客户端/服务器端必须具有协议限制
应用于 client_end
或 server_end
的第一个约束条件必须指向
转换为 protocol
定义:
library test.bad.fi0168;
protocol MyProtocol {
MyMethod(resource struct {
server server_end;
});
};
添加指向所需协议的约束条件:
library test.good.fi0168;
protocol MyProtocol {
MyMethod(resource struct {
server server_end:MyProtocol;
});
};
fi-0169:盒装类型不得为可选项
不能对 box<T>
形式的类型应用 optional
约束:
library test.bad.fi0169;
type Color = struct {
red byte;
green byte;
blue byte;
};
type MyStruct = struct {
maybe_color box<Color>:optional;
};
根据定义,盒装类型是可选的,因此添加额外的限制条件 不必要和多余的:
library test.good.fi0169;
type Color = struct {
red byte;
green byte;
blue byte;
};
type MyStruct = struct {
maybe_color box<Color>;
};
fi-0170
fi-0171:盒装类型应改用可选约束条件
只有使用 struct
布局的类型可以装箱;union
、vector
、string
、
client_end
、server_end
和 zx.Handle
必须使用 optional
约束条件
:
library test.bad.fi0171;
using zx;
type MyStruct = resource struct {
my_resource_member box<zx.Handle>;
};
将 box<T>
转换为 T:optional
以解决此问题:
library test.good.fi0171;
using zx;
type MyStruct = resource struct {
my_resource_member zx.Handle:optional;
};
只有可以在不更改线形形状的情况下可选的 FIDL 类型
可以使用 optional
限制条件。请参阅
可选指南或下方的展开内容,详细了解
信息。
FIDL 配方:可选性
您可以将某些 FIDL 类型设置为可选,而无需更改
包含 :optional
约束条件的消息。
此外,table
布局始终是可选的,而 struct
布局从不
。如需将 struct
设为可选,必须将其封装在 box<T>
中,从而
更改其所含消息的导线形状。
基本类型 | 可选版本 | 可选设置会改变电线布局吗? |
---|---|---|
struct {...} |
box<struct {...}> |
是 |
table {...} |
table {...} |
否 |
union {...} |
union {...}:optional |
否 |
vector<T> |
vector<T>:optional |
否 |
string |
string:optional |
否 |
zx.Handle |
zx.Handle:optional |
否 |
client_end:P |
client_end:<P, optional> |
否 |
server_end:P |
server_end:<P, optional> |
否 |
所有其他类型(bits
、enum
、array<T, N>
和基元类型)都不可以
都是可选的
在此变体中,我们允许键值存储区将其他键值存储区视为
成员。简而言之,我们把它变成了一棵树。为此,我们会使用
value
的定义,其中定义使用双成员 union
:一种变体
存储使用与之前相同的 vector<byte>
类型的叶节点,而另一个
以其他嵌套存储区的形式存储分支节点。
推理
在这里,我们可以看到“可选性”的多种用途,通过它可以声明一个类型, 可能会存在。FIDL 中有三种可选性:
- 始终存储的类型
不符合要求
因此有一种内置方式可以描述“缺失”通过
null 信封。正在启用
这些类型的可选性不会影响它们所属的消息的电线形状
它只是更改对特定标签有效的值
类型。
union
、vector<T>
、client_end
、server_end
和zx.Handle
都可以通过添加:optional
约束条件使所有类型都是可选的。 通过将value
union
设置为可选,我们能够引入 null条目,采用不存在的value
的形式。这意味着,空bytes
和不存在/空的store
属性是无效值。 - 与上述类型不同,
struct
布局没有额外的空间, 可以存储 null 标头。因此,需要将其封装在 信封、更改所包含邮件的网线形状 位置为确保这种电线修改效果清晰易读,Item
struct
类型必须封装在box<T>
类型模板中。 - 最后,
table
布局始终是可选的。缺失table
只是一种 且未设置任何成员。
树状结构是自然的自引用数据结构:树中的任何节点都可以
包含具有纯数据(本例中为字符串)的叶,或具有
节点。这需要递归:Item
的定义现在以传递方式传递
需要依赖自身!在 FIDL 中表示递归类型可能有点棘手,
特别是考虑到我们目前获得的支持
受限。只要有
自引用创建的循环中至少有一个可选类型。对于
实例,在这里我们将 items
struct
成员定义为 box<Item>
,
从而打破 include 循环。
这些更改还大量使用了匿名类型,即
声明是内嵌在它们的唯一使用点,而不是直接使用,
它们自己的顶级 type
声明。默认情况下
生成的语言绑定中的类型取自其本地上下文。对于
实例中,新引入的 flexible union
会使用其所属成员的
名称为 Value
,新引入的 struct
将变为 Store
,依此类推。
由于这种启发法有时可能会导致冲突,因此 FIDL 提供了一种逃逸方法。
允许作者手动替换匿名类型生成的
名称。这是通过 @generated_name
属性完成的,该属性允许
更改后端生成的名称在这里,我们可以使用
Store
类型已重命名为 NestedStore
,以防止与
protocol
声明。
实现
FIDL、CML 和 Realm 接口定义修改如下:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
客户端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
服务器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
大区
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
然后,可以使用任何受支持的语言编写客户端和服务器实现:
Rust
客户端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
服务器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++(自然)
客户端
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
服务器
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++(有线)
客户端
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
服务器
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
客户端
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
服务器
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0172:资源定义必须使用 uint32 子类型
resource_definition
声明的子类型必须为 uint32
:
library test.bad.fi0172;
type MySubtype = strict enum : uint32 {
NONE = 0;
};
resource_definition MyResource : uint8 {
properties {
subtype MySubtype;
};
};
如需修复此错误,请将子类型更改为 uint32
:
library test.good.fi0172;
type MySubtype = strict enum : uint32 {
NONE = 0;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
};
};
这是一个与 FIDL 的内部实现相关的错误,因此应仅 向负责 FIDL 核心库的开发者提供。最终用户 应该不会看到此错误。
它所引用的 resource_definition
声明是 FIDL 的内部方法,
定义资源,并且将来可能会作为
处理泛化工作的一部分。
fi-0173:资源定义必须指定子类型
resource_definition
声明不能省略 subtype
成员:
library test.bad.fi0173;
resource_definition MyResource : uint32 {
properties {
rights uint32;
};
};
使此成员指向有效的 enum : uint32
声明:
library test.good.fi0173;
resource_definition MyResource : uint32 {
properties {
subtype flexible enum : uint32 {};
rights uint32;
};
};
这是一个与 FIDL 的内部实现相关的错误,因此应仅 向负责 FIDL 核心库的开发者提供。最终用户 应该不会看到此错误。
它所引用的 resource_definition
声明是 FIDL 的内部方法,
定义资源,并且将来可能会作为
处理泛化工作的一部分。
fi-0174
fi-0175:资源定义子类型属性必须引用枚举
resource_definition
声明不能使用非 enum
作为 subtype
成员:
library test.bad.fi0175;
resource_definition MyResource : uint32 {
properties {
subtype struct {};
};
};
使此成员指向有效的 enum : uint32
声明:
library test.good.fi0175;
resource_definition MyResource : uint32 {
properties {
subtype flexible enum : uint32 {};
};
};
这是一个与 FIDL 的内部实现相关的错误,因此应仅 向负责 FIDL 核心库的开发者提供。最终用户 应该不会看到此错误。
它所引用的 resource_definition
声明是 FIDL 的内部方法,
定义资源,并且将来可能会作为
处理泛化工作的一部分。
fi-0176
fi-0177:资源定义权限属性必须引用位
resource_definition
声明不能使用非 bits
作为 rights
成员:
library test.bad.fi0177;
type MySubtype = enum : uint32 {
NONE = 0;
VMO = 3;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
rights string;
};
};
使此成员指向有效的 bits : uint32
声明:
library test.good.fi0177;
type MySubtype = enum : uint32 {
NONE = 0;
VMO = 3;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
rights uint32;
};
};
这是一个与 FIDL 的内部实现相关的错误,因此应仅 向负责 FIDL 核心库的开发者提供。最终用户 应该不会看到此错误。
它所引用的 resource_definition
声明是 FIDL 的内部方法,
定义资源,并且将来可能会作为
处理泛化工作的一部分。
fi-0178:未使用的导入
未引用通过 using
声明导入的依赖项会导致错误:
library test.bad.fi0178;
using dependent;
type Foo = struct {
does_not int64;
use_dependent int32;
};
确保在库导入中使用了所有此类导入 引用导入,或移除未使用的依赖项:
library test.good.fi0178;
using dependent;
type Foo = struct {
dep dependent.Bar;
};
fi-0179:无法约束 Newtype
不支持 RFC-0052: Type aliasing and new types 中的新类型
允许被限制例如,新类型的 string
不能
使用 :optional
进行约束:
library test.bad.fi0179;
type Name = string;
type Info = struct {
name Name:optional;
};
在这种情况下,我们可以将 name
字段放在
而不是结构体:
library test.good.fi0179;
type Name = string;
type Info = table {
1: name Name;
};
此限制简化了 newtype 的设计。不清楚这个 API 一般来说,受约束的新类型应具有与 ABI 相似的内容(例如, 新类型本身具有约束条件, type?)。
fi-0180:Zircon C 类型处于实验阶段
内置类型 usize
、uintptr
、uchar
和 experimental_pointer
为
Zither 项目开发的。它们无法在普通 FIDL 中使用
库:
library test.bad.fi0180;
type Data = struct {
size usize64;
};
请改用其他类型,例如 uint64
而不是 usize
:
library test.good.fi0180;
type Data = struct {
size uint64;
};
fi-0181:库属性参数引用常量
不允许引用库声明中的属性参数 常量:
@custom_attribute(VALUE)
library test.bad.fi0181a;
const VALUE string = "hello";
请改为提供字面量参数:
@custom_attribute("hello")
library test.good.fi0181a;
之所以存在此限制,是因为很少需要用到它,并且支持它意味着 编译器的复杂性
fi-0182
fi-0183
fi-0184:非预期的控制字符
字符串字面量不得包含原始控制字符 (ASCII)
个字符(从 0x00
到 0x1f
):
library test.bad.fi0184;
const TAB string = " "; // literal tab character
而应使用转义序列。在本例中,\t
是正确的名称:
library test.good.fi0184a;
const TAB string = "\t";
或者,您也可以使用 Unicode 转义序列。这适用于任何 Unicode 代码点:
library test.good.fi0184b;
const TAB string = "\u{9}";
字符串字面量中不允许使用原始控制字符,因为它们 要么是空格,要么不可打印,因此会造成混淆,并且难以 请注意,如果直接嵌入 FIDL 源文件,
fi-0185:Unicode 转义序列缺少大括号
字符串字面量中的 Unicode 转义序列必须使用大括号指定代码点:
library test.bad.fi0185;
const SMILE string = "\u";
如需修正该错误,请用大括号指定一个码位:
library test.good.fi0185;
const SMILE string = "\u{1F600}";
fi-0186:Unicode 转义序列未结束
字符串字面量中的 Unicode 转义序列必须终止:
library test.bad.fi0186;
const SMILE string = "\u{1F600";
如需终止转义序列,请添加右大括号 }
:
library test.good.fi0186;
const SMILE string = "\u{1F600}";
fi-0187:Unicode 转义序列为空
字符串字面量中的 Unicode 转义序列必须至少包含一个十六进制数字:
library test.bad.fi0187;
const SMILE string = "\u{}";
若要修正该错误,请添加十六进制数字,以指定 Unicode 代码点:
library test.good.fi0187;
const SMILE string = "\u{1F600}";
fi-0188:Unicode 转义序列中的数字过多
字符串字面量中的 Unicode 转义序列不得超过 6 位十六进制数字:
library test.bad.fi0188;
const SMILE string = "\u{001F600}";
若要修正该错误,请最多指定 6 位十六进制数字。在这种情况下, 零个数:
library test.good.fi0188;
const SMILE string = "\u{1F600}";
存在此限制是因为所有有效的 Unicode 代码点都适合 6 个十六进制值 数字,因此没有理由允许使用超过该数字。
fi-0189:Unicode 代码点过大
字符串字面量中的 Unicode 转义序列不能指定 Unicode 码位
超过上限 0x10ffff
:
library test.bad.fi0189;
const TOO_LARGE string = "\u{110000}";
请改为确保代码点有效:
library test.good.fi0189;
const MAX_CODEPOINT string = "\u{10ffff}";
fi-0190
fi-0191:方法必须指定严格程度
此错误表示 FIDL 方法没有 strict
或 flexible
修饰符。
library test.bad.fi0191;
open protocol Example {
OneWay();
};
如需解决此问题,请在该方法中添加 strict
或 flexible
。如果这是
现有方法,则必须使用 strict
,并且应该会看到兼容性
指南,了解如何更改为 flexible
。如果
这是一种新方法,您应该查看针对 API 的 API 评分准则
有选择性的指南。
library test.good.fi0191;
open protocol Example {
flexible OneWay();
};
FIDL 目前正在进行迁移,以便处理未知的
(如 RFC-0138 中所定义)。借助这项新功能,
修饰符 strict
和 flexible
,以应用于 FIDL 方法和事件。
过去,所有方法的行为都类似于 strict
,不过,在
则默认值为 flexible
。避免混淆
以及将方法默认修饰符
strict
到 flexible
,在此过渡期间需要使用方法修饰符
。迁移完成后,状态会从“错误”更改为“
linter 建议。
如需详细了解未知交互,请参阅 FIDL 语言 参考。
fi-0192:协议必须指定开放性
此错误表示 FIDL 协议没有 open
、ajar
或
closed
修饰符。
library test.bad.fi0192;
protocol ImplicitOpenness {};
若要解决此问题,请在协议中添加 open
、ajar
或 closed
。如果这是
现有协议,则必须使用 closed
,并查看兼容性
指南,了解如何更改为 open
或
ajar
。如果这是个新方法,您应该会看到 API
评分准则。
library test.good.fi0192;
open protocol ImplicitOpenness {};
FIDL 目前正在进行迁移,以便处理未知的
(如 RFC-0138 中所定义)。这项新功能新增了
新增了适用于 FIDL 协议的修饰符 open
、ajar
和 closed
。
过去,所有协议的行为都类似于 closed
,但是
则默认值为 open
。为了避免混淆和
使用协议默认修饰符
从 closed
更改为 open
,在此次过渡期间需要使用协议修饰符
。迁移完成后,状态会从“错误”更改为“
linter 建议。
如需详细了解未知交互,请参阅 FIDL 语言 参考。
fi-0193:无法添加方框类型
结构体以外的类型不能装箱。例如,基元类型无法装箱:
library test.bad.fi0193;
type MyStruct = struct {
my_member box<bool>;
};
如需将基元装箱,请改为将其放入单成员 struct
中:
library test.good.fi0193;
type MyStruct = struct {
my_member box<struct {
my_bool bool;
}>;
};
请注意,有些类型可通过使用 optional
设为可选。
限制条件。请参阅可选指南,或
。
FIDL 配方:可选性
您可以将某些 FIDL 类型设置为可选,而无需更改
包含 :optional
约束条件的消息。
此外,table
布局始终是可选的,而 struct
布局从不
。如需将 struct
设为可选,必须将其封装在 box<T>
中,从而
更改其所含消息的导线形状。
基本类型 | 可选版本 | 可选设置会改变电线布局吗? |
---|---|---|
struct {...} |
box<struct {...}> |
是 |
table {...} |
table {...} |
否 |
union {...} |
union {...}:optional |
否 |
vector<T> |
vector<T>:optional |
否 |
string |
string:optional |
否 |
zx.Handle |
zx.Handle:optional |
否 |
client_end:P |
client_end:<P, optional> |
否 |
server_end:P |
server_end:<P, optional> |
否 |
所有其他类型(bits
、enum
、array<T, N>
和基元类型)都不可以
都是可选的
在此变体中,我们允许键值存储区将其他键值存储区视为
成员。简而言之,我们把它变成了一棵树。为此,我们会使用
value
的定义,其中定义使用双成员 union
:一种变体
存储使用与之前相同的 vector<byte>
类型的叶节点,而另一个
以其他嵌套存储区的形式存储分支节点。
推理
在这里,我们可以看到“可选性”的多种用途,通过它可以声明一个类型, 可能会存在。FIDL 中有三种可选性:
- 始终存储的类型
不符合要求
因此有一种内置方式可以描述“缺失”通过
null 信封。正在启用
这些类型的可选性不会影响它们所属的消息的电线形状
它只是更改对特定标签有效的值
类型。
union
、vector<T>
、client_end
、server_end
和zx.Handle
都可以通过添加:optional
约束条件使所有类型都是可选的。 通过将value
union
设置为可选,我们能够引入 null条目,采用不存在的value
的形式。这意味着,空bytes
和不存在/空的store
属性是无效值。 - 与上述类型不同,
struct
布局没有额外的空间, 可以存储 null 标头。因此,需要将其封装在 信封、更改所包含邮件的网线形状 位置为确保这种电线修改效果清晰易读,Item
struct
类型必须封装在box<T>
类型模板中。 - 最后,
table
布局始终是可选的。缺失table
只是一种 且未设置任何成员。
树状结构是自然的自引用数据结构:树中的任何节点都可以
包含具有纯数据(本例中为字符串)的叶,或具有
节点。这需要递归:Item
的定义现在以传递方式传递
需要依赖自身!在 FIDL 中表示递归类型可能有点棘手,
特别是考虑到我们目前获得的支持
受限。只要有
自引用创建的循环中至少有一个可选类型。对于
实例,在这里我们将 items
struct
成员定义为 box<Item>
,
从而打破 include 循环。
这些更改还大量使用了匿名类型,即
声明是内嵌在它们的唯一使用点,而不是直接使用,
它们自己的顶级 type
声明。默认情况下
生成的语言绑定中的类型取自其本地上下文。对于
实例中,新引入的 flexible union
会使用其所属成员的
名称为 Value
,新引入的 struct
将变为 Store
,依此类推。
由于这种启发法有时可能会导致冲突,因此 FIDL 提供了一种逃逸方法。
允许作者手动替换匿名类型生成的
名称。这是通过 @generated_name
属性完成的,该属性允许
更改后端生成的名称在这里,我们可以使用
Store
类型已重命名为 NestedStore
,以防止与
protocol
声明。
实现
FIDL、CML 和 Realm 接口定义修改如下:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
客户端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
服务器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
大区
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
然后,可以使用任何受支持的语言编写客户端和服务器实现:
Rust
客户端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
服务器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++(自然)
客户端
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
服务器
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++(有线)
客户端
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
服务器
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
客户端
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
服务器
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0194
fi-0195
fi-0196
fi-0200
fi-0201:未选择平台版本
如果在编译版本化 FIDL 库时没有选择 版本:
// fidlc --files test.fidl --out test.json
@available(platform="foo", added=1)
library test.bad.fi0201;
如需解决此问题,请使用 --available
命令行标志选择一个版本:
// fidlc --files test.fidl --out test.json --available foo:1
@available(platform="foo", added=1)
library test.good.fi0201;
版本必须是大于或等于 1 的数字,或
版本 NEXT
和 HEAD
。有关详情,请参阅
FIDL 版本控制。
fi-0202
fi-0203:移除和替换项是互斥的
@available
属性支持参数 removed
和 replaced
,但
不能同时使用:
@available(added=1)
library test.bad.fi0203;
protocol Foo {
@available(removed=2, replaced=2)
Foo();
};
如需修正该错误,请删除其中一个参数。如果您要移除
元素,保留 removed
并删除 replaced
:
@available(added=1)
library test.good.fi0203a;
open protocol Foo {
@available(removed=2)
strict Foo();
};
或者,如果您使用新定义交换元素,
replaced
并删除 removed
:
@available(added=1)
library test.good.fi0203b;
open protocol Foo {
@available(replaced=2)
strict Foo();
@available(added=2)
flexible Foo();
};
将 removed
和 replaced
结合使用是没有意义的,因为它们
相反的意思。当某个元素标记为 removed
时,fidlc 会验证
同一版本没有添加替换元素。当
元素标记为 replaced
,fidlc 会验证是否存在替换元素
元素。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0204:无法替换库
@available
属性的 replaced
参数不能用于库
声明:
@available(added=1, replaced=2)
library test.bad.fi0204;
请改用 removed
参数:
@available(added=1, removed=2)
library test.good.fi0204;
replaced
参数用于指示将元素替换为新的
定义。因为我们假设每个
库只有一个定义它的文件集。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0205:@available(removed=N)
无效
如果某个元素标记为 @available(removed=N)
,则表示该元素不能
在版本 N
中不再使用。您不能重复使用其名称:
@available(added=1)
library test.bad.fi0204;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如果您想用新定义(相同的 API 和 ABI)替换该元素,请使用
replaced
实参,而不是 removed
实参:
@available(added=1)
library test.good.fi0204a;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如果您想移除该元素并定义一个新的、不相关的元素(不同的 API 和 ABI),请为新元素选择其他名称:
@available(added=1)
library test.good.fi0204b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible NewBar();
};
如果您确实想重复使用该名称(相同的 API、不同的 ABI),请使用
renamed
参数,用于在移除后重命名旧元素,释放其
原始名称:
@available(added=1)
library test.good.fi0204c;
open protocol Foo {
@available(removed=2, renamed="DeprecatedBar")
strict Bar();
@available(added=2)
@selector("NewBar")
flexible Bar();
};
请注意,在这种情况下,您必须使用 @selector
来确保新方法具有
不同的 ABI。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0206:@available(replaced=N)
无效
如果某个元素标记为 @available(replaced=N)
,则表示该元素
替换为标记为 @available(added=N)
的新定义。FIDL 编译器
如果找不到这样的定义,则会报告错误:
@available(added=1)
library test.bad.fi0205;
open protocol Foo {
@available(replaced=2)
strict Bar();
};
如果您不想替换该元素,请改用 removed
参数
(针对 replaced
参数):
@available(added=1)
library test.good.fi0205a;
open protocol Foo {
@available(removed=2)
strict Bar();
};
如果您打算替换该元素,请添加替换定义:
@available(added=1)
library test.good.fi0205b;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0207:形状整数溢出
FIDL 类型的大小不能致使其大小溢出 uint32
:
library test.bad.fi0207;
type Foo = struct {
bytes array<uint64, 536870912>;
};
如需修正该错误,请使用较小的数组大小:
library test.good.fi0207;
type Foo = struct {
bytes array<uint64, 100>;
};
实际上,FIDL 类型应该远小于 232 个字节,因为 通常通过 Zircon 通道发送,每通道 64 KiB 消息。
fi-0208:预留平台
某些平台名称为 FIDL 保留的名称。例如,“未版本控制” platform 保留,用于表示不使用版本控制的库:
@available(platform="unversioned", added=1)
library test.bad.fi0208;
请选择其他平台名称:
@available(platform="foo", added=1)
library test.good.fi0208;
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0209:不允许使用预留字段
FIDL 不再支持 reserved
表或联合字段:
library test.bad.fi0209;
type User = table {
1: reserved;
2: email string;
};
预留字段的主要目的是避免意外重用序数。
有了 FIDL 版本控制,
不再是问题您可以为旧字段@available(removed=N)
将它们(及其序数)保留在源文件中:
@available(added=1)
library test.good.fi0209a;
type User = table {
@available(removed=2)
1: name string;
2: email string;
};
reserved
的另一个用途是记录序数的预期用途。
在这种情况下,请考虑在不稳定的 API 级别 HEAD
中定义该字段:
@available(added=1)
library test.good.fi0209b;
type User = table {
@available(added=HEAD)
1: name string;
2: email string;
};
如果 reserved
有任何其他用途,请考虑发表评论:
library test.good.fi0209c;
type User = table {
// We skip ordinal 1 because...
2: email string;
};
fi-0210:@discoverable 客户端或服务器参数中的位置无效
对于可检测到的协议,允许的 client
或 server
位置必须是
(可能为空)以英文逗号分隔的 platform
和 external
列表。也就是说:
- ""
- "platform"
- "external"
- "platform,external"
- "external,platform"
library test.bad.fi0210;
@discoverable(server="platform,canada")
protocol Example {};
请确保以下参数正确无误:
library test.good.fi0210;
@discoverable(server="platform")
protocol Example {};
fi-0211:无法重命名元素
@available
属性的 renamed
参数只能用于
而不是在声明本身:
@available(added=1)
library test.bad.fi0211;
@available(replaced=2, renamed="Bar")
type Foo = struct {};
@available(added=2)
type Bar = struct {};
移除旧声明并添加新声明,而不是重命名声明:
@available(added=1)
library test.good.fi0211;
@available(removed=2)
type Foo = struct {};
@available(added=2)
type Bar = struct {};
只有成员支持重命名,因为 FIDL 编译器可以与 其 ABI 身份(例如表序数),以确保正确完成。
以下元素也不允许使用 renamed
参数:
library
:您无法从内部重命名库,因为 FIDL 工具链 假设每个库都有一个名称您应该创建一个 具有所需名称并将用户迁移到该库。compose
:您无法重命名协议组合,因为组合 开头不是名称。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0212:重命名,但未替换或移除
@available
参数 renamed
本身是不允许的。必须使用
与 replaced
或 removed
参数搭配使用:
@available(added=1)
library test.bad.fi0212;
protocol Foo {
@available(renamed="New")
Old();
};
如果您只想重命名版本为 N
的元素,请使用 replaced=N
,
使用标记为 added=N
的新名称定义替换:
@available(added=1)
library test.good.fi0212a;
protocol Foo {
@available(replaced=2, renamed="New")
Old();
@available(added=2)
@selector("Old")
New();
};
在这种情况下,替换方法必须针对 ABI 替换 @selector
兼容性。
或者,如果您想移除版本为 N
的元素并引用它
移除后替换为其他名称,请使用 removed=N
:
@available(added=1)
library test.good.fi0212b;
protocol Foo {
@available(removed=2, renamed="DeprecatedOld")
Old();
};
在这种情况下,只有在定位多个版本时才会使用新名称
(例如 --available test:1,2
),因为这是包含该元素的唯一方式
同时定位到移除后的某个版本
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0213:重命名为相同名称
使用 @available
参数 renamed
重命名元素时,新名称
不能与元素的原始名称相同:
@available(added=1)
library test.bad.fi0213;
type Foo = table {
@available(replaced=2, renamed="bar")
1: bar string;
@available(added=2)
1: bar string:10;
};
如需修正该错误,请移除 renamed
参数:
@available(added=1)
library test.good.fi0213a;
type Foo = table {
@available(replaced=2)
1: bar string;
@available(added=2)
1: bar string:10;
};
或者,保留 renamed
参数,但选择其他名称:
@available(added=1)
library test.good.fi0213b;
type Foo = table {
@available(replaced=2, renamed="baz")
1: bar string;
@available(added=2)
1: baz string:10;
};
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0214:@available(removed=N, renamed="NewName")
无效
这与 fi-0205: Invalid @available(removed=N)
类似,但当
renamed
参数。
如果某个元素标记为 @available(removed=N, renamed="NewName")
,则表示
版本 N
中无法再使用该元素,并已重命名为“NewName”
移除后您不能使用“NewName”其他条件:
@available(added=1)
library test.bad.fi0214;
open protocol Foo {
@available(removed=2, renamed="NewName")
strict OldName();
@available(added=2)
flexible NewName();
};
如果您想重命名元素,同时保留其 ABI,请使用 replaced
参数,而不是 removed
参数:
@available(added=1)
library test.good.fi0214a;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
@available(added=2)
@selector("OldName")
flexible NewName();
};
请注意,在这种情况下,您必须使用 @selector
来确保重命名的方法
具有相同的 ABI。
如果您希望新元素具有不同的 ABI,则保留 removed
和
确保 renamed
参数和新元素使用不同的名称:
@available(added=1)
library test.good.fi0214b;
open protocol Foo {
@available(removed=2, renamed="NewName")
strict OldName();
@available(added=2)
strict DifferentName();
};
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0215:@available(replaced=N, renamed="NewName")
无效
这与 fi-0206: Invalid @available(replaced=N)
一样,但对于
renamed
参数。
如果某个元素标记为 @available(replaced=N, renamed="NewName")
,则表示
该元素会被名为“NewName”的新定义替换并使用
@available(added=N)
。如果 FIDL 编译器找不到
这样定义:
@available(added=1)
library test.bad.fi0215;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
};
要修正该错误,请使用新名称定义一个元素:
@available(added=1)
library test.good.fi0215;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
@available(added=2)
@selector("OldName")
strict NewName();
};
请注意,您必须使用 @selector
来确保重命名的方法具有相同的
ABI。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0216:@available(removed=N)
无效 (ABI)
类似于 fi-0205: Invalid @available(removed=N)
,但实际为
元素名称被重用时,其 ABI 为:
@available(added=1)
library test.bad.fi0216;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
@selector("Bar")
flexible Qux();
};
如果您要故意替换元素的 ABI,请使用
参数 replaced
和 renamed
,而不是 removed
:
@available(added=1)
library test.good.fi0216a;
open protocol Foo {
@available(replaced=2, renamed="Qux")
strict Bar();
@available(added=2)
@selector("Bar")
flexible Qux();
};
如果您不打算重复使用该 ABI,请选择其他 ABI。在本示例中,我们
可以移除 @selector
属性,并使用该方法的默认选择器
在其名称上添加以下代码:
@available(added=1)
library test.good.fi0216b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible Qux();
};
除了方法,其他成员也可能会发生此错误。对于位和枚举 则 ABI 是整数值。对于结构体成员,ABI 是字节 偏移。对于表成员和联合成员,ABI 是序数。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0217:@available(replaced=N)
无效 (ABI)
这与 fi-0206: Invalid @available(replaced=N)
一样,但对于
只能找到与元素名称(而非其 ABI)匹配的替换:
@available(added=1)
library test.bad.fi0217;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
@selector("NotBar")
flexible Bar();
};
如果您打算替换该元素,请确保其 ABI 匹配。在此示例中
我们可以移除 @selector
属性,因为两个方法已有相同的
名称:
@available(added=1)
library test.good.fi0217a;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如果您不打算替换 ABI,请使用 removed
而不是 replaced
。
在这种情况下,我们还需要选择其他名称,以避免与
旧:
@available(added=1)
library test.good.fi0217b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible NotBar();
};
如果您确实想重复使用名称,而不是 ABI,请使用 removed
,而不是
replaced
,并在移除后使用 renamed
重命名旧元素,
释放其原始名称:
@available(added=1)
library test.good.fi0217c;
open protocol Foo {
@available(removed=2, renamed="DeprecatedBar")
strict Bar();
@available(added=2)
@selector("NotBar")
flexible Bar();
};
除了方法,其他成员也可能会发生此错误。对于位和枚举 则 ABI 是整数值。对于结构体成员,ABI 是字节 偏移。对于表成员和联合成员,ABI 是序数。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0218:修饰符可用性参数无效
在 FIDL 版本控制修饰符语法中,您只能使用参数 added
和 removed
。不允许使用 deprecated
等其他 @available
参数:
@available(added=1)
library test.bad.fi0218;
type Foo = resource(deprecated=2) struct {};
如需修正该错误,请移除不受支持的参数:
@available(added=1)
library test.good.fi0218;
type Foo = resource struct {};
与声明和成员不同,修饰符没有自己的生命周期, 因此,弃用、替换和重命名等概念 。只能添加和移除修饰符。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0219:无法更改方法严格程度
通过 FIDL 版本控制修饰符语法,您可以添加或移除 strict
和
flexible
修饰符。但是,如果双向方法
不使用 error
语法,因为此类更改会破坏 ABI:
@available(added=1)
library test.bad.fi0219;
open protocol Foo {
strict(removed=2) flexible(added=2) Method() -> ();
};
而应移除严格方法,并添加具有不同 进行替换:
@available(added=1)
library test.good.fi0219a;
open protocol Foo {
@available(removed=2)
strict Method() -> ();
@available(added=2)
flexible NewMethod() -> ();
};
或者,您也可以使用 renamed
参数和 @selector
属性:
@available(added=1)
library test.good.fi0219b;
open protocol Foo {
@available(removed=2, renamed="StrictMethod")
strict Method() -> ();
@available(added=2)
@selector("FlexibleMethod")
flexible Method() -> ();
};
不允许更改没有错误语法的双向方法的严格程度 因为这会改变响应的形状。双向方法 或使用错误语法,FIDL 会自动生成结果并集, 封装响应。因此,更改严格程度只能用于双向 包含错误语法的方法。
如需详细了解版本控制,请参阅 FIDL 版本控制。
fi-0220:在版本范围内找不到名称
如果在特定版本中找不到名称,但该名称位于
在其他版本中发现的。例如,版本 1 中添加的元素 Foo
不能
引用了版本 2 中添加的另一个元素 Bar
,因为 Bar
不存在
:
@available(added=1)
library test.bad.fi0220;
alias Foo = Bar;
@available(added=2)
type Bar = struct {};
在本例中,我们可以修正错误,添加一个 @available
属性以使 Foo
还添加了以下版本:
@available(added=1)
library test.good.fi0220;
@available(added=2)
alias Foo = Bar;
@available(added=2)
type Bar = struct {};
如需详细了解版本控制,请参阅 FIDL 版本控制。