4 个版本 (2 个重大更新)
| 0.3.0 | 2024 年 3 月 13 日 |
|---|---|
| 0.2.0 | 2024 年 2 月 21 日 |
| 0.1.1 | 2024 年 1 月 20 日 |
| 0.1.0 | 2024 年 1 月 19 日 |
#613 在 WebAssembly 中
在 homestar-runtime 中使用
1MB
5.5K SLoC
大纲
描述
这个 wasm 库管理了 wasmtime 运行时,提供从/到 Wasm Interace Types (WIT) 解释器/翻译层的 IPLD 解释器,并实现了与 Ipvm 标准的 Wasm 任务一起工作的输入接口。
更多信息,请参阅我们的 Homestar 读取说明。
在 IPLD 和 WIT 之间进行解释
我们的递归解释器能够基于已知的 WIT 接口类型,双向翻译运行时的 IPLD 数据模型 和 WIT 值。
基本类型
我们将首先介绍 WIT 基本类型。
布尔值
本节概述了 IPLD 布尔值 (Ipld::Bool) 和 WIT bool 运行时值之间的转换过程。
-
IPLD 到 WIT 转换:
当一个 WIT 函数期望一个
bool输入时,一个Ipld::Bool值(无论是true还是false)被映射到一个boolWIT 运行时值。示例:考虑一个如下定义的 WIT 函数
export fn: func(a: bool) -> bool;给定此函数的 JSON 输入
{ "args": [true] }true转换为Ipld::Bool,然后翻译并作为布尔参数(bool)传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当 WIT 函数返回布尔值时,它可以翻译回
Ipld::Bool。
IPLD 架构定义:
type IPLDBooleanAsWit bool
整数
本节概述了 IPLD 整数值(Ipld::Integer)与 WIT integer 运行时值之间的转换过程。
ty ::= 'u8' | 'u16' | 'u32' | 'u64'
| 's8' | 's16' | 's32' | 's64'
-
IPLD 到 WIT 转换:
通常,当 WIT 函数期望整数输入时,一个
Ipld::Integer值会被映射到整数 WIT 运行时值。示例:考虑一个如下定义的 WIT 函数
export fn: func(a: s32) -> s32;给定此函数的 JSON 输入
{ "args": [1] }1转换为Ipld::Integer,然后翻译并作为整数参数(s32)传递给fn。注意:然而,如果 WIT 接口输入参数是
float类型,但传入的值是Ipld::Integer,则 IPLD 值将被强制转换为float,并在剩余的计算中保持该类型。这种转换是为了提供给 JavaScript 的便利,例如,数字1.0转换为1。 -
WIT 到 IPLD 的翻译:
相反,当 WIT 函数返回整数值(非浮点数)时,它可以转换回
Ipld::Integer。
IPLD 架构定义:
type IPLDIntegerAsWit union {
| U8 int
| U16 int
| U32 int
| U64 int
| S8 int
| S16 int
| S32 int
| S64 int
| Float32In int
| Float64In int
} representation kinded
type WitAsIpldInteger union {
| U8 int
| U16 int
| U32 int
| U64 int
| S8 int
| S16 int
| S32 int
| S64 int
| Float32Out float
| Float64Out float
} representation kinded
浮点数
本节概述了 IPLD 浮点值(Ipld::Float)与 WIT float 运行时值之间的转换过程。
组件模型 支持以下浮点类型。
ty ::= 'float32' | 'float64'
-
IPLD 到 WIT 转换:
通常,当 WIT 函数期望浮点数输入时,一个
Ipld::Float值会被映射到浮点 WIT 运行时值。如果需要,进行从f32到f64的类型转换。示例:考虑一个如下定义的 WIT 函数
export fn: func(a: f64) -> f64;给定此函数的 JSON 输入
{ "args": [1.0] }1.0转换为Ipld::Float,然后翻译并作为浮点参数(f64)传递给fn。注意:然而,如果 WIT 接口输入参数是 WIT 整数类型之一,但传入的值是
Ipld::Integer,则 IPLD 值将被强制转换为该整数类型,并在剩余的计算中保持该类型。 -
WIT 到 IPLD 的翻译:
相反,当 WIT 函数返回
float32或float64值时,它可以转换回Ipld::Float。注意:在将
float32转换为float64时,后者是 IPLD 的默认精度(见 IPLD),精度将会丢失。在这次转换中,解释器将使用十进制精度。
IPLD 架构定义:
type IPLDFloatAsWit union {
| Float32 float
| Float64 float
} representation kinded
type WitAsIpldFloat union {
| Float32 float
| Float64 float
} representation kinded
字符串
本节概述了 IPLD 字符串值(Ipld::String)与各种 WIT 运行时值之间的转换过程。一个 Ipld::String 值可以解释为一个 string、一个 char 或一个(没有有效负载的)enum 区分符。
-
string-
IPLD 到 WIT 转换
当 WIT 函数期望一个
string输入时,一个Ipld::String值会被映射为一个 WIT 运行时值中的string。示例:
export fn: func(a: string) -> string;给定此函数的 JSON 输入
{ "args": ["Saspirilla"] }"Saspirilla"被转换为Ipld::String,然后将其翻译并作为字符串参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当从 WIT 函数返回一个
string值时,它会转换回一个Ipld::String。
-
-
char-
IPLD 到 WIT 转换
当 WIT 函数期望一个
char输入时,一个Ipld::String值会被映射为一个 WIT 运行时值中的char。示例:
export fn: func(a: char) -> char;给定此函数的 JSON 输入
{ "args": ["S"] }"S"被转换为Ipld::String,然后将其翻译并作为 char 参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当从 WIT 函数返回一个 char 值时,它会转换回一个
Ipld::String。
-
-
enum:枚举语句定义了一个新的类型,其语义等价于一个没有有效负载类型的变体。
-
IPLD 到 WIT 转换
当 WIT 函数期望一个
enum输入时,一个Ipld::String值会被映射为一个 WIT 运行时值中的enum。示例:
enum color { Red, Green, Blue } export fn: func(a: color) -> string;给定此函数的 JSON 输入
{ "args": ["Green"] }"Green"被转换为Ipld::String,然后将其翻译并作为 enum 参数传递给fn(例如color)。您必须提供一个匹配其中一个区分符的字符串。 -
WIT 到 IPLD 的翻译:
相反,当从 WIT 函数返回一个 enum 值时,它可以转换回一个
Ipld::String值。
-
IPLD 架构定义:
type Enum enum {
| Red
| Green
| Blue
}
type IPLDStringAsWit union {
| Enum Enum
| String string
| Char string
| Listu8In string
} representation kinded
type WitAsIpldString union {
| Enum Enum
| String string
| Char string
| Listu8Out bytes
} representation kinded
字节
本节概述了 IPLD 字节值(Ipld::Bytes)与各种 WIT 运行时值之间的转换过程。一个 Ipld::Bytes 值可以解释为一个 list<u8> 或一个 string。
-
list:-
IPLD 到 WIT 转换
当WIT函数期望接收一个
list<u8>输入时,一个Ipld::Bytes值被映射为一个list<u8>WIT运行时值。示例:
export fn: func(a: list<u8>) -> list<u8>;给定此函数的 JSON 输入
{ "args": [{"/": {"bytes": "aGVsbDA"}}] }"aGVsbDA"被转换为一个Ipld::Bytes,然后被转换成字节并作为一个list<u8>参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当一个
list<u8>值从WIT函数返回时,如果列表包含有效的u8值,则将其转换回一个Ipld::Bytes值。
-
-
string-
IPLD 到 WIT 转换
当WIT函数期望接收一个
string输入时,一个Ipld::Bytes值被映射为一个stringWIT运行时值。示例:
export fn: func(a: string) -> string;给定此函数的 JSON 输入
{ "args": [{"/": {"bytes": "aGVsbDA"}}] }"aGVsbDA"被转换为一个Ipld::Bytes,然后被转换成字符串并作为一个string参数传递给fn。 -
WIT 到 IPLD 的翻译:
在这里,当一个字符串值从WIT函数返回时,它被转换为一个
Ipld::String值,因为我们无法确定它最初是bytes。.
-
IPLD 架构定义:
type IPLDBytesAsWit union {
| ListU8 bytes
| StringIn bytes
} representation kinded
type WitAsIpldBytes union {
| ListU8 bytes
| StringOut string
} representation kinded
空值
本节概述了IPLD空值(Ipld::Null)与各种WIT运行时值之间的转换过程。一个Ipld::Null值可以解释为string或option。
这里我们将仅涵盖字符串的情况,稍后回到option的情况。
-
IPLD 到 WIT 转换
当WIT函数期望接收一个
string输入时,一个Ipld::Null值被映射为一个"null"stringWIT运行时值。示例:
export fn: func(a: string) -> string;给定此函数的 JSON 输入
{ "args": [null] }null被转换为一个Ipld::Null,然后被转换并作为一个带有值"null"的string参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当一个值为
"null"的字符串从WIT函数返回时,它可以被转换为一个Ipld::Null值。
IPLD 架构定义:
type None unit representation null
type IPLDNullAsWit union {
| None
| String string
} representation kinded
type WitAsIpldNull union {
| None
| String string
} representation kinded
链接
本节概述了 IPLD 链接值(Ipld::Link)与 WIT 运行时字符串值之间的翻译过程。在 WIT 中,Ipld::Link 总是解释为字符串,反之亦然。
-
IPLD 到 WIT 转换
当 WIT 函数期望一个
string输入时,一个Ipld::Link值被映射为一个 WIT 运行时字符串值,根据链接是 Cidv0 或 Cidv1 进行相应的翻译。示例:
export fn: func(a: string) -> string;给定此函数的 JSON 输入
{ "args": ["bafybeia32q3oy6u47x624rmsmgrrlpn7ulruissmz5z2ap6alv7goe7h3q"] }"bafybeia32q3oy6u47x624rmsmgrrlpn7ulruissmz5z2ap6alv7goe7h3q"被转换为一个Ipld::Link,然后被翻译并作为字符串参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当从 WIT 函数返回一个字符串值,并且它可以转换为 Cid 时,它可以被翻译成一个
Ipld::Link值。
IPLD 架构定义:
type IPLDLinkAsWit &String link
type WitAsIpldLink &String link
非原始类型
接下来,我们将介绍更有趣的 WIT 非原始类型。
列表值
本节概述了 IPLD 列表值(Ipld::List)与各种 WIT 运行时值之间的翻译过程。一个 Ipld::List 值可以解释为一个列表、元组、一组 flags 或一个 result。
我们将在下面回到 result 的情况,并在这里介绍其他可能性。.
-
-
IPLD 到 WIT 转换
当 WIT 函数期望一个
list输入时,一个Ipld::List值被映射为一个 WIT 运行时列表值。示例:
export fn: func(a: list<s32>, b: s32) -> list<s32>;给定此函数的 JSON 输入
{ "args": [[1, 2, 3], 44] }[1, 2, 3]被转换为一个Ipld::List,然后被翻译并作为list<s32>参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当从 WIT 函数返回一个列表值时,它被翻译回一个
Ipld::List值。
-
-
元组:-
IPLD 到 WIT 转换
当 WIT 函数期望一个
tuple输入时,一个Ipld::List值被映射为一个 WIT 运行时元组值。示例:
type ipv6-socket-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>; export fn: func(a: ipv6-socket-address) -> tuple<u32, u32>;给定此函数的 JSON 输入
{ "args": [[8193, 3512, 34211, 0, 0, 35374, 880, 29492]] }[8193, 3512, 34211, 0, 0, 35374, 880, 29492]被转换为一个Ipld::List,然后翻译并作为tuple<u16, u16, u16, u16, u16, u16, u16>参数传递给fn。如果列表长度与元组接口类型中的字段数量不匹配,则解释器将抛出错误。
-
WIT 到 IPLD 的翻译:
相反,当 WIT 函数返回一个
tuple值时,它被翻译回一个Ipld::List值。
-
-
标志:flags代表一个带有每个位的名称的位集结构。类型表示一组命名布尔值。在命名类型的实例中,每个标志将要么为 true,要么为 false。-
IPLD 到 WIT 转换
当 WIT 函数期望一个
flags输入时,一个Ipld::List值被映射到flagsWIT 运行时值。当用作输入时,您可以将要打开/设置为 true 的标志作为字符串的子集设置。当用作输出时,您将获得一个字符串列表,表示设置为 true 的标志。
示例:
flags permissions { read, write, exec, } export fn: func(perm: permissions) -> bool;给定此函数的 JSON 输入
{ "args": [["read", "write"]] }[read, write]被转换为一个Ipld::List,然后翻译并作为permissions参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当 WIT 函数返回一个
flags值时,它被翻译回一个Ipld::List值。
-
IPLD 架构定义:
type IPLDListAsWit union {
| List [any]
| Tuple [any]
| Flags [string]
} representation kinded
type WitAsIpldList union {
| List [any]
| Tuple [any]
| Flags [string]
} representation kinded
映射
本节概述了 IPLD 映射值 (Ipld::Map) 与各种 WIT 运行时值 之间的翻译过程。一个 Ipld::Map 值可以解释为 record、variant 或两个元素 tuple 的列表之一。
-
记录:-
IPLD 到 WIT 转换
当 WIT 函数期望一个
record输入时,一个Ipld::Map值被映射到一个recordWIT 运行时值。示例:
record pair { x: u32, y: u32, } export fn: func(a: pair) -> u32;给定此函数的 JSON 输入
{ "args": [{"x": 1, "y": 2}] }{"x": 1, "y": 2}被转换为一个Ipld::Map,然后翻译并作为pair参数传递给fn。映射中的键必须与记录类型中的字段名称匹配.
-
WIT 到 IPLD 的翻译:
相反,当一个
record值从 WIT 函数返回时,它会转换回一个Ipld::Map值。
-
-
变体:变体语句定义了一个新的类型,其中类型的实例恰好匹配该类型列出的变体之一。这与代数数据类型中的“求和”类型类似(或者如果你熟悉 Rust,则类似于枚举)。变体也可以被视为带标签的联合体。
变体的每个情况都可以有一个可选的类型/有效负载与之关联,当值具有该特定情况标签时存在。
-
IPLD 到 WIT 转换
当 WIT 函数期望一个
variant输入时,一个Ipld::Map值会被映射到variantWIT 运行时值。示例:
variant filter { all, none, some(list<string>), } export fn: func(a: filter);给定此函数的 JSON 输入
{ "args": [{"some" : ["a", "b", "c"]}] }{"some" : ["a", "b", "c"]}转换成一个Ipld::Map,然后转换并作为filter参数传递给fn,其中键是变体名称,值是有效负载。映射中的键必须与变体类型中的变体名称匹配.
-
WIT 到 IPLD 的翻译:
相反,当一个
variant值从 WIT 函数返回时,它会转换回一个Ipld::Map值,其中标签是键,有效负载是值。
-
-
list:-
IPLD 到 WIT 转换
当 WIT 函数期望一个包含两个元素
tuples的嵌套list作为输入时,一个Ipld::Map值会被映射到那个特定的 WIT 运行时值。示例:
export fn: func(a: list<tuple<string, u32>>) -> list<u32>;给定此函数的 JSON 输入
{ "args": [{"a": 1, "b": 2}] }{"a": 1, "b": 2}转换成一个Ipld::Map,然后转换并作为list<tuple<string, u32>>参数传递给fn。 -
WIT 到 IPLD 的翻译:
相反,当从 WIT 函数返回一个包含两个元素
tuples的列表时,它可以转换回一个Ipld::Map值。
-
IPLD 架构定义:
type TupleAsMap {string:any} representation listpairs
type IPLDMapAsWit union {
| Record {string:any}
| Variant {string:any}
| List TupleAsMap
} representation kinded
type WitAsIpldMap union {
| Record {string:any}
| Variant {string:any}
| List TupleAsMap
} representation kinded
WIT 选项
本节概述了 WIT 选项运行时值(类型为 option)与各种 IPLD 值之间的转换过程。一个 WIT 选项 可以解释为 Ipld::Null 或任何其他 IPLD 值。
-
IPLD 到 WIT 转换
当WIT函数期望接收一个
option作为输入时,将一个Ipld::Null值映射为WIT选项的None/Unit情况。否则,任何其他IPLD值将直接映射为其对应的WIT运行时值。示例:
export fn: func(a: option<s32>) -> option<s32>;-
Some情况-
Json输入:
{ "args": [1] }
-
-
None情况-
Json输入:
{ "args": [null] }
-
1被转换为Ipld::Integer,然后将其转换为整数参数(s32),作为选项的Some情况传入fn。null被转换为Ipld::Null,然后将其转换为选项的None/Unit情况传入fn(即在WIT中没有值)。基本上,您可以将
Ipld::Any视为Some情况,将Ipld::Null视为None情况。 -
-
WIT 到 IPLD 的翻译:
相反,当WIT函数返回一个
option值时,如果它是None/Unit情况,则可以将其翻译回一个Ipld::Null值;如果是Some情况,则可以将其翻译回任何其他IPLD值。
IPLD 架构定义:
type IpldAsWitOption union {
| Some any
| None
} representation kinded
type WitAsIpldOption union {
| Some any
| None
} representation kinded
WIT结果
本节概述了WIT结果运行时值(类型为result)与各种IPLD值之间的翻译过程。我们将结果视为一个包含两个元素的Ipld::List的Left/Right either类型。
result可以解释为以下模式之一
-
Ok(带有有效载荷)-
IPLD 到 WIT 转换
当WIT函数期望接收一个
result作为输入时,一个Ipld::List值可以被映射为resultWIT运行时值的Ok情况,包括一个有效载荷。示例:
export fn: func(a: result<s32, string>) -> result<s32, string>;给定此函数的 JSON 输入
{ "args": [[47, null]] }[47, null]被转换为Ipld::List,然后将其翻译并作为带有有效载荷47的Ok情况传入fn,与左侧的s32类型匹配。 -
WIT 到 IPLD 的翻译:
相反,当从WIT函数返回一个
result值时,它可以被翻译回具有这种特定结构的Ipld::List。
-
-
Err(带有负载)-
IPLD 到 WIT 转换
示例:
export fn: func(a: result<s32, string>) -> result<s32, string>;给定此函数的 JSON 输入
{ "args": [[null, "error message"]] }[null, "错误消息"]被转换为一个Ipld::List,然后将其翻译并作为带有负载的"错误消息"的Err情况传递给result参数,该负载与右侧的字符串类型匹配。 -
WIT 到 IPLD 的翻译:
相反,当从WIT函数返回一个
result值时,它可以被翻译回具有这种特定结构的Ipld::List。
-
-
Ok情况(无负载)-
IPLD 到 WIT 转换
示例:
export fn: func(a: result<_, string>) -> result<_, string>;给定此函数的 JSON 输入
{ "args": [[47, null]] }[47, null]被转换为一个Ipld::List,然后将其翻译并作为Ok情况传递给result参数。由于不需要负载(在上述类型中表示为_),因此47未使用。 -
WIT 到 IPLD 的翻译:
在此,当此特定的
Ok情况从WIT函数返回时,它可以被转换回一个Ipld::List,但内部结构为[1, null],表示Ok(非错误)情况,并且丢弃了1负载。
-
-
Err情况(无负载)-
IPLD 到 WIT 转换
示例:
export fn: func(a: result<s32, _>) -> result<s32, _>;给定此函数的 JSON 输入
{ "args": [[null, "error message"]] }[null, "错误消息"]被转换为一个Ipld::List,然后将其翻译并作为Err情况传递给result参数。由于不需要负载(在上述类型中表示为_),因此"错误消息"未使用。 -
WIT 到 IPLD 的翻译:
在此,当此特定的
Err情况从WIT函数返回时,它可以被转换回一个Ipld::List,但内部结构为[null, 1],表示Err(错误)情况,并且丢弃了1负载。
-
IPLD 架构定义:
type Null unit representation null
type IpldAsWitResult union {
| Ok [any, Null]
| Err [Null, any]
} representation kinded
type WitAsIpldResult union {
| Ok [any, Null]
| OkNone [1, Null]
| Err [Null, any]
| ErrNone [Null, 1]
} representation kinded
注意:any用于表示任何非Null的类型。因此,给定一个具有result类型的输入,以下JSON值的
{
"args": [null, null]
}
将无法被转换为一个Wit Ipld::List运行时值,因为它在映射到哪个情况时是不确定的。
依赖项
~99MB
~2M SLoC