Repository: tetratelabs/wazero
Pull request #162 (2022-01-24 05:02:23)

wat: begins type consolidation with wasm.X
This reduces the amount of conversion work by using wasm.X types when
parsing the text format. A follow-up change is needed to complete this
as remaining sections imply index resolution.

Before:
```
BenchmarkCodecExample/text.DecodeModule
BenchmarkCodecExample/text.DecodeModule-16           	   79468	     15224 ns/op	    7456 B/op	     331 allocs/op
```

After:
```
BenchmarkCodecExample/text.DecodeModule
BenchmarkCodecExample/text.DecodeModule-16           	   76357	     14908 ns/op	    7328 B/op	     323 allocs/op
```

codefromthecrypt: actually, I can add name section parsing into this PR

Patch:
diff --git a/wasm/module.go b/wasm/module.go
index aa9f3cca..3f54a0bc 100644
--- a/wasm/module.go
+++ b/wasm/module.go
@@ -22,8 +22,12 @@ type EncodeModule func(m *Module) (bytes []byte)
 type Module struct {
 	// TypeSection contains the unique FunctionType of functions imported or defined in this module.
 	//
-	// See https://www.w3.org/TR/wasm-core-1/#type-section%E2%91%A0
+	// Note: Currently, there is no type ambiguity in the index as WebAssembly 1.0 only defines function type.
+	// In the future, other types may be introduced to support features such as module linking.
+	//
+	// See https://www.w3.org/TR/wasm-core-1/#types%E2%91%A0%E2%91%A0
 	TypeSection []*FunctionType
+
 	// ImportSection contains imported functions, tables, memories or globals required for instantiation
 	// (Store.Instantiate).
 	//
@@ -122,8 +126,18 @@ type Module struct {
 // See https://www.w3.org/TR/wasm-core-1/#binary-index
 type Index = uint32
 
+// FunctionType is a possibly empty function signature.
+//
+// See https://www.w3.org/TR/wasm-core-1/#function-types%E2%91%A0
 type FunctionType struct {
-	Params, Results []ValueType
+	// Params are the possibly empty sequence of value types accepted by a function with this signature.
+	Params []ValueType
+
+	// Results are the possibly empty sequence of value types returned by a function with this signature.
+	//
+	// Note: In WebAssembly 1.0 (MVP), there can be at most one result.
+	// See https://www.w3.org/TR/wasm-core-1/#result-types%E2%91%A0
+	Results []ValueType
 }
 
 // ValueType is the binary encoding of a type such as i32
diff --git a/wasm/text/bind.go b/wasm/text/bind.go
index d9c30935..8d90e027 100644
--- a/wasm/text/bind.go
+++ b/wasm/text/bind.go
@@ -7,30 +7,25 @@ import (
 )
 
 // bindIndices ensures any indices point are numeric or returns a FormatError if they cannot be bound.
-func bindIndices(m *module) error {
-	typeToIndex := map[*typeFunc]wasm.Index{}
-	typeNameToIndex := map[string]wasm.Index{}
+func bindIndices(m *module, typeNameToIndex, funcNameToIndex map[string]wasm.Index) error {
+	typeToIndex := map[*wasm.FunctionType]wasm.Index{}
 	for i, t := range m.types {
 		ui := wasm.Index(i)
-		if t.name != "" {
-			typeNameToIndex[t.name] = ui
-		}
 		typeToIndex[t] = ui
 	}
 
-	funcNameToIndex, err := bindFunctionTypes(m, typeToIndex, typeNameToIndex)
-	if err != nil {
+	if err := bindFunctionTypes(m, typeNameToIndex, typeToIndex); err != nil {
 		return err
 	}
 
-	indexCount := uint32(len(m.importFuncs) + len(m.funcs))
+	indexCount := uint32(len(m.importFuncs) + len(m.code))
 
-	if err = bindExportFuncs(m, indexCount, funcNameToIndex); err != nil {
+	if err := bindExportFuncs(m, funcNameToIndex, indexCount); err != nil {
 		return err
 	}
 
 	if m.startFunction != nil {
-		err = bindIndex(indexCount, funcNameToIndex, m.startFunction, "module.start", -1)
+		err := bindIndex(indexCount, funcNameToIndex, m.startFunction, "module.start", -1)
 		if err != nil {
 			return err
 		}
@@ -44,12 +39,7 @@ func bindIndices(m *module) error {
 // Failure cases are when a symbolic identifier points nowhere or a numeric index is out of range.
 // Ex. (import "Math" "PI" (func (type $t0))) exists, but (type $t0 (func ...)) does not.
 //  or (import "Math" "PI" (func (type 32))) exists, but there are only 10 types.
-func bindFunctionTypes(m *module, typeToIndex map[*typeFunc]uint32, typeNameToIndex map[string]uint32) (map[string]uint32, error) {
-	funcNameToIndex := map[string]wasm.Index{}
-	for _, na := range m.funcNames {
-		funcNameToIndex[na.Name] = na.Index
-	}
-
+func bindFunctionTypes(m *module, typeNameToIndex map[string]wasm.Index, typeToIndex map[*wasm.FunctionType]wasm.Index) error {
 	typeCount := uint32(len(m.types))
 	importCount := uint32(len(m.importFuncs))
 
@@ -72,26 +62,26 @@ func bindFunctionTypes(m *module, typeToIndex map[*typeFunc]uint32, typeNameToIn
 
 		err := bindIndex(typeCount, typeNameToIndex, idx, context, int64(i))
 		if err != nil {
-			return nil, err
+			return err
 		}
 
 		// If there's an inlined type now, it must contain the same signature as the index, and may contain names.
 		if tu.typeInlined != nil {
 			realType := m.types[idx.numeric]
 			ti := tu.typeInlined
-			if !funcTypeEquals(realType, ti.typeFunc.params, ti.typeFunc.result) {
-				return nil, &FormatError{ti.line, ti.col, fmt.Sprintf(context, i),
+			if !funcTypeEquals(realType, ti.typeFunc.Params, ti.typeFunc.Results) {
+				return &FormatError{ti.line, ti.col, fmt.Sprintf(context, i),
 					fmt.Errorf("inlined type doesn't match type index %d", idx.numeric),
 				}
 			}
 		}
 	}
-	return funcNameToIndex, nil
+	return nil
 }
 
 // bindExportFuncs ensures all module.exportFuncs point to valid numeric indices or returns a FormatError if one
 // cannot be bound.
-func bindExportFuncs(m *module, indexCount uint32, funcNameToIndex map[string]uint32) (err error) {
+func bindExportFuncs(m *module, funcNameToIndex map[string]wasm.Index, indexCount uint32) (err error) {
 	for _, e := range m.exportFuncs {
 		err = bindIndex(indexCount, funcNameToIndex, e.funcIndex, "module.exports[%d].func", int64(e.exportIndex))
 		if err != nil {
@@ -140,3 +130,47 @@ func checkIndexInRange(idx *index, count uint32, context string, contextArg0 int
 	}
 	return nil
 }
+
+// mergeLocalNames produces wasm.NameSection LocalNames. This has to be done post-parse as types can be defined after
+// functions that use them.
+func mergeLocalNames(m *module, typeParamNames map[wasm.Index]wasm.NameMap) wasm.IndirectNameMap {
+	paramNames := m.names.LocalNames
+	j, jLen := 0, len(paramNames)
+	if typeParamNames == nil && jLen == 0 {
+		return nil
+	}
+
+	// Parameters can be named on the type, and overridden via a function. This loop collects the final name for each
+	// function's parameters regardless of if it is an imported function or module defined.
+	var result wasm.IndirectNameMap
+	funcIndexSize := uint32(len(m.typeUses))
+	for i := uint32(0); i < funcIndexSize; i++ {
+		// Seek to see if we have any function-defined parameter names
+		var inlinedNames wasm.NameMap
+		for ; j < jLen; j++ {
+			next := paramNames[j]
+			if next.Index > i {
+				break // we have parameter names, but starting at a later index
+			} else if next.Index == i {
+				inlinedNames = next.NameMap
+				break
+			}
+		}
+
+		// Use any inlined names or default to any on the type
+		typeNames, hasType := typeParamNames[m.typeUses[i].typeIndex.numeric]
+		var localNames wasm.NameMap
+		if inlinedNames == nil && !hasType {
+			continue
+		} else if inlinedNames == nil {
+			localNames = typeNames
+		} else {
+			// On conflict, choose the function names, as merge rules aren't defined in the specification. If there are
+			// names on the function, the user added them. They may not intend to inherit names they didn't define!
+			localNames = inlinedNames
+		}
+		result = append(result, &wasm.NameMapAssoc{Index: i, NameMap: localNames})
+	}
+	// TODO: function local names
+	return result
+}
diff --git a/wasm/text/decoder.go b/wasm/text/decoder.go
index 3af33c7b..bb0cce40 100644
--- a/wasm/text/decoder.go
+++ b/wasm/text/decoder.go
@@ -15,20 +15,7 @@ func DecodeModule(source []byte) (result *wasm.Module, err error) {
 	}
 
 	result = &wasm.Module{}
-
-	// Next, we need to convert the types from the text format into the binary one. This is easy because the only
-	// difference is that the text format has type names and the binary format does not.
-	typeCount := len(m.types)
-	if typeCount > 0 {
-		result.TypeSection = make([]*wasm.FunctionType, typeCount)
-		for i, t := range m.types {
-			var results []wasm.ValueType
-			if t.result != 0 {
-				results = []wasm.ValueType{t.result}
-			}
-			result.TypeSection[i] = &wasm.FunctionType{Params: t.params, Results: results}
-		}
-	}
+	result.TypeSection = m.types
 
 	// Now, handle any imported functions. Notably, we retain the same insertion order as defined in the text format in
 	// case a numeric index is used for the start function (or another reason such as the call instruction).
@@ -44,18 +31,14 @@ func DecodeModule(source []byte) (result *wasm.Module, err error) {
 		}
 	}
 
-	// Next, split the function into the Function and CodeSection
-	funcCount := len(m.funcs)
-	if funcCount > 0 {
+	// Add the type use of the function into the function section.
+	funcCount := len(m.code)
+	if funcCount != 0 {
 		result.FunctionSection = make([]wasm.Index, funcCount)
-		result.CodeSection = make([]*wasm.Code, funcCount)
-		for i, f := range m.funcs {
-			result.FunctionSection[i] = m.typeUses[importFuncCount+i].typeIndex.numeric
-			result.CodeSection[i] = &wasm.Code{
-				LocalTypes: nil, // TODO: locals
-				Body:       f.body,
-			}
+		for i := 0; i < funcCount; i++ {
+			result.FunctionSection[i] = m.typeUses[i+importFuncCount].typeIndex.numeric
 		}
+		result.CodeSection = m.code
 	}
 
 	// Now, handle any exported functions. Notably, we retain the same insertion order as defined in the text format.
@@ -76,55 +59,6 @@ func DecodeModule(source []byte) (result *wasm.Module, err error) {
 	if m.startFunction != nil {
 		result.StartSection = &m.startFunction.numeric
 	}
-
-	// Don't set the name section unless we found a name!
-	if localNames := mergeLocalNames(m); localNames != nil || m.name != "" || m.funcNames != nil {
-		result.NameSection = &wasm.NameSection{
-			ModuleName:    m.name,
-			FunctionNames: m.funcNames,
-			LocalNames:    localNames,
-		}
-	}
+	result.NameSection = m.names
 	return
 }
-
-// mergeLocalNames produces wasm.NameSection LocalNames. This has to be done post-parse as types can be defined after
-// functions that use them.
-func mergeLocalNames(m *module) wasm.IndirectNameMap {
-	j, jLen := 0, len(m.paramNames)
-	if m.typeParamNames == nil && jLen == 0 {
-		return nil
-	}
-
-	// Parameters can be named on the type, and overridden via a function. This loop collects the final name for each
-	// function's parameters regardless of if it is an imported function or module defined.
-	var result wasm.IndirectNameMap
-	funcIndexSize := uint32(len(m.typeUses))
-	for i := uint32(0); i < funcIndexSize; i++ {
-		// seek to see if we have any function-defined parameter names
-		var inlinedNames wasm.NameMap
-		for ; j < jLen; j++ {
-			next := m.paramNames[j]
-			if next.Index > i {
-				break // we have parameter names, but starting at a later index
-			} else if next.Index == i {
-				inlinedNames = next.NameMap
-				break
-			}
-		}
-		typeNames, hasType := m.typeParamNames[m.typeUses[i].typeIndex.numeric]
-		var localNames wasm.NameMap
-		if inlinedNames == nil && !hasType {
-			continue
-		} else if inlinedNames == nil {
-			localNames = typeNames
-		} else {
-			// On conflict, choose the function names, as merge rules aren't defined in the specification. If there are
-			// names on the function, the user added them. They may not intend to inherit names they didn't define!
-			localNames = inlinedNames
-		}
-		result = append(result, &wasm.NameMapAssoc{Index: i, NameMap: localNames})
-	}
-	// TODO: function local names
-	return result
-}
diff --git a/wasm/text/parser.go b/wasm/text/parser.go
index a2bf7911..41402987 100644
--- a/wasm/text/parser.go
+++ b/wasm/text/parser.go
@@ -82,6 +82,21 @@ type moduleParser struct {
 	typeParser  *typeParser
 	indexParser *indexParser
 	funcParser  *funcParser
+
+	// typeNameToIndex is only used for resolving symbolic index names to numeric ones.
+	//
+	// Note: This is not encoded in the wasm.NameSection as there is no type name section in WebAssembly 1.0 (MVP)
+	typeNameToIndex map[string]wasm.Index
+
+	// funcNameToIndex is only used for resolving symbolic index names to numeric ones. It contains any imported or
+	// module-defined function names and points to the function index which is preceded by imports.
+	//
+	// Note: this should be updated alongside module.names FunctionNames
+	funcNameToIndex map[string]wasm.Index
+
+	// typeParamNames are nil when no types had named (param) fields.
+	// Note: This is a map not a wasm.IndirectNameMap as late lookup is needed for after parsing
+	typeParamNames map[wasm.Index]wasm.NameMap
 }
 
 // parse has the same signature as tokenParser and called by lex on each token.
@@ -99,7 +114,7 @@ func (p *moduleParser) parse(tok tokenType, tokenBytes []byte, line, col uint32)
 // * module is the result of parsing or nil on error
 // * err is a FormatError invoking the parser, dangling block comments or unexpected characters.
 func parseModule(source []byte) (*module, error) {
-	p := moduleParser{source: source, module: &module{}, indexParser: &indexParser{}}
+	p := moduleParser{source: source, module: &module{names: &wasm.NameSection{}}, indexParser: &indexParser{}}
 	p.typeParser = &typeParser{m: &p}
 	p.funcParser = &funcParser{m: &p, onBodyEnd: p.parseFuncEnd}
 
@@ -115,10 +130,17 @@ func parseModule(source []byte) (*module, error) {
 	p.module.types = append(p.module.types, p.typeParser.inlinedTypes...)
 
 	// Ensure indices only point to numeric values
-	if err = bindIndices(p.module); err != nil {
+	if err = bindIndices(p.module, p.typeNameToIndex, p.funcNameToIndex); err != nil {
 		return nil, err
 	}
 
+	// Don't set the name section unless we found a name!
+	names := p.module.names
+	names.LocalNames = mergeLocalNames(p.module, p.typeParamNames)
+	if names.ModuleName == "" && names.FunctionNames == nil && names.LocalNames == nil {
+		p.module.names = nil
+	}
+
 	return p.module, nil
 }
 
@@ -162,7 +184,7 @@ func (p *moduleParser) beginField(tok tokenType, fieldName []byte, _, _ uint32)
 			p.tokenParser = p.parseImportModule
 		case "func":
 			p.currentField = fieldModuleFunc
-			p.module.funcs = append(p.module.funcs, &function{})
+			p.module.code = append(p.module.code, &wasm.Code{})
 			p.tokenParser = p.parseFuncName
 		case "export":
 			p.currentField = fieldModuleExport
@@ -183,9 +205,8 @@ func (p *moduleParser) beginField(tok tokenType, fieldName []byte, _, _ uint32)
 		// Add the next import func object and ready for parsing it.
 		if string(fieldName) == "func" {
 			p.module.importFuncs = append(p.module.importFuncs, &importFunc{
-				module:      string(p.currentValue0),
-				name:        string(p.currentValue1),
-				importIndex: p.currentFuncIndex,
+				module: string(p.currentValue0),
+				name:   string(p.currentValue1),
 			})
 
 			p.currentField = fieldModuleImportFunc
@@ -237,7 +258,7 @@ func (p *moduleParser) endField() {
 //   calls parseModule here --^
 func (p *moduleParser) parseModuleName(tok tokenType, tokenBytes []byte, line, col uint32) error {
 	if tok == tokenID { // Ex. $Math
-		p.module.name = string(stripDollar(tokenBytes))
+		p.module.names.ModuleName = string(stripDollar(tokenBytes))
 		p.tokenParser = p.parseModule
 		return nil
 	}
@@ -334,15 +355,26 @@ func (p *moduleParser) parseTypeFunc(tok tokenType, tokenBytes []byte, line, col
 // the token is tokenRParen and sets the next parser to parseType on tokenRParen.
 func (p *moduleParser) parseTypeFuncEnd(tok tokenType, tokenBytes []byte, _, _ uint32) error {
 	if tok == tokenRParen {
-		sig, localNames := p.typeParser.getType(string(p.currentValue0))
+		sig, localNames := p.typeParser.getType()
+		idx := wasm.Index(len(p.module.types))
+
+		typeName := string(p.currentValue0)
+		if typeName != "" {
+			if p.typeNameToIndex == nil {
+				p.typeNameToIndex = map[string]wasm.Index{typeName: idx}
+			} else {
+				p.typeNameToIndex[typeName] = idx
+			}
+		}
+
 		if localNames != nil {
-			idx := wasm.Index(len(p.module.types))
-			if p.module.typeParamNames == nil {
-				p.module.typeParamNames = map[wasm.Index]wasm.NameMap{idx: localNames}
+			if p.typeParamNames == nil {
+				p.typeParamNames = map[wasm.Index]wasm.NameMap{idx: localNames}
 			} else {
-				p.module.typeParamNames[idx] = localNames
+				p.typeParamNames[idx] = localNames
 			}
 		}
+
 		p.module.types = append(p.module.types, sig)
 		p.currentValue0 = nil
 		p.currentField = fieldModuleType
@@ -446,14 +478,24 @@ func (p *moduleParser) parseImport(tok tokenType, tokenBytes []byte, _, _ uint32
 //                    calls parseImportFunc here --^
 func (p *moduleParser) parseImportFuncName(tok tokenType, tokenBytes []byte, line, col uint32) error {
 	if tok == tokenID { // Ex. $main
-		na := &wasm.NameAssoc{Index: p.currentFuncIndex, Name: string(stripDollar(tokenBytes))}
-		p.module.funcNames = append(p.module.funcNames, na)
+		p.addFuncName(tokenBytes)
 		p.tokenParser = p.parseImportFunc
 		return nil
 	}
 	return p.parseImportFunc(tok, tokenBytes, line, col)
 }
 
+// addFuncName adds the normalized ('$' stripped) function name to the parser cache and the wasm.NameSection.
+func (p *moduleParser) addFuncName(idToken []byte) {
+	na := &wasm.NameAssoc{Index: p.currentFuncIndex, Name: string(stripDollar(idToken))}
+	p.module.names.FunctionNames = append(p.module.names.FunctionNames, na)
+	if p.funcNameToIndex == nil {
+		p.funcNameToIndex = map[string]wasm.Index{na.Name: na.Index}
+	} else {
+		p.funcNameToIndex[na.Name] = na.Index
+	}
+}
+
 // parseImportFunc is the second parser inside the imported function field. This passes control to the typeParser until
 // any signature is read, then sets the next parser to parseImportFuncEnd.
 //
@@ -482,8 +524,8 @@ func (p *moduleParser) parseImportFuncEnd(tok tokenType, tokenBytes []byte, _, _
 		tu, paramNames := p.typeParser.getTypeUse()
 		p.module.typeUses = append(p.module.typeUses, tu)
 		if paramNames != nil {
-			p.module.paramNames =
-				append(p.module.paramNames, &wasm.NameMapAssoc{Index: p.currentFuncIndex, NameMap: paramNames})
+			p.module.names.LocalNames =
+				append(p.module.names.LocalNames, &wasm.NameMapAssoc{Index: p.currentFuncIndex, NameMap: paramNames})
 		}
 		p.currentField = fieldModuleImport
 		p.tokenParser = p.parseImport
@@ -503,8 +545,7 @@ func (p *moduleParser) parseImportFuncEnd(tok tokenType, tokenBytes []byte, _, _
 //              calls parseFunc here --^
 func (p *moduleParser) parseFuncName(tok tokenType, tokenBytes []byte, line, col uint32) error {
 	if tok == tokenID { // Ex. $main
-		na := &wasm.NameAssoc{Index: p.currentFuncIndex, Name: string(stripDollar(tokenBytes))}
-		p.module.funcNames = append(p.module.funcNames, na)
+		p.addFuncName(tokenBytes)
 		p.tokenParser = p.parseFunc
 		return nil
 	}
@@ -552,11 +593,12 @@ func (p *moduleParser) parseFuncEnd(tok tokenType, tokenBytes []byte, _, _ uint3
 		tu, paramNames := p.typeParser.getTypeUse()
 		p.module.typeUses = append(p.module.typeUses, tu)
 		if paramNames != nil {
-			p.module.paramNames =
-				append(p.module.paramNames, &wasm.NameMapAssoc{Index: p.currentFuncIndex, NameMap: paramNames})
+			p.module.names.LocalNames =
+				append(p.module.names.LocalNames, &wasm.NameMapAssoc{Index: p.currentFuncIndex, NameMap: paramNames})
 		}
-		fn := p.module.funcs[p.currentFuncIndex-uint32(len(p.module.importFuncs))]
-		fn.body = p.funcParser.getBody()
+		code := p.module.code[p.currentFuncIndex-uint32(len(p.module.importFuncs))]
+		// TODO: localTypes
+		code.Body = p.funcParser.getBody()
 
 		// Multiple funcs are allowed, so advance in case there's a next.
 		p.currentFuncIndex++
diff --git a/wasm/text/type_parser.go b/wasm/text/type_parser.go
index 033036af..992bbdd6 100644
--- a/wasm/text/type_parser.go
+++ b/wasm/text/type_parser.go
@@ -1,10 +1,12 @@
 package text
 
 import (
+	"bytes"
 	"errors"
 	"fmt"
 
 	"github.com/tetratelabs/wazero/wasm"
+	"github.com/tetratelabs/wazero/wasm/internal/leb128"
 )
 
 // typeParsingState is used to give an appropriate typeParser.errorContext
@@ -59,7 +61,7 @@ type typeParser struct {
 	//
 	// For example, here the `(param i32)` type is initially considered inlined until the module type with the same
 	// signature is read later: (module (import (func (param i32))) (type (func (param i32))))`
-	inlinedTypes []*typeFunc
+	inlinedTypes []*wasm.FunctionType
 
 	// currentTypeIndex is set when there's a "type" field in a type use
 	// See https://www.w3.org/TR/wasm-core-1/#type-uses%E2%91%A0
@@ -88,8 +90,8 @@ type typeParser struct {
 	// parameters are abbreviated, ex. (param i32 i32), the currentParamField will be less than the type count.
 	foundParam bool
 
-	// currentResult is zero until set, and only set once as WebAssembly 1.0 only supports up to one result.
-	currentResult wasm.ValueType
+	// currentResults is empty until set, and only set once as WebAssembly 1.0 only supports up to one result.
+	currentResults []wasm.ValueType
 
 	// currentTypeUseStartLine tracks the start column of a type use in case there's an error later
 	currentTypeUseStartLine uint32
@@ -158,7 +160,7 @@ func (p *typeParser) reset() {
 	p.currentParams = nil
 	p.currentParamNames = nil
 	p.currentParamField = 0
-	p.currentResult = 0
+	p.currentResults = nil
 }
 
 func (p *typeParser) parseTypeIndexEnd(index *index) {
@@ -276,16 +278,16 @@ func (p *typeParser) parseParam(tok tokenType, tokenBytes []byte, _, _ uint32) e
 func (p *typeParser) parseResult(tok tokenType, tokenBytes []byte, _, _ uint32) error {
 	switch tok {
 	case tokenKeyword: // Ex. i32
-		if p.currentResult != 0 {
+		if p.currentResults != nil {
 			return errors.New("redundant type")
 		}
 		vt, err := parseValueType(tokenBytes)
 		if err != nil {
 			return err
 		}
-		p.currentResult = vt
+		p.currentResults = leb128.EncodeUint32(uint32(vt)) // reuse cache
 	case tokenRParen: // end of this field
-		if p.currentResult == 0 {
+		if p.currentResults == nil {
 			return errors.New("expected a type")
 		}
 		p.m.tokenParser = p.onTypeEnd
@@ -308,7 +310,7 @@ func (p *typeParser) errorContext() string {
 	return ""
 }
 
-var typeFuncEmpty = &typeFunc{}
+var typeFuncEmpty = &wasm.FunctionType{}
 
 // getTypeUse finalizes any current params or result and returns the current typeIndex and/or type. localNames are only
 // returned if defined inline.
@@ -317,13 +319,13 @@ func (p *typeParser) getTypeUse() (ty *typeUse, paramNames wasm.NameMap) {
 	paramNames = p.currentParamNames
 
 	// Don't conflate lack of verification type with nullary
-	if ty.typeIndex != nil && funcTypeEquals(typeFuncEmpty, p.currentParams, p.currentResult) {
+	if ty.typeIndex != nil && funcTypeEquals(typeFuncEmpty, p.currentParams, p.currentResults) {
 		return
 	}
 
 	// Search for an existing signature that matches the current type in the module types.
 	for _, t := range p.m.module.types {
-		if funcTypeEquals(t, p.currentParams, p.currentResult) {
+		if funcTypeEquals(t, p.currentParams, p.currentResults) {
 			ty.typeInlined = &inlinedTypeFunc{t, p.currentTypeUseStartLine, p.currentTypeUseStartCol}
 			return
 		}
@@ -331,14 +333,14 @@ func (p *typeParser) getTypeUse() (ty *typeUse, paramNames wasm.NameMap) {
 
 	// Search for an existing signature that matches the current type in the pending inlined types
 	for _, t := range p.inlinedTypes {
-		if funcTypeEquals(t, p.currentParams, p.currentResult) {
+		if funcTypeEquals(t, p.currentParams, p.currentResults) {
 			ty.typeInlined = &inlinedTypeFunc{t, p.currentTypeUseStartLine, p.currentTypeUseStartCol}
 			return
 		}
 	}
 
 	ty.typeInlined = &inlinedTypeFunc{
-		typeFunc: &typeFunc{"", p.currentParams, p.currentResult},
+		typeFunc: &wasm.FunctionType{Params: p.currentParams, Results: p.currentResults},
 		line:     p.currentTypeUseStartLine,
 		col:      p.currentTypeUseStartCol,
 	}
@@ -351,15 +353,19 @@ func (p *typeParser) getTypeUse() (ty *typeUse, paramNames wasm.NameMap) {
 	return
 }
 
+func funcTypeEquals(f *wasm.FunctionType, params []wasm.ValueType, results []wasm.ValueType) bool {
+	return bytes.Equal(f.Params, params) && bytes.Equal(f.Results, results)
+}
+
 // getType finalizes any current params or result and returns the current type and any paramNames for it.
 //
 // If the current type is in typeParser.inlinedTypes, it is removed prior to returning.
-func (p *typeParser) getType(typeName string) (sig *typeFunc, paramNames wasm.NameMap) {
+func (p *typeParser) getType() (sig *wasm.FunctionType, paramNames wasm.NameMap) {
 	paramNames = p.currentParamNames
 
 	// Search inlined types in case a matching type was found after its type use.
 	for i, t := range p.inlinedTypes {
-		if funcTypeEquals(t, p.currentParams, p.currentResult) {
+		if funcTypeEquals(t, p.currentParams, p.currentResults) {
 			// If we got here, we found a type field after a type use. This means it wasn't an inlined type, rather an
 			// out-of-order type. Hence, remove it from the inlined types and add it to the module types.
 			p.inlinedTypes = append(p.inlinedTypes[:i], p.inlinedTypes[i+1:]...)
@@ -371,7 +377,7 @@ func (p *typeParser) getType(typeName string) (sig *typeFunc, paramNames wasm.Na
 	// While inlined types are supposed to re-use an existing type index, there's no no unique constraint on explicitly
 	// defined module types. This means a duplicate type is not a bug: we don't check module.types first.
 	if sig == nil {
-		sig = &typeFunc{typeName, p.currentParams, p.currentResult}
+		sig = &wasm.FunctionType{Params: p.currentParams, Results: p.currentResults}
 	}
 	return
 }
diff --git a/wasm/text/types.go b/wasm/text/types.go
index effb63b4..8fafd1f9 100644
--- a/wasm/text/types.go
+++ b/wasm/text/types.go
@@ -1,7 +1,6 @@
 package text
 
 import (
-	"bytes"
 	"fmt"
 
 	"github.com/tetratelabs/wazero/wasm"
@@ -14,42 +13,18 @@ import (
 //
 // See https://www.w3.org/TR/wasm-core-1/#functions%E2%91%A7
 type module struct {
-	// name is optional. For example, "test".
-	// See https://www.w3.org/TR/wasm-core-1/#modules%E2%91%A0%E2%91%A2
-	//
-	// Note: The name may also be stored in the wasm.Module CustomSection under the key "name" subsection 0.
-	// See https://www.w3.org/TR/wasm-core-1/#binary-namesec
-	name string
-
-	// types are the unique function signatures of this module added in insertion order. Ex. (type... (func...))
-	//
-	// Note: Currently, there is no type ambiguity in the index as WebAssembly 1.0 only defines function type.
-	// In the future, other types may be introduced to support features such as module linking.
-	//
-	// See https://www.w3.org/TR/wasm-core-1/#types%E2%91%A0%E2%91%A0
-	types []*typeFunc
-
-	// typeParamNames are nil when no types had named (param) fields.
-	// Note: This is a map not a wasm.IndirectNameMap as late lookup is needed for after parsing
-	typeParamNames map[wasm.Index]wasm.NameMap
-
-	// funcNames are nil when no importFunc or function had a name
-	//
-	// See wasm.NameSection FunctionNames
-	funcNames wasm.NameMap
-
-	// paramNames are nil when no importFuncs or function had named (param) fields.
-	//
-	// Note: When set, this combines with any typeParamNames to produce wasm.NameSection LocalNames.
-	// This can't be done when parsing a function because types can be declared after the function that uses them.
-	// See https://www.w3.org/TR/wasm-core-1/#modules%E2%91%A0%E2%91%A2
-	paramNames wasm.IndirectNameMap
+	// types are the wasm.Module TypeSection
+	types []*wasm.FunctionType
 
 	// importFuncs are imports describing functions added in insertion order. Ex (import... (func...))
 	importFuncs []*importFunc
 
-	// funcs are functions added in insertion order. Ex (module (func...))
-	funcs []*function
+	// code are wasm.Module CodeSection, holding the function locals and body
+	//
+	// Note: the type usage of this function is in module.typeUses at the index in module.funcs offset by the length of
+	// module.importFuncs. For example, if this function is module.funcs[2] and there are 3 module.importFuncs. The type use
+	// is at module.typeUses[5]
+	code []*wasm.Code
 
 	// typeUses comprise the function index namespace of importFuncs followed by funcs
 	typeUses []*typeUse
@@ -62,10 +37,21 @@ type module struct {
 	//
 	// See https://www.w3.org/TR/wasm-core-1/#start-function%E2%91%A4
 	startFunction *index
+
+	// names are the wasm.Module NameSection
+	//
+	// * ModuleName: ex. "test" if (module $test)
+	// * FunctionNames: nil when no importFunc or function had a name
+	// * LocalNames: nil when no importFuncs or function had named (param) fields.
+	//
+	// Note: LocalNames will be incomplete until the end of parsing because types can be declared after a function that
+	// uses them. typeUses are analyzed later for this reason.
+	// See https://www.w3.org/TR/wasm-core-1/#modules%E2%91%A0%E2%91%A2
+	names *wasm.NameSection
 }
 
 type inlinedTypeFunc struct {
-	typeFunc *typeFunc
+	typeFunc *wasm.FunctionType
 
 	// line is the line in the source where the typeFunc was defined.
 	line uint32
@@ -101,36 +87,6 @@ type index struct {
 	col uint32
 }
 
-// typeFunc corresponds to the text format of a WebAssembly type use.
-//
-// Note: nothing is required per specification. Ex `(type (func))` is valid!
-//
-// See https://www.w3.org/TR/wasm-core-1/#text-functype
-type typeFunc struct {
-	// name is a symbolic index.ID present when explicitly defined in module.types. Ex. v_v
-	//
-	// name is only used for debugging. At runtime, types are called based on raw numeric index. The type index space
-	// begins those explicitly defined in module.types, followed by any inlined ones.
-	name string // TODO: presumably, this must be unique as it is a symbolic identifier?
-
-	// params are the possibly empty sequence of value types accepted by a function with this signature.
-	//
-	// Note: In WebAssembly 1.0 (MVP), there can be at most one result.
-	// See https://www.w3.org/TR/wasm-core-1/#result-types%E2%91%A0
-	params []wasm.ValueType
-
-	// result is the value type of the signature or zero if there is none.
-	//
-	// Note: We use this shortcut instead of a slice because in WebAssembly 1.0 (MVP), there can be at most one result.
-	// See https://www.w3.org/TR/wasm-core-1/#result-types%E2%91%A0
-	result wasm.ValueType
-}
-
-// funcTypeEquals allows you to compare signatures ignoring names
-func funcTypeEquals(t *typeFunc, params []wasm.ValueType, result wasm.ValueType) bool {
-	return bytes.Equal(t.params, params) && t.result == result
-}
-
 // importFunc corresponds to the text format of a WebAssembly function import.
 //
 // Note: the type usage of this import is at module.typeUses the same index as module.importFuncs
@@ -138,9 +94,6 @@ func funcTypeEquals(t *typeFunc, params []wasm.ValueType, result wasm.ValueType)
 //
 // See https://www.w3.org/TR/wasm-core-1/#imports%E2%91%A0
 type importFunc struct {
-	// importIndex is the zero-based index in module.imports. This is needed because imports are not always functions.
-	importIndex wasm.Index
-
 	// module is the possibly empty module name to import. Ex. "" or "Math"
 	//
 	// Note: This is not necessarily the module.name
@@ -152,20 +105,6 @@ type importFunc struct {
 	name string
 }
 
-// function corresponds to the text format of a WebAssembly function.
-//
-// Note: the type usage of this function is in module.typeUses at the index in module.funcs offset by the length of
-// module.importFuncs. For example, if this function is module.funcs[2] and there are 3 module.importFuncs. The type use
-// is at module.typeUses[5]
-//
-// Note: nothing is required per specification. Ex `(func)` is valid!
-//
-// See https://www.w3.org/TR/wasm-core-1/#functions%E2%91%A7
-type function struct {
-	// body are the instructions of this function encoded WebAssembly 1.0 (MVP) binary format
-	body []byte
-}
-
 // typeUse corresponds to the text format of an indexed or inlined type signature.
 //
 // Note: nothing is required per specification. Ex `(func)` is valid!


Test patch:
diff --git a/wasm/text/bind_test.go b/wasm/text/bind_test.go
index 57ab724c..ba5b79af 100644
--- a/wasm/text/bind_test.go
+++ b/wasm/text/bind_test.go
@@ -10,185 +10,169 @@ import (
 
 func TestBindIndices(t *testing.T) {
 	i32 := wasm.ValueTypeI32
-	paramI32I32ResultI32 := &typeFunc{params: []wasm.ValueType{i32, i32}, result: i32}
-	paramI32I32I32I32ResultI32 := &typeFunc{params: []wasm.ValueType{i32, i32, i32, i32}, result: i32}
+	paramI32I32ResultI32 := &wasm.FunctionType{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}
+	paramI32I32I32I32ResultI32 := &wasm.FunctionType{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}}
 	indexZero, indexOne := &index{numeric: 0}, &index{numeric: 1}
 
 	tests := []struct {
-		name            string
-		input, expected *module
+		name                 string
+		inputModule          *module
+		inputTypeNameToIndex map[string]wasm.Index
+		inputFuncNameToIndex map[string]wasm.Index
+		expected             *module
 	}{
 		{
 			name: "import function: inlined type to numeric index",
-			input: &module{
-				types: []*typeFunc{paramI32I32I32I32ResultI32},
+			inputModule: &module{
+				types: []*wasm.FunctionType{paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeInlined: &inlinedTypeFunc{paramI32I32I32I32ResultI32, 0, 0}},
 				},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "fd_write"}},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{"runtime.fd_write": wasm.Index(0)},
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				types:       []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "fd_write"}},
 			},
 		},
 		{
 			name: "import function: multiple inlined types to numeric indices",
-			input: &module{
-				types: []*typeFunc{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+			inputModule: &module{
+				types: []*wasm.FunctionType{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeInlined: &inlinedTypeFunc{paramI32I32ResultI32, 0, 0}},
 					{typeInlined: &inlinedTypeFunc{paramI32I32I32I32ResultI32, 0, 0}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{
+				"runtime.args_sizes_get": wasm.Index(0),
+				"runtime.fd_write":       wasm.Index(1),
+			},
 			expected: &module{
-				types:    []*typeFunc{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+				types:    []*wasm.FunctionType{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
 			},
 		},
 		{
 			name: "import function: multiple inlined types to same numeric index",
-			input: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+			inputModule: &module{
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeInlined: &inlinedTypeFunc{paramI32I32ResultI32, 0, 0}},
 					{typeInlined: &inlinedTypeFunc{paramI32I32ResultI32, 0, 0}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_get"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
 				},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{
+				"runtime.args_sizes_get": wasm.Index(0),
+				"runtime.fd_write":       wasm.Index(1),
+			},
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{{typeIndex: indexOne}, {typeIndex: indexOne}},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_get"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
 				},
 			},
 		},
 		{
 			name: "import function: multiple type names to numeric indices",
-			input: &module{
-				types: []*typeFunc{
-					typeFuncEmpty,
-					{name: "i32i32_i32", params: []wasm.ValueType{i32, i32}, result: i32},
-					{name: "i32i32i32i32_i32", params: []wasm.ValueType{i32, i32, i32, i32}, result: i32},
+			inputModule: &module{
+				types: []*wasm.FunctionType{
+					{},
+					{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
+					{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
 				},
 				typeUses: []*typeUse{
 					{typeIndex: &index{ID: "i32i32_i32", line: 5, col: 86}},
 					{typeIndex: &index{ID: "i32i32i32i32_i32", line: 6, col: 76}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
 			},
+			inputTypeNameToIndex: map[string]wasm.Index{
+				"i32i32_i32": wasm.Index(1), "i32i32i32i32_i32": wasm.Index(2),
+			},
+			inputFuncNameToIndex: map[string]wasm.Index{
+				"runtime.args_sizes_get": wasm.Index(0),
+				"runtime.fd_write":       wasm.Index(1),
+			},
 			expected: &module{
-				types: []*typeFunc{
-					typeFuncEmpty,
-					{name: "i32i32_i32", params: []wasm.ValueType{i32, i32}, result: i32},
-					{name: "i32i32i32i32_i32", params: []wasm.ValueType{i32, i32, i32, i32}, result: i32},
+				types: []*wasm.FunctionType{
+					{},
+					{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
+					{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
 				},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: 1, line: 5, col: 86}},
 					{typeIndex: &index{numeric: 2, line: 6, col: 76}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
 			},
 		},
 		{
 			name: "import function: multiple type numeric indices left alone",
-			input: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+			inputModule: &module{
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: 1, line: 5, col: 86}},
 					{typeIndex: &index{numeric: 2, line: 6, col: 76}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{
+				"runtime.args_sizes_get": wasm.Index(0),
+				"runtime.fd_write":       wasm.Index(1),
+			},
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: 1, line: 5, col: 86}},
 					{typeIndex: &index{numeric: 2, line: 6, col: 76}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
-				},
-				funcNames: wasm.NameMap{
-					{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
 			},
 		},
 		{
 			name: "export imported func",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "bar", exportIndex: wasm.Index(0), funcIndex: &index{ID: "bar", line: 3, col: 22}},
 				},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{"bar": wasm.Index(0)},
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "bar", exportIndex: wasm.Index(0), funcIndex: &index{numeric: 0, line: 3, col: 22}},
 				},
@@ -196,21 +180,20 @@ func TestBindIndices(t *testing.T) {
 		},
 		{
 			name: "export imported func twice",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{ID: "bar", line: 3, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{ID: "bar", line: 4, col: 22}},
 				},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{"bar": wasm.Index(0)},
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{numeric: 0, line: 3, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{numeric: 0, line: 4, col: 22}},
@@ -219,33 +202,20 @@ func TestBindIndices(t *testing.T) {
 		},
 		{
 			name: "export different func",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{module: "foo", name: "bar", importIndex: wasm.Index(0)},
-					{module: "baz", name: "qux", importIndex: wasm.Index(1)},
-				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "qux"},
-				},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "foo", name: "bar"}, {module: "baz", name: "qux"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{ID: "bar", line: 4, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{ID: "qux", line: 5, col: 22}},
 				},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{"bar": wasm.Index(0), "qux": wasm.Index(1)},
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{module: "foo", name: "bar", importIndex: wasm.Index(0)},
-					{module: "baz", name: "qux", importIndex: wasm.Index(1)},
-				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "qux"},
-				},
+				types:       []*wasm.FunctionType{{}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "foo", name: "bar"}, {module: "baz", name: "qux"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{numeric: 0, line: 4, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{numeric: 1, line: 5, col: 22}},
@@ -254,33 +224,32 @@ func TestBindIndices(t *testing.T) {
 		},
 		{
 			name: "start: imported function name to numeric index",
-			input: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
 				importFuncs:   []*importFunc{{}, {}},
-				funcNames:     wasm.NameMap{{Index: wasm.Index(0), Name: "one"}, {Index: wasm.Index(1), Name: "two"}},
 				startFunction: &index{ID: "two", line: 3, col: 9},
 			},
+			inputFuncNameToIndex: map[string]wasm.Index{"one": wasm.Index(0), "two": wasm.Index(1)},
 			expected: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
 				importFuncs:   []*importFunc{{}, {}},
-				funcNames:     wasm.NameMap{{Index: wasm.Index(0), Name: "one"}, {Index: wasm.Index(1), Name: "two"}},
 				startFunction: &index{numeric: 1, line: 3, col: 9},
 			},
 		},
 		{
 			name: "start: imported function numeric index left alone",
-			input: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}},
-				importFuncs:   []*importFunc{{name: "hello", importIndex: wasm.Index(0)}},
+				importFuncs:   []*importFunc{{name: "hello"}},
 				startFunction: &index{numeric: 0, line: 3, col: 9},
 			},
 			expected: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}},
-				importFuncs:   []*importFunc{{name: "hello", importIndex: wasm.Index(0)}},
+				importFuncs:   []*importFunc{{name: "hello"}},
 				startFunction: &index{numeric: 0, line: 3, col: 9},
 			},
 		},
@@ -290,9 +259,9 @@ func TestBindIndices(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			err := bindIndices(tc.input)
+			err := bindIndices(tc.inputModule, tc.inputTypeNameToIndex, tc.inputFuncNameToIndex)
 			require.NoError(t, err)
-			require.Equal(t, tc.expected, tc.input)
+			require.Equal(t, tc.expected, tc.inputModule)
 		})
 	}
 }
@@ -302,14 +271,16 @@ func TestBindIndices_Errors(t *testing.T) {
 	indexZero := &index{}
 
 	tests := []struct {
-		name        string
-		input       *module
-		expectedErr string
+		name                 string
+		inputModule          *module
+		inputTypeNameToIndex map[string]wasm.Index
+		inputFuncNameToIndex map[string]wasm.Index
+		expectedErr          string
 	}{
 		{
 			name: "import function: type points out of range",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: &index{numeric: 1, line: 3, col: 9}}},
 				importFuncs: []*importFunc{{name: "hello"}},
 			},
@@ -317,8 +288,8 @@ func TestBindIndices_Errors(t *testing.T) {
 		},
 		{
 			name: "import function: type points nowhere",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: &index{ID: "main", line: 3, col: 9}}},
 				importFuncs: []*importFunc{{name: "hello"}},
 			},
@@ -326,86 +297,84 @@ func TestBindIndices_Errors(t *testing.T) {
 		},
 		{
 			name: "import function: inlined type doesn't match indexed",
-			input: &module{
-				types: []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types: []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{
-					{typeIndex: indexZero, typeInlined: &inlinedTypeFunc{&typeFunc{params: []wasm.ValueType{i32, i32}}, 3, 9}},
-				},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "", name: "hello"},
+					{typeIndex: indexZero, typeInlined: &inlinedTypeFunc{&wasm.FunctionType{Params: []wasm.ValueType{i32, i32}}, 3, 9}},
 				},
+				importFuncs: []*importFunc{{module: "", name: "hello"}},
 			},
 			expectedErr: "3:9: inlined type doesn't match type index 0 in module.import[0].func.type",
 		},
 		{
 			name: "function: type points out of range",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: &index{numeric: 1, line: 3, col: 9}}},
-				funcs:    []*function{{body: end}},
+				code:     []*wasm.Code{{Body: end}},
 			},
 			expectedErr: "3:9: index 1 is out of range [0..0] in module.func[0].type",
 		},
 		{
 			name: "function: type points nowhere",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: &index{ID: "main", line: 3, col: 9}}},
-				funcs:    []*function{{body: end}},
+				code:     []*wasm.Code{{Body: end}},
 			},
 			expectedErr: "3:9: unknown ID $main in module.func[0].type",
 		},
 		{
 			name: "function: inlined type doesn't match indexed",
-			input: &module{
-				types: []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types: []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{
 					{typeIndex: indexZero,
-						typeInlined: &inlinedTypeFunc{&typeFunc{params: []wasm.ValueType{i32, i32}}, 3, 9}},
+						typeInlined: &inlinedTypeFunc{&wasm.FunctionType{Params: []wasm.ValueType{i32, i32}}, 3, 9}},
 				},
-				funcs: []*function{{body: end}},
+				code: []*wasm.Code{{Body: end}},
 			},
 			expectedErr: "3:9: inlined type doesn't match type index 0 in module.func[0].type",
 		},
 		{
 			name: "export func points out of range",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "bar", exportIndex: wasm.Index(0), funcIndex: &index{numeric: 3, line: 3, col: 22}},
 				},
 			},
-			expectedErr: "3:22: index 3 is out of range [0..0] in module.exports[0].func",
+			inputFuncNameToIndex: map[string]wasm.Index{"bar": wasm.Index(0)},
+			expectedErr:          "3:22: index 3 is out of range [0..0] in module.exports[0].func",
 		},
 		{
 			name: "export func points nowhere",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "bar", exportIndex: wasm.Index(0), funcIndex: &index{ID: "qux", line: 3, col: 22}},
 				},
 			},
-			expectedErr: "3:22: unknown ID $qux in module.exports[0].func",
+			inputFuncNameToIndex: map[string]wasm.Index{"bar": wasm.Index(0)},
+			expectedErr:          "3:22: unknown ID $qux in module.exports[0].func",
 		},
 		{
 			name: "start points out of range",
-			input: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+			inputModule: &module{
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}},
-				importFuncs:   []*importFunc{{name: "hello", importIndex: wasm.Index(0)}},
+				importFuncs:   []*importFunc{{name: "hello"}},
 				startFunction: &index{numeric: 1, line: 3, col: 9},
 			},
 			expectedErr: "3:9: index 1 is out of range [0..0] in module.start",
 		},
 		{
 			name: "start points nowhere",
-			input: &module{
+			inputModule: &module{
 				startFunction: &index{ID: "main", line: 1, col: 16},
 			},
 			expectedErr: "1:16: unknown ID $main in module.start",
@@ -416,8 +385,221 @@ func TestBindIndices_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			err := bindIndices(tc.input)
+			err := bindIndices(tc.inputModule, tc.inputTypeNameToIndex, tc.inputFuncNameToIndex)
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
 }
+
+func TestMergeLocalNames(t *testing.T) {
+	i32 := wasm.ValueTypeI32
+	paramI32I32ResultI32 := &wasm.FunctionType{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}
+	indexZero, indexOne := &index{numeric: 0}, &index{numeric: 1}
+	localGet0End := []byte{wasm.OpcodeLocalGet, 0x00, wasm.OpcodeEnd}
+
+	tests := []struct {
+		name                string
+		inputModule         *module
+		inputTypeParamNames map[wasm.Index]wasm.NameMap
+		expected            wasm.IndirectNameMap
+	}{
+		{
+			name: "no parameter names",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				names:       &wasm.NameSection{},
+			},
+		},
+		{
+			name: "type parameter names, but no import function parameter names",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				names:       &wasm.NameSection{},
+			},
+			inputTypeParamNames: map[wasm.Index]wasm.NameMap{
+				wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "import function parameter names, but no type parameter names",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+					},
+				},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "type parameter names, but no import function parameter names - function 2",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "", name: ""}, {module: "wasi_snapshot_preview1", name: "args_get"}},
+				names:       &wasm.NameSection{},
+			},
+			inputTypeParamNames: map[wasm.Index]wasm.NameMap{
+				wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "import function parameter names, but no type parameter names - function 2",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "", name: ""}, {module: "wasi_snapshot_preview1", name: "args_get"}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+					},
+				},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "conflict on import function parameter names and type parameter names",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+					},
+				},
+			},
+			inputTypeParamNames: map[wasm.Index]wasm.NameMap{
+				wasm.Index(1): {{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "type parameter names, but no function parameter names",
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexOne}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names:    &wasm.NameSection{},
+			},
+			inputTypeParamNames: map[wasm.Index]wasm.NameMap{
+				wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "function parameter names, but no type parameter names",
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexOne}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+					},
+				},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "type parameter names, but no function parameter names - function 2",
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
+				code:     []*wasm.Code{{Body: end}, {Body: localGet0End}},
+				names:    &wasm.NameSection{},
+			},
+			inputTypeParamNames: map[wasm.Index]wasm.NameMap{
+				wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "function parameter names, but no type parameter names - function 2",
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
+				code:     []*wasm.Code{{Body: end}, {Body: localGet0End}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+					},
+				},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "conflict on function parameter names and type parameter names",
+			inputModule: &module{
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexOne}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+					},
+				},
+			},
+			inputTypeParamNames: map[wasm.Index]wasm.NameMap{
+				wasm.Index(1): {{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
+			},
+		},
+		{
+			name: "import and module defined function have same type, but different parameter names",
+			inputModule: &module{
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexOne}, {typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "", name: ""}},
+				code:        []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}}},
+						{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(0), Name: "r"}}},
+					},
+				},
+			},
+			expected: wasm.IndirectNameMap{
+				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}}},
+				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(0), Name: "r"}}},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+
+		t.Run(tc.name, func(t *testing.T) {
+			require.Equal(t, tc.expected, mergeLocalNames(tc.inputModule, tc.inputTypeParamNames))
+		})
+	}
+}
diff --git a/wasm/text/decoder_test.go b/wasm/text/decoder_test.go
index d7947b54..91a7d9f7 100644
--- a/wasm/text/decoder_test.go
+++ b/wasm/text/decoder_test.go
@@ -330,213 +330,6 @@ func TestDecodeModule(t *testing.T) {
 	}
 }
 
-func TestMergeLocalNames(t *testing.T) {
-	i32 := wasm.ValueTypeI32
-	paramI32I32ResultI32 := &typeFunc{params: []wasm.ValueType{i32, i32}, result: i32}
-	indexZero, indexOne := &index{numeric: 0}, &index{numeric: 1}
-	localGet0End := []byte{wasm.OpcodeLocalGet, 0x00, wasm.OpcodeEnd}
-
-	tests := []struct {
-		name     string
-		input    *module
-		expected wasm.IndirectNameMap
-	}{
-		{
-			name: "no parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-			},
-		},
-		{
-			name: "type parameter names, but no import function parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "import function parameter names, but no type parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "type parameter names, but no import function parameter names - function 2",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "", name: ""},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "import function parameter names, but no type parameter names - function 2",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "", name: ""},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "conflict on import function parameter names and type parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(1): {{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}},
-				},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "type parameter names, but no function parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				funcs:    []*function{{body: localGet0End}},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "function parameter names, but no type parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				funcs:    []*function{{body: localGet0End}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "type parameter names, but no function parameter names - function 2",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
-				funcs:    []*function{{body: end}, {body: localGet0End}},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(1): {{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "function parameter names, but no type parameter names - function 2",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
-				funcs:    []*function{{body: end}, {body: localGet0End}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "conflict on function parameter names and type parameter names",
-			input: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				funcs:    []*function{{body: localGet0End}},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(1): {{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}},
-				},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "argv"}, {Index: wasm.Index(0), Name: "argv_buf"}}},
-			},
-		},
-		{
-			name: "import and module defined function have same type, but different parameter names",
-			input: &module{
-				types:       []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses:    []*typeUse{{typeIndex: indexOne}, {typeIndex: indexOne}},
-				importFuncs: []*importFunc{{importIndex: wasm.Index(0), module: "", name: ""}},
-				funcs:       []*function{{body: localGet0End}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}}},
-					{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(0), Name: "r"}}},
-				},
-			},
-			expected: wasm.IndirectNameMap{
-				{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(0), Name: "y"}}},
-				{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(0), Name: "r"}}},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		tc := tt
-
-		t.Run(tc.name, func(t *testing.T) {
-			require.Equal(t, tc.expected, mergeLocalNames(tc.input))
-		})
-	}
-}
-
 func TestDecodeModule_Errors(t *testing.T) {
 	tests := []struct{ name, input, expectedErr string }{
 		{
diff --git a/wasm/text/parser_test.go b/wasm/text/parser_test.go
index dc6085b9..ec4f8e35 100644
--- a/wasm/text/parser_test.go
+++ b/wasm/text/parser_test.go
@@ -11,14 +11,14 @@ import (
 
 func TestParseModule(t *testing.T) {
 	f32, i32, i64 := wasm.ValueTypeF32, wasm.ValueTypeI32, wasm.ValueTypeI64
-	paramI32 := &typeFunc{params: []wasm.ValueType{i32}}
-	paramI32I32ResultI32 := &typeFunc{params: []wasm.ValueType{i32, i32}, result: i32}
-	paramI32I32I32I32ResultI32 := &typeFunc{params: []wasm.ValueType{i32, i32, i32, i32}, result: i32}
-	paramI32I32I32I32I32I64I32I32ResultI32 := &typeFunc{
-		params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32},
-		result: i32,
+	paramI32 := &wasm.FunctionType{Params: []wasm.ValueType{i32}}
+	paramI32I32ResultI32 := &wasm.FunctionType{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}
+	paramI32I32I32I32ResultI32 := &wasm.FunctionType{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}}
+	paramI32I32I32I32I32I64I32I32ResultI32 := &wasm.FunctionType{
+		Params:  []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32},
+		Results: []wasm.ValueType{i32},
 	}
-	resultI32 := &typeFunc{result: i32}
+	resultI32 := &wasm.FunctionType{Results: []wasm.ValueType{i32}}
 	indexZero, indexOne := &index{numeric: wasm.Index(0)}, &index{numeric: wasm.Index(1)}
 	localGet0End := []byte{wasm.OpcodeLocalGet, 0x00, wasm.OpcodeEnd}
 
@@ -35,15 +35,15 @@ func TestParseModule(t *testing.T) {
 		{
 			name:     "only name",
 			input:    "(module $tools)",
-			expected: &module{name: "tools"},
+			expected: &module{names: &wasm.NameSection{ModuleName: "tools"}},
 		},
 		{
 			name:  "type func one empty",
 			input: "(module (type $i32i32_i32 (func (param i32 i32) (result i32))) (type (func)))",
 			expected: &module{
-				types: []*typeFunc{
-					{name: "i32i32_i32", params: []wasm.ValueType{i32, i32}, result: i32},
-					typeFuncEmpty,
+				types: []*wasm.FunctionType{
+					{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
+					{},
 				},
 			},
 		},
@@ -51,26 +51,16 @@ func TestParseModule(t *testing.T) {
 			name:  "type func param names",
 			input: "(module (type $mul (func (param $x f32) (param $y f32) (result f32))))",
 			expected: &module{
-				types: []*typeFunc{
-					{name: "mul", params: []wasm.ValueType{f32, f32}, result: f32},
-				},
-				typeParamNames: map[wasm.Index]wasm.NameMap{wasm.Index(0): {
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "x"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "y"},
-				}},
+				types: []*wasm.FunctionType{{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}}},
 			},
 		},
 		{
-			name:  "type func mixed param names", // Verifies we can handle less param fields than params
+			name:  "type func mixed param names", // Verifies we can handle less param fields than Params
 			input: "(module (type (func (param i32 i32) (param $v i32) (param i64) (param $t f32))))",
 			expected: &module{
-				types: []*typeFunc{
-					{params: []wasm.ValueType{i32, i32, i32, i64, f32}},
+				types: []*wasm.FunctionType{
+					{Params: []wasm.ValueType{i32, i32, i32, i64, f32}},
 				},
-				typeParamNames: map[wasm.Index]wasm.NameMap{wasm.Index(0): {
-					&wasm.NameAssoc{Index: wasm.Index(2), Name: "v"},
-					&wasm.NameAssoc{Index: wasm.Index(4), Name: "t"},
-				}},
 			},
 		},
 		{
@@ -81,20 +71,10 @@ func TestParseModule(t *testing.T) {
 	(type $add (func (param $l f32) (param $r f32) (result f32)))
 )`,
 			expected: &module{
-				types: []*typeFunc{
-					{name: "mul", params: []wasm.ValueType{f32, f32}, result: f32},
-					typeFuncEmpty,
-					{name: "add", params: []wasm.ValueType{f32, f32}, result: f32},
-				},
-				typeParamNames: map[wasm.Index]wasm.NameMap{
-					wasm.Index(0): {
-						&wasm.NameAssoc{Index: wasm.Index(0), Name: "x"},
-						&wasm.NameAssoc{Index: wasm.Index(1), Name: "y"},
-					},
-					wasm.Index(2): {
-						&wasm.NameAssoc{Index: wasm.Index(0), Name: "l"},
-						&wasm.NameAssoc{Index: wasm.Index(1), Name: "r"},
-					},
+				types: []*wasm.FunctionType{
+					{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
+					{},
+					{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
 				},
 			},
 		},
@@ -105,22 +85,22 @@ func TestParseModule(t *testing.T) {
 	(type (func))
 )`,
 			expected: &module{
-				types: []*typeFunc{
-					typeFuncEmpty, // module types are always before inlined types
+				types: []*wasm.FunctionType{
+					{}, // module types are always before inlined types
 					paramI32I32I32I32ResultI32,
 				},
-				typeUses: []*typeUse{{typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "fd_write"},
+				typeUses:    []*typeUse{{typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "fd_write"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 			},
 		},
 		{
 			name:  "import func empty",
 			input: "(module (import \"foo\" \"bar\" (func)))", // ok empty sig
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
 			},
@@ -132,7 +112,7 @@ func TestParseModule(t *testing.T) {
 	(import "foo" "bar" (func))
 )`,
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
 			},
@@ -144,7 +124,7 @@ func TestParseModule(t *testing.T) {
 	(type (func))
 )`,
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
 			},
@@ -157,12 +137,9 @@ func TestParseModule(t *testing.T) {
 	(type (func))
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "foo", name: "bar"},
-					{importIndex: wasm.Index(1), module: "baz", name: "qux"},
-				},
+				types:       []*wasm.FunctionType{{}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "foo", name: "bar"}, {module: "baz", name: "qux"}},
 			},
 		},
 		{
@@ -172,7 +149,7 @@ func TestParseModule(t *testing.T) {
 	(import "foo" "bar" (func))
 )`,
 			expected: &module{
-				types:       []*typeFunc{paramI32I32I32I32ResultI32, typeFuncEmpty},
+				types:       []*wasm.FunctionType{paramI32I32I32I32ResultI32, {}},
 				typeUses:    []*typeUse{{typeIndex: indexOne}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
 			},
@@ -181,12 +158,9 @@ func TestParseModule(t *testing.T) {
 			name:  "import func empty twice",
 			input: "(module (import \"foo\" \"bar\" (func)) (import \"baz\" \"qux\" (func)))", // ok empty sig
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "foo", name: "bar"},
-					{importIndex: wasm.Index(1), module: "baz", name: "qux"},
-				},
+				types:       []*wasm.FunctionType{{}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "foo", name: "bar"}, {module: "baz", name: "qux"}},
 			},
 		},
 		{
@@ -195,12 +169,12 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write (param i32) (param i32) (param i32) (param i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "fd_write"},
+				types:       []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "fd_write"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 			},
 		},
 		{
@@ -209,12 +183,12 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write (param i32 i32 i32 i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "fd_write"},
+				types:       []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "fd_write"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 			},
 		},
 		{
@@ -225,12 +199,12 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write (param i32) (param i32 i32) (param i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "fd_write"},
+				types:       []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "fd_write"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
 			},
 		},
 		{
@@ -239,19 +213,19 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "proc_exit" (func $runtime.proc_exit (param i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "proc_exit"},
+				types:       []*wasm.FunctionType{paramI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "proc_exit"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.proc_exit"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.proc_exit"}},
 			},
 		},
 		{
 			name:  "import func inlined type no param",
 			input: `(module (import "" "" (func (result i32))))`,
 			expected: &module{
-				types:       []*typeFunc{resultI32},
+				types:       []*wasm.FunctionType{resultI32},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{}},
 			},
@@ -262,12 +236,12 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "path_open" (func $runtime.path_open (param i32) (param i32) (param i32) (param i32) (param i32) (param i64) (param i64) (param i32) (param i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32I32I64I32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "path_open"},
+				types:       []*wasm.FunctionType{paramI32I32I32I32I32I64I32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "path_open"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
 			},
 		},
 		{
@@ -276,12 +250,12 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "path_open" (func $runtime.path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32I32I64I32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "path_open"},
+				types:       []*wasm.FunctionType{paramI32I32I32I32I32I64I32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "path_open"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
 			},
 		},
 		{
@@ -291,15 +265,17 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write (param i32 i32 i32 i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+				types:    []*wasm.FunctionType{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					},
 				},
 			},
 		},
@@ -313,22 +289,24 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write (type $i32i32i32i32_i32)))
 )`,
 			expected: &module{
-				types: []*typeFunc{
-					typeFuncEmpty,
-					{name: "i32i32_i32", params: []wasm.ValueType{i32, i32}, result: i32},
-					{name: "i32i32i32i32_i32", params: []wasm.ValueType{i32, i32, i32, i32}, result: i32},
+				types: []*wasm.FunctionType{
+					{},
+					{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
+					{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
 				},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 5, col: 88}},
 					{typeIndex: &index{numeric: wasm.Index(2), line: 6, col: 76}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					},
 				},
 			},
 		},
@@ -342,18 +320,20 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write (type 2)))
 )`,
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 5, col: 88}},
 					{typeIndex: &index{numeric: wasm.Index(2), line: 6, col: 76}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "fd_write"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "fd_write"},
 				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					},
 				},
 			},
 		},
@@ -365,15 +345,17 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "args_sizes_get" (func $runtime.args_sizes_get (param i32 i32) (result i32)))
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+				types:    []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{{typeIndex: indexOne}, {typeIndex: indexOne}},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "args_get"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
 				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+					},
 				},
 			},
 		},
@@ -386,18 +368,20 @@ func TestParseModule(t *testing.T) {
 	(import "wasi_snapshot_preview1" "args_sizes_get" (func $runtime.args_sizes_get (type 1)))
 )`,
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 4, col: 76}},
 					{typeIndex: &index{numeric: wasm.Index(1), line: 5, col: 88}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "args_get"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
 				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+					},
 				},
 			},
 		},
@@ -410,18 +394,20 @@ func TestParseModule(t *testing.T) {
 	(type (func (param i32 i32) (result i32)))
 )`,
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 3, col: 76}},
 					{typeIndex: &index{numeric: wasm.Index(1), line: 4, col: 88}},
 				},
 				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-					{importIndex: wasm.Index(1), module: "wasi_snapshot_preview1", name: "args_sizes_get"},
+					{module: "wasi_snapshot_preview1", name: "args_get"},
+					{module: "wasi_snapshot_preview1", name: "args_sizes_get"},
 				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+					},
 				},
 			},
 		},
@@ -429,16 +415,16 @@ func TestParseModule(t *testing.T) {
 			name:  "import func param names",
 			input: "(module (import \"Math\" \"Mul\" (func $mul (param $x f32) (param $y f32) (result f32))))",
 			expected: &module{
-				types: []*typeFunc{
-					{params: []wasm.ValueType{f32, f32}, result: f32},
-				},
-				typeUses: []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "Math", name: "Mul"},
+				types: []*wasm.FunctionType{
+					{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
 				},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "mul"}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "Math", name: "Mul"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "mul"}},
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
+					},
 				},
 			},
 		},
@@ -449,32 +435,33 @@ func TestParseModule(t *testing.T) {
 	(import "Math" "Add" (func $add (param $l f32) (param $r f32) (result f32)))
 )`,
 			expected: &module{
-				types: []*typeFunc{
-					{params: []wasm.ValueType{f32, f32}, result: f32},
-				},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "Math", name: "Mul"},
-					{importIndex: wasm.Index(1), module: "Math", name: "Add"},
-				},
-				funcNames: wasm.NameMap{{Index: wasm.Index(0), Name: "mul"}, {Index: wasm.Index(1), Name: "add"}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
-					{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(1), Name: "r"}}},
+				types: []*wasm.FunctionType{
+					{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
+				},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "Math", name: "Mul"}, {module: "Math", name: "Add"}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "mul"}, {Index: wasm.Index(1), Name: "add"}},
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
+						{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(1), Name: "r"}}},
+					},
 				},
 			},
 		},
 		{
-			name:  "import func mixed param names", // Verifies we can handle less param fields than params
+			name:  "import func mixed param names", // Verifies we can handle less param fields than Params
 			input: "(module (import \"\" \"\" (func (param i32 i32) (param $v i32) (param i64) (param $t f32))))",
 			expected: &module{
-				types: []*typeFunc{
-					{params: []wasm.ValueType{i32, i32, i32, i64, f32}},
+				types: []*wasm.FunctionType{
+					{Params: []wasm.ValueType{i32, i32, i32, i64, f32}},
 				},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
-				importFuncs: []*importFunc{{importIndex: wasm.Index(0)}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(2), Name: "v"}, {Index: wasm.Index(4), Name: "t"}}},
+				importFuncs: []*importFunc{{module: "", name: ""}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(2), Name: "v"}, {Index: wasm.Index(4), Name: "t"}}},
+					},
 				},
 			},
 		},
@@ -482,9 +469,9 @@ func TestParseModule(t *testing.T) {
 			name:  "func empty",
 			input: "(module (func))", // ok empty sig
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: indexZero}},
-				funcs:    []*function{{body: end}},
+				code:     []*wasm.Code{{Body: end}},
 			},
 		},
 		{
@@ -494,9 +481,9 @@ func TestParseModule(t *testing.T) {
 	(func)
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: indexZero}},
-				funcs:    []*function{{body: end}},
+				code:     []*wasm.Code{{Body: end}},
 			},
 		},
 		{
@@ -506,9 +493,9 @@ func TestParseModule(t *testing.T) {
 	(type (func))
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: indexZero}},
-				funcs:    []*function{{body: end}},
+				code:     []*wasm.Code{{Body: end}},
 			},
 		},
 		{
@@ -519,9 +506,9 @@ func TestParseModule(t *testing.T) {
 	(type (func))
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				funcs:    []*function{{body: end}, {body: end}},
+				code:     []*wasm.Code{{Body: end}, {Body: end}},
 			},
 		},
 		{
@@ -531,18 +518,18 @@ func TestParseModule(t *testing.T) {
 	(func)
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32I32I32ResultI32, typeFuncEmpty},
+				types:    []*wasm.FunctionType{paramI32I32I32I32ResultI32, {}},
 				typeUses: []*typeUse{{typeIndex: indexOne}},
-				funcs:    []*function{{body: end}},
+				code:     []*wasm.Code{{Body: end}},
 			},
 		},
 		{
 			name:  "func empty twice",
 			input: "(module (func) (func))", // ok empty sig
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
+				types:    []*wasm.FunctionType{{}},
 				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				funcs:    []*function{{body: end}, {body: end}},
+				code:     []*wasm.Code{{Body: end}, {Body: end}},
 			},
 		},
 		{
@@ -551,10 +538,12 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.fd_write (param i32) (param i32) (param i32) (param i32) (result i32) local.get 0 )
 )`,
 			expected: &module{
-				types:     []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				types:    []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				},
 			},
 		},
 		{
@@ -563,10 +552,12 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.fd_write (param i32 i32 i32 i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:     []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				types:    []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				},
 			},
 		},
 		{
@@ -577,10 +568,12 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.fd_write (param i32) (param i32 i32) (param i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:     []*typeFunc{paramI32I32I32I32ResultI32},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				types:    []*wasm.FunctionType{paramI32I32I32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.fd_write"}},
+				},
 			},
 		},
 		{
@@ -589,19 +582,21 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.proc_exit (param i32))
 )`,
 			expected: &module{
-				types:     []*typeFunc{paramI32},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: end}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.proc_exit"}},
+				types:    []*wasm.FunctionType{paramI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: end}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.proc_exit"}},
+				},
 			},
 		},
 		{
 			name:  "func inlined type no param",
 			input: `(module (func (result i32) local.get 0))`,
 			expected: &module{
-				types:    []*typeFunc{resultI32},
+				types:    []*wasm.FunctionType{resultI32},
 				typeUses: []*typeUse{{typeIndex: indexZero}},
-				funcs:    []*function{{body: localGet0End}},
+				code:     []*wasm.Code{{Body: localGet0End}},
 			},
 		},
 		{
@@ -610,10 +605,12 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.path_open (param i32) (param i32) (param i32) (param i32) (param i32) (param i64) (param i64) (param i32) (param i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:     []*typeFunc{paramI32I32I32I32I32I64I32I32ResultI32},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
+				types:    []*wasm.FunctionType{paramI32I32I32I32I32I64I32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
+				},
 			},
 		},
 		{
@@ -622,10 +619,12 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:     []*typeFunc{paramI32I32I32I32I32I64I32I32ResultI32},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
+				types:    []*wasm.FunctionType{paramI32I32I32I32I32I64I32I32ResultI32},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.path_open"}},
+				},
 			},
 		},
 		{
@@ -635,12 +634,14 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.fd_write (param i32 i32 i32 i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+				types:    []*wasm.FunctionType{paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexOne}},
-				funcs:    []*function{{body: localGet0End}, {body: localGet0End}},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+				code:     []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					},
 				},
 			},
 		},
@@ -654,19 +655,21 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.fd_write (type $i32i32i32i32_i32) local.get 0)
 )`,
 			expected: &module{
-				types: []*typeFunc{
-					typeFuncEmpty,
-					{name: "i32i32_i32", params: []wasm.ValueType{i32, i32}, result: i32},
-					{name: "i32i32i32i32_i32", params: []wasm.ValueType{i32, i32, i32, i32}, result: i32},
+				types: []*wasm.FunctionType{
+					{},
+					{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
+					{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
 				},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 5, col: 38}},
 					{typeIndex: &index{numeric: wasm.Index(2), line: 6, col: 32}},
 				},
-				funcs: []*function{{body: localGet0End}, {body: localGet0End}},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+				code: []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					},
 				},
 			},
 		},
@@ -680,15 +683,17 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.fd_write (type 2) local.get 0 )
 )`,
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32, paramI32I32I32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 5, col: 38}},
 					{typeIndex: &index{numeric: wasm.Index(2), line: 6, col: 32}},
 				},
-				funcs: []*function{{body: localGet0End}, {body: localGet0End}},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+				code: []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.fd_write"},
+					},
 				},
 			},
 		},
@@ -700,15 +705,15 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.args_sizes_get (param i32 i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
-				typeUses: []*typeUse{{typeIndex: indexOne}, {typeIndex: indexOne}},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				funcs: []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+				types:       []*wasm.FunctionType{{}, paramI32I32ResultI32},
+				typeUses:    []*typeUse{{typeIndex: indexOne}, {typeIndex: indexOne}},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				code:        []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+					},
 				},
 			},
 		},
@@ -721,18 +726,18 @@ func TestParseModule(t *testing.T) {
 	(func $runtime.args_sizes_get (type 1) local.get 0)
 )`,
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 4, col: 76}},
 					{typeIndex: &index{numeric: wasm.Index(1), line: 5, col: 38}},
 				},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				funcs: []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				code:        []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+					},
 				},
 			},
 		},
@@ -745,18 +750,18 @@ func TestParseModule(t *testing.T) {
 	(type (func (param i32 i32) (result i32) ))
 )`,
 			expected: &module{
-				types: []*typeFunc{typeFuncEmpty, paramI32I32ResultI32},
+				types: []*wasm.FunctionType{{}, paramI32I32ResultI32},
 				typeUses: []*typeUse{
 					{typeIndex: &index{numeric: wasm.Index(1), line: 3, col: 76}},
 					{typeIndex: &index{numeric: wasm.Index(1), line: 4, col: 38}},
 				},
-				importFuncs: []*importFunc{
-					{importIndex: wasm.Index(0), module: "wasi_snapshot_preview1", name: "args_get"},
-				},
-				funcs: []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+				importFuncs: []*importFunc{{module: "wasi_snapshot_preview1", name: "args_get"}},
+				code:        []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "runtime.args_get"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "runtime.args_sizes_get"},
+					},
 				},
 			},
 		},
@@ -764,12 +769,14 @@ func TestParseModule(t *testing.T) {
 			name:  "func param names",
 			input: "(module (func $one (param $x i32) (param $y i32) (result i32) local.get 0))",
 			expected: &module{
-				types:     []*typeFunc{{params: []wasm.ValueType{i32, i32}, result: i32}},
-				typeUses:  []*typeUse{{typeIndex: indexZero}},
-				funcs:     []*function{{body: localGet0End}},
-				funcNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "one"}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
+				types:    []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}},
+				typeUses: []*typeUse{{typeIndex: indexZero}},
+				code:     []*wasm.Code{{Body: localGet0End}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "one"}},
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
+					},
 				},
 			},
 		},
@@ -780,24 +787,28 @@ func TestParseModule(t *testing.T) {
 	(func (param $l i32) (param $r i32) (result i32) local.get 0)
 )`,
 			expected: &module{
-				types:    []*typeFunc{{params: []wasm.ValueType{i32, i32}, result: i32}},
+				types:    []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}},
 				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				funcs:    []*function{{body: localGet0End}, {body: localGet0End}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
-					{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(1), Name: "r"}}},
+				code:     []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "x"}, {Index: wasm.Index(1), Name: "y"}}},
+						{Index: wasm.Index(1), NameMap: wasm.NameMap{{Index: wasm.Index(0), Name: "l"}, {Index: wasm.Index(1), Name: "r"}}},
+					},
 				},
 			},
 		},
 		{
-			name:  "func mixed param names", // Verifies we can handle less param fields than params
+			name:  "func mixed param names", // Verifies we can handle less param fields than Params
 			input: "(module (func (param i32 i32) (param $v i32) (param i64) (param $t f32)))",
 			expected: &module{
-				types:    []*typeFunc{{params: []wasm.ValueType{i32, i32, i32, i64, f32}}},
+				types:    []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32, i32, i64, f32}}},
 				typeUses: []*typeUse{{typeIndex: indexZero}},
-				funcs:    []*function{{body: end}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(2), Name: "v"}, {Index: wasm.Index(4), Name: "t"}}},
+				code:     []*wasm.Code{{Body: end}},
+				names: &wasm.NameSection{
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{{Index: wasm.Index(2), Name: "v"}, {Index: wasm.Index(4), Name: "t"}}},
+					},
 				},
 			},
 		},
@@ -808,13 +819,15 @@ func TestParseModule(t *testing.T) {
 	(export "bar" (func $bar))
 )`,
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "bar", exportIndex: wasm.Index(0), funcIndex: &index{numeric: wasm.Index(0), line: 3, col: 22}},
 				},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
+				},
 			},
 		},
 		{
@@ -824,7 +837,7 @@ func TestParseModule(t *testing.T) {
 	(export "bar" (func 0))
 )`,
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
 				exportFuncs: []*exportFunc{{name: "bar", funcIndex: &index{numeric: 0, line: 3, col: 22}}},
@@ -838,14 +851,16 @@ func TestParseModule(t *testing.T) {
 	(export "bar" (func $bar))
 )`,
 			expected: &module{
-				types:       []*typeFunc{typeFuncEmpty},
+				types:       []*wasm.FunctionType{{}},
 				typeUses:    []*typeUse{{typeIndex: indexZero}},
 				importFuncs: []*importFunc{{module: "foo", name: "bar"}},
-				funcNames:   wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{numeric: wasm.Index(0), line: 3, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{numeric: wasm.Index(0), line: 4, col: 22}},
 				},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"}},
+				},
 			},
 		},
 		{
@@ -857,20 +872,19 @@ func TestParseModule(t *testing.T) {
 	(export "bar" (func $qux))
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{module: "foo", name: "bar", importIndex: wasm.Index(0)},
-					{module: "baz", name: "qux", importIndex: wasm.Index(1)},
-				},
-				funcNames: wasm.NameMap{
-					&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"},
-					&wasm.NameAssoc{Index: wasm.Index(1), Name: "qux"},
-				},
+				types:       []*wasm.FunctionType{{}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "foo", name: "bar"}, {module: "baz", name: "qux"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{numeric: wasm.Index(0), line: 4, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{numeric: wasm.Index(1), line: 5, col: 22}},
 				},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{
+						&wasm.NameAssoc{Index: wasm.Index(0), Name: "bar"},
+						&wasm.NameAssoc{Index: wasm.Index(1), Name: "qux"},
+					},
+				},
 			},
 		},
 		{
@@ -882,12 +896,9 @@ func TestParseModule(t *testing.T) {
 	(export "bar" (func 1))
 )`,
 			expected: &module{
-				types:    []*typeFunc{typeFuncEmpty},
-				typeUses: []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
-				importFuncs: []*importFunc{
-					{module: "foo", name: "bar", importIndex: wasm.Index(0)},
-					{module: "baz", name: "qux", importIndex: wasm.Index(1)},
-				},
+				types:       []*wasm.FunctionType{{}},
+				typeUses:    []*typeUse{{typeIndex: indexZero}, {typeIndex: indexZero}},
+				importFuncs: []*importFunc{{module: "foo", name: "bar"}, {module: "baz", name: "qux"}},
 				exportFuncs: []*exportFunc{
 					{name: "foo", exportIndex: wasm.Index(0), funcIndex: &index{numeric: wasm.Index(0), line: 4, col: 22}},
 					{name: "bar", exportIndex: wasm.Index(1), funcIndex: &index{numeric: wasm.Index(1), line: 5, col: 22}},
@@ -901,11 +912,13 @@ func TestParseModule(t *testing.T) {
 	(start $hello)
 )`,
 			expected: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}},
 				importFuncs:   []*importFunc{{name: "hello"}},
-				funcNames:     wasm.NameMap{{Index: wasm.Index(0), Name: "hello"}},
 				startFunction: &index{numeric: wasm.Index(0), line: 3, col: 9},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "hello"}},
+				},
 			},
 		},
 		{
@@ -915,9 +928,9 @@ func TestParseModule(t *testing.T) {
 	(start 0)
 )`,
 			expected: &module{
-				types:         []*typeFunc{typeFuncEmpty},
+				types:         []*wasm.FunctionType{{}},
 				typeUses:      []*typeUse{{typeIndex: indexZero}},
-				importFuncs:   []*importFunc{{name: "hello", importIndex: wasm.Index(0)}},
+				importFuncs:   []*importFunc{{name: "hello"}},
 				startFunction: &index{numeric: wasm.Index(0), line: 3, col: 9},
 			},
 		},
@@ -935,20 +948,22 @@ func TestParseModule(t *testing.T) {
     (export "AddInt" (func $addInt))
 )`,
 			expected: &module{
-				types:    []*typeFunc{paramI32I32ResultI32},
+				types:    []*wasm.FunctionType{paramI32I32ResultI32},
 				typeUses: []*typeUse{{typeIndex: indexZero}},
-				funcs: []*function{
-					{body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}},
+				code: []*wasm.Code{
+					{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}},
 				},
 				exportFuncs: []*exportFunc{
 					{name: "AddInt", exportIndex: wasm.Index(0), funcIndex: &index{numeric: wasm.Index(0), line: 10, col: 28}},
 				},
-				funcNames: wasm.NameMap{{Index: wasm.Index(0), Name: "addInt"}},
-				paramNames: wasm.IndirectNameMap{
-					{Index: wasm.Index(0), NameMap: wasm.NameMap{
-						{Index: wasm.Index(0), Name: "value_1"},
-						{Index: wasm.Index(1), Name: "value_2"},
-					}},
+				names: &wasm.NameSection{
+					FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "addInt"}},
+					LocalNames: wasm.IndirectNameMap{
+						{Index: wasm.Index(0), NameMap: wasm.NameMap{
+							{Index: wasm.Index(0), Name: "value_1"},
+							{Index: wasm.Index(1), Name: "value_2"},
+						}},
+					},
 				},
 			},
 		},