Repository: tetratelabs/wazero
Pull request #1292 (2023-03-27 05:08:56)

Removes validatedActiveElementSegment
We allocated `[]validatedActiveElementSegment` during compilation, but 
no need to do that at the end of the day.


Patch:
diff --git a/internal/wasm/module.go b/internal/wasm/module.go
index 7b5cf376..cf7b030a 100644
--- a/internal/wasm/module.go
+++ b/internal/wasm/module.go
@@ -152,16 +152,6 @@ type Module struct {
 	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#custom-section%E2%91%A0
 	CustomSections []*CustomSection
 
-	// validatedActiveElementSegments are built on Validate when
-	// SectionIDElement is non-empty and all inputs are valid.
-	//
-	// Note: elementSegments retain Module.ElementSection order. Since an
-	// ElementSegment can overlap with another, order preservation ensures a
-	// consistent initialization result.
-	//
-	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-instances%E2%91%A0
-	validatedActiveElementSegments []validatedActiveElementSegment
-
 	// DataCountSection is the optional section and holds the number of data segments in the data section.
 	//
 	// Note: This may exist in WebAssembly 2.0 or WebAssembly 1.0 with CoreFeatureBulkMemoryOperations.
@@ -272,7 +262,7 @@ func (m *Module) Validate(enabledFeatures api.CoreFeatures) error {
 		}
 	} // No need to validate host functions as NewHostModule validates
 
-	if _, err = m.validateTable(enabledFeatures, tables, MaximumTableIndex); err != nil {
+	if err = m.validateTable(enabledFeatures, tables, MaximumTableIndex); err != nil {
 		return err
 	}
 
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index 81012fb3..74fc46f3 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -174,20 +174,29 @@ func (m *ModuleInstance) buildElementInstances(elements []ElementSegment) {
 	}
 }
 
-func (m *ModuleInstance) applyElements(elems []validatedActiveElementSegment) {
+func (m *ModuleInstance) applyElements(elems []ElementSegment) {
 	for elemI := range elems {
 		elem := &elems[elemI]
+		if !elem.IsActive() ||
+			// Per https://github.com/WebAssembly/spec/issues/1427 init can be no-op.
+			len(elem.Init) == 0 {
+			continue
+		}
 		var offset uint32
-		if elem.opcode == OpcodeGlobalGet {
-			global := m.Globals[elem.arg]
+		if elem.OffsetExpr.Opcode == OpcodeGlobalGet {
+			// Ignore error as it's already validated.
+			globalIdx, _, _ := leb128.LoadUint32(elem.OffsetExpr.Data)
+			global := m.Globals[globalIdx]
 			offset = uint32(global.Val)
 		} else {
-			offset = elem.arg // constant
+			// Ignore error as it's already validated.
+			o, _, _ := leb128.LoadInt32(elem.OffsetExpr.Data)
+			offset = uint32(o)
 		}
 
-		table := m.Tables[elem.tableIndex]
+		table := m.Tables[elem.TableIndex]
 		references := table.References
-		if int(offset)+len(elem.init) > len(references) {
+		if int(offset)+len(elem.Init) > len(references) {
 			// ErrElementOffsetOutOfBounds is the error raised when the active element offset exceeds the table length.
 			// Before CoreFeatureReferenceTypes, this was checked statically before instantiation, after the proposal,
 			// this must be raised as runtime error (as in assert_trap in spectest), not even an instantiation error.
@@ -199,11 +208,11 @@ func (m *ModuleInstance) applyElements(elems []validatedActiveElementSegment) {
 		}
 
 		if table.Type == RefTypeExternref {
-			for i := 0; i < len(elem.init); i++ {
+			for i := 0; i < len(elem.Init); i++ {
 				references[offset+uint32(i)] = Reference(0)
 			}
 		} else {
-			for i, fnIndex := range elem.init {
+			for i, fnIndex := range elem.Init {
 				if fnIndex != ElementInitNullReference {
 					references[offset+uint32(i)] = m.Engine.FunctionInstanceReference(fnIndex)
 				}
@@ -386,7 +395,7 @@ func (s *Store) instantiate(
 		return nil, err
 	}
 
-	m.applyElements(module.validatedActiveElementSegments)
+	m.applyElements(module.ElementSection)
 
 	// Execute the start function.
 	if module.StartSection != nil {
diff --git a/internal/wasm/table.go b/internal/wasm/table.go
index 19e07985..cd20fec0 100644
--- a/internal/wasm/table.go
+++ b/internal/wasm/table.go
@@ -124,42 +124,15 @@ type ElementInstance struct {
 // Reference is the runtime representation of RefType which is either RefTypeFuncref or RefTypeExternref.
 type Reference = uintptr
 
-// validatedActiveElementSegment is like ElementSegment of active mode except the inputs are expanded and validated based on defining module.
-//
-// Note: The global imported at globalIdx may have an offset value that is out-of-bounds for the corresponding table.
-type validatedActiveElementSegment struct {
-	// opcode is OpcodeGlobalGet or OpcodeI32Const
-	opcode Opcode
-
-	// arg is the only argument to opcode, which when applied results in the offset to add to init indices.
-	//  * OpcodeGlobalGet: position in the global index of an imported Global ValueTypeI32 holding the offset.
-	//  * OpcodeI32Const: a constant ValueTypeI32 offset.
-	arg uint32
-
-	// init are a range of table elements whose values are positions in the function index. This range
-	// replaces any values in TableInstance.Table at an offset arg which is a constant if opcode == OpcodeI32Const or
-	// derived from a globalIdx if opcode == OpcodeGlobalGet
-	init []Index
-
-	// tableIndex is the table's index to which this active element will be applied.
-	tableIndex Index
-}
-
 // validateTable ensures any ElementSegment is valid. This caches results via Module.validatedActiveElementSegments.
 // Note: limitsType are validated by decoders, so not re-validated here.
-func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table, maximumTableIndex uint32) ([]validatedActiveElementSegment, error) {
+func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table, maximumTableIndex uint32) error {
 	if len(tables) > int(maximumTableIndex) {
-		return nil, fmt.Errorf("too many tables in a module: %d given with limit %d", len(tables), maximumTableIndex)
-	}
-
-	if m.validatedActiveElementSegments != nil {
-		return m.validatedActiveElementSegments, nil
+		return fmt.Errorf("too many tables in a module: %d given with limit %d", len(tables), maximumTableIndex)
 	}
 
 	importedTableCount := m.ImportTableCount
 
-	ret := make([]validatedActiveElementSegment, 0, m.SectionElementCount(SectionIDElement))
-
 	// Create bounds checks as these can err prior to instantiation
 	funcCount := m.ImportFunctionCount + m.SectionElementCount(SectionIDFunction)
 
@@ -174,25 +147,25 @@ func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table,
 			// Any offset applied is to the element, not the function index: validate here if the funcidx is sound.
 			for ei, funcIdx := range elem.Init {
 				if funcIdx != ElementInitNullReference && funcIdx >= funcCount {
-					return nil, fmt.Errorf("%s[%d].init[%d] funcidx %d out of range", SectionIDName(SectionIDElement), idx, ei, funcIdx)
+					return fmt.Errorf("%s[%d].init[%d] funcidx %d out of range", SectionIDName(SectionIDElement), idx, ei, funcIdx)
 				}
 			}
 		} else {
 			for j, elem := range elem.Init {
 				if elem != ElementInitNullReference {
-					return nil, fmt.Errorf("%s[%d].init[%d] must be ref.null but was %v", SectionIDName(SectionIDElement), idx, j, elem)
+					return fmt.Errorf("%s[%d].init[%d] must be ref.null but was %v", SectionIDName(SectionIDElement), idx, j, elem)
 				}
 			}
 		}
 
 		if elem.IsActive() {
 			if len(tables) <= int(elem.TableIndex) {
-				return nil, fmt.Errorf("unknown table %d as active element target", elem.TableIndex)
+				return fmt.Errorf("unknown table %d as active element target", elem.TableIndex)
 			}
 
 			t := tables[elem.TableIndex]
 			if t.Type != elem.Type {
-				return nil, fmt.Errorf("element type mismatch: table has %s but element has %s",
+				return fmt.Errorf("element type mismatch: table has %s but element has %s",
 					RefTypeName(t.Type), RefTypeName(elem.Type),
 				)
 			}
@@ -202,46 +175,31 @@ func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table,
 			if oc == OpcodeGlobalGet {
 				globalIdx, _, err := leb128.LoadUint32(elem.OffsetExpr.Data)
 				if err != nil {
-					return nil, fmt.Errorf("%s[%d] couldn't read global.get parameter: %w", SectionIDName(SectionIDElement), idx, err)
+					return fmt.Errorf("%s[%d] couldn't read global.get parameter: %w", SectionIDName(SectionIDElement), idx, err)
 				} else if err = m.verifyImportGlobalI32(SectionIDElement, idx, globalIdx); err != nil {
-					return nil, err
+					return err
 				}
-
-				if initCount == 0 {
-					continue // Per https://github.com/WebAssembly/spec/issues/1427 init can be no-op, but validate anyway!
-				}
-
-				ret = append(ret, validatedActiveElementSegment{opcode: oc, arg: globalIdx, init: elem.Init, tableIndex: elem.TableIndex})
 			} else if oc == OpcodeI32Const {
-				// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
-				o, _, err := leb128.LoadInt32(elem.OffsetExpr.Data)
-				if err != nil {
-					return nil, fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), idx, err)
-				}
-				offset := Index(o)
-
 				// Per https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L117 we must pass if imported
 				// table has set its min=0. Per https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L142, we
 				// have to do fail if module-defined min=0.
 				if !enabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) && elem.TableIndex >= importedTableCount {
+					// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
+					o, _, err := leb128.LoadInt32(elem.OffsetExpr.Data)
+					if err != nil {
+						return fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), idx, err)
+					}
+					offset := Index(o)
 					if err = checkSegmentBounds(t.Min, uint64(initCount)+uint64(offset), idx); err != nil {
-						return nil, err
+						return err
 					}
 				}
-
-				if initCount == 0 {
-					continue // Per https://github.com/WebAssembly/spec/issues/1427 init can be no-op, but validate anyway!
-				}
-
-				ret = append(ret, validatedActiveElementSegment{opcode: oc, arg: offset, init: elem.Init, tableIndex: elem.TableIndex})
 			} else {
-				return nil, fmt.Errorf("%s[%d] has an invalid const expression: %s", SectionIDName(SectionIDElement), idx, InstructionName(oc))
+				return fmt.Errorf("%s[%d] has an invalid const expression: %s", SectionIDName(SectionIDElement), idx, InstructionName(oc))
 			}
 		}
 	}
-
-	m.validatedActiveElementSegments = ret
-	return ret, nil
+	return nil
 }
 
 // buildTable returns TableInstances if the module defines or imports a table.
@@ -263,25 +221,24 @@ func (m *ModuleInstance) buildTables(module *Module, skipBoundCheck bool) (err e
 		idx++
 	}
 
-	elementSegments := module.validatedActiveElementSegments
-	if len(elementSegments) == 0 {
-		return
-	}
-
 	if !skipBoundCheck {
-		for elemI := range elementSegments { // Do not loop over the value since elementSegments is a slice of value.
-			elem := &elementSegments[elemI]
-			table := m.Tables[elem.tableIndex]
+		for elemI := range module.ElementSection { // Do not loop over the value since elementSegments is a slice of value.
+			elem := &module.ElementSection[elemI]
+			table := m.Tables[elem.TableIndex]
 			var offset uint32
-			if elem.opcode == OpcodeGlobalGet {
-				global := m.Globals[elem.arg]
+			if elem.OffsetExpr.Opcode == OpcodeGlobalGet {
+				// Ignore error as it's already validated.
+				globalIdx, _, _ := leb128.LoadUint32(elem.OffsetExpr.Data)
+				global := m.Globals[globalIdx]
 				offset = uint32(global.Val)
-			} else {
-				offset = elem.arg // constant
+			} else { // i32.const
+				// Ignore error as it's already validated.
+				o, _, _ := leb128.LoadInt32(elem.OffsetExpr.Data)
+				offset = uint32(o)
 			}
 
 			// Check to see if we are out-of-bounds
-			initCount := uint64(len(elem.init))
+			initCount := uint64(len(elem.Init))
 			if err = checkSegmentBounds(table.Min, uint64(offset)+initCount, Index(elemI)); err != nil {
 				return
 			}
diff --git a/site/content/community/users.md b/site/content/community/users.md
index 80c0af35..2609ae84 100644
--- a/site/content/community/users.md
+++ b/site/content/community/users.md
@@ -10,12 +10,12 @@ considering their efforts before starting your own!
 
 ### Libraries
 
-| Name             | Description                                       |
-|:-----------------|---------------------------------------------------|
-| [go-pdfium][23]  | [PDFium][24] bindings to do PDF operations in Go  |
-| [go-re2][7]      | high performance regular expressions              |
-| [go-sqlite3][11] | [SQLite][12] bindings, `database/sql` driver      |
-| [mjml-go][19]    | Compile [MJML][20] to HTML directly in Go         |
+| Name             | Description                                                                                          |
+|:-----------------|------------------------------------------------------------------------------------------------------|
+| [go-pdfium][23]  | [PDFium][24] bindings to do PDF operations in Go, also available as end application [pdfium-cli][25] |
+| [go-re2][7]      | high performance regular expressions                                                                 |
+| [go-sqlite3][11] | [SQLite][12] bindings, `database/sql` driver                                                         |
+| [mjml-go][19]    | Compile [MJML][20] to HTML directly in Go                                                            |
 
 ### General purpose plugins
 
@@ -106,3 +106,5 @@ experience.
 [23]: https://github.com/klippa-app/go-pdfium
 
 [24]: https://pdfium.googlesource.com/pdfium/
+
+[25]: https://github.com/klippa-app/pdfium-cli


Test patch:
diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go
index 996b9f9e..8c363b2e 100644
--- a/internal/wasm/store_test.go
+++ b/internal/wasm/store_test.go
@@ -879,7 +879,9 @@ func globalsContain(globals []*GlobalInstance, want *GlobalInstance) bool {
 	return false
 }
 
-func TestModuleInstance_applyTableInits(t *testing.T) {
+func TestModuleInstance_applyElementsapplyElements(t *testing.T) {
+	leb128_100 := leb128.EncodeInt32(100)
+
 	t.Run("extenref", func(t *testing.T) {
 		m := &ModuleInstance{}
 		m.Tables = []*TableInstance{{Type: RefTypeExternref, References: make([]Reference, 10)}}
@@ -888,16 +890,16 @@ func TestModuleInstance_applyTableInits(t *testing.T) {
 		}
 
 		// This shouldn't panic.
-		m.applyElements([]validatedActiveElementSegment{{arg: 100}})
-		m.applyElements([]validatedActiveElementSegment{
-			{arg: 0, init: make([]Index, 3)},
-			{arg: 100}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
-			{arg: 5, init: make([]Index, 5)},
+		m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}}})
+		m.applyElements([]ElementSegment{
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: make([]Index, 3)},
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
 		})
 		require.Equal(t, []Reference{0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
 			m.Tables[0].References)
-		m.applyElements([]validatedActiveElementSegment{
-			{arg: 5, init: make([]Index, 5)},
+		m.applyElements([]ElementSegment{
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
 		})
 		require.Equal(t, []Reference{0, 0, 0, 0xffff, 0xffff, 0, 0, 0, 0, 0}, m.Tables[0].References)
 	})
@@ -914,16 +916,16 @@ func TestModuleInstance_applyTableInits(t *testing.T) {
 		}
 
 		// This shouldn't panic.
-		m.applyElements([]validatedActiveElementSegment{{arg: 100}})
-		m.applyElements([]validatedActiveElementSegment{
-			{arg: 0, init: []Index{0, 1, 2}},
-			{arg: 100}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
-			{arg: 5, init: make([]Index, 5)},
+		m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: []Index{1, 2, 3}}})
+		m.applyElements([]ElementSegment{
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0, 1, 2}},
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
 		})
 		require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
 			m.Tables[0].References)
-		m.applyElements([]validatedActiveElementSegment{
-			{arg: 5, init: []Index{0, ElementInitNullReference, 2}},
+		m.applyElements([]ElementSegment{
+			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: []Index{0, ElementInitNullReference, 2}},
 		})
 		require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xa, 0xffff, 0xaaa, 0xffff, 0xffff},
 			m.Tables[0].References)
diff --git a/internal/wasm/table_test.go b/internal/wasm/table_test.go
index f4601923..fe28f3e2 100644
--- a/internal/wasm/table_test.go
+++ b/internal/wasm/table_test.go
@@ -83,29 +83,24 @@ func TestModule_validateTable(t *testing.T) {
 	const maxTableIndex = 5
 	three := uint32(3)
 	tests := []struct {
-		name     string
-		input    *Module
-		expected []validatedActiveElementSegment
+		name  string
+		input *Module
 	}{
 		{
-			name:     "empty",
-			input:    &Module{},
-			expected: []validatedActiveElementSegment{},
+			name:  "empty",
+			input: &Module{},
 		},
 		{
-			name:     "min zero",
-			input:    &Module{TableSection: []Table{{}}},
-			expected: []validatedActiveElementSegment{},
+			name:  "min zero",
+			input: &Module{TableSection: []Table{{}}},
 		},
 		{
-			name:     "maximum number of tables",
-			input:    &Module{TableSection: []Table{{}, {}, {}, {}, {}}},
-			expected: []validatedActiveElementSegment{},
+			name:  "maximum number of tables",
+			input: &Module{TableSection: []Table{{}, {}, {}, {}, {}}},
 		},
 		{
-			name:     "min/max",
-			input:    &Module{TableSection: []Table{{Min: 1, Max: &three}}},
-			expected: []validatedActiveElementSegment{},
+			name:  "min/max",
+			input: &Module{TableSection: []Table{{Min: 1, Max: &three}}},
 		},
 		{ // See: https://github.com/WebAssembly/spec/issues/1427
 			name: "constant derived element offset=0 and no index",
@@ -121,7 +116,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{},
 		},
 		{
 			name: "constant derived element offset=0 and one index",
@@ -138,9 +132,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeI32Const, arg: 0, init: []Index{0}},
-			},
 		},
 		{
 			name: "constant derived element offset - ignores min on imported table",
@@ -158,9 +149,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeI32Const, arg: 0, init: []Index{0}},
-			},
 		},
 		{
 			name: "constant derived element offset=0 and one index - imported table",
@@ -177,9 +165,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeI32Const, arg: 0, init: []Index{0}},
-			},
 		},
 		{
 			name: "constant derived element offset and two indices",
@@ -196,9 +181,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeI32Const, arg: 1, init: []Index{0, 2}},
-			},
 		},
 		{ // See: https://github.com/WebAssembly/spec/issues/1427
 			name: "imported global derived element offset and no index",
@@ -217,7 +199,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{},
 		},
 		{
 			name: "imported global derived element offset and one index",
@@ -237,9 +218,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
-			},
 		},
 		{
 			name: "imported global derived element offset and one index - imported table",
@@ -259,9 +237,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
-			},
 		},
 		{
 			name: "imported global derived element offset - ignores min on imported table",
@@ -281,9 +256,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
-			},
 		},
 		{
 			name: "imported global derived element offset - two indices",
@@ -304,9 +276,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeGlobalGet, arg: 1, init: []Index{0, 2}},
-			},
 		},
 		{
 			name: "mixed elementSegments - const before imported global",
@@ -332,10 +301,6 @@ func TestModule_validateTable(t *testing.T) {
 					},
 				},
 			},
-			expected: []validatedActiveElementSegment{
-				{opcode: OpcodeI32Const, arg: 1, init: []Index{0, 2}},
-				{opcode: OpcodeGlobalGet, arg: 1, init: []Index{1, 2}},
-			},
 		},
 	}
 
@@ -346,15 +311,11 @@ func TestModule_validateTable(t *testing.T) {
 			_, _, _, tables, err := tc.input.AllDeclarations()
 			require.NoError(t, err)
 
-			vt, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
+			err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
 			require.NoError(t, err)
-			require.Equal(t, tc.expected, vt)
 
-			// Ensure it was cached. We have to use Equal not Same because this is a slice, not a pointer.
-			require.Equal(t, vt, tc.input.validatedActiveElementSegments)
-			vt2, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
+			err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
 			require.NoError(t, err)
-			require.Equal(t, vt, vt2)
 		})
 	}
 }
@@ -681,7 +642,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			_, _, _, tables, err := tc.input.AllDeclarations()
 			require.NoError(t, err)
-			_, err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
+			err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
@@ -704,33 +665,33 @@ func TestModule_buildTables(t *testing.T) {
 		{
 			name: "empty",
 			module: &Module{
-				validatedActiveElementSegments: []validatedActiveElementSegment{},
+				ElementSection: []ElementSegment{},
 			},
 		},
 		{
 			name: "min zero",
 			module: &Module{
-				TableSection:                   []Table{{Type: RefTypeFuncref}},
-				validatedActiveElementSegments: []validatedActiveElementSegment{},
+				TableSection:   []Table{{Type: RefTypeFuncref}},
+				ElementSection: []ElementSegment{},
 			},
 			expectedTables: []*TableInstance{{References: make([]Reference, 0), Min: 0, Type: RefTypeFuncref}},
 		},
 		{
 			name: "min/max",
 			module: &Module{
-				TableSection:                   []Table{{Min: 1, Max: &three}},
-				validatedActiveElementSegments: []validatedActiveElementSegment{},
+				TableSection:   []Table{{Min: 1, Max: &three}},
+				ElementSection: []ElementSegment{},
 			},
 			expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1, Max: &three}},
 		},
 		{ // See: https://github.com/WebAssembly/spec/issues/1427
 			name: "constant derived element offset=0 and no index",
 			module: &Module{
-				TypeSection:                    []FunctionType{{}},
-				TableSection:                   []Table{{Min: 1}},
-				FunctionSection:                []Index{0},
-				CodeSection:                    []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{},
+				TypeSection:     []FunctionType{{}},
+				TableSection:    []Table{{Min: 1}},
+				FunctionSection: []Index{0},
+				CodeSection:     []Code{codeEnd},
+				ElementSection:  []ElementSegment{},
 			},
 			expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
 		},
@@ -738,8 +699,8 @@ func TestModule_buildTables(t *testing.T) {
 			name: "null extern refs",
 			module: &Module{
 				TableSection: []Table{{Min: 10, Type: RefTypeExternref}},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 5, init: []Index{ElementInitNullReference, ElementInitNullReference, ElementInitNullReference}}, // three null refs.
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: []Index{ElementInitNullReference, ElementInitNullReference, ElementInitNullReference}}, // three null refs.
 				},
 			},
 			expectedTables: []*TableInstance{{References: make([]Reference, 10), Min: 10, Type: RefTypeExternref}},
@@ -751,8 +712,8 @@ func TestModule_buildTables(t *testing.T) {
 				TableSection:    []Table{{Min: 1}},
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 0, init: []Index{0}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
 				},
 			},
 			expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
@@ -763,8 +724,8 @@ func TestModule_buildTables(t *testing.T) {
 				TypeSection:     []FunctionType{{}},
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 0, init: []Index{0}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
 				},
 			},
 			importedTables: []*TableInstance{{Min: 2}},
@@ -777,8 +738,8 @@ func TestModule_buildTables(t *testing.T) {
 				ImportSection:   []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1}}},
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 0, init: []Index{0}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
 				},
 			},
 			importedTables: []*TableInstance{{Min: 1}},
@@ -791,8 +752,8 @@ func TestModule_buildTables(t *testing.T) {
 				TableSection:    []Table{{Min: 3}},
 				FunctionSection: []Index{0, 0, 0, 0},
 				CodeSection:     []Code{codeEnd, codeEnd, codeEnd, codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 1, init: []Index{0, 2}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}}, Init: []Index{0, 2}},
 				},
 			},
 			expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
@@ -804,10 +765,10 @@ func TestModule_buildTables(t *testing.T) {
 				ImportSection: []Import{
 					{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
 				},
-				TableSection:                   []Table{{Min: 1}},
-				FunctionSection:                []Index{0},
-				CodeSection:                    []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{},
+				TableSection:    []Table{{Min: 1}},
+				FunctionSection: []Index{0},
+				CodeSection:     []Code{codeEnd},
+				ElementSection:  []ElementSegment{},
 			},
 			importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
 			expectedTables:  []*TableInstance{{References: make([]Reference, 1), Min: 1}},
@@ -822,8 +783,8 @@ func TestModule_buildTables(t *testing.T) {
 				TableSection:    []Table{{Min: 2}},
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
 				},
 			},
 			importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
@@ -839,8 +800,8 @@ func TestModule_buildTables(t *testing.T) {
 				},
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
 				},
 			},
 			importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
@@ -857,8 +818,8 @@ func TestModule_buildTables(t *testing.T) {
 				},
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
 				},
 			},
 			importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
@@ -888,10 +849,6 @@ func TestModule_buildTables(t *testing.T) {
 						TableIndex: 0,
 					},
 				},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{tableIndex: 1, opcode: OpcodeGlobalGet, arg: 0, init: []Index{ElementInitNullReference, 2}},
-					{tableIndex: 0, opcode: OpcodeGlobalGet, arg: 1, init: []Index{0, 2}},
-				},
 			},
 			importedGlobals: []*GlobalInstance{
 				{Type: GlobalType{ValType: ValueTypeI64}, Val: 3},
@@ -913,9 +870,9 @@ func TestModule_buildTables(t *testing.T) {
 				TableSection:    []Table{{Min: 3}},
 				FunctionSection: []Index{0, 0, 0, 0},
 				CodeSection:     []Code{codeEnd, codeEnd, codeEnd, codeEnd},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 1, init: []Index{0, 2}},
-					{opcode: OpcodeGlobalGet, arg: 1, init: []Index{1, 2}},
+				ElementSection: []ElementSegment{
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}}, Init: []Index{0, 2}},
+					{OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}}, Init: []Index{1, 2}},
 				},
 			},
 			importedGlobals: []*GlobalInstance{
@@ -960,13 +917,10 @@ func TestModule_buildTable_Errors(t *testing.T) {
 				CodeSection:     []Code{codeEnd},
 				ElementSection: []ElementSegment{
 					{
-						OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
+						OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{2}},
 						Init:       []Index{0},
 					},
 				},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeI32Const, arg: 2, init: []Index{0}},
-				},
 			},
 			importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
 			expectedErr:    "element[0].init exceeds min table size",
@@ -987,9 +941,6 @@ func TestModule_buildTable_Errors(t *testing.T) {
 						Init:       []Index{0},
 					},
 				},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
-				},
 			},
 			importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 2}},
 			expectedErr:     "element[0].init exceeds min table size",
@@ -1011,9 +962,6 @@ func TestModule_buildTable_Errors(t *testing.T) {
 						Init:       []Index{0},
 					},
 				},
-				validatedActiveElementSegments: []validatedActiveElementSegment{
-					{opcode: OpcodeGlobalGet, arg: 0, init: []Index{0}},
-				},
 			},
 			importedTables:  []*TableInstance{{References: make([]Reference, 2), Min: 2}},
 			importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 2}},