///|
/// 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.
///|
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(Int)
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?
} derive(Debug, Eq)
///|
/// A subset of instance type declarations (enough for wasmtime/component-spec).
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(Int, Int, Int)
// (alias export ...) inside an instance type declaration.
// `kind` is the component model external kind discriminator.
AliasInstanceExport(Int, Int, String)
Export(String, Array[NameAttribute], ExternDesc)
} derive(Debug, Eq)
///|
/// A subset of component type declarations (enough for WIT-encoded components).
pub(all) enum ComponentDecl {
// Embedded core typedef (module type / func type / etc).
CoreType(Bytes)
Type(TypeDef)
// (alias outer ...) inside a component type declaration.
AliasOuter(Int, Int, Int)
// (alias export ...) inside a component type declaration.
AliasInstanceExport(Int, Int, 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, Int)
MapType(ValType, ValType)
Record(Array[RecordField])
Variant(Array[VariantCase])
Tuple(Array[ValType])
Flags(Array[String])
Enum(Array[String])
Option(ValType)
Result(ValType?, ValType?)
Own(Int)
Borrow(Int)
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(Int, Int, Int?, ResourceKind)
ComponentType(Array[ComponentDecl])
InstanceTypeEmpty
InstanceType(Array[InstanceDecl])
} derive(Debug, Eq)
///|
pub impl Show for TypeDef with fn output(self, logger) {
logger.write_string(@types.compact_show_repr(Repr(self).to_string()))
}
///|
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 : Int?
} derive(Debug, Eq)
///|
///|
fn Reader::read_label(self : Reader) -> String raise ComponentParseError {
let len = self.read_leb_u32()
let bytes = self.read_bytes(len)
decode_utf8_validated(bytes)
}
///|
fn parse_prim_valtype(op : Int) -> 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) -> ValType raise ComponentParseError {
let v = reader.read_sleb_i32()
if v >= 0 {
TypeIdx(v)
} else {
match parse_prim_valtype_from_sleb(v) {
Some(p) => Prim(p)
None => raise InvalidTypeSection
}
}
}
///|
fn parse_paramlist(reader : Reader) -> Array[Param] raise ComponentParseError {
let n = reader.read_leb_u32()
let params : Array[Param] = []
for _ in 0.. 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 : Int,
) -> FuncType raise ComponentParseError {
let is_async = op == 0x43
let params = parse_paramlist(reader)
let result = parse_resultlist(reader)
{ is_async, params, result, }
}
///|
fn parse_nameattributes_string(
reader : Reader,
) -> (String, Array[NameAttribute]) raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
let len = reader.read_leb_u32()
let bytes = reader.read_bytes(len)
(decode_utf8_validated(bytes), [])
} else if tag == 0x01 {
let len = reader.read_leb_u32()
let bytes = reader.read_bytes(len)
(decode_utf8_validated(bytes), [])
} else if tag == 0x02 {
let len = reader.read_leb_u32()
let bytes = reader.read_bytes(len)
(decode_utf8_validated(bytes), parse_attributes(reader))
} else {
raise InvalidTypeSection
}
}
///|
fn parse_typebound_for_instancedecl(
reader : Reader,
) -> TypeBound raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
Eq(reader.read_typeidx())
} else if tag == 0x01 {
SubResource
} else {
raise InvalidTypeSection
}
}
///|
fn parse_valuebound_for_instancedecl(
reader : Reader,
) -> ValueBound raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
Eq(reader.read_leb_u32())
} else if tag == 0x01 {
Type(parse_valtype(reader))
} else {
raise InvalidTypeSection
}
}
///|
fn parse_externdesc_for_instancedecl(
reader : Reader,
) -> ExternDesc raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
let must_be_core_module = reader.read_u8()
if must_be_core_module != 0x11 {
raise InvalidTypeSection
}
CoreModuleType(reader.read_leb_u32())
} else if tag == 0x01 {
FuncType(reader.read_typeidx())
} else if tag == 0x02 {
Value(parse_valuebound_for_instancedecl(reader))
} else if tag == 0x03 {
Type(parse_typebound_for_instancedecl(reader))
} else if tag == 0x04 {
ComponentType(reader.read_typeidx())
} else if tag == 0x05 {
InstanceType(reader.read_typeidx())
} else {
raise InvalidTypeSection
}
}
///|
fn parse_alias_kind(reader : Reader) -> Int 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_leb_u32()
for _i in 0.. ignore
}
let nresults = reader.read_leb_u32()
for _i in 0.. ignore
}
}
///|
fn skip_core_extern_type(
reader : Reader,
kind : Int,
) -> Unit raise ComponentParseError {
match kind {
0x00 => reader.read_leb_u32() |> ignore // func type index
0x01 => {
reader.read_u8() |> ignore // elem/ref type
skip_limits(reader)
}
0x02 => skip_limits(reader)
0x03 => {
reader.read_u8() |> ignore // valtype
reader.read_u8() |> ignore // mut
}
0x04 => reader.read_leb_u32() |> ignore // tag type index
_ => raise InvalidTypeSection
}
}
///|
fn skip_core_moduletype(reader : Reader) -> Unit raise ComponentParseError {
let ndecls = reader.read_leb_u32()
for _i in 0.. {
reader.read_label() |> ignore // module
reader.read_label() |> 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_label() |> 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_leb_u32()
for _ in 0.. ignore
}
if reader.read_u8() != 0x60 {
raise InvalidTypeSection
}
skip_core_functype_body(reader)
} else {
raise InvalidTypeSection
}
}
///|
fn bytes_sub(bytes : Bytes, start : Int, end : Int) -> Bytes {
let out : Array[Byte] = []
for i in start.. InstanceDecl raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
let start = reader.pos
skip_core_typedef(reader)
let end = reader.pos
CoreType(bytes_sub(reader.data, start, end))
} else if tag == 0x01 {
Type(parse_typedef(reader))
} else if tag == 0x02 {
// aliasdecl ::= 0x02 kind: target: ...
let kind = parse_alias_kind(reader)
let target = reader.read_u8()
if target == 0x00 {
// InstanceExport { instance_index, name }
let instance_idx = reader.read_leb_u32()
// Alias targets use the plain `name` string encoding (length + bytes),
// not the export-name string encoding used by import/export decls.
let name = reader.read_label()
AliasInstanceExport(kind, instance_idx, name)
} else if target == 0x02 {
// Outer { count, index }
let count = reader.read_leb_u32()
let idx = reader.read_leb_u32()
AliasOuter(kind, count, idx)
} else {
raise InvalidTypeSection
}
} else if tag == 0x04 {
let (name, attributes) = parse_nameattributes_string(reader)
let desc = parse_externdesc_for_instancedecl(reader)
Export(name, attributes, desc)
} else {
raise UnsupportedTypeOpcode(tag)
}
}
///|
fn parse_componentdecl(
reader : Reader,
) -> ComponentDecl raise ComponentParseError {
let tag = reader.read_u8()
if tag == 0x00 {
let start = reader.pos
skip_core_typedef(reader)
let end = reader.pos
CoreType(bytes_sub(reader.data, start, end))
} else if tag == 0x01 {
Type(parse_typedef(reader))
} else if tag == 0x02 {
// aliasdecl ::= 0x02 kind: target: ...
let kind = parse_alias_kind(reader)
let target = reader.read_u8()
if target == 0x00 {
// InstanceExport { instance_index, name }
let instance_idx = reader.read_leb_u32()
let name = reader.read_label()
AliasInstanceExport(kind, instance_idx, name)
} else if target == 0x02 {
// Outer { count, index }
let count = reader.read_leb_u32()
let idx = reader.read_leb_u32()
AliasOuter(kind, count, idx)
} else {
raise InvalidTypeSection
}
} else if tag == 0x03 {
let (name, attributes) = parse_nameattributes_string(reader)
let desc = parse_externdesc_for_instancedecl(reader)
Import(name, attributes, desc)
} else if tag == 0x04 {
let (name, attributes) = parse_nameattributes_string(reader)
let desc = parse_externdesc_for_instancedecl(reader)
Export(name, attributes, desc)
} else {
raise UnsupportedTypeOpcode(tag)
}
}
///|
fn parse_typedef(reader : Reader) -> TypeDef raise ComponentParseError {
let op = reader.read_u8()
match op {
0x40 | 0x43 => FuncType(parse_functype(reader, op))
0x65 => {
// futuretype ::= 0x65 t?:
let tag = reader.read_u8()
if tag == 0x00 {
Future(None)
} else if tag == 0x01 {
Future(Some(parse_valtype(reader)))
} else {
raise InvalidTypeSection
}
}
0x66 => {
// streamtype ::= 0x66 t?:
let tag = reader.read_u8()
if tag == 0x00 {
Stream(None)
} else if tag == 0x01 {
Stream(Some(parse_valtype(reader)))
} else {
raise InvalidTypeSection
}
}
0x68 => Borrow(reader.read_typeidx())
0x69 => Own(reader.read_typeidx())
0x6a => {
let has_ok = reader.read_u8()
let ok = if has_ok == 0x00 {
None
} else if has_ok == 0x01 {
Some(parse_valtype(reader))
} else {
raise InvalidTypeSection
}
let has_err = reader.read_u8()
let err = if has_err == 0x00 {
None
} else if has_err == 0x01 {
Some(parse_valtype(reader))
} else {
raise InvalidTypeSection
}
Result(ok, err)
}
0x6b => Option(parse_valtype(reader))
0x6d => {
let n = reader.read_leb_u32()
let cases : Array[String] = []
for _ in 0.. {
let n = reader.read_leb_u32()
let cases : Array[String] = []
for _ in 0.. {
let n = reader.read_leb_u32()
let tys : Array[ValType] = []
for _ in 0.. 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 => {
let n = reader.read_leb_u32()
let cases : Array[VariantCase] = []
for _ in 0.. {
let n = reader.read_leb_u32()
let fields : Array[RecordField] = []
for _ in 0.. {
// resourcetype ::= 0x3f rep: dtor:
let rep = reader.read_u8()
let has_dtor = reader.read_u8()
let dtor = if has_dtor == 0x00 {
None
} else if has_dtor == 0x01 {
Some(reader.read_leb_u32())
} else {
raise InvalidTypeSection
}
ResourceType(-1, rep, dtor, GuestDefined)
}
0x41 => {
// componenttype ::= 0x41 cd*:vec()
let n = reader.read_leb_u32()
let decls : Array[ComponentDecl] = []
for _ in 0.. {
// instancetype ::= 0x42 id*:vec()
let n = reader.read_leb_u32()
if n == 0 {
InstanceTypeEmpty
} else {
let decls : Array[InstanceDecl] = []
for _ in 0..
match parse_prim_valtype(op) {
Some(p) => DefValType(p)
None => raise UnsupportedTypeOpcode(op)
}
}
}
///|
pub fn parse_type_section(
payload : Bytes,
) -> Array[TypeDef] raise ComponentParseError {
let reader = Reader::Reader(payload)
let n = reader.read_leb_u32()
let types : Array[TypeDef] = []
for _ in 0..