While compilers should be conservative when targeting WebAssembly Core features, runtimes should be lenient as otherwise people need to constantly turn on all features. Currently, most examples have to turn on 2.0 features because compilers such as AssemblyScript and TinyGo use them by default. This matches the policy with the reality, and should make first time use easier. This top-levels an internal type as `api.CoreFeatures` and defaults to 2.0 as opposed to 1.0, our previous default. This is less cluttered than the excess of `WithXXX` methods we had prior to implementing all planned WebAssembly Core Specification 1.0 features. Finally, this backfills rationale as flat config types were a distinct decision even if feature set selection muddied the topic. Signed-off-by: Adrian Cole <adrian@tetrate.io>
89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
package binary
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/internal/leb128"
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
)
|
|
|
|
// dataSegmentPrefix represents three types of data segments.
|
|
//
|
|
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-section
|
|
type dataSegmentPrefix = uint32
|
|
|
|
const (
|
|
// dataSegmentPrefixActive is the prefix for the version 1.0 compatible data segment, which is classified as "active" in 2.0.
|
|
dataSegmentPrefixActive dataSegmentPrefix = 0x0
|
|
// dataSegmentPrefixPassive prefixes the "passive" data segment as in version 2.0 specification.
|
|
dataSegmentPrefixPassive dataSegmentPrefix = 0x1
|
|
// dataSegmentPrefixActiveWithMemoryIndex is the active prefix with memory index encoded which is defined for futur use as of 2.0.
|
|
dataSegmentPrefixActiveWithMemoryIndex dataSegmentPrefix = 0x2
|
|
)
|
|
|
|
func decodeDataSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.DataSegment, error) {
|
|
dataSegmentPrefx, _, err := leb128.DecodeUint32(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read data segment prefix: %w", err)
|
|
}
|
|
|
|
if dataSegmentPrefx != dataSegmentPrefixActive {
|
|
if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
|
|
return nil, fmt.Errorf("non-zero prefix for data segment is invalid as %w", err)
|
|
}
|
|
}
|
|
|
|
var expr *wasm.ConstantExpression
|
|
switch dataSegmentPrefx {
|
|
case dataSegmentPrefixActive,
|
|
dataSegmentPrefixActiveWithMemoryIndex:
|
|
// Active data segment as in
|
|
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-section
|
|
if dataSegmentPrefx == 0x2 {
|
|
d, _, err := leb128.DecodeUint32(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read memory index: %v", err)
|
|
} else if d != 0 {
|
|
return nil, fmt.Errorf("memory index must be zero but was %d", d)
|
|
}
|
|
}
|
|
|
|
expr, err = decodeConstantExpression(r, enabledFeatures)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read offset expression: %v", err)
|
|
}
|
|
case dataSegmentPrefixPassive:
|
|
// Passive data segment doesn't need const expr nor memory index encoded.
|
|
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-section
|
|
default:
|
|
return nil, fmt.Errorf("invalid data segment prefix: 0x%x", dataSegmentPrefx)
|
|
}
|
|
|
|
vs, _, err := leb128.DecodeUint32(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get the size of vector: %v", err)
|
|
}
|
|
|
|
b := make([]byte, vs)
|
|
if _, err := io.ReadFull(r, b); err != nil {
|
|
return nil, fmt.Errorf("read bytes for init: %v", err)
|
|
}
|
|
|
|
return &wasm.DataSegment{
|
|
OffsetExpression: expr,
|
|
Init: b,
|
|
}, nil
|
|
}
|
|
|
|
func encodeDataSegment(d *wasm.DataSegment) (ret []byte) {
|
|
// Currently multiple memories are not supported.
|
|
ret = append(ret, leb128.EncodeInt32(0)...)
|
|
ret = append(ret, encodeConstantExpression(d.OffsetExpression)...)
|
|
ret = append(ret, leb128.EncodeUint32(uint32(len(d.Init)))...)
|
|
ret = append(ret, d.Init...)
|
|
return
|
|
}
|