Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -0,0 +1,77 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// Package constant contains WASM constant definitions.
package constant
// Magic bytes at the beginning of every WASM file ("\0asm").
const Magic = uint32(0x6D736100)
// Version defines the WASM version.
const Version = uint32(1)
// WASM module section IDs.
const (
CustomSectionID uint8 = iota
TypeSectionID
ImportSectionID
FunctionSectionID
TableSectionID
MemorySectionID
GlobalSectionID
ExportSectionID
StartSectionID
ElementSectionID
CodeSectionID
DataSectionID
)
// FunctionTypeID indicates the start of a function type definition.
const FunctionTypeID = byte(0x60)
// ValueType represents an intrinsic value type in WASM.
const (
ValueTypeF64 byte = iota + 0x7C
ValueTypeF32
ValueTypeI64
ValueTypeI32
)
// WASM import descriptor types.
const (
ImportDescType byte = iota
ImportDescTable
ImportDescMem
ImportDescGlobal
)
// WASM export descriptor types.
const (
ExportDescType byte = iota
ExportDescTable
ExportDescMem
ExportDescGlobal
)
// ElementTypeAnyFunc indicates the type of a table import.
const ElementTypeAnyFunc byte = 0x70
// BlockTypeEmpty represents a block type.
const BlockTypeEmpty byte = 0x40
// WASM global varialbe mutability flag.
const (
Const byte = iota
Mutable
)
// NameSectionCustomID is the ID of the "Name" section Custom Section
const NameSectionCustomID = "name"
// Subtypes of the 'name' custom section
const (
NameSectionModuleType byte = iota
NameSectionFunctionsType
NameSectionLocalsType
)
@@ -0,0 +1,6 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// Package encoding implements WASM module reading and writing.
package encoding
@@ -0,0 +1,965 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package encoding
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/open-policy-agent/opa/internal/leb128"
"github.com/open-policy-agent/opa/internal/wasm/constant"
"github.com/open-policy-agent/opa/internal/wasm/instruction"
"github.com/open-policy-agent/opa/internal/wasm/module"
"github.com/open-policy-agent/opa/internal/wasm/opcode"
"github.com/open-policy-agent/opa/internal/wasm/types"
)
// ReadModule reads a binary-encoded WASM module from r.
func ReadModule(r io.Reader) (*module.Module, error) {
wr := &reader{r: r, n: 0}
module, err := readModule(wr)
if err != nil {
return nil, fmt.Errorf("offset 0x%x: %w", wr.n, err)
}
return module, nil
}
// ReadCodeEntry reads a binary-encoded WASM code entry from r.
func ReadCodeEntry(r io.Reader) (*module.CodeEntry, error) {
wr := &reader{r: r, n: 0}
entry, err := readCodeEntry(wr)
if err != nil {
return nil, fmt.Errorf("offset 0x%x: %w", wr.n, err)
}
return entry, nil
}
// CodeEntries returns the WASM code entries contained in r.
func CodeEntries(m *module.Module) ([]*module.CodeEntry, error) {
entries := make([]*module.CodeEntry, len(m.Code.Segments))
for i, s := range m.Code.Segments {
buf := bytes.NewBuffer(s.Code)
entry, err := ReadCodeEntry(buf)
if err != nil {
return nil, err
}
entries[i] = entry
}
return entries, nil
}
type reader struct {
r io.Reader
n int
}
func (r *reader) Read(bs []byte) (int, error) {
n, err := r.r.Read(bs)
r.n += n
return n, err
}
func readModule(r io.Reader) (*module.Module, error) {
if err := readMagic(r); err != nil {
return nil, err
}
if err := readVersion(r); err != nil {
return nil, err
}
var m module.Module
if err := readSections(r, &m); err != io.EOF {
return nil, err
}
return &m, nil
}
func readCodeEntry(r io.Reader) (*module.CodeEntry, error) {
var entry module.CodeEntry
if err := readLocals(r, &entry.Func.Locals); err != nil {
return nil, fmt.Errorf("local declarations: %w", err)
}
return &entry, readExpr(r, &entry.Func.Expr)
}
func readMagic(r io.Reader) error {
var v uint32
if err := binary.Read(r, binary.LittleEndian, &v); err != nil {
return err
} else if v != constant.Magic {
return errors.New("illegal magic value")
}
return nil
}
func readVersion(r io.Reader) error {
var v uint32
if err := binary.Read(r, binary.LittleEndian, &v); err != nil {
return err
} else if v != constant.Version {
return errors.New("illegal wasm version")
}
return nil
}
func readSections(r io.Reader, m *module.Module) error {
for {
id, err := readByte(r)
if err != nil {
return err
}
size, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
buf := make([]byte, size)
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
bufr := bytes.NewReader(buf)
switch id {
case constant.StartSectionID:
if err := readStartSection(bufr, &m.Start); err != nil {
return fmt.Errorf("start section: %w", err)
}
case constant.CustomSectionID:
var name string
if err := readByteVectorString(bufr, &name); err != nil {
return fmt.Errorf("read custom section type: %w", err)
}
if name == "name" {
if err := readCustomNameSections(bufr, &m.Names); err != nil {
return fmt.Errorf("custom 'name' section: %w", err)
}
} else {
if err := readCustomSection(bufr, name, &m.Customs); err != nil {
return fmt.Errorf("custom section: %w", err)
}
}
case constant.TypeSectionID:
if err := readTypeSection(bufr, &m.Type); err != nil {
return fmt.Errorf("type section: %w", err)
}
case constant.ImportSectionID:
if err := readImportSection(bufr, &m.Import); err != nil {
return fmt.Errorf("import section: %w", err)
}
case constant.TableSectionID:
if err := readTableSection(bufr, &m.Table); err != nil {
return fmt.Errorf("table section: %w", err)
}
case constant.MemorySectionID:
if err := readMemorySection(bufr, &m.Memory); err != nil {
return fmt.Errorf("memory section: %w", err)
}
case constant.GlobalSectionID:
if err := readGlobalSection(bufr, &m.Global); err != nil {
return fmt.Errorf("global section: %w", err)
}
case constant.FunctionSectionID:
if err := readFunctionSection(bufr, &m.Function); err != nil {
return fmt.Errorf("function section: %w", err)
}
case constant.ExportSectionID:
if err := readExportSection(bufr, &m.Export); err != nil {
return fmt.Errorf("export section: %w", err)
}
case constant.ElementSectionID:
if err := readElementSection(bufr, &m.Element); err != nil {
return fmt.Errorf("element section: %w", err)
}
case constant.DataSectionID:
if err := readDataSection(bufr, &m.Data); err != nil {
return fmt.Errorf("data section: %w", err)
}
case constant.CodeSectionID:
if err := readRawCodeSection(bufr, &m.Code); err != nil {
return fmt.Errorf("code section: %w", err)
}
default:
return errors.New("illegal section id")
}
}
}
func readCustomSection(r io.Reader, name string, s *[]module.CustomSection) error {
buf, err := io.ReadAll(r)
if err != nil {
return err
}
*s = append(*s, module.CustomSection{
Name: name,
Data: buf,
})
return nil
}
func readCustomNameSections(r io.Reader, s *module.NameSection) error {
for {
id, err := readByte(r)
if err != nil {
if err == io.EOF {
break
}
return err
}
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
buf := make([]byte, n)
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
bufr := bytes.NewReader(buf)
switch id {
case constant.NameSectionModuleType:
err = readNameSectionModule(bufr, s)
case constant.NameSectionFunctionsType:
err = readNameSectionFunctions(bufr, s)
case constant.NameSectionLocalsType:
err = readNameSectionLocals(bufr, s)
}
if err != nil {
return err
}
}
return nil
}
func readNameSectionModule(r io.Reader, s *module.NameSection) error {
return readByteVectorString(r, &s.Module)
}
func readNameSectionFunctions(r io.Reader, s *module.NameSection) error {
nm, err := readNameMap(r)
if err != nil {
return err
}
s.Functions = nm
return nil
}
func readNameMap(r io.Reader) ([]module.NameMap, error) {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return nil, err
}
nm := make([]module.NameMap, n)
for i := range n {
var name string
id, err := leb128.ReadVarUint32(r)
if err != nil {
return nil, err
}
if err := readByteVectorString(r, &name); err != nil {
return nil, err
}
nm[i] = module.NameMap{Index: id, Name: name}
}
return nm, nil
}
func readNameSectionLocals(r io.Reader, s *module.NameSection) error {
n, err := leb128.ReadVarUint32(r) // length of vec(indirectnameassoc)
if err != nil {
return err
}
for range n {
id, err := leb128.ReadVarUint32(r) // func index
if err != nil {
return err
}
nm, err := readNameMap(r)
if err != nil {
return err
}
for _, m := range nm {
s.Locals = append(s.Locals, module.LocalNameMap{
FuncIndex: id,
NameMap: module.NameMap{
Index: m.Index,
Name: m.Name,
}})
}
}
return nil
}
func readStartSection(r io.Reader, s *module.StartSection) error {
idx, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.FuncIndex = &idx
return nil
}
func readTypeSection(r io.Reader, s *module.TypeSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var ftype module.FunctionType
if err := readFunctionType(r, &ftype); err != nil {
return err
}
s.Functions = append(s.Functions, ftype)
}
return nil
}
func readImportSection(r io.Reader, s *module.ImportSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var imp module.Import
if err := readImport(r, &imp); err != nil {
return err
}
s.Imports = append(s.Imports, imp)
}
return nil
}
func readTableSection(r io.Reader, s *module.TableSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var table module.Table
if elem, err := readByte(r); err != nil {
return err
} else if elem != constant.ElementTypeAnyFunc {
return errors.New("illegal element type")
}
table.Type = types.Anyfunc
if err := readLimits(r, &table.Lim); err != nil {
return err
}
s.Tables = append(s.Tables, table)
}
return nil
}
func readMemorySection(r io.Reader, s *module.MemorySection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var mem module.Memory
if err := readLimits(r, &mem.Lim); err != nil {
return err
}
s.Memories = append(s.Memories, mem)
}
return nil
}
func readGlobalSection(r io.Reader, s *module.GlobalSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var global module.Global
if err := readGlobal(r, &global); err != nil {
return err
}
s.Globals = append(s.Globals, global)
}
return nil
}
func readFunctionSection(r io.Reader, s *module.FunctionSection) error {
return readVarUint32Vector(r, &s.TypeIndices)
}
func readExportSection(r io.Reader, s *module.ExportSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var exp module.Export
if err := readExport(r, &exp); err != nil {
return err
}
s.Exports = append(s.Exports, exp)
}
return nil
}
func readElementSection(r io.Reader, s *module.ElementSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var seg module.ElementSegment
if err := readElementSegment(r, &seg); err != nil {
return err
}
s.Segments = append(s.Segments, seg)
}
return nil
}
func readDataSection(r io.Reader, s *module.DataSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var seg module.DataSegment
if err := readDataSegment(r, &seg); err != nil {
return err
}
s.Segments = append(s.Segments, seg)
}
return nil
}
func readRawCodeSection(r io.Reader, s *module.RawCodeSection) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
for range n {
var seg module.RawCodeSegment
if err := readRawCodeSegment(r, &seg); err != nil {
return err
}
s.Segments = append(s.Segments, seg)
}
return nil
}
func readFunctionType(r io.Reader, ftype *module.FunctionType) error {
if b, err := readByte(r); err != nil {
return err
} else if b != constant.FunctionTypeID {
return fmt.Errorf("illegal function type id 0x%x", b)
}
if err := readValueTypeVector(r, &ftype.Params); err != nil {
return err
}
return readValueTypeVector(r, &ftype.Results)
}
func readGlobal(r io.Reader, global *module.Global) error {
if err := readValueType(r, &global.Type); err != nil {
return err
}
b, err := readByte(r)
if err != nil {
return err
}
if b == 1 {
global.Mutable = true
} else if b != 0 {
return errors.New("illegal mutability flag")
}
return readConstantExpr(r, &global.Init)
}
func readImport(r io.Reader, imp *module.Import) error {
if err := readByteVectorString(r, &imp.Module); err != nil {
return err
}
if err := readByteVectorString(r, &imp.Name); err != nil {
return err
}
b, err := readByte(r)
if err != nil {
return err
}
if b == constant.ImportDescType {
index, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
imp.Descriptor = module.FunctionImport{
Func: index,
}
return nil
}
if b == constant.ImportDescTable {
if elem, err := readByte(r); err != nil {
return err
} else if elem != constant.ElementTypeAnyFunc {
return errors.New("illegal element type")
}
desc := module.TableImport{
Type: types.Anyfunc,
}
if err := readLimits(r, &desc.Lim); err != nil {
return err
}
imp.Descriptor = desc
return nil
}
if b == constant.ImportDescMem {
desc := module.MemoryImport{}
if err := readLimits(r, &desc.Mem.Lim); err != nil {
return err
}
imp.Descriptor = desc
return nil
}
if b == constant.ImportDescGlobal {
desc := module.GlobalImport{}
if err := readValueType(r, &desc.Type); err != nil {
return err
}
b, err := readByte(r)
if err != nil {
return err
}
if b == 1 {
desc.Mutable = true
} else if b != 0 {
return errors.New("illegal mutability flag")
}
return nil
}
return errors.New("illegal import descriptor type")
}
func readExport(r io.Reader, exp *module.Export) error {
if err := readByteVectorString(r, &exp.Name); err != nil {
return err
}
b, err := readByte(r)
if err != nil {
return err
}
switch b {
case constant.ExportDescType:
exp.Descriptor.Type = module.FunctionExportType
case constant.ExportDescTable:
exp.Descriptor.Type = module.TableExportType
case constant.ExportDescMem:
exp.Descriptor.Type = module.MemoryExportType
case constant.ExportDescGlobal:
exp.Descriptor.Type = module.GlobalExportType
default:
return errors.New("illegal export descriptor type")
}
exp.Descriptor.Index, err = leb128.ReadVarUint32(r)
if err != nil {
return err
}
return nil
}
func readElementSegment(r io.Reader, seg *module.ElementSegment) error {
if err := readVarUint32(r, &seg.Index); err != nil {
return err
}
if err := readConstantExpr(r, &seg.Offset); err != nil {
return err
}
return readVarUint32Vector(r, &seg.Indices)
}
func readDataSegment(r io.Reader, seg *module.DataSegment) error {
if err := readVarUint32(r, &seg.Index); err != nil {
return err
}
if err := readConstantExpr(r, &seg.Offset); err != nil {
return err
}
return readByteVector(r, &seg.Init)
}
func readRawCodeSegment(r io.Reader, seg *module.RawCodeSegment) error {
return readByteVector(r, &seg.Code)
}
func readConstantExpr(r io.Reader, expr *module.Expr) error {
instrs := make([]instruction.Instruction, 0)
for {
b, err := readByte(r)
if err != nil {
return err
}
switch opcode.Opcode(b) {
case opcode.I32Const:
i32, err := leb128.ReadVarInt32(r)
if err != nil {
return err
}
instrs = append(instrs, instruction.I32Const{Value: i32})
case opcode.I64Const:
i64, err := leb128.ReadVarInt64(r)
if err != nil {
return err
}
instrs = append(instrs, instruction.I64Const{Value: i64})
case opcode.End:
expr.Instrs = instrs
return nil
default:
return fmt.Errorf("illegal constant expr opcode 0x%x", b)
}
}
}
func readExpr(r io.Reader, expr *module.Expr) (err error) {
defer func() {
if r := recover(); r != nil {
switch r := r.(type) {
case error:
err = r
default:
err = errors.New("unknown panic")
}
}
}()
return readInstructions(r, &expr.Instrs)
}
func readInstructions(r io.Reader, instrs *[]instruction.Instruction) error {
ret := make([]instruction.Instruction, 0)
for {
b, err := readByte(r)
if err != nil {
return err
}
switch opcode.Opcode(b) {
case opcode.I32Const:
ret = append(ret, instruction.I32Const{Value: leb128.MustReadVarInt32(r)})
case opcode.I64Const:
ret = append(ret, instruction.I64Const{Value: leb128.MustReadVarInt64(r)})
case opcode.I32Eqz:
ret = append(ret, instruction.I32Eqz{})
case opcode.GetLocal:
ret = append(ret, instruction.GetLocal{Index: leb128.MustReadVarUint32(r)})
case opcode.SetLocal:
ret = append(ret, instruction.SetLocal{Index: leb128.MustReadVarUint32(r)})
case opcode.Call:
ret = append(ret, instruction.Call{Index: leb128.MustReadVarUint32(r)})
case opcode.CallIndirect:
ret = append(ret, instruction.CallIndirect{
Index: leb128.MustReadVarUint32(r),
Reserved: mustReadByte(r),
})
case opcode.BrIf:
ret = append(ret, instruction.BrIf{Index: leb128.MustReadVarUint32(r)})
case opcode.Return:
ret = append(ret, instruction.Return{})
case opcode.Block:
block := instruction.Block{}
if err := readBlockValueType(r, block.Type); err != nil {
return err
}
if err := readInstructions(r, &block.Instrs); err != nil {
return err
}
ret = append(ret, block)
case opcode.Loop:
loop := instruction.Loop{}
if err := readBlockValueType(r, loop.Type); err != nil {
return err
}
if err := readInstructions(r, &loop.Instrs); err != nil {
return err
}
ret = append(ret, loop)
case opcode.End:
*instrs = ret
return nil
default:
return fmt.Errorf("illegal opcode 0x%x", b)
}
}
}
func mustReadByte(r io.Reader) byte {
b, err := readByte(r)
if err != nil {
panic(err)
}
return b
}
func readLimits(r io.Reader, l *module.Limit) error {
b, err := readByte(r)
if err != nil {
return err
}
minLim, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
l.Min = minLim
if b == 1 {
maxLim, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
l.Max = &maxLim
} else if b != 0 {
return errors.New("illegal limit flag")
}
return nil
}
func readLocals(r io.Reader, locals *[]module.LocalDeclaration) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
ret := make([]module.LocalDeclaration, n)
for i := range n {
if err := readVarUint32(r, &ret[i].Count); err != nil {
return err
}
if err := readValueType(r, &ret[i].Type); err != nil {
return err
}
}
*locals = ret
return nil
}
func readByteVector(r io.Reader, v *[]byte) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
buf := make([]byte, n)
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
*v = buf
return nil
}
func readByteVectorString(r io.Reader, v *string) error {
var buf []byte
if err := readByteVector(r, &buf); err != nil {
return err
}
*v = string(buf)
return nil
}
func readVarUint32Vector(r io.Reader, v *[]uint32) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
ret := make([]uint32, n)
for i := range n {
if err := readVarUint32(r, &ret[i]); err != nil {
return err
}
}
*v = ret
return nil
}
func readValueTypeVector(r io.Reader, v *[]types.ValueType) error {
n, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
ret := make([]types.ValueType, n)
for i := range n {
if err := readValueType(r, &ret[i]); err != nil {
return err
}
}
*v = ret
return nil
}
func readVarUint32(r io.Reader, v *uint32) error {
var err error
*v, err = leb128.ReadVarUint32(r)
return err
}
func readValueType(r io.Reader, v *types.ValueType) error {
if b, err := readByte(r); err != nil {
return err
} else if b == constant.ValueTypeI32 {
*v = types.I32
} else if b == constant.ValueTypeI64 {
*v = types.I64
} else if b == constant.ValueTypeF32 {
*v = types.F32
} else if b == constant.ValueTypeF64 {
*v = types.F64
} else {
return fmt.Errorf("illegal value type: 0x%x", b)
}
return nil
}
func readBlockValueType(r io.Reader, v *types.ValueType) error {
if b, err := readByte(r); err != nil {
return err
} else if b == constant.ValueTypeI32 {
*v = types.I32
} else if b == constant.ValueTypeI64 {
*v = types.I64
} else if b == constant.ValueTypeF32 {
*v = types.F32
} else if b == constant.ValueTypeF64 {
*v = types.F64
} else if b != constant.BlockTypeEmpty {
return fmt.Errorf("illegal value type: 0x%x", b)
}
return nil
}
func readByte(r io.Reader) (byte, error) {
buf := make([]byte, 1)
_, err := io.ReadFull(r, buf)
return buf[0], err
}
@@ -0,0 +1,778 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package encoding
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"github.com/open-policy-agent/opa/internal/leb128"
"github.com/open-policy-agent/opa/internal/wasm/constant"
"github.com/open-policy-agent/opa/internal/wasm/instruction"
"github.com/open-policy-agent/opa/internal/wasm/module"
"github.com/open-policy-agent/opa/internal/wasm/opcode"
"github.com/open-policy-agent/opa/internal/wasm/types"
)
// WriteModule writes a binary-encoded representation of module to w.
func WriteModule(w io.Writer, module *module.Module) error {
if err := writeMagic(w); err != nil {
return err
}
if err := writeVersion(w); err != nil {
return err
}
if module == nil {
return nil
}
if err := writeTypeSection(w, module.Type); err != nil {
return err
}
if err := writeImportSection(w, module.Import); err != nil {
return err
}
if err := writeFunctionSection(w, module.Function); err != nil {
return err
}
if err := writeTableSection(w, module.Table); err != nil {
return err
}
if err := writeMemorySection(w, module.Memory); err != nil {
return err
}
if err := writeGlobalSection(w, module.Global); err != nil {
return err
}
if err := writeExportSection(w, module.Export); err != nil {
return err
}
if err := writeStartSection(w, module.Start); err != nil {
return err
}
if err := writeElementSection(w, module.Element); err != nil {
return err
}
if err := writeRawCodeSection(w, module.Code); err != nil {
return err
}
if err := writeDataSection(w, module.Data); err != nil {
return err
}
if err := writeNameSection(w, module.Names); err != nil {
return err
}
for _, custom := range module.Customs {
if err := writeCustomSection(w, custom); err != nil {
return err
}
}
return nil
}
// WriteCodeEntry writes a binary encoded representation of entry to w.
func WriteCodeEntry(w io.Writer, entry *module.CodeEntry) error {
if err := leb128.WriteVarUint32(w, uint32(len(entry.Func.Locals))); err != nil {
return err
}
for _, local := range entry.Func.Locals {
if err := leb128.WriteVarUint32(w, local.Count); err != nil {
return err
}
if err := writeValueType(w, local.Type); err != nil {
return err
}
}
return writeInstructions(w, entry.Func.Expr.Instrs)
}
func writeMagic(w io.Writer) error {
return binary.Write(w, binary.LittleEndian, constant.Magic)
}
func writeVersion(w io.Writer) error {
return binary.Write(w, binary.LittleEndian, constant.Version)
}
func writeStartSection(w io.Writer, s module.StartSection) error {
if s.FuncIndex == nil {
return nil
}
if err := writeByte(w, constant.StartSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, *s.FuncIndex); err != nil {
return err
}
return writeRawSection(w, &buf)
}
func writeTypeSection(w io.Writer, s module.TypeSection) error {
if len(s.Functions) == 0 {
return nil
}
if err := writeByte(w, constant.TypeSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Functions))); err != nil {
return err
}
for _, fsig := range s.Functions {
if err := writeFunctionType(&buf, fsig); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeImportSection(w io.Writer, s module.ImportSection) error {
if len(s.Imports) == 0 {
return nil
}
if err := writeByte(w, constant.ImportSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Imports))); err != nil {
return err
}
for _, imp := range s.Imports {
if err := writeImport(&buf, imp); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeGlobalSection(w io.Writer, s module.GlobalSection) error {
if len(s.Globals) == 0 {
return nil
}
if err := writeByte(w, constant.GlobalSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Globals))); err != nil {
return err
}
for _, global := range s.Globals {
if err := writeGlobal(&buf, global); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeFunctionSection(w io.Writer, s module.FunctionSection) error {
if len(s.TypeIndices) == 0 {
return nil
}
if err := writeByte(w, constant.FunctionSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.TypeIndices))); err != nil {
return err
}
for _, idx := range s.TypeIndices {
if err := leb128.WriteVarUint32(&buf, idx); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeTableSection(w io.Writer, s module.TableSection) error {
if len(s.Tables) == 0 {
return nil
}
if err := writeByte(w, constant.TableSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Tables))); err != nil {
return err
}
for _, table := range s.Tables {
switch table.Type {
case types.Anyfunc:
if err := writeByte(&buf, constant.ElementTypeAnyFunc); err != nil {
return err
}
default:
return errors.New("illegal table element type")
}
if err := writeLimits(&buf, table.Lim); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeMemorySection(w io.Writer, s module.MemorySection) error {
if len(s.Memories) == 0 {
return nil
}
if err := writeByte(w, constant.MemorySectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Memories))); err != nil {
return err
}
for _, mem := range s.Memories {
if err := writeLimits(&buf, mem.Lim); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeExportSection(w io.Writer, s module.ExportSection) error {
if len(s.Exports) == 0 {
return nil
}
if err := writeByte(w, constant.ExportSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Exports))); err != nil {
return err
}
for _, exp := range s.Exports {
if err := writeByteVector(&buf, []byte(exp.Name)); err != nil {
return err
}
var tpe byte
switch exp.Descriptor.Type {
case module.FunctionExportType:
tpe = constant.ExportDescType
case module.TableExportType:
tpe = constant.ExportDescTable
case module.MemoryExportType:
tpe = constant.ExportDescMem
case module.GlobalExportType:
tpe = constant.ExportDescGlobal
default:
return fmt.Errorf("illegal export descriptor type 0x%x", exp.Descriptor.Type)
}
if err := writeByte(&buf, tpe); err != nil {
return err
}
if err := leb128.WriteVarUint32(&buf, exp.Descriptor.Index); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeElementSection(w io.Writer, s module.ElementSection) error {
if len(s.Segments) == 0 {
return nil
}
if err := writeByte(w, constant.ElementSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Segments))); err != nil {
return err
}
for _, seg := range s.Segments {
if err := leb128.WriteVarUint32(&buf, seg.Index); err != nil {
return err
}
if err := writeInstructions(&buf, seg.Offset.Instrs); err != nil {
return err
}
if err := writeVarUint32Vector(&buf, seg.Indices); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeRawCodeSection(w io.Writer, s module.RawCodeSection) error {
if len(s.Segments) == 0 {
return nil
}
if err := writeByte(w, constant.CodeSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Segments))); err != nil {
return err
}
for _, seg := range s.Segments {
if err := leb128.WriteVarUint32(&buf, uint32(len(seg.Code))); err != nil {
return err
}
if _, err := buf.Write(seg.Code); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeDataSection(w io.Writer, s module.DataSection) error {
if len(s.Segments) == 0 {
return nil
}
if err := writeByte(w, constant.DataSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := leb128.WriteVarUint32(&buf, uint32(len(s.Segments))); err != nil {
return err
}
for _, seg := range s.Segments {
if err := leb128.WriteVarUint32(&buf, seg.Index); err != nil {
return err
}
if err := writeInstructions(&buf, seg.Offset.Instrs); err != nil {
return err
}
if err := writeByteVector(&buf, seg.Init); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeNameSection(w io.Writer, s module.NameSection) error {
if s.Module == "" && len(s.Functions) == 0 && len(s.Locals) == 0 {
return nil
}
if err := writeByte(w, constant.CustomSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := writeByteVector(&buf, []byte(constant.NameSectionCustomID)); err != nil {
return err
}
// "module" subsection
if s.Module != "" {
if err := writeByte(&buf, constant.NameSectionModuleType); err != nil {
return err
}
var mbuf bytes.Buffer
if err := writeByteVector(&mbuf, []byte(s.Module)); err != nil {
return err
}
if err := writeRawSection(&buf, &mbuf); err != nil {
return err
}
}
// "functions" subsection
if len(s.Functions) != 0 {
if err := writeByte(&buf, constant.NameSectionFunctionsType); err != nil {
return err
}
var fbuf bytes.Buffer
if err := writeNameMap(&fbuf, s.Functions); err != nil {
return err
}
if err := writeRawSection(&buf, &fbuf); err != nil {
return err
}
}
// "locals" subsection
if len(s.Locals) != 0 {
if err := writeByte(&buf, constant.NameSectionLocalsType); err != nil {
return err
}
funs := map[uint32][]module.NameMap{}
for _, e := range s.Locals {
funs[e.FuncIndex] = append(funs[e.FuncIndex], module.NameMap{Index: e.Index, Name: e.Name})
}
var lbuf bytes.Buffer
if err := leb128.WriteVarUint32(&lbuf, uint32(len(funs))); err != nil {
return err
}
for fidx, e := range funs {
if err := leb128.WriteVarUint32(&lbuf, fidx); err != nil {
return err
}
if err := writeNameMap(&lbuf, e); err != nil {
return err
}
}
if err := writeRawSection(&buf, &lbuf); err != nil {
return err
}
}
return writeRawSection(w, &buf)
}
func writeNameMap(buf io.Writer, nm []module.NameMap) error {
if err := leb128.WriteVarUint32(buf, uint32(len(nm))); err != nil {
return err
}
for _, m := range nm {
if err := leb128.WriteVarUint32(buf, m.Index); err != nil {
return err
}
if err := writeByteVector(buf, []byte(m.Name)); err != nil {
return err
}
}
return nil
}
func writeCustomSection(w io.Writer, s module.CustomSection) error {
if err := writeByte(w, constant.CustomSectionID); err != nil {
return err
}
var buf bytes.Buffer
if err := writeByteVector(&buf, []byte(s.Name)); err != nil {
return err
}
if _, err := io.Copy(&buf, bytes.NewReader(s.Data)); err != nil {
return err
}
return writeRawSection(w, &buf)
}
func writeFunctionType(w io.Writer, fsig module.FunctionType) error {
if err := writeByte(w, constant.FunctionTypeID); err != nil {
return err
}
if err := writeValueTypeVector(w, fsig.Params); err != nil {
return err
}
return writeValueTypeVector(w, fsig.Results)
}
func writeImport(w io.Writer, imp module.Import) error {
if err := writeByteVector(w, []byte(imp.Module)); err != nil {
return err
}
if err := writeByteVector(w, []byte(imp.Name)); err != nil {
return err
}
switch desc := imp.Descriptor.(type) {
case module.FunctionImport:
if err := writeByte(w, constant.ImportDescType); err != nil {
return err
}
return leb128.WriteVarUint32(w, desc.Func)
case module.TableImport:
if err := writeByte(w, constant.ImportDescTable); err != nil {
return err
}
if err := writeByte(w, constant.ElementTypeAnyFunc); err != nil {
return err
}
return writeLimits(w, desc.Lim)
case module.MemoryImport:
if err := writeByte(w, constant.ImportDescMem); err != nil {
return err
}
return writeLimits(w, desc.Mem.Lim)
case module.GlobalImport:
if err := writeByte(w, constant.ImportDescGlobal); err != nil {
return err
}
if err := writeValueType(w, desc.Type); err != nil {
return err
}
if desc.Mutable {
return writeByte(w, constant.Mutable)
}
return writeByte(w, constant.Const)
default:
return errors.New("illegal import descriptor type")
}
}
func writeGlobal(w io.Writer, global module.Global) error {
if err := writeValueType(w, global.Type); err != nil {
return err
}
var err error
if global.Mutable {
err = writeByte(w, constant.Mutable)
} else {
err = writeByte(w, constant.Const)
}
if err != nil {
return err
}
return writeInstructions(w, global.Init.Instrs)
}
func writeInstructions(w io.Writer, instrs []instruction.Instruction) error {
for i, instr := range instrs {
_, err := w.Write([]byte{byte(instr.Op())})
if err != nil {
return err
}
for _, arg := range instr.ImmediateArgs() {
var err error
switch arg := arg.(type) {
case int32:
err = leb128.WriteVarInt32(w, arg)
case int64:
err = leb128.WriteVarInt64(w, arg)
case uint32:
err = leb128.WriteVarUint32(w, arg)
case uint64:
err = leb128.WriteVarUint64(w, arg)
case float32:
u32 := math.Float32bits(arg)
err = binary.Write(w, binary.LittleEndian, u32)
case float64:
u64 := math.Float64bits(arg)
err = binary.Write(w, binary.LittleEndian, u64)
case byte:
_, err = w.Write([]byte{arg})
default:
return fmt.Errorf("illegal immediate argument type on instruction %d", i)
}
if err != nil {
return err
}
}
if si, ok := instr.(instruction.StructuredInstruction); ok {
if err := writeBlockValueType(w, si.BlockType()); err != nil {
return err
}
if err := writeInstructions(w, si.Instructions()); err != nil {
return err
}
}
}
_, err := w.Write([]byte{byte(opcode.End)})
return err
}
func writeLimits(w io.Writer, lim module.Limit) error {
if lim.Max == nil {
if err := writeByte(w, 0); err != nil {
return err
}
} else {
if err := writeByte(w, 1); err != nil {
return err
}
}
if err := leb128.WriteVarUint32(w, lim.Min); err != nil {
return err
}
if lim.Max != nil {
return leb128.WriteVarUint32(w, *lim.Max)
}
return nil
}
func writeVarUint32Vector(w io.Writer, v []uint32) error {
if err := leb128.WriteVarUint32(w, uint32(len(v))); err != nil {
return err
}
for i := range v {
if err := leb128.WriteVarUint32(w, v[i]); err != nil {
return err
}
}
return nil
}
func writeValueTypeVector(w io.Writer, v []types.ValueType) error {
if err := leb128.WriteVarUint32(w, uint32(len(v))); err != nil {
return err
}
for i := range v {
if err := writeValueType(w, v[i]); err != nil {
return err
}
}
return nil
}
func writeBlockValueType(w io.Writer, v *types.ValueType) error {
var b byte
if v != nil {
switch *v {
case types.I32:
b = constant.ValueTypeI32
case types.I64:
b = constant.ValueTypeI64
case types.F32:
b = constant.ValueTypeF32
case types.F64:
b = constant.ValueTypeF64
}
} else {
b = constant.BlockTypeEmpty
}
return writeByte(w, b)
}
func writeValueType(w io.Writer, v types.ValueType) error {
var b byte
switch v {
case types.I32:
b = constant.ValueTypeI32
case types.I64:
b = constant.ValueTypeI64
case types.F32:
b = constant.ValueTypeF32
case types.F64:
b = constant.ValueTypeF64
}
return writeByte(w, b)
}
func writeRawSection(w io.Writer, buf *bytes.Buffer) error {
size := buf.Len()
if err := leb128.WriteVarUint32(w, uint32(size)); err != nil {
return err
}
_, err := io.Copy(w, buf)
return err
}
func writeByteVector(w io.Writer, bs []byte) error {
if err := leb128.WriteVarUint32(w, uint32(len(bs))); err != nil {
return err
}
_, err := w.Write(bs)
return err
}
func writeByte(w io.Writer, b byte) error {
buf := make([]byte, 1)
buf[0] = b
_, err := w.Write(buf)
return err
}
@@ -0,0 +1,183 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package instruction
import (
"github.com/open-policy-agent/opa/internal/wasm/opcode"
"github.com/open-policy-agent/opa/internal/wasm/types"
)
// !!! If you find yourself adding support for more control
// instructions (br_table, if, ...), please adapt the
// `withControlInstr` functions of
// `compiler/wasm/optimizations.go`
// Unreachable represents a WASM unreachable instruction.
type Unreachable struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (Unreachable) Op() opcode.Opcode {
return opcode.Unreachable
}
// Nop represents a WASM no-op instruction.
type Nop struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (Nop) Op() opcode.Opcode {
return opcode.Nop
}
// Block represents a WASM block instruction.
type Block struct {
NoImmediateArgs
Type *types.ValueType
Instrs []Instruction
}
// Op returns the opcode of the instruction
func (Block) Op() opcode.Opcode {
return opcode.Block
}
// BlockType returns the type of the block's return value.
func (i Block) BlockType() *types.ValueType {
return i.Type
}
// Instructions returns the instructions contained in the block.
func (i Block) Instructions() []Instruction {
return i.Instrs
}
// If represents a WASM if instruction.
// NOTE(sr): we only use if with one branch so far!
type If struct {
NoImmediateArgs
Type *types.ValueType
Instrs []Instruction
}
// Op returns the opcode of the instruction.
func (If) Op() opcode.Opcode {
return opcode.If
}
// BlockType returns the type of the if's THEN branch.
func (i If) BlockType() *types.ValueType {
return i.Type
}
// Instructions represents the instructions contained in the if's THEN branch.
func (i If) Instructions() []Instruction {
return i.Instrs
}
// Loop represents a WASM loop instruction.
type Loop struct {
NoImmediateArgs
Type *types.ValueType
Instrs []Instruction
}
// Op returns the opcode of the instruction.
func (Loop) Op() opcode.Opcode {
return opcode.Loop
}
// BlockType returns the type of the loop's return value.
func (i Loop) BlockType() *types.ValueType {
return i.Type
}
// Instructions represents the instructions contained in the loop.
func (i Loop) Instructions() []Instruction {
return i.Instrs
}
// Br represents a WASM br instruction.
type Br struct {
Index uint32
}
// Op returns the opcode of the instruction.
func (Br) Op() opcode.Opcode {
return opcode.Br
}
// ImmediateArgs returns the block index to break to.
func (i Br) ImmediateArgs() []any {
return []any{i.Index}
}
// BrIf represents a WASM br_if instruction.
type BrIf struct {
Index uint32
}
// Op returns the opcode of the instruction.
func (BrIf) Op() opcode.Opcode {
return opcode.BrIf
}
// ImmediateArgs returns the block index to break to.
func (i BrIf) ImmediateArgs() []any {
return []any{i.Index}
}
// Call represents a WASM call instruction.
type Call struct {
Index uint32
}
// Op returns the opcode of the instruction.
func (Call) Op() opcode.Opcode {
return opcode.Call
}
// ImmediateArgs returns the function index.
func (i Call) ImmediateArgs() []any {
return []any{i.Index}
}
// CallIndirect represents a WASM call_indirect instruction.
type CallIndirect struct {
Index uint32 // type index
Reserved byte // zero for now
}
// Op returns the opcode of the instruction.
func (CallIndirect) Op() opcode.Opcode {
return opcode.CallIndirect
}
// ImmediateArgs returns the function index.
func (i CallIndirect) ImmediateArgs() []any {
return []any{i.Index, i.Reserved}
}
// Return represents a WASM return instruction.
type Return struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (Return) Op() opcode.Opcode {
return opcode.Return
}
// End represents the special WASM end instruction.
type End struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (End) Op() opcode.Opcode {
return opcode.End
}
@@ -0,0 +1,33 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// Package instruction defines WASM instruction types.
package instruction
import (
"github.com/open-policy-agent/opa/internal/wasm/opcode"
"github.com/open-policy-agent/opa/internal/wasm/types"
)
// NoImmediateArgs indicates the instruction has no immediate arguments.
type NoImmediateArgs struct {
}
// ImmediateArgs returns the immedate arguments of an instruction.
func (NoImmediateArgs) ImmediateArgs() []any {
return nil
}
// Instruction represents a single WASM instruction.
type Instruction interface {
Op() opcode.Opcode
ImmediateArgs() []any
}
// StructuredInstruction represents a structured control instruction like br_if.
type StructuredInstruction interface {
Instruction
BlockType() *types.ValueType
Instructions() []Instruction
}
@@ -0,0 +1,39 @@
// Copyright 2019 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package instruction
import "github.com/open-policy-agent/opa/internal/wasm/opcode"
// I32Load represents the WASM i32.load instruction.
type I32Load struct {
Offset int32
Align int32 // expressed as a power of two
}
// Op returns the opcode of the instruction.
func (I32Load) Op() opcode.Opcode {
return opcode.I32Load
}
// ImmediateArgs returns the static offset and alignment operands.
func (i I32Load) ImmediateArgs() []any {
return []any{i.Align, i.Offset}
}
// I32Store represents the WASM i32.store instruction.
type I32Store struct {
Offset int32
Align int32 // expressed as a power of two
}
// Op returns the opcode of the instruction.
func (I32Store) Op() opcode.Opcode {
return opcode.I32Store
}
// ImmediateArgs returns the static offset and alignment operands.
func (i I32Store) ImmediateArgs() []any {
return []any{i.Align, i.Offset}
}
@@ -0,0 +1,199 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package instruction
import (
"github.com/open-policy-agent/opa/internal/wasm/opcode"
)
// I32Const represents the WASM i32.const instruction.
type I32Const struct {
Value int32
}
// Op returns the opcode of the instruction.
func (I32Const) Op() opcode.Opcode {
return opcode.I32Const
}
// ImmediateArgs returns the i32 value to push onto the stack.
func (i I32Const) ImmediateArgs() []any {
return []any{i.Value}
}
// I64Const represents the WASM i64.const instruction.
type I64Const struct {
Value int64
}
// Op returns the opcode of the instruction.
func (I64Const) Op() opcode.Opcode {
return opcode.I64Const
}
// ImmediateArgs returns the i64 value to push onto the stack.
func (i I64Const) ImmediateArgs() []any {
return []any{i.Value}
}
// F32Const represents the WASM f32.const instruction.
type F32Const struct {
Value int32
}
// Op returns the opcode of the instruction.
func (F32Const) Op() opcode.Opcode {
return opcode.F32Const
}
// ImmediateArgs returns the f32 value to push onto the stack.
func (i F32Const) ImmediateArgs() []any {
return []any{i.Value}
}
// F64Const represents the WASM f64.const instruction.
type F64Const struct {
Value float64
}
// Op returns the opcode of the instruction.
func (F64Const) Op() opcode.Opcode {
return opcode.F64Const
}
// ImmediateArgs returns the f64 value to push onto the stack.
func (i F64Const) ImmediateArgs() []any {
return []any{i.Value}
}
// I32Eqz represents the WASM i32.eqz instruction.
type I32Eqz struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32Eqz) Op() opcode.Opcode {
return opcode.I32Eqz
}
// I32Eq represents the WASM i32.eq instruction.
type I32Eq struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32Eq) Op() opcode.Opcode {
return opcode.I32Eq
}
// I32Ne represents the WASM i32.ne instruction.
type I32Ne struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32Ne) Op() opcode.Opcode {
return opcode.I32Ne
}
// I32GtS represents the WASM i32.gt_s instruction.
type I32GtS struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32GtS) Op() opcode.Opcode {
return opcode.I32GtS
}
// I32GeS represents the WASM i32.ge_s instruction.
type I32GeS struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32GeS) Op() opcode.Opcode {
return opcode.I32GeS
}
// I32LtS represents the WASM i32.lt_s instruction.
type I32LtS struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32LtS) Op() opcode.Opcode {
return opcode.I32LtS
}
// I32LeS represents the WASM i32.le_s instruction.
type I32LeS struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32LeS) Op() opcode.Opcode {
return opcode.I32LeS
}
// I32Add represents the WASM i32.add instruction.
type I32Add struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32Add) Op() opcode.Opcode {
return opcode.I32Add
}
// I64Add represents the WASM i64.add instruction.
type I64Add struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I64Add) Op() opcode.Opcode {
return opcode.I64Add
}
// F32Add represents the WASM f32.add instruction.
type F32Add struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (F32Add) Op() opcode.Opcode {
return opcode.F32Add
}
// F64Add represents the WASM f64.add instruction.
type F64Add struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (F64Add) Op() opcode.Opcode {
return opcode.F64Add
}
// I32Mul represents the WASM i32.mul instruction.
type I32Mul struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32Mul) Op() opcode.Opcode {
return opcode.I32Mul
}
// I32Sub represents the WASM i32.sub instruction.
type I32Sub struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (I32Sub) Op() opcode.Opcode {
return opcode.I32Sub
}
@@ -0,0 +1,29 @@
// Copyright 2021 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package instruction
import (
"github.com/open-policy-agent/opa/internal/wasm/opcode"
)
// Drop reprsents a WASM drop instruction.
type Drop struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (Drop) Op() opcode.Opcode {
return opcode.Drop
}
// Select reprsents a WASM select instruction.
type Select struct {
NoImmediateArgs
}
// Op returns the opcode of the instruction.
func (Select) Op() opcode.Opcode {
return opcode.Select
}
@@ -0,0 +1,54 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package instruction
import "github.com/open-policy-agent/opa/internal/wasm/opcode"
// GetLocal represents the WASM get_local instruction.
type GetLocal struct {
Index uint32
}
// Op returns the opcode of the instruction.
func (GetLocal) Op() opcode.Opcode {
return opcode.GetLocal
}
// ImmediateArgs returns the index of the local variable to push onto the stack.
func (i GetLocal) ImmediateArgs() []any {
return []any{i.Index}
}
// SetLocal represents the WASM set_local instruction.
type SetLocal struct {
Index uint32
}
// Op returns the opcode of the instruction.
func (SetLocal) Op() opcode.Opcode {
return opcode.SetLocal
}
// ImmediateArgs returns the index of the local variable to set with the top of
// the stack.
func (i SetLocal) ImmediateArgs() []any {
return []any{i.Index}
}
// TeeLocal represents the WASM tee_local instruction.
type TeeLocal struct {
Index uint32
}
// Op returns the opcode of the instruction.
func (TeeLocal) Op() opcode.Opcode {
return opcode.TeeLocal
}
// ImmediateArgs returns the index of the local variable to "tee" with the top of
// the stack (like set, but retaining the top of the stack).
func (i TeeLocal) ImmediateArgs() []any {
return []any{i.Index}
}
@@ -0,0 +1,385 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package module
import (
"fmt"
"strings"
"github.com/open-policy-agent/opa/internal/wasm/instruction"
"github.com/open-policy-agent/opa/internal/wasm/types"
)
type (
// Module represents a WASM module.
Module struct {
Version uint32
Start StartSection
Type TypeSection
Import ImportSection
Function FunctionSection
Table TableSection
Memory MemorySection
Element ElementSection
Global GlobalSection
Export ExportSection
Code RawCodeSection
Data DataSection
Customs []CustomSection
Names NameSection
}
// StartSection represents a WASM start section.
StartSection struct {
FuncIndex *uint32
}
// TypeSection represents a WASM type section.
TypeSection struct {
Functions []FunctionType
}
// ImportSection represents a WASM import section.
ImportSection struct {
Imports []Import
}
// FunctionSection represents a WASM function section.
FunctionSection struct {
TypeIndices []uint32
}
// TableSection represents a WASM table section.
TableSection struct {
Tables []Table
}
// MemorySection represents a Wasm memory section.
MemorySection struct {
Memories []Memory
}
// ElementSection represents a WASM element section.
ElementSection struct {
Segments []ElementSegment
}
// GlobalSection represents a WASM global section.
GlobalSection struct {
Globals []Global
}
// ExportSection represents a WASM export section.
ExportSection struct {
Exports []Export
}
// RawCodeSection represents a WASM code section. The code section is left as a
// raw byte sequence.
RawCodeSection struct {
Segments []RawCodeSegment
}
// DataSection represents a WASM data section.
DataSection struct {
Segments []DataSegment
}
// CustomSection represents a WASM custom section.
CustomSection struct {
Name string
Data []byte
}
// NameSection represents the WASM custom section "name".
NameSection struct {
Module string
Functions []NameMap
Locals []LocalNameMap
}
// NameMap maps function or local arg indices to their names.
NameMap struct {
Index uint32
Name string
}
// LocalNameMap maps function indices, and argument indices for the args
// of the indexed function to their names.
LocalNameMap struct {
FuncIndex uint32
NameMap
}
// FunctionType represents a WASM function type definition.
FunctionType struct {
Params []types.ValueType
Results []types.ValueType
}
// Import represents a WASM import statement.
Import struct {
Module string
Name string
Descriptor ImportDescriptor
}
// ImportDescriptor represents a WASM import descriptor.
ImportDescriptor interface {
fmt.Stringer
Kind() ImportDescriptorType
}
// ImportDescriptorType defines allowed kinds of import descriptors.
ImportDescriptorType int
// FunctionImport represents a WASM function import statement.
FunctionImport struct {
Func uint32
}
// MemoryImport represents a WASM memory import statement.
MemoryImport struct {
Mem MemType
}
// MemType defines the attributes of a memory import.
MemType struct {
Lim Limit
}
// TableImport represents a WASM table import statement.
TableImport struct {
Type types.ElementType
Lim Limit
}
// ElementSegment represents a WASM element segment.
ElementSegment struct {
Index uint32
Offset Expr
Indices []uint32
}
// GlobalImport represents a WASM global variable import statement.
GlobalImport struct {
Type types.ValueType
Mutable bool
}
// Limit represents a WASM limit.
Limit struct {
Min uint32
Max *uint32
}
// Table represents a WASM table statement.
Table struct {
Type types.ElementType
Lim Limit
}
// Memory represents a Wasm memory statement.
Memory struct {
Lim Limit
}
// Global represents a WASM global statement.
Global struct {
Type types.ValueType
Mutable bool
Init Expr
}
// Export represents a WASM export statement.
Export struct {
Name string
Descriptor ExportDescriptor
}
// ExportDescriptor represents a WASM export descriptor.
ExportDescriptor struct {
Type ExportDescriptorType
Index uint32
}
// ExportDescriptorType defines the allowed kinds of export descriptors.
ExportDescriptorType int
// RawCodeSegment represents a binary-encoded WASM code segment.
RawCodeSegment struct {
Code []byte
}
// DataSegment represents a WASM data segment.
DataSegment struct {
Index uint32
Offset Expr
Init []byte
}
// Expr represents a WASM expression.
Expr struct {
Instrs []instruction.Instruction
}
// CodeEntry represents a code segment entry.
CodeEntry struct {
Func Function
}
// Function represents a function in a code segment.
Function struct {
Locals []LocalDeclaration
Expr Expr
}
// LocalDeclaration represents a local variable declaration.
LocalDeclaration struct {
Count uint32
Type types.ValueType
}
)
// Defines the allowed kinds of imports.
const (
FunctionImportType ImportDescriptorType = iota
TableImportType
MemoryImportType
GlobalImportType
)
func (x ImportDescriptorType) String() string {
switch x {
case FunctionImportType:
return "func"
case TableImportType:
return "table"
case MemoryImportType:
return "memory"
case GlobalImportType:
return "global"
}
panic("illegal value")
}
// Defines the allowed kinds of exports.
const (
FunctionExportType ExportDescriptorType = iota
TableExportType
MemoryExportType
GlobalExportType
)
func (x ExportDescriptorType) String() string {
switch x {
case FunctionExportType:
return "func"
case TableExportType:
return "table"
case MemoryExportType:
return "memory"
case GlobalExportType:
return "global"
}
panic("illegal value")
}
// Kind returns the function import type kind.
func (FunctionImport) Kind() ImportDescriptorType {
return FunctionImportType
}
func (i FunctionImport) String() string {
return fmt.Sprintf("%v[type=%v]", i.Kind(), i.Func)
}
// Kind returns the memory import type kind.
func (MemoryImport) Kind() ImportDescriptorType {
return MemoryImportType
}
func (i MemoryImport) String() string {
return fmt.Sprintf("%v[%v]", i.Kind(), i.Mem.Lim)
}
// Kind returns the table import type kind.
func (TableImport) Kind() ImportDescriptorType {
return TableImportType
}
func (i TableImport) String() string {
return fmt.Sprintf("%v[%v, %v]", i.Kind(), i.Type, i.Lim)
}
// Kind returns the global import type kind.
func (GlobalImport) Kind() ImportDescriptorType {
return GlobalImportType
}
func (i GlobalImport) String() string {
return fmt.Sprintf("%v[%v, mut=%v]", i.Kind(), i.Type, i.Mutable)
}
func (tpe FunctionType) String() string {
params := make([]string, len(tpe.Params))
results := make([]string, len(tpe.Results))
for i := range tpe.Params {
params[i] = tpe.Params[i].String()
}
for i := range tpe.Results {
results[i] = tpe.Results[i].String()
}
return "(" + strings.Join(params, ", ") + ") -> (" + strings.Join(results, ", ") + ")"
}
// Equal returns true if tpe equals other.
func (tpe FunctionType) Equal(other FunctionType) bool {
if len(tpe.Params) != len(other.Params) || len(tpe.Results) != len(other.Results) {
return false
}
for i := range tpe.Params {
if tpe.Params[i] != other.Params[i] {
return false
}
}
for i := range tpe.Results {
if tpe.Results[i] != other.Results[i] {
return false
}
}
return true
}
func (imp Import) String() string {
return fmt.Sprintf("%v %v.%v", imp.Descriptor.String(), imp.Module, imp.Name)
}
func (exp Export) String() string {
return fmt.Sprintf("%v[%v] %v", exp.Descriptor.Type, exp.Descriptor.Index, exp.Name)
}
func (seg RawCodeSegment) String() string {
return fmt.Sprintf("<code %d bytes>", len(seg.Code))
}
func (seg DataSegment) String() string {
return fmt.Sprintf("<data index=%v [%v] len=%d bytes>", seg.Index, seg.Offset, len(seg.Init))
}
func (e Expr) String() string {
return fmt.Sprintf("%d instr(s)", len(e.Instrs))
}
func (lim Limit) String() string {
if lim.Max == nil {
return fmt.Sprintf("min=%v", lim.Min)
}
return fmt.Sprintf("min=%v max=%v", lim.Min, lim.Max)
}
@@ -0,0 +1,84 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package module
import (
"encoding/hex"
"fmt"
"io"
)
// PrettyOption defines options for controlling pretty printing.
type PrettyOption struct {
Contents bool // show raw byte content of data+code sections.
}
// Pretty writes a human-readable representation of m to w.
func Pretty(w io.Writer, m *Module, opts ...PrettyOption) {
fmt.Fprintln(w, "version:", m.Version)
fmt.Fprintln(w, "types:")
for _, fn := range m.Type.Functions {
fmt.Fprintln(w, " -", fn)
}
fmt.Fprintln(w, "imports:")
for i, imp := range m.Import.Imports {
if imp.Descriptor.Kind() == FunctionImportType {
fmt.Printf(" - [%d] %v\n", i, imp)
} else {
fmt.Fprintln(w, " -", imp)
}
}
fmt.Fprintln(w, "functions:")
for _, fn := range m.Function.TypeIndices {
if fn >= uint32(len(m.Type.Functions)) {
fmt.Fprintln(w, " -", "???")
} else {
fmt.Fprintln(w, " -", m.Type.Functions[fn])
}
}
fmt.Fprintln(w, "exports:")
for _, exp := range m.Export.Exports {
fmt.Fprintln(w, " -", exp)
}
fmt.Fprintln(w, "code:")
for _, seg := range m.Code.Segments {
fmt.Fprintln(w, " -", seg)
}
fmt.Fprintln(w, "data:")
for _, seg := range m.Data.Segments {
fmt.Fprintln(w, " -", seg)
}
if len(opts) == 0 {
return
}
fmt.Fprintln(w)
for _, opt := range opts {
if opt.Contents {
newline := false
if len(m.Data.Segments) > 0 {
fmt.Fprintln(w, "data section:")
for _, seg := range m.Data.Segments {
if newline {
fmt.Fprintln(w)
}
fmt.Fprintln(w, hex.Dump(seg.Init))
newline = true
}
newline = false
}
if len(m.Code.Segments) > 0 {
fmt.Fprintln(w, "code section:")
for _, seg := range m.Code.Segments {
if newline {
fmt.Fprintln(w)
}
fmt.Fprintln(w, hex.Dump(seg.Code))
newline = true
}
newline = false
}
}
}
}
@@ -0,0 +1,218 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// Package opcode contains constants and utilities for working with WASM opcodes.
package opcode
// Opcode represents a WASM instruction opcode.
type Opcode byte
// Control instructions.
const (
Unreachable Opcode = iota
Nop
Block
Loop
If
Else
)
const (
// End defines the special end WASM opcode.
End Opcode = 0x0B
)
// Extended control instructions.
const (
Br Opcode = iota + 0x0C
BrIf
BrTable
Return
Call
CallIndirect
)
// Parameter instructions.
const (
Drop Opcode = iota + 0x1A
Select
)
// Variable instructions.
const (
GetLocal Opcode = iota + 0x20
SetLocal
TeeLocal
GetGlobal
SetGlobal
)
// Memory instructions.
const (
I32Load Opcode = iota + 0x28
I64Load
F32Load
F64Load
I32Load8S
I32Load8U
I32Load16S
I32Load16U
I64Load8S
I64Load8U
I64Load16S
I64Load16U
I64Load32S
I64Load32U
I32Store
I64Store
F32Store
F64Store
I32Store8
I32Store16
I64Store8
I64Store16
I64Store32
MemorySize
MemoryGrow
)
// Numeric instructions.
const (
I32Const Opcode = iota + 0x41
I64Const
F32Const
F64Const
I32Eqz
I32Eq
I32Ne
I32LtS
I32LtU
I32GtS
I32GtU
I32LeS
I32LeU
I32GeS
I32GeU
I64Eqz
I64Eq
I64Ne
I64LtS
I64LtU
I64GtS
I64GtU
I64LeS
I64LeU
I64GeS
I64GeU
F32Eq
F32Ne
F32Lt
F32Gt
F32Le
F32Ge
F64Eq
F64Ne
F64Lt
F64Gt
F64Le
F64Ge
I32Clz
I32Ctz
I32Popcnt
I32Add
I32Sub
I32Mul
I32DivS
I32DivU
I32RemS
I32RemU
I32And
I32Or
I32Xor
I32Shl
I32ShrS
I32ShrU
I32Rotl
I32Rotr
I64Clz
I64Ctz
I64Popcnt
I64Add
I64Sub
I64Mul
I64DivS
I64DivU
I64RemS
I64RemU
I64And
I64Or
I64Xor
I64Shl
I64ShrS
I64ShrU
I64Rotl
I64Rotr
F32Abs
F32Neg
F32Ceil
F32Floor
F32Trunc
F32Nearest
F32Sqrt
F32Add
F32Sub
F32Mul
F32Div
F32Min
F32Max
F32Copysign
F64Abs
F64Neg
F64Ceil
F64Floor
F64Trunc
F64Nearest
F64Sqrt
F64Add
F64Sub
F64Mul
F64Div
F64Min
F64Max
F64Copysign
I32WrapI64
I32TruncSF32
I32TruncUF32
I32TruncSF64
I32TruncUF64
I64ExtendSI32
I64ExtendUI32
I64TruncSF32
I64TruncUF32
I64TruncSF64
I64TruncUF64
F32ConvertSI32
F32ConvertUI32
F32ConvertSI64
F32ConvertUI64
F32DemoteF64
F64ConvertSI32
F64ConvertUI32
F64ConvertSI64
F64ConvertUI64
F64PromoteF32
I32ReinterpretF32
I64ReinterpretF64
F32ReinterpretI32
F64ReinterpretI64
)
@@ -0,0 +1,12 @@
// Copyright 2021 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// +build opa_wasm generate
package capabilities
// ABIVersions returns the ABI versions that this SDK supports
func ABIVersions() [][2]int {
return [][2]int{{1, 1}, {1, 2}}
}
@@ -0,0 +1,14 @@
// Copyright 2021 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
//go:build !opa_wasm && !generate
// +build !opa_wasm,!generate
package capabilities
// ABIVersions returns the supported Wasm ABI versions for this
// build: none
func ABIVersions() [][2]int {
return nil
}
@@ -0,0 +1,36 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// Package types defines the WASM value type constants.
package types
// ValueType represents an intrinsic value in WASM.
type ValueType int
// Defines the intrinsic value types.
const (
I32 ValueType = iota
I64
F32
F64
)
func (tpe ValueType) String() string {
if tpe == I32 {
return "i32"
} else if tpe == I64 {
return "i64"
} else if tpe == F32 {
return "f32"
}
return "f64"
}
// ElementType defines the type of table elements.
type ElementType int
const (
// Anyfunc is the union of all table types.
Anyfunc ElementType = iota
)
@@ -0,0 +1,18 @@
// Copyright 2020 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package util
// PageSize represents the WASM page size in bytes.
const PageSize = 65535
// Pages converts a byte size to Pages, rounding up as necessary.
func Pages(n uint32) uint32 {
pages := n / PageSize
if pages*PageSize == n {
return pages
}
return pages + 1
}