///|
/// Parsing for component type section (section 7 in the component binary format).
///
/// For now, this only supports a small subset (primvaltype + basic functype)
/// to unblock end-to-end parsing and testing.
///|
#derive(Debug, Eq)
pub(all) enum PrimValType {
Bool
S8
U8
S16
U16
S32
U32
S64
U64
F32
F64
Char
String
ErrorContext
}
///|
#derive(Debug, Eq)
pub(all) enum ValType {
TypeIdx(Int32)
Prim(PrimValType)
}
///|
#derive(Debug, Eq)
pub(all) enum ResourceKind {
/// Resources defined within a component.
GuestDefined
/// Resources imported from the host environment.
HostDefined
}
///|
#derive(Debug, Eq)
pub(all) struct Param {
label : String
ty : ValType
}
///|
#derive(Debug, Eq)
pub(all) struct FuncType {
is_async : Bool
params : Array[Param]
result : ValType?
}
///|
/// A subset of instance type declarations (enough for wasmtime/component-spec).
#derive(Debug, Eq)
pub(all) enum InstanceDecl {
// Embedded core typedef (module type / func type / etc).
CoreType(Bytes)
Type(TypeDef)
// (alias outer ...) inside an instance type declaration.
// `kind` is the component model external kind discriminator (e.g. Type=0x03).
AliasOuter(Int32, Int32, Int32)
// (alias export ...) inside an instance type declaration.
// `kind` is the component model external kind discriminator.
AliasInstanceExport(Int32, Int32, String)
Export(String, Array[NameAttribute], ExternDesc)
}
///|
/// A subset of component type declarations (enough for WIT-encoded components).
#derive(Debug, Eq)
pub(all) enum ComponentDecl {
// Embedded core typedef (module type / func type / etc).
CoreType(Bytes)
Type(TypeDef)
// (alias outer ...) inside a component type declaration.
AliasOuter(Int32, Int32, Int32)
// (alias export ...) inside a component type declaration.
AliasInstanceExport(Int32, Int32, String)
Import(String, Array[NameAttribute], ExternDesc)
Export(String, Array[NameAttribute], ExternDesc)
}
///|
#derive(Debug, Eq)
pub(all) enum TypeDef {
DefValType(PrimValType)
FuncType(FuncType)
List(ValType)
FixedList(ValType, Int32)
MapType(ValType, ValType)
Record(Array[RecordField])
Variant(Array[VariantCase])
Tuple(Array[ValType])
Flags(Array[String])
Enum(Array[String])
Option(ValType)
Result(ValType?, ValType?)
Own(Int32)
Borrow(Int32)
Stream(ValType?)
Future(ValType?)
// Nominal resource type. `id` is filled at instantiation time.
// `rep` is the core wasm value type (e.g. i32=0x7f).
// `dtor` is an optional destructor core func index.
// `kind` tracks whether the resource originates from the component or host.
ResourceType(Int32, Int32, Int32?, ResourceKind)
ComponentType(Array[ComponentDecl])
InstanceTypeEmpty
InstanceType(Array[InstanceDecl])
}
///|
pub impl Show for TypeDef with fn output(self, logger) {
logger.write_string(@types.compact_show_repr(Repr(self).to_string()))
}
///|
#derive(Debug, Eq)
pub(all) struct RecordField {
label : String
ty : ValType
}
///|
#derive(Debug, Eq)
pub(all) struct VariantCase {
label : String
ty : ValType?
// `refines` points at a previously defined case index.
refines : Int32?
}
///|
///|
fn parse_prim_valtype(op : Int32) -> PrimValType? {
match op {
0x7f => Some(Bool)
0x7e => Some(S8)
0x7d => Some(U8)
0x7c => Some(S16)
0x7b => Some(U16)
0x7a => Some(S32)
0x79 => Some(U32)
0x78 => Some(S64)
0x77 => Some(U64)
0x76 => Some(F32)
0x75 => Some(F64)
0x74 => Some(Char)
0x73 => Some(String)
0x64 => Some(ErrorContext)
_ => None
}
}
///|
fn parse_valtype(
reader : Reader,
invalid~ : ComponentParseError = InvalidTypeSection,
) -> ValType raise ComponentParseError {
let value = reader.read_s33()
if 0L <= value && value <= 0x7FFF_FFFFL {
return TypeIdx(value.to_int())
}
if -64L <= value &&
value < 0L &&
parse_prim_valtype(value.to_int() & 0x7F) is Some(primitive) {
return Prim(primitive)
}
raise invalid
}
///|
fn parse_resultlist(reader : Reader) -> ValType? raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
Some(parse_valtype(reader))
} else if tag == 0x01 {
let must_be_zero = reader.read_u8()
if must_be_zero != 0x00 {
raise InvalidTypeSection
}
None
} else {
raise InvalidTypeSection
}
}
///|
fn parse_functype(
reader : Reader,
op : Int32,
) -> FuncType raise ComponentParseError {
let is_async = op == 0x43
let params : Array[Param] = reader.read_vector(r => {
label: r.read_name(),
ty: parse_valtype(r),
})
let result = parse_resultlist(reader)
{ is_async, params, result, }
}
///|
fn parse_alias_kind(reader : Reader) -> Int32 raise ComponentParseError {
// Alias kinds use the same encoding as Sort in the import/export sections:
// 0x00 for core items, or a single-byte tag for component items.
let tag = reader.read_u8()
if tag == 0x00 {
let core_sort = reader.read_u8()
// Encode core sorts in a separate range to avoid collisions with 0x01..0x05.
0x100 + core_sort
} else {
tag
}
}
///|
fn skip_limits(reader : Reader) -> Unit raise ComponentParseError {
let flags = reader.read_u8()
reader.read_leb_u32() |> ignore
if (flags & 0x01) != 0 {
reader.read_leb_u32() |> ignore
}
}
///|
fn skip_core_functype_inline(reader : Reader) -> Unit raise ComponentParseError {
let form = reader.read_u8()
if form != 0x60 {
raise InvalidTypeSection
}
skip_core_functype_body(reader)
}
///|
fn skip_core_functype_body(reader : Reader) -> Unit raise ComponentParseError {
let nparams = reader.read_count()
for _i in 0.. Unit raise ComponentParseError {
let opcode = reader.read_u8()
if opcode == 0x63 || opcode == 0x64 {
reader.read_s33() |> ignore
}
}
///|
fn skip_core_extern_type(
reader : Reader,
kind : Int32,
) -> Unit raise ComponentParseError {
match kind {
0x00 => reader.read_leb_u32() |> ignore // func type index
0x01 => {
skip_core_valtype(reader)
skip_limits(reader)
}
0x02 => skip_limits(reader)
0x03 => {
skip_core_valtype(reader)
reader.read_u8() |> ignore // mut
}
0x04 => {
if reader.read_u8() != 0x00 {
raise InvalidTypeSection
}
reader.read_leb_u32() |> ignore // tag type index
}
_ => raise InvalidTypeSection
}
}
///|
fn skip_core_moduletype(reader : Reader) -> Unit raise ComponentParseError {
let ndecls = reader.read_count()
for _i in 0.. {
reader.read_name() |> ignore // module
reader.read_name() |> ignore // name
let kind = reader.read_u8()
skip_core_extern_type(reader, kind)
}
0x01 => skip_core_functype_inline(reader)
0x02 => {
// alias decl: sort + target + indices
reader.read_u8() |> ignore
let target = reader.read_u8()
if target == 0x01 {
reader.read_leb_u32() |> ignore
reader.read_leb_u32() |> ignore
} else {
raise InvalidTypeSection
}
}
0x03 => {
reader.read_name() |> ignore // name
let kind = reader.read_u8()
skip_core_extern_type(reader, kind)
}
_ => raise InvalidTypeSection
}
}
}
///|
fn skip_core_typedef(reader : Reader) -> Unit raise ComponentParseError {
let op = reader.read_u8()
if op == 0x50 {
skip_core_moduletype(reader)
} else if op == 0x60 {
skip_core_functype_body(reader)
} else if op == 0x00 {
if reader.read_u8() != 0x50 {
raise InvalidTypeSection
}
let supertype_count = reader.read_count()
for _ in 0.. ignore
}
if reader.read_u8() != 0x60 {
raise InvalidTypeSection
}
skip_core_functype_body(reader)
} else {
raise InvalidTypeSection
}
}
///|
fn parse_type_declaration(
reader : Reader,
allow_import : Bool,
) -> ComponentDecl raise ComponentParseError {
match reader.read_u8() {
0 => {
let start = reader.pos
skip_core_typedef(reader)
CoreType(reader.data[start:reader.pos].to_owned())
}
1 => Type(parse_typedef(reader))
2 => {
let kind = parse_alias_kind(reader)
match reader.read_u8() {
0 =>
AliasInstanceExport(kind, reader.read_leb_u32(), reader.read_name())
2 => AliasOuter(kind, reader.read_leb_u32(), reader.read_leb_u32())
_ => raise InvalidTypeSection
}
}
3 if allow_import => {
let (name, attrs) = parse_annotated_name(
reader,
Reader::read_name,
InvalidTypeSection,
)
Import(name, attrs, parse_externdesc(reader))
}
4 => {
let (name, attrs) = parse_annotated_name(
reader,
Reader::read_name,
InvalidTypeSection,
)
Export(name, attrs, parse_externdesc(reader))
}
tag => raise UnsupportedTypeOpcode(tag)
}
}
///|
fn parse_instancedecl(
reader : Reader,
) -> InstanceDecl raise ComponentParseError {
match parse_type_declaration(reader, false) {
CoreType(bytes) => CoreType(bytes)
Type(ty) => Type(ty)
AliasOuter(kind, count, idx) => AliasOuter(kind, count, idx)
AliasInstanceExport(kind, idx, name) => AliasInstanceExport(kind, idx, name)
Export(name, attrs, desc) => Export(name, attrs, desc)
Import(_, _, _) => raise UnsupportedTypeOpcode(3)
}
}
///|
fn parse_typedef(reader : Reader) -> TypeDef raise ComponentParseError {
let op = reader.read_u8()
match op {
0x40 | 0x43 => FuncType(parse_functype(reader, op))
0x65 => Future(reader.read_optional(r => parse_valtype(r)))
0x66 => Stream(reader.read_optional(r => parse_valtype(r)))
0x68 => Borrow(reader.read_typeidx())
0x69 => Own(reader.read_typeidx())
0x6a =>
Result(
reader.read_optional(r => parse_valtype(r)),
reader.read_optional(r => parse_valtype(r)),
)
0x6b => Option(parse_valtype(reader))
0x6d => Enum(reader.read_vector(Reader::read_name))
0x6e => Flags(reader.read_vector(Reader::read_name))
0x6f => Tuple(reader.read_vector(r => parse_valtype(r)))
0x70 => List(parse_valtype(reader))
0x67 => {
let ty = parse_valtype(reader)
let len = reader.read_leb_u32()
FixedList(ty, len)
}
0x63 => MapType(parse_valtype(reader), parse_valtype(reader))
0x71 =>
Variant(
reader.read_vector(r => {
let label = r.read_name()
let ty = r.read_optional(item => parse_valtype(item))
let refines = r.read_optional(Reader::read_leb_u32)
{ label, ty, refines, }
}),
)
0x72 =>
Record(
reader.read_vector(r => { label: r.read_name(), ty: parse_valtype(r), }),
)
0x3f => {
let rep = reader.read_u8()
let dtor = reader.read_optional(Reader::read_leb_u32)
ResourceType(-1, rep, dtor, GuestDefined)
}
0x41 =>
ComponentType(reader.read_vector(r => parse_type_declaration(r, true)))
0x42 => {
let decls = reader.read_vector(parse_instancedecl)
if decls.is_empty() {
InstanceTypeEmpty
} else {
InstanceType(decls)
}
}
_ =>
match parse_prim_valtype(op) {
Some(p) => DefValType(p)
None => raise UnsupportedTypeOpcode(op)
}
}
}
///|
pub fn parse_type_section(
payload : Bytes,
) -> Array[TypeDef] raise ComponentParseError {
parse_vector_section(payload, parse_typedef, InvalidTypeSection)
}