Source File
table.go
Belonging Package
github.com/tetratelabs/wazero/internal/wasm
package wasmimport ()// Table describes the limits of elements and its type in a table.type Table struct {Min uint32Max *uint32Type RefType}// RefType is either RefTypeFuncref or RefTypeExternref as of WebAssembly core 2.0.type RefType = byteconst (// RefTypeFuncref represents a reference to a function.RefTypeFuncref = ValueTypeFuncref// RefTypeExternref represents a reference to a host object, which is not currently supported in wazero.RefTypeExternref = ValueTypeExternref)func ( RefType) ( string) {switch {case RefTypeFuncref:= "funcref"case RefTypeExternref:= "externref"default:= fmt.Sprintf("unknown(0x%x)", )}return}// ElementMode represents a mode of element segment which is either active, passive or declarative.//// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/modules.html#element-segmentstype ElementMode = byteconst (// ElementModeActive is the mode which requires the runtime to initialize table with the contents in .Init field combined with OffsetExpr.ElementModeActive ElementMode = iota// ElementModePassive is the mode which doesn't require the runtime to initialize table, and only used with OpcodeTableInitName.ElementModePassive// ElementModeDeclarative is introduced in reference-types proposal which can be used to declare function indexes used by OpcodeRefFunc.ElementModeDeclarative)// ElementSegment are initialization instructions for a TableInstance//// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-elemtype ElementSegment struct {// OffsetExpr returns the table element offset to apply to Init indices.// Note: This can be validated prior to instantiation unless it includes OpcodeGlobalGet (an imported global).OffsetExpr ConstantExpression// TableIndex is the table's index to which this element segment is applied.// Note: This is used if and only if the Mode is active.TableIndex Index// Followings are set/used regardless of the Mode.// Init indices are (nullable) table elements where each index is the function index by which the module initialize the table.Init []Index// Type holds the type of this element segment, which is the RefType in WebAssembly 2.0.Type RefType// Mode is the mode of this element segment.Mode ElementMode}const (// ElementInitNullReference represents the null reference in ElementSegment's Init.// In Wasm spec, an init item represents either Function's Index or null reference,// and in wazero, we limit the maximum number of functions available in a module to// MaximumFunctionIndex. Therefore, it is safe to use 1 << 31 to represent the null// reference in Element segments.ElementInitNullReference Index = 1 << 31// elementInitImportedGlobalReferenceType represents an init item which is resolved via an imported global constexpr.// The actual function reference stored at Global is only known at instantiation-time, so we set this flag// to items of ElementSegment.Init at binary decoding, and unwrap this flag at instantiation to resolve the value.//// This might collide the init element resolved via ref.func instruction which is resolved with the func index at decoding,// but in practice, that is not allowed in wazero thanks to our limit MaximumFunctionIndex. Thus, it is safe to set this flag// in init element to indicate as such.elementInitImportedGlobalReferenceType Index = 1 << 30)// unwrapElementInitGlobalReference takes an item of the init vector of an ElementSegment,// and returns the Global index if it is supposed to get generated from a global.// ok is true if the given init item is as such.func unwrapElementInitGlobalReference( Index) ( Index, bool) {if &elementInitImportedGlobalReferenceType == elementInitImportedGlobalReferenceType {return &^ elementInitImportedGlobalReferenceType, true}return , false}// WrapGlobalIndexAsElementInit wraps the given index as an init item which is resolved via an imported global value.// See the comments on elementInitImportedGlobalReferenceType for more details.func ( Index) Index {return | elementInitImportedGlobalReferenceType}// IsActive returns true if the element segment is "active" mode which requires the runtime to initialize table// with the contents in .Init field.func ( *ElementSegment) () bool {return .Mode == ElementModeActive}// TableInstance represents a table of (RefTypeFuncref) elements in a module.//// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-instances%E2%91%A0type TableInstance struct {// References holds references whose type is either RefTypeFuncref or RefTypeExternref (unsupported).//// Currently, only function references are supported.References []Reference// Min is the minimum (function) elements in this table and cannot grow to accommodate ElementSegment.Min uint32// Max if present is the maximum (function) elements in this table, or nil if unbounded.Max *uint32// Type is either RefTypeFuncref or RefTypeExternRef.Type RefType// The following is only used when the table is exported.// involvingModuleInstances is a set of module instances which are involved in the table instance.// This is critical for safety purpose because once a table is imported, it can hold any reference to// any function in the owner and importing module instances. Therefore, these module instance,// transitively the compiled modules, must be alive as long as the table instance is alive.involvingModuleInstances []*ModuleInstance// involvingModuleInstancesMutex is a mutex to protect involvingModuleInstances.involvingModuleInstancesMutex sync.RWMutex}// ElementInstance represents an element instance in a module.//// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#element-instancestype ElementInstance = []Reference// Reference is the runtime representation of RefType which is either RefTypeFuncref or RefTypeExternref.type Reference = uintptr// validateTable ensures any ElementSegment is valid. This caches results via Module.validatedActiveElementSegments.// Note: limitsType are validated by decoders, so not re-validated here.func ( *Module) ( api.CoreFeatures, []Table, uint32) error {if len() > int() {return fmt.Errorf("too many tables in a module: %d given with limit %d", len(), )}:= .ImportTableCount// Create bounds checks as these can err prior to instantiation:= .ImportFunctionCount + .SectionElementCount(SectionIDFunction):= .ImportGlobalCount + .SectionElementCount(SectionIDGlobal)// Now, we have to figure out which table elements can be resolved before instantiation and also fail early if there// are any imported globals that are known to be invalid by their declarations.for := range .ElementSection {:= &.ElementSection[]:= Index():= uint32(len(.Init))// Any offset applied is to the element, not the function index: validate here if the funcidx is sound.for , := range .Init {if == ElementInitNullReference {continue}, := unwrapElementInitGlobalReference()if {if >= {return fmt.Errorf("%s[%d].init[%d] global index %d out of range", SectionIDName(SectionIDElement), , , )}} else {if .Type == RefTypeExternref {return fmt.Errorf("%s[%d].init[%d] must be ref.null but was %d", SectionIDName(SectionIDElement), , , )}if >= {return fmt.Errorf("%s[%d].init[%d] func index %d out of range", SectionIDName(SectionIDElement), , , )}}}if .IsActive() {if len() <= int(.TableIndex) {return fmt.Errorf("unknown table %d as active element target", .TableIndex)}:= [.TableIndex]if .Type != .Type {return fmt.Errorf("element type mismatch: table has %s but element has %s",RefTypeName(.Type), RefTypeName(.Type),)}// global.get needs to be discovered during initialization:= .OffsetExpr.Opcodeif == OpcodeGlobalGet {, , := leb128.LoadUint32(.OffsetExpr.Data)if != nil {return fmt.Errorf("%s[%d] couldn't read global.get parameter: %w", SectionIDName(SectionIDElement), , )} else if = .verifyImportGlobalI32(SectionIDElement, , ); != nil {return}} else if == OpcodeI32Const {// 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 !.IsEnabled(api.CoreFeatureReferenceTypes) && .TableIndex >= {// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md, , := leb128.LoadInt32(.OffsetExpr.Data)if != nil {return fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), , )}:= Index()if = checkSegmentBounds(.Min, uint64()+uint64(), ); != nil {return}}} else {return fmt.Errorf("%s[%d] has an invalid const expression: %s", SectionIDName(SectionIDElement), , InstructionName())}}}return nil}// buildTable returns TableInstances if the module defines or imports a table.// - importedTables: returned as `tables` unmodified.// - importedGlobals: include all instantiated, imported globals.//// If the result `init` is non-nil, it is the `tableInit` parameter of Engine.NewModuleEngine.//// Note: An error is only possible when an ElementSegment.OffsetExpr is out of range of the TableInstance.Min.func ( *ModuleInstance) ( *Module, bool) ( error) {:= .ImportTableCountfor := range .TableSection {:= &.TableSection[]// The module defining the table is the one that sets its Min/Max etc..Tables[] = &TableInstance{References: make([]Reference, .Min), Min: .Min, Max: .Max,Type: .Type,}++}if ! {for := range .ElementSection { // Do not loop over the value since elementSegments is a slice of value.:= &.ElementSection[]:= .Tables[.TableIndex]var uint32if .OffsetExpr.Opcode == OpcodeGlobalGet {// Ignore error as it's already validated., , := leb128.LoadUint32(.OffsetExpr.Data):= .Globals[]= uint32(.Val)} else { // i32.const// Ignore error as it's already validated., , := leb128.LoadInt32(.OffsetExpr.Data)= uint32()}// Check to see if we are out-of-bounds:= uint64(len(.Init))if = checkSegmentBounds(.Min, uint64()+, Index()); != nil {return}}}return}// checkSegmentBounds fails if the capacity needed for an ElementSegment.Init is larger than limitsType.Min//// WebAssembly 1.0 (20191205) doesn't forbid growing to accommodate element segments, and spectests are inconsistent.// For example, the spectests enforce elements within Table limitsType.Min, but ignore Import.DescTable min. What this// means is we have to delay offset checks on imported tables until we link to them.// e.g. https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L117 wants pass on min=0 for import// e.g. https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L142 wants fail on min=0 module-definedfunc checkSegmentBounds( uint32, uint64, Index) error { // uint64 in case offset was set to -1if > uint64() {return fmt.Errorf("%s[%d].init exceeds min table size", SectionIDName(SectionIDElement), )}return nil}func ( *Module) ( SectionID, Index, uint32) error {:= uint32(math.MaxUint32) // +1 == 0for := range .ImportSection {:= &.ImportSection[]if .Type == ExternTypeGlobal {++if == {if .DescGlobal.ValType != ValueTypeI32 {return fmt.Errorf("%s[%d] (global.get %d): import[%d].global.ValType != i32", SectionIDName(), , , )}return nil}}}return fmt.Errorf("%s[%d] (global.get %d): out of range of imported globals", SectionIDName(), , )}// Grow appends the `initialRef` by `delta` times into the References slice.// Returns -1 if the operation is not valid, otherwise the old length of the table.//// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-grow-xfunc ( *TableInstance) ( uint32, Reference) ( uint32) {= uint32(len(.References))if == 0 {return}if := int64() + int64(); // adding as 64bit ints to avoid overflow.>= math.MaxUint32 || (.Max != nil && > int64(*.Max)) {return 0xffffffff // = -1 in signed 32-bit integer.}.References = append(.References, make([]uintptr, )...)if == 0 {return}// Uses the copy trick for faster filling the new region with the initial value.// https://github.com/golang/go/blob/go1.24.0/src/slices/slices.go#L514-L517:= .References[:][0] =for := 1; < len(); *= 2 {copy([:], [:])}return}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |