in wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt [79:401]
fun parseModule(): WasmModule {
if (b.readUInt32() != 0x6d736100u)
error("InvalidMagicNumber")
val version = b.readUInt32()
if (version != validVersion)
error("InvalidVersion(version.toLong(), listOf(validVersion.toLong()))")
var maxSectionId = 0
while (true) {
val sectionId = try {
b.readVarUInt7().toInt()
} catch (e: Throwable) { // Unexpected end
break
}
if (sectionId > 12) error("InvalidSectionId(sectionId)")
require(sectionId == 12 || maxSectionId == 12 || sectionId == 0 || sectionId > maxSectionId) {
"Section ID $sectionId came after $maxSectionId"
}
maxSectionId = maxOf(sectionId, maxSectionId)
val sectionLength = b.readVarUInt32AsInt()
b.limitSize(sectionLength, "Wasm section $sectionId of size $sectionLength") {
when (sectionId) {
// Skip custom section
0 -> b.readBytes(sectionLength)
// Type section
1 -> {
forEachVectorElement {
when (val type = readTypeDeclaration()) {
is WasmFunctionType ->
functionTypes += type
is WasmStructDeclaration ->
gcTypes += type
is WasmArrayDeclaration -> {}
}
}
}
// Import section
2 -> {
forEachVectorElement {
val importPair = WasmImportDescriptor(readString(), WasmSymbol(readString()))
when (val kind = b.readByte().toInt()) {
0 -> {
val index = b.readVarUInt32AsInt()
importedFunctions += WasmFunction.Imported(
name = "",
type = FunctionHeapType(index),
importPair = importPair,
).also { importsInOrder.add(it) }
}
// Table
1 -> {
val elementType = readRefType()
val limits = readLimits()
importedTables.add(WasmTable(limits, elementType, importPair).also { importsInOrder.add(it) })
}
2 -> {
val limits = readLimits()
importedMemories.add(WasmMemory(limits, importPair).also { importsInOrder.add(it) })
}
3 -> {
importedGlobals.add(
WasmGlobal(
name = "",
type = readValueType(),
isMutable = b.readVarUInt1(),
init = emptyList(),
importPair = importPair
).also { importsInOrder.add(it) }
)
}
4 -> {
val tag = readTag(importPair)
importedTags.add(tag)
importsInOrder.add(tag)
}
else -> error(
"Unsupported import kind $kind"
)
}
}
}
// Function section
3 -> {
forEachVectorElement {
val index = b.readVarUInt32AsInt()
val functionType = functionTypes[index]
definedFunctions.add(
WasmFunction.Defined(
"",
FunctionHeapType(index),
locals = functionType.parameterTypes.mapIndexed { index, wasmType ->
WasmLocal(index, "", wasmType, true)
}.toMutableList()
)
)
}
}
// Table section
4 -> {
forEachVectorElement {
val elementType = readRefType()
val limits = readLimits()
table.add(
WasmTable(limits, elementType)
)
}
}
// Memory section
5 -> {
forEachVectorElement {
val limits = readLimits()
memory.add(WasmMemory(limits))
}
}
// Tag section
13 -> {
forEachVectorElement {
tags.add(readTag())
}
}
// Globals section
6 -> {
forEachVectorElement {
val expr = mutableListOf<WasmInstr>()
globals.add(
WasmGlobal(
name = "",
type = readValueType(),
isMutable = b.readVarUInt1(),
init = expr
)
)
readExpression(expr)
}
}
// Export section
7 -> {
forEachVectorElement {
val name = readString()
val kind = b.readByte().toInt()
val index = b.readVarUInt32AsInt()
exports.add(
when (kind) {
0 -> WasmExport.Function(name, funByIdx(index))
1 -> WasmExport.Table(name, tableByIdx(index))
2 -> WasmExport.Memory(name, memoryByIdx(index))
3 -> WasmExport.Global(name, globalByIdx(index))
4 -> WasmExport.Tag(name, tagByIdx(index))
else -> error("Invalid export kind $kind")
}
)
}
}
// Start section
8 -> {
require(startFunction == null) { "Start function is already defined" }
startFunction = funByIdx(b.readVarUInt32AsInt())
}
// Element section
9 -> {
forEachVectorElement {
val firstByte = b.readUByte().toInt()
val mode: WasmElement.Mode = when (firstByte) {
0, 4 -> {
val offset = readExpression()
WasmElement.Mode.Active(tableByIdx(0), offset)
}
1, 5 ->
WasmElement.Mode.Passive
2, 6 -> {
val tableIdx = b.readVarUInt32()
val offset = readExpression()
WasmElement.Mode.Active(tableByIdx(tableIdx.toInt()), offset)
}
3, 7 ->
WasmElement.Mode.Declarative
else ->
error("Invalid element first byte $firstByte")
}
val type = if (firstByte < 5) {
if (firstByte in 1..3) {
val elemKind = b.readByte()
require(elemKind == 0.toByte())
}
WasmFuncRef
} else {
readValueType()
}
val values: List<WasmTable.Value> = mapVector {
if (firstByte < 4) {
WasmTable.Value.Function(funByIdx(b.readVarUInt32AsInt()))
} else {
val exprBody = mutableListOf<WasmInstr>()
readExpression(exprBody)
WasmTable.Value.Expression(exprBody)
}
}
elements += WasmElement(
type,
values,
mode,
)
}
}
// Code section
10 -> {
forEachVectorElement { functionId ->
val function = definedFunctions[functionId.toInt()]
val size = b.readVarUInt32AsInt()
b.limitSize(size, "function body size") {
mapVector {
val count = b.readVarUInt32AsInt()
val valueType = readValueType()
val firstLocalId =
function.locals.lastOrNull()?.id?.plus(1) ?: 0
repeat(count) { thisIdx ->
function.locals.add(
WasmLocal(
firstLocalId + thisIdx,
"",
valueType,
false
)
)
}
}
readExpression(function.instructions, function.locals)
}
}
}
// Data section
11 -> {
forEachVectorElement {
val mode = when (val firstByte = b.readByte().toInt()) {
0 -> WasmDataMode.Active(0, readExpression())
1 -> WasmDataMode.Passive
2 -> WasmDataMode.Active(b.readVarUInt32AsInt(), readExpression())
else -> error("Unsupported data mode $firstByte")
}
val size = b.readVarUInt32AsInt()
val bytes = b.readBytes(size)
data += WasmData(mode, bytes)
}
}
// Data count section
12 -> {
b.readVarUInt32() // Data count
dataCount = true
}
}
}
}
val definedDeclarations = BinaryToIrResolver()
functionTypes.forEach { type ->
definedDeclarations.functionTypes.add(type)
}
gcTypes.forEach { type ->
definedDeclarations.gcTypes.add(type)
}
importedFunctions.forEach { function ->
definedDeclarations.functions.add(function)
}
definedFunctions.forEach { function ->
definedDeclarations.functions.add(function)
}
importedGlobals.forEach { global ->
definedDeclarations.globalFields.add(global)
}
globals.forEach { global ->
definedDeclarations.globalFields.add(global)
}
return WasmModule(
resolver = definedDeclarations,
recGroups = (functionTypes + gcTypes).map { listOf(it) },
importsInOrder = importsInOrder,
importedFunctions = importedFunctions,
importedMemories = importedMemories,
importedTables = importedTables,
importedGlobals = importedGlobals,
importedTags = importedTags,
definedFunctions = definedFunctions,
tables = table,
memories = memory,
globals = globals,
exports = exports,
startFunction = startFunction,
elements = elements,
data = data,
dataCount = dataCount,
tags = tags
).also {
it.calculateIds()
}
}