Removes validatedActiveElementSegment (#1292)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}},
|
||||
|
||||
Reference in New Issue
Block a user