///| Decoders for the index-bearing sections of a version-1 WebAssembly module.
///| They are intentionally separate from `parse_module`: a caller can choose a
///|
/// fast structural inspection or a complete metadata pass.
fn section_cursor(binary : Module, kind : SectionKind) -> Cursor? {
match binary.find_section(kind) {
None => None
Some(section) =>
Some(
Cursor::with_bounds(
binary.bytes,
section.payload.start,
section.payload.end,
),
)
}
}
///|
fn expect_finished(
cursor : Cursor,
kind : SectionKind,
) -> Result[Unit, DecodeError] {
if cursor.is_finished() {
Ok(())
} else {
Err(
InvalidSection(
kind.id(),
cursor.position,
"trailing bytes in decoded payload",
),
)
}
}
///|
fn read_value_type(cursor : Cursor) -> Result[ValueType, DecodeError] {
let position = cursor.position
match cursor.read_u8() {
Err(error) => Err(error)
Ok(byte) =>
match value_type_from_byte(byte) {
Some(value) => Ok(value)
None =>
Err(UnsupportedFeature("value type " + byte.to_string(), position))
}
}
}
///|
fn read_value_vector(cursor : Cursor) -> Result[Array[ValueType], DecodeError] {
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let values : Array[ValueType] = []
for _ in 0.. values.push(value)
Err(error) => return Err(error)
}
}
Ok(values)
}
///|
fn read_external_kind(cursor : Cursor) -> Result[ExternalKind, DecodeError] {
let position = cursor.position
match cursor.read_u8() {
Err(error) => Err(error)
Ok(0) => Ok(FunctionExternal)
Ok(1) => Ok(TableExternal)
Ok(2) => Ok(MemoryExternal)
Ok(3) => Ok(GlobalExternal)
Ok(4) => Ok(TagExternal)
Ok(value) =>
Err(
InvalidSection(
2,
position,
"unknown external kind " + value.to_string(),
),
)
}
}
///|
fn read_limits(cursor : Cursor) -> Result[Limits, DecodeError] {
let position = cursor.position
let flags = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
if flags < 0 || flags > 7 {
return Err(
UnsupportedFeature("limits flags " + flags.to_string(), position),
)
}
let minimum = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let maximum = if (flags & 1) != 0 {
match cursor.read_var_u32() {
Ok(value) => Some(value)
Err(error) => return Err(error)
}
} else {
None
}
match maximum {
Some(value) if value < minimum =>
Err(ValidationError(position, "limits maximum is less than minimum"))
_ =>
Ok({
minimum,
maximum,
shared: (flags & 2) != 0,
memory64: (flags & 4) != 0,
})
}
}
///|
fn skip_const_expr(cursor : Cursor) -> Result[Span, DecodeError] {
let start = cursor.position
let mut done = false
while !done {
let position = cursor.position
match cursor.read_u8() {
Err(error) => return Err(error)
Ok(0x0b) => done = true
Ok(0x41) =>
match cursor.read_var_i32() {
Ok(_) => ()
Err(error) => return Err(error)
}
Ok(0x42) => {
// i64.const is encoded as signed LEB128 and may need ten bytes.
let mut count = 0
let mut finished = false
while !finished {
if count == 10 {
return Err(InvalidLeb128(position, "i64 constant is too long"))
}
match cursor.read_u8() {
Err(error) => return Err(error)
Ok(byte) => {
count = count + 1
finished = (byte & 0x80) == 0
}
}
}
}
Ok(0x43) =>
match cursor.skip(4) {
Ok(_) => ()
Err(error) => return Err(error)
}
Ok(0x44) =>
match cursor.skip(8) {
Ok(_) => ()
Err(error) => return Err(error)
}
Ok(0x23) | Ok(0xd0) | Ok(0xd2) =>
match cursor.read_var_u32() {
Ok(_) => ()
Err(error) => return Err(error)
}
Ok(opcode) =>
return Err(
UnsupportedFeature(
"constant-expression opcode " + opcode.to_string(),
position,
),
)
}
}
Ok({ start, end: cursor.position })
}
///|
fn decode_type_section(
binary : Module,
) -> Result[Array[FunctionType], DecodeError] {
let cursor = match section_cursor(binary, Type) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let types : Array[FunctionType] = []
for _ in 0.. ()
Ok(value) =>
return Err(
UnsupportedFeature("type form " + value.to_string(), position),
)
Err(error) => return Err(error)
}
let params = match read_value_vector(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
let results = match read_value_vector(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
types.push({ params, results })
}
match expect_finished(cursor, Type) {
Ok(_) => Ok(types)
Err(error) => Err(error)
}
}
///|
fn decode_import_section(binary : Module) -> Result[Array[Import], DecodeError] {
let cursor = match section_cursor(binary, Import) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let imports : Array[Import] = []
for _ in 0.. value
Err(error) => return Err(error)
}
let name = match cursor.read_name() {
Ok(value) => value
Err(error) => return Err(error)
}
let kind = match read_external_kind(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
let type_index = match kind {
FunctionExternal | TagExternal =>
match cursor.read_var_u32() {
Ok(value) => Some(value)
Err(error) => return Err(error)
}
TableExternal => {
match read_value_type(cursor) {
Ok(_) => ()
Err(error) => return Err(error)
}
match read_limits(cursor) {
Ok(_) => ()
Err(error) => return Err(error)
}
None
}
MemoryExternal => {
match read_limits(cursor) {
Ok(_) => ()
Err(error) => return Err(error)
}
None
}
GlobalExternal => {
match read_value_type(cursor) {
Ok(_) => ()
Err(error) => return Err(error)
}
match cursor.read_u8() {
Ok(0) | Ok(1) => ()
Ok(value) =>
return Err(
ValidationError(
cursor.position - 1,
"invalid global mutability " + value.to_string(),
),
)
Err(error) => return Err(error)
}
None
}
}
imports.push({ module_name, name, kind, type_index })
}
match expect_finished(cursor, Import) {
Ok(_) => Ok(imports)
Err(error) => Err(error)
}
}
///|
fn decode_function_section(binary : Module) -> Result[Array[Int], DecodeError] {
let cursor = match section_cursor(binary, Function) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let types : Array[Int] = []
for _ in 0.. types.push(value)
Err(error) => return Err(error)
}
}
match expect_finished(cursor, Function) {
Ok(_) => Ok(types)
Err(error) => Err(error)
}
}
///|
fn decode_table_section(
binary : Module,
) -> Result[Array[TableInfo], DecodeError] {
let cursor = match section_cursor(binary, Table) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let tables : Array[TableInfo] = []
for _ in 0.. value
Err(error) => return Err(error)
}
match element_type {
FuncRef | ExternRef => ()
_ =>
return Err(
ValidationError(
cursor.position - 1,
"table element type must be a reference type",
),
)
}
let limits = match read_limits(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
tables.push({ element_type, limits })
}
match expect_finished(cursor, Table) {
Ok(_) => Ok(tables)
Err(error) => Err(error)
}
}
///|
fn decode_memory_section(
binary : Module,
) -> Result[Array[MemoryInfo], DecodeError] {
let cursor = match section_cursor(binary, Memory) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let memories : Array[MemoryInfo] = []
for _ in 0.. value
Err(error) => return Err(error)
}
if limits.shared && limits.maximum is None {
return Err(
ValidationError(cursor.position, "shared memories require a maximum"),
)
}
memories.push({ limits, })
}
match expect_finished(cursor, Memory) {
Ok(_) => Ok(memories)
Err(error) => Err(error)
}
}
///|
fn decode_global_section(
binary : Module,
) -> Result[Array[GlobalInfo], DecodeError] {
let cursor = match section_cursor(binary, Global) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let globals : Array[GlobalInfo] = []
for _ in 0.. value
Err(error) => return Err(error)
}
let mutable = match cursor.read_u8() {
Ok(0) => false
Ok(1) => true
Ok(value) =>
return Err(
ValidationError(
cursor.position - 1,
"invalid global mutability " + value.to_string(),
),
)
Err(error) => return Err(error)
}
let init_span = match skip_const_expr(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
globals.push({ value_type, mutable, init_span })
}
match expect_finished(cursor, Global) {
Ok(_) => Ok(globals)
Err(error) => Err(error)
}
}
///|
fn decode_export_section(binary : Module) -> Result[Array[Export], DecodeError] {
let cursor = match section_cursor(binary, Export) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let exports : Array[Export] = []
for _ in 0.. value
Err(error) => return Err(error)
}
let kind = match read_external_kind(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
let index = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
exports.push({ name, kind, index })
}
match expect_finished(cursor, Export) {
Ok(_) => Ok(exports)
Err(error) => Err(error)
}
}
///|
fn decode_start_section(binary : Module) -> Result[Int?, DecodeError] {
let cursor = match section_cursor(binary, Start) {
Some(value) => value
None => return Ok(None)
}
let index = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
match expect_finished(cursor, Start) {
Ok(_) => Ok(Some(index))
Err(error) => Err(error)
}
}
///|
fn decode_code_section(binary : Module) -> Result[Array[CodeBody], DecodeError] {
let cursor = match section_cursor(binary, Code) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let bodies : Array[CodeBody] = []
for _ in 0.. value
Err(error) => return Err(error)
}
let body = match cursor.subcursor(body_size) {
Ok(value) => value
Err(_) =>
return Err(
InvalidSection(
Code.id(),
cursor.position,
"function body extends past code section",
),
)
}
let group_count = match body.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let locals : Array[ValueType] = []
for _ in 0.. value
Err(error) => return Err(error)
}
let type_ = match read_value_type(body) {
Ok(value) => value
Err(error) => return Err(error)
}
for _ in 0.. ()
Err(error) => return Err(error)
}
bodies.push({
locals,
instruction_span: { start: instruction_start, end: body.position },
byte_length: body_size,
})
}
match expect_finished(cursor, Code) {
Ok(_) => Ok(bodies)
Err(error) => Err(error)
}
}
///|
fn decode_data_count_section(binary : Module) -> Result[Int?, DecodeError] {
let cursor = match section_cursor(binary, DataCount) {
Some(value) => value
None => return Ok(None)
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
match expect_finished(cursor, DataCount) {
Ok(_) => Ok(Some(count))
Err(error) => Err(error)
}
}
///|
fn decode_tag_section(binary : Module) -> Result[Array[TagInfo], DecodeError] {
let cursor = match section_cursor(binary, Tag) {
Some(value) => value
None => return Ok([])
}
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let tags : Array[TagInfo] = []
for _ in 0.. ()
Ok(attribute) =>
return Err(
UnsupportedFeature("tag attribute " + attribute.to_string(), position),
)
Err(error) => return Err(error)
}
let type_index = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
tags.push({ type_index, })
}
match expect_finished(cursor, Tag) {
Ok(_) => Ok(tags)
Err(error) => Err(error)
}
}
///|
pub fn decode_module(binary : Module) -> Result[DecodedModule, DecodeError] {
let types = match decode_type_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let imports = match decode_import_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let function_types = match decode_function_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let tables = match decode_table_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let memories = match decode_memory_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let globals = match decode_global_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let exports = match decode_export_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let start_function = match decode_start_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let element_segments = match decode_element_segments(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let code_bodies = match decode_code_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let data_segments = match decode_data_segments(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let data_count = match decode_data_count_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let tags = match decode_tag_section(binary) {
Ok(value) => value
Err(error) => return Err(error)
}
let warnings : Array[String] = []
if function_types.length() != code_bodies.length() {
warnings.push(
"function section has " +
function_types.length().to_string() +
" entries but code section has " +
code_bodies.length().to_string(),
)
}
Ok({
binary,
types,
imports,
function_types,
tables,
memories,
globals,
exports,
start_function,
code_bodies,
element_segments,
data_segments,
data_count,
tags,
warnings,
})
}