RFC-0087:RFC-0050 更新:FIDL 方法參數語法 | |
---|---|
狀態 | 已接受 |
領域 |
|
說明 | 明確定義頂層類型,藉此修改指定要求和回應參數的語法。 |
問題 | |
毛皮變化 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2021-03-09 |
審查日期 (年-月-日) | 2021-04-14 |
摘要
RFC-0050 之後,FIDL 方法要求或回應的參數將
以 (name1 Type1, name2 Type2)
格式內嵌 (以隱含形式)
使用參數做為結構的成員,定義結構體的要求/回應類型。
這個 RFC 建議變更語法,明確指定頂層類型,
例如:(struct { name1 Type1; name2 Type2; })
。使用者也可以
除了結構體外,也指定聯集或資料表的要求/回應類型。這個
RFC 對線格式沒有影響。
另請參閱:
術語
在此 RFC 中,「wrapping a type in struct in struct」(在結構體中包裝類型)是指將 ID
並定義由該 Y 元件組成的新結構體
類型。舉例來說,在結構中包裝 T
類型,指的是定義新的類型
type Wrapped = struct { my_t T; }
。這項技巧適用於
對於 FIDL 語言的特定限制。舉例來說,uint8
不得
可為空值,但結構體可以有效擁有可為空值的
uint8
。值得注意的是,T
和
Wrapped
的線路格式完全相同。
提振精神
變更語法,明確指定頂層類型,例如:(struct {
name1 Type1; name2 Type2; })
透過以下方式提供兩個主要優點:
- 將 ABI 的影響放在語法的最前處,遵循 FIDL 的
設計原則。例如,編寫
(struct { name1 Type1; })
而非(name1 Type1)
,會明確表示 要求或回應類型屬於結構體,因此會新增或移除 新參數與 ABI 或 API 不相容。 - 這樣就能讓使用者指定不同的頂層類型 需要額外的間接層級包裝類型在結構體中。 除了透過內嵌定義提升可讀性, FIDL 編譯器會挑選適當的名稱,而不是減輕負擔 開發人員。其中一個可能的做法是 請求參數的擴充性是優先考量 時,使用者可以使用資料表而非結構體。
這項 RFC 導入時間可透過以下兩種方式連結至 RFC-0050:
- RFC 引入了匿名版面配置,可讓您重複使用版面配置 這個語法可以指定要求/回應類型 並另外命名
- 這個 RFC 中提議的語法變更可分組至現有 RFC-0050 需要實作和遷移,因此不需要 獨立遷移作業
設計
語法
變更前:
protocol Oven {
StartBake(temp Temperature);
// message with no payload
-> OnReady();
};
變更後:
protocol Oven {
StartBake(struct { temp Temperature; });
// message with no payload
-> OnReady();
};
完整的可用方法變化版本如下:
MyMethod(struct { ... }) -> (struct { ... }); // Two-way
MyMethod(struct { ... }) -> (); // Two-way, but response is empty
MyMethod() -> (struct { ... }); // Two-way, but request is empty
MyMethod() -> (); // Two-way, but both request and response are empty
MyMethod() -> (struct { ... }) error zx.status; // Two-way; response leverages error syntax
MyMethod() -> () error zx.status; // Error: must specify a type for success case.
MyMethod(struct { ... }); // One-way
MyMethod(); // One-way, but request is empty
-> MyMethod(struct { ... }) // Event
-> MyMethod(); // Event, but response is empty
更正式的
protocol-method = ( attribute-list ) , IDENTIFIER , parameter-list,
( "->" , parameter-list , ( "error" type-constructor ) ) ;
protocol-event = ( attribute-list ) , "->" , IDENTIFIER , parameter-list ;
parameter-list = "(" , ( parameter ( "," , parameter )+ ) , ")" ;
parameter = ( attribute-list ) , type-constructor , IDENTIFIER ;
會變成:
protocol-method = ( attribute-list ) , IDENTIFIER , method-params
( "->" , method-params ( "error" type-constructor ) ) ;
protocol-event = ( attribute-list ) , "->" , IDENTIFIER , method-params;
method-params = "(" , type-constructor , ")"
type
如 RFC-0050 中所定義,即
參照現有類型 (例如 MyType<args>:constraints
) 或匿名
版面配置 (例如struct { name Type; }:constraints
。
雖然文法允許任意類型做為要求和 FIDL 編譯器會驗證頂層類型 結構體、聯集或資料表
如同 RFC-0050 中指定的,編譯器保留了 任何內嵌的頂層要求或回應類型,因此能夠將 而不是以內嵌樣式為依據 (例如 可讀性)。舉例來說 可能變更為:
protocol MyProtocol {
Foo(struct {
// input param
input uint32;
}) -> (struct {
// output param
output uint32;
});
};
結束日期:
type FooRequest = struct {
// input param
input uint32;
};
type FooResponse = struct {
// output param
output uint32;
};
protocol MyProtocol {
Foo(FooRequest) -> (FooResponse);
}
沒有 API 或 ABI 的影響 (假設 FooRequest
和 FooResponse
由編譯器保留的名稱)。
繫結
繫結的主要影響是,API 與一組要求/回應參數組合的對應形式都會經過簡化,或是不對 視要求或回應的頂層類型而定。目前所在的位置 已簡化與未整併產生的 API 的例項。
這裡有個「扁平化」API 指的是使用要求
直接替換回應參數
以及結構體例如,
調整為 FIDL 方法 GetName(struct { id uint32; }) -> (struct { name string; })
是:void GetName(uint32_t id, GetNameCallback callback)
。
FIDL 中指定的參數會直接對應到 C++ 中的函式參數。
「非整併」API 是指頂層類型本身
向使用者呈現的資訊在上一個範例中,這看起來會像這樣:
void GetName(GetNameRequest req, GetNameCallback callback)
。GetNameRequest
對應於頂層結構體類型,且會有一個 uint32
id
] 欄位。
在目前的語法中,所有頂層要求或回應類型都來自
隱含結構,簡化參數,讓參數直接對應
我們接受函式簽章的引數,因為新增或移除
struct 成員無論如何都與 ABI 和 API 不相容 (即
產生的繫結不會對保證
由 FIDL 提供)。不過,並非如此,例如資料表和聯集
這項服務支援新增及移除成員因此,在某些情況下
因為在支援語言與測試語言的相容性保證下,
建構用於表示方法的建構 (在此範例中為位置
C++ 中的函式引數) 的限制比頂端實體引數更加嚴格
層級類型 (例如資料表或聯集)以上述範例再次說明
都代表GetName(table { 1: id uint32; }) -> (table { 1: name string;})
您需要產生形式為 void
GetName(GetNameRequest req, GetNameCallback callback)
的非整形簽章,才能維護
資料表頂層類型提供的相容性保證。
對於產生的函式或方法,Dart 等部分程式設計語言 方法是在傳送要求中使用具名引數 但由於原因在於 也不必在接收方法中加入新參數
總結來說,使用扁平化 API 做為結構的繫結程式碼可能需要
則可提供不同的非扁平化 API (如果頂層類型為資料表或
聯集。繫結目前已產生未整併的 API:
例如,在 LLCPP 中的 MyProtocol::MyRequest
或 MyProtocol::MyResponse
,
在頂層結構體的 API 之間沒有如此差異
要求/回應,或頂層聯集或資料表要求/回應。
JSON IR
系統會變更 maybe_request
和 maybe_response
的 JSON 項目。舊
結構定義:
"maybe_request": {
"description": "Optional list of interface method request parameters",
"type": "array",
"items": {
"$ref": "#/definitions/interface-method-parameter"
}
},
會變成:
"maybe_request_payload": {
"description": "Optional type of the request",
"$ref": "#/definitions/compound-identifier"
},
(與 maybe_response
相同)
「"maybe_request_payload"
」欄位已存在與這個形狀相符,但
尚未在 JSON IR 中指定,這是「
訊息表示法」。實務上,JSON IR 變化
這個 RFC 需要完成從 "maybe_request"
到
"maybe_request_payload"
(請參閱「導入」一節)。
實作
這個 RFC 的實作包含兩個部分:第一個是 修改所有現有檔案,使其符合新語法的外觀變更 ,第二部分將 FIDL 編譯器和繫結變更為 允許資料表和聯集做為頂層類型。語法變更將會 作為 RFC-0050 FIDL 語法的一部分,但支援 可以延遲處理聯集和資料表頂層類型,以免發生 無法使用 FIDL 語法改良專案的區塊所有寫入 「新增」語法都必須符合 RFC、 正式的 FIDL 文法也會更新以反映設計 RFC-0050 其餘的時間。
在某些情況下,現有的繫結會啟用頂層類型的 不需要大量列出要求和回應的表格和聯集 除了處理新的 JSON IR 格式之外,還其他功能。如果不是,例如 繫結中的編碼和解碼作業取決於 頂層型別是一個結構體,有兩種可能的方法:
- 第一種方法是先將所有資料表和聯集納入結構中, 編碼和解碼作業這可能很不吸引人,因為如要使用這項功能, 並為編碼和解碼新增了額外步驟。
- 另一種方法是修改編碼/解碼程式碼, 支援非結構體的輸入內容。目前已經有一些程式碼 假設輸入內容一律為結構 (例如 LLCPP 中的特徵只會針對結構體產生,以及要求和回應 Rust 中的編碼是透過元組 (而非結構體) 發生,但數字 才能實現這項假設 並掌握在兩者間做出的取捨 。這種方式除了方法呼叫以外, 因此,您就不需要包裝在永久性結構中的結構體類型 資料用途
JSON IR
https://fxbug.dev/42157011 中有
已將遷移作業進行中,以移動 "maybe_request"
和
"maybe_response"
欄位移出 JSON IR,因此只要
要求和回應類型只會在 FIDL 後端發生。這項作業已暫停
,但將會繼續執行,以便實作此 RFC。
目前,C++ 後端是唯一會使用
"maybe_request"
和 "maybe_response"
(但其他使用 JSON 的程式庫
FIDL 轉碼器等 IR 也需要更新。
安全性與隱私權
此 RFC 不會修改 FIDL 線格式,因此不會影響安全性 和隱私權
測試
這個 RFC 將會使用現有的基礎架構進行測試,包括單元測試、黃金級測試、 和整合測試 (例如 FIDL 相容性測試)。
說明文件
啟用這項功能後,應新增說明文件 (包括範例) 說明新功能
缺點、替代方案與不明
語法
此 RFC 中建議的語法會讓使用結構體做為 較為精簡的類型,因為需要明確指定。 其他選擇可能包括為常見用途介紹語法糖 (例如 保留結構體目前的語法,並針對 表格和聯集) 的特性,但無論在何種情況下,都較為明確 比降低詳細程度更重要
語法中另一個可能被視為無吸引力的部分
加在括號中:(struct { ... })
,這個問題也曾討論到
FTP-058。首先說的是保持一致風格:請保留大括號
可確保要求內類型之語法與
則為 FIDL 檔案中其他種類的型別。為避開 FTP-058 所採取的方法
將多餘的大括號替換成空格 (例如 MyMethod struct { ... } ->
union { ... };
),或許也適用。在 FIDL 文字中,這個函式
樣式與提案的其餘部分一致
與 Rechsia 殼層中所用的
FIDL 更是以 C-family/Go 為基礎的語法。
最後,我們建議的另一個替代方案是變更語法
用於指定類型,以便與方法參數語法對齊:結構體會是
方法是使用元組/記錄 (例如語法):type MyStruct = (foo Foo, bar
Bar);
。這樣一來,我們就可以在頂層
level 類型是藉由省略額外的括號 MyMethod(foo
Foo, bar Bar);
的結構。以下舉例說明,建議內容如下:
// Declare a struct with two fields foo, bar.
type SomeStruct = (foo Foo, bar Bar);
protocol MyProtocol {
// Declare a method with two request parameters.
// The two parameters are stored in a struct.
MyStructMethod(foo Foo, bar Bar);
// Declare a method with two optional parameters.
// The two parameters are stored in a table.
MyTableMethod table { 1: foo Foo, 2: bar Bar };
};
繫結
如設計中所述,在許多情況下,繫結無法壓平或 在產生的資料表和聯集產生的 API 中,內嵌頂層類型的成員 與 struct 的類似,以免引入 相容性限制適用於方法頂層類型成員時機的規則 內嵌生成式 AI 或不容易記住 需要依賴文件或產生的程式碼檢查 決定每個 FIDL 通訊協定方法的最終 API 為何。這個 會造成一些複雜情況,也就是 繫結 API 持續內嵌/簡化頂層類型成員。
理論上,您可以透過不分割要求的方式來提供一致的 API 但實際上這是不可行的 必須遷移所有依賴於這個 API 的使用者程式碼執行個體 ( 通常是與 FIDL 方法互動的使用者程式碼)。
既有藝術與參考資料
此 RFC 中建議的語法較接近 gRPC 中使用的語法,其中 方法 就是用單一 protobuf 訊息指定回應與回應。
ctiller@google.com 之前曾建議過與此 RFC 類似的想法,
允許使用序數語法 (例如 MyMethod(1: foo Foo; 2: bar Bar)
) 暗示
頂層類型是資料表而非結構體主要差別在於
此 RFC 支援頂層聯集以及表格和結構體,
因為聯集也會使用
有些奇蹟