This commit is contained in:
Cadey Ratio 2018-06-17 23:39:04 +00:00
parent 6aa9a6d149
commit 7b073fd67e
117 changed files with 20868 additions and 198 deletions

View File

@ -1,3 +1,3 @@
include_rules
: *.go |> go build -o %o |> ../../bin/land
: *.go |> vgo build -o %o |> ../../bin/land

7
go.mod Normal file
View File

@ -0,0 +1,7 @@
module git.xeserv.us/xena/land
require (
github.com/Xe/ln v0.0.0-20180508032019-ae4f7456500d
github.com/go-interpreter/wagon v0.0.0-20180520092357-d4bc452fd57e
github.com/pkg/errors v0.8.0
)

3
go.modverify Normal file
View File

@ -0,0 +1,3 @@
github.com/Xe/ln v0.0.0-20180508032019-ae4f7456500d h1:dQgIfnQayXRfcxefFjA7rfL2hIouSrIEiOyB2hypQTw=
github.com/go-interpreter/wagon v0.0.0-20180520092357-d4bc452fd57e h1:l4e38KpmR/OxRvJrZtqQ0yFwuGH8ta8ZZkOA+h5ovPg=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=

3
userland/install.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
npm install

File diff suppressed because it is too large Load Diff

8
vendor/github.com/Xe/ln/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,8 @@
language: go
go:
- "1.x"
- "1.9"
script:
- go test -v -race -cover ./...

25
vendor/github.com/Xe/ln/LICENSE generated vendored Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2015, Andrew Gwozdziewycz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

29
vendor/github.com/Xe/ln/README.md generated vendored Normal file
View File

@ -0,0 +1,29 @@
# ln: The Natural Logger for Go
`ln` provides a simple interface to logging, and metrics, and
obviates the need to utilize purpose built metrics packages, like
`go-metrics` for simple use cases.
The design of `ln` centers around the idea of key-value pairs, which
can be interpreted on the fly, but "Filters" to do things such as
aggregated metrics, and report said metrics to, say Librato, or
statsd.
"Filters" are like WSGI, or Rack Middleware. They are run "top down"
and can abort an emitted log's output at any time, or continue to let
it through the chain. However, the interface is slightly different
than that. Rather than encapsulating the chain with partial function
application, we utilize a simpler method, namely, each plugin defines
an `Apply` function, which takes as an argument the log event, and
performs the work of the plugin, only if the Plugin "Applies" to this
log event.
If `Apply` returns `false`, the iteration through the rest of the
filters is aborted, and the log is dropped from further processing.
## Current Status: Initial Development / Concept
## Copyright
(c) 2015, Andrew Gwozdziewycz, BSD Licensed. See LICENSE for more
info.

11
vendor/github.com/Xe/ln/action.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
package ln
// Action is a convenience helper for logging the "action" being performed as
// part of a log line.
//
// It is a convenience wrapper for the following:
//
// ln.Log(ctx, fer, f, ln.Action("writing frozberry sales reports to database"))
func Action(act string) Fer {
return F{"action": act}
}

34
vendor/github.com/Xe/ln/context.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package ln
import (
"context"
)
type ctxKey int
const (
fKey = iota
)
// WithF stores or appends a given F instance into a context.
func WithF(ctx context.Context, f F) context.Context {
pf, ok := FFromContext(ctx)
if !ok {
return context.WithValue(ctx, fKey, f)
}
pf.Extend(f)
return context.WithValue(ctx, fKey, pf)
}
// FFromContext fetches the `F` out of the context if it exists.
func FFromContext(ctx context.Context) (F, bool) {
fvp := ctx.Value(fKey)
if fvp == nil {
return nil, false
}
f, ok := fvp.(F)
return f, ok
}

23
vendor/github.com/Xe/ln/context_test.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
package ln
import (
"context"
"testing"
)
func TestContextStorage(t *testing.T) {
var val interface{} = "bar"
ctx := context.Background()
ctx = WithF(ctx, F{"foo": val})
f, ok := FFromContext(ctx)
if !ok {
t.Fatal("expected F to be in context but it wasn't")
}
if cmp, ok := f["foo"]; ok {
if cmp != val {
t.Fatalf("expected %v from context, got: %v", val, cmp)
}
}
}

25
vendor/github.com/Xe/ln/doc.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
/*
Package ln is the Natural Logger for Go
`ln` provides a simple interface to logging, and metrics, and
obviates the need to utilize purpose built metrics packages, like
`go-metrics` for simple use cases.
The design of `ln` centers around the idea of key-value pairs, which
can be interpreted on the fly, but "Filters" to do things such as
aggregated metrics, and report said metrics to, say Librato, or
statsd.
"Filters" are like WSGI, or Rack Middleware. They are run "top down"
and can abort an emitted log's output at any time, or continue to let
it through the chain. However, the interface is slightly different
than that. Rather than encapsulating the chain with partial function
application, we utilize a simpler method, namely, each plugin defines
an `Apply` function, which takes as an argument the log event, and
performs the work of the plugin, only if the Plugin "Applies" to this
log event.
If `Apply` returns `false`, the iteration through the rest of the
filters is aborted, and the log is dropped from further processing.
*/
package ln

67
vendor/github.com/Xe/ln/filter.go generated vendored Normal file
View File

@ -0,0 +1,67 @@
package ln
import (
"context"
"io"
"sync"
)
// Filter interface for defining chain filters
type Filter interface {
Apply(ctx context.Context, e Event) bool
Run()
Close()
}
// FilterFunc allows simple functions to implement the Filter interface
type FilterFunc func(ctx context.Context, e Event) bool
// Apply implements the Filter interface
func (ff FilterFunc) Apply(ctx context.Context, e Event) bool {
return ff(ctx, e)
}
// Run implements the Filter interface
func (ff FilterFunc) Run() {}
// Close implements the Filter interface
func (ff FilterFunc) Close() {}
// WriterFilter implements a filter, which arbitrarily writes to an io.Writer
type WriterFilter struct {
sync.Mutex
Out io.Writer
Formatter Formatter
}
// NewWriterFilter creates a filter to add to the chain
func NewWriterFilter(out io.Writer, format Formatter) *WriterFilter {
if format == nil {
format = DefaultFormatter
}
return &WriterFilter{
Out: out,
Formatter: format,
}
}
// Apply implements the Filter interface
func (w *WriterFilter) Apply(ctx context.Context, e Event) bool {
output, err := w.Formatter.Format(ctx, e)
if err == nil {
w.Lock()
w.Out.Write(output)
w.Unlock()
}
return true
}
// Run implements the Filter interface
func (w *WriterFilter) Run() {}
// Close implements the Filter interface
func (w *WriterFilter) Close() {}
// NilFilter is safe to return as a Filter, but does nothing
var NilFilter = FilterFunc(func(_ context.Context, e Event) bool { return true })

111
vendor/github.com/Xe/ln/formatter.go generated vendored Normal file
View File

@ -0,0 +1,111 @@
package ln
import (
"bytes"
"context"
"fmt"
"time"
)
var (
// DefaultTimeFormat represents the way in which time will be formatted by default
DefaultTimeFormat = time.RFC3339
)
// Formatter defines the formatting of events
type Formatter interface {
Format(ctx context.Context, e Event) ([]byte, error)
}
// DefaultFormatter is the default way in which to format events
var DefaultFormatter Formatter
func init() {
DefaultFormatter = NewTextFormatter()
}
// TextFormatter formats events as key value pairs.
// Any remaining text not wrapped in an instance of `F` will be
// placed at the end.
type TextFormatter struct {
TimeFormat string
}
// NewTextFormatter returns a Formatter that outputs as text.
func NewTextFormatter() Formatter {
return &TextFormatter{TimeFormat: DefaultTimeFormat}
}
// Format implements the Formatter interface
func (t *TextFormatter) Format(_ context.Context, e Event) ([]byte, error) {
var writer bytes.Buffer
writer.WriteString("time=\"")
writer.WriteString(e.Time.Format(t.TimeFormat))
writer.WriteString("\"")
keys := make([]string, len(e.Data))
i := 0
for k := range e.Data {
keys[i] = k
i++
}
for _, k := range keys {
v := e.Data[k]
writer.WriteByte(' ')
if shouldQuote(k) {
writer.WriteString(fmt.Sprintf("%q", k))
} else {
writer.WriteString(k)
}
writer.WriteByte('=')
switch v.(type) {
case string:
vs, _ := v.(string)
if shouldQuote(vs) {
fmt.Fprintf(&writer, "%q", vs)
} else {
writer.WriteString(vs)
}
case error:
tmperr, _ := v.(error)
es := tmperr.Error()
if shouldQuote(es) {
fmt.Fprintf(&writer, "%q", es)
} else {
writer.WriteString(es)
}
case time.Time:
tmptime, _ := v.(time.Time)
writer.WriteString(tmptime.Format(time.RFC3339))
default:
fmt.Fprint(&writer, v)
}
}
if len(e.Message) > 0 {
fmt.Fprintf(&writer, " _msg=%q", e.Message)
}
writer.WriteByte('\n')
return writer.Bytes(), nil
}
func shouldQuote(s string) bool {
for _, b := range s {
if !((b >= 'A' && b <= 'Z') ||
(b >= 'a' && b <= 'z') ||
(b >= '0' && b <= '9') ||
(b == '-' || b == '.' || b == '#' ||
b == '/' || b == '_')) {
return true
}
}
return false
}

184
vendor/github.com/Xe/ln/logger.go generated vendored Normal file
View File

@ -0,0 +1,184 @@
package ln
import (
"context"
"os"
"time"
"github.com/pkg/errors"
)
// Logger holds the current priority and list of filters
type Logger struct {
Filters []Filter
}
// DefaultLogger is the default implementation of Logger
var DefaultLogger *Logger
func init() {
var defaultFilters []Filter
// Default to STDOUT for logging, but allow LN_OUT to change it.
out := os.Stdout
if os.Getenv("LN_OUT") == "<stderr>" {
out = os.Stderr
}
defaultFilters = append(
defaultFilters,
FilterFunc(opnameInEvents),
NewWriterFilter(out, nil),
)
DefaultLogger = &Logger{
Filters: defaultFilters,
}
}
// F is a key-value mapping for structured data.
type F map[string]interface{}
// Extend concatentates one F with one or many Fer instances.
func (f F) Extend(other ...Fer) {
for _, ff := range other {
for k, v := range ff.F() {
f[k] = v
}
}
}
// F makes F an Fer
func (f F) F() F {
return f
}
// Fer allows any type to add fields to the structured logging key->value pairs.
type Fer interface {
F() F
}
// Event represents an event
type Event struct {
Time time.Time
Data F
Message string
}
// Log is the generic logging method.
func (l *Logger) Log(ctx context.Context, xs ...Fer) {
event := Event{Time: time.Now()}
addF := func(bf F) {
if event.Data == nil {
event.Data = bf
} else {
for k, v := range bf {
event.Data[k] = v
}
}
}
for _, f := range xs {
addF(f.F())
}
ctxf, ok := FFromContext(ctx)
if ok {
addF(ctxf)
}
if os.Getenv("LN_DEBUG_ALL_EVENTS") == "1" {
frame := callersFrame()
if event.Data == nil {
event.Data = make(F)
}
event.Data["_lineno"] = frame.lineno
event.Data["_function"] = frame.function
event.Data["_filename"] = frame.filename
}
l.filter(ctx, event)
}
func (l *Logger) filter(ctx context.Context, e Event) {
for _, f := range l.Filters {
if !f.Apply(ctx, e) {
return
}
}
}
// Error logs an error and information about the context of said error.
func (l *Logger) Error(ctx context.Context, err error, xs ...Fer) {
data := F{}
frame := callersFrame()
data["_lineno"] = frame.lineno
data["_function"] = frame.function
data["_filename"] = frame.filename
data["err"] = err
cause := errors.Cause(err)
if cause != nil && cause.Error() != err.Error() {
data["cause"] = cause.Error()
}
xs = append(xs, data)
l.Log(ctx, xs...)
}
// Fatal logs this set of values, then exits with status code 1.
func (l *Logger) Fatal(ctx context.Context, xs ...Fer) {
xs = append(xs, F{"fatal": true})
l.Log(ctx, xs...)
os.Exit(1)
}
// FatalErr combines Fatal and Error.
func (l *Logger) FatalErr(ctx context.Context, err error, xs ...Fer) {
xs = append(xs, F{"fatal": true})
data := F{}
frame := callersFrame()
data["_lineno"] = frame.lineno
data["_function"] = frame.function
data["_filename"] = frame.filename
data["err"] = err
cause := errors.Cause(err)
if cause != nil && cause.Error() != err.Error() {
data["cause"] = cause.Error()
}
xs = append(xs, data)
l.Log(ctx, xs...)
os.Exit(1)
}
// Default Implementation
// Log is the generic logging method.
func Log(ctx context.Context, xs ...Fer) {
DefaultLogger.Log(ctx, xs...)
}
// Error logs an error and information about the context of said error.
func Error(ctx context.Context, err error, xs ...Fer) {
DefaultLogger.Error(ctx, err, xs...)
}
// Fatal logs this set of values, then exits with status code 1.
func Fatal(ctx context.Context, xs ...Fer) {
DefaultLogger.Fatal(ctx, xs...)
}
// FatalErr combines Fatal and Error.
func FatalErr(ctx context.Context, err error, xs ...Fer) {
DefaultLogger.FatalErr(ctx, err, xs...)
}

118
vendor/github.com/Xe/ln/logger_test.go generated vendored Normal file
View File

@ -0,0 +1,118 @@
package ln
import (
"bytes"
"context"
"fmt"
"testing"
"time"
"github.com/Xe/ln/opname"
)
var ctx context.Context
func setup(t *testing.T) (*bytes.Buffer, func()) {
ctx = context.Background()
out := bytes.Buffer{}
oldFilters := DefaultLogger.Filters
DefaultLogger.Filters = []Filter{
FilterFunc(opnameInEvents),
NewWriterFilter(&out, nil),
}
return &out, func() {
DefaultLogger.Filters = oldFilters
}
}
func TestSimpleError(t *testing.T) {
out, teardown := setup(t)
defer teardown()
Log(ctx, F{"err": fmt.Errorf("This is an Error!!!")}, F{"msg": "fooey", "bar": "foo"})
data := []string{
`err="This is an Error!!!"`,
`fooey`,
`bar=foo`,
}
for _, line := range data {
if !bytes.Contains(out.Bytes(), []byte(line)) {
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
}
}
}
func TestTimeConversion(t *testing.T) {
out, teardown := setup(t)
defer teardown()
var zeroTime time.Time
Log(ctx, F{"zero": zeroTime})
data := []string{
`zero=0001-01-01T00:00:00Z`,
}
for _, line := range data {
if !bytes.Contains(out.Bytes(), []byte(line)) {
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
}
}
}
func TestDebug(t *testing.T) {
out, teardown := setup(t)
defer teardown()
mctx := opname.With(ctx, "test")
// set priority to Debug
Error(mctx, fmt.Errorf("This is an Error!!!"), F{})
data := []string{
`err="This is an Error!!!"`,
`_lineno=`,
`_function=ln.TestDebug`,
`_filename=github.com/Xe/ln/logger_test.go`,
`operation=test`,
}
for _, line := range data {
if !bytes.Contains(out.Bytes(), []byte(line)) {
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
}
}
}
func TestFer(t *testing.T) {
out, teardown := setup(t)
defer teardown()
underTest := foobar{Foo: 1, Bar: "quux"}
Log(ctx, underTest)
data := []string{
`foo=1`,
`bar=quux`,
}
for _, line := range data {
if !bytes.Contains(out.Bytes(), []byte(line)) {
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
}
}
}
type foobar struct {
Foo int
Bar string
}
func (f foobar) F() F {
return F{
"foo": f.Foo,
"bar": f.Bar,
}
}

19
vendor/github.com/Xe/ln/message.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
package ln
import "fmt"
func withLevel(level, format string, args ...interface{}) Fer {
return F{level: fmt.Sprintf(format, args...), "level": level}
}
// Info adds an informational connotation to this log line. This is the
// "default" state for things that can be ignored.
func Info(format string, args ...interface{}) Fer {
return withLevel("info", format, args...)
}
// Debug adds a debugging connotation to this log line. This may be ignored
// or aggressively sampled in order to save ram.
func Debug(format string, args ...interface{}) Fer {
return withLevel("debug", format, args...)
}

15
vendor/github.com/Xe/ln/opname.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
package ln
import (
"context"
"github.com/Xe/ln/opname"
)
func opnameInEvents(ctx context.Context, e Event) bool {
if op, ok := opname.Get(ctx); ok {
e.Data["operation"] = op
}
return true
}

31
vendor/github.com/Xe/ln/opname/opname.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
// Package opname contains an extensible "operation name" construct for go
// applications. This allows a user to create a base operation, eg:
// "createWidget" and then sub-operations such as "pgSaveWidget" and they will
// get composed such as "createWidget.pgSaveWidget" in log lines. Each
// operation name adds a lightweight "layer" to the context.
package opname
import (
"context"
)
type ctxKey int
const key ctxKey = iota
// Get fetches the operation name from the given context.
func Get(ctx context.Context) (string, bool) {
val, ok := ctx.Value(key).(string)
return val, ok
}
// With stores the current operation name to the context, optionally prepending
// the existing operation name in the context if it exists.
func With(ctx context.Context, name string) context.Context {
prep, ok := Get(ctx)
if ok {
name = prep + "." + name
}
return context.WithValue(ctx, key, name)
}

35
vendor/github.com/Xe/ln/opname/opname_test.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
package opname
import (
"context"
"testing"
)
func TestOpname(t *testing.T) {
ctx := context.Background()
ctxBase := With(ctx, "base")
ctxExtended := With(ctxBase, "extended")
_, ok := Get(ctx)
if ok {
t.Fatal("shouldn't get an operation name")
}
val, ok := Get(ctxBase)
if !ok {
t.Fatal("ctxBase should have an operation name")
}
if val != "base" {
t.Fatalf("wanted base, got: %v", val)
}
val, ok = Get(ctxExtended)
if !ok {
t.Fatal("ctxExtended should have an operation name")
}
if val != "base.extended" {
t.Fatalf("wanted base.extended, got: %v", val)
}
}

44
vendor/github.com/Xe/ln/stack.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
package ln
import (
"os"
"runtime"
"strings"
)
type frame struct {
filename string
function string
lineno int
}
// skips 2 frames, since Caller returns the current frame, and we need
// the caller's caller.
func callersFrame() frame {
var out frame
pc, file, line, ok := runtime.Caller(3)
if !ok {
return out
}
srcLoc := strings.LastIndex(file, "/src/")
if srcLoc >= 0 {
file = file[srcLoc+5:]
}
out.filename = file
out.function = functionName(pc)
out.lineno = line
return out
}
func functionName(pc uintptr) string {
fn := runtime.FuncForPC(pc)
if fn == nil {
return "???"
}
name := fn.Name()
beg := strings.LastIndex(name, string(os.PathSeparator))
return name[beg+1:]
// end := strings.LastIndex(name, string(os.PathSeparator))
// return name[end+1 : len(name)]
}

469
vendor/github.com/go-interpreter/wagon/disasm/disasm.go generated vendored Normal file
View File

@ -0,0 +1,469 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package disasm provides functions for disassembling WebAssembly bytecode.
package disasm
import (
"bytes"
"encoding/binary"
"errors"
"io"
"math"
"github.com/go-interpreter/wagon/internal/stack"
"github.com/go-interpreter/wagon/wasm"
"github.com/go-interpreter/wagon/wasm/leb128"
ops "github.com/go-interpreter/wagon/wasm/operators"
)
// Instr describes an instruction, consisting of an operator, with its
// appropriate immediate value(s).
type Instr struct {
Op ops.Op
// Immediates are arguments to an operator in the bytecode stream itself.
// Valid value types are:
// - (u)(int/float)(32/64)
// - wasm.BlockType
Immediates []interface{}
NewStack *StackInfo // non-nil if the instruction creates or unwinds a stack.
Block *BlockInfo // non-nil if the instruction starts or ends a new block.
Unreachable bool // whether the operator can be reached during execution
// IsReturn is true if executing this instruction will result in the
// function returning. This is true for branches (br, br_if) to
// the depth <max_relative_depth> + 1, or the return operator itself.
// If true, NewStack for this instruction is nil.
IsReturn bool
// If the operator is br_table (ops.BrTable), this is a list of StackInfo
// fields for each of the blocks/branches referenced by the operator.
Branches []StackInfo
}
// StackInfo stores details about a new stack created or unwinded by an instruction.
type StackInfo struct {
StackTopDiff int64 // The difference between the stack depths at the end of the block
PreserveTop bool // Whether the value on the top of the stack should be preserved while unwinding
IsReturn bool // Whether the unwind is equivalent to a return
}
// BlockInfo stores details about a block created or ended by an instruction.
type BlockInfo struct {
Start bool // If true, this instruction starts a block. Else this instruction ends it.
Signature wasm.BlockType // The block signature
// Indices to the accompanying control operator.
// For 'if', this is the index to the 'else' operator.
IfElseIndex int
// For 'else', this is the index to the 'if' operator.
ElseIfIndex int
// The index to the `end' operator for if/else/loop/block.
EndIndex int
// For end, it is the index to the operator that starts the block.
BlockStartIndex int
}
// Disassembly is the result of disassembling a WebAssembly function.
type Disassembly struct {
Code []Instr
MaxDepth int // The maximum stack depth that can be reached while executing this function
}
func (d *Disassembly) checkMaxDepth(depth int) {
if depth > d.MaxDepth {
d.MaxDepth = depth
}
}
func pushPolymorphicOp(indexStack [][]int, index int) {
indexStack[len(indexStack)-1] = append(indexStack[len(indexStack)-1], index)
}
func isInstrReachable(indexStack [][]int) bool {
return len(indexStack[len(indexStack)-1]) == 0
}
var ErrStackUnderflow = errors.New("disasm: stack underflow")
// Disassemble disassembles the given function. It also takes the function's
// parent module as an argument for locating any other functions referenced by
// fn.
func Disassemble(fn wasm.Function, module *wasm.Module) (*Disassembly, error) {
code := fn.Body.Code
reader := bytes.NewReader(code)
disas := &Disassembly{}
// A stack of int arrays holding indices to instructions that make the stack
// polymorphic. Each block has its corresponding array. We start with one
// array for the root stack
blockPolymorphicOps := [][]int{[]int{}}
// a stack of current execution stack depth values, so that the depth for each
// stack is maintained indepepdently for calculating discard values
stackDepths := &stack.Stack{}
stackDepths.Push(0)
blockIndices := &stack.Stack{} // a stack of indices to operators which start new blocks
curIndex := 0
var lastOpReturn bool
for {
op, err := reader.ReadByte()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
logger.Printf("stack top is %d", stackDepths.Top())
opStr, err := ops.New(op)
if err != nil {
return nil, err
}
instr := Instr{
Op: opStr,
Immediates: [](interface{}){},
}
if op == ops.End || op == ops.Else {
// There are two possible cases here:
// 1. The corresponding block/if/loop instruction
// *is* reachable, and an instruction somewhere in this
// block (and NOT in a nested block) makes the stack
// polymorphic. In this case, this end/else is reachable.
//
// 2. The corresponding block/if/loop instruction
// is *not* reachable, which makes this end/else unreachable
// too.
isUnreachable := blockIndices.Len() != len(blockPolymorphicOps)-1
instr.Unreachable = isUnreachable
} else {
instr.Unreachable = !isInstrReachable(blockPolymorphicOps)
}
logger.Printf("op: %s, unreachable: %v", opStr.Name, instr.Unreachable)
if !opStr.Polymorphic && !instr.Unreachable {
top := int(stackDepths.Top())
top -= len(opStr.Args)
stackDepths.SetTop(uint64(top))
if top < -1 {
return nil, ErrStackUnderflow
}
if opStr.Returns != wasm.ValueType(wasm.BlockTypeEmpty) {
top++
stackDepths.SetTop(uint64(top))
}
disas.checkMaxDepth(top)
}
switch op {
case ops.Unreachable:
pushPolymorphicOp(blockPolymorphicOps, curIndex)
case ops.Drop:
if !instr.Unreachable {
stackDepths.SetTop(stackDepths.Top() - 1)
}
case ops.Select:
if !instr.Unreachable {
stackDepths.SetTop(stackDepths.Top() - 2)
}
case ops.Return:
if !instr.Unreachable {
stackDepths.SetTop(stackDepths.Top() - uint64(len(fn.Sig.ReturnTypes)))
}
pushPolymorphicOp(blockPolymorphicOps, curIndex)
lastOpReturn = true
case ops.End, ops.Else:
// The max depth reached while execing the current block
curDepth := stackDepths.Top()
blockStartIndex := blockIndices.Pop()
blockSig := disas.Code[blockStartIndex].Block.Signature
instr.Block = &BlockInfo{
Start: false,
Signature: blockSig,
}
if op == ops.End {
instr.Block.BlockStartIndex = int(blockStartIndex)
disas.Code[blockStartIndex].Block.EndIndex = curIndex
} else { // ops.Else
instr.Block.ElseIfIndex = int(blockStartIndex)
disas.Code[blockStartIndex].Block.IfElseIndex = int(blockStartIndex)
}
// The max depth reached while execing the last block
// If the signature of the current block is not empty,
// this will be incremented.
// Same with ops.Br/BrIf, we subtract 2 instead of 1
// to get the depth of the *parent* block of the branch
// we want to take.
prevDepthIndex := stackDepths.Len() - 2
prevDepth := stackDepths.Get(prevDepthIndex)
if op != ops.Else && blockSig != wasm.BlockTypeEmpty && !instr.Unreachable {
stackDepths.Set(prevDepthIndex, prevDepth+1)
disas.checkMaxDepth(int(stackDepths.Get(prevDepthIndex)))
}
if !lastOpReturn {
elemsDiscard := int(curDepth) - int(prevDepth)
if elemsDiscard < -1 {
return nil, ErrStackUnderflow
}
instr.NewStack = &StackInfo{
StackTopDiff: int64(elemsDiscard),
PreserveTop: blockSig != wasm.BlockTypeEmpty,
}
logger.Printf("discard %d elements, preserve top: %v", elemsDiscard, instr.NewStack.PreserveTop)
} else {
instr.NewStack = &StackInfo{}
}
logger.Printf("setting new stack for %s block (%d)", disas.Code[blockStartIndex].Op.Name, blockStartIndex)
disas.Code[blockStartIndex].NewStack = instr.NewStack
if !instr.Unreachable {
blockPolymorphicOps = blockPolymorphicOps[:len(blockPolymorphicOps)-1]
}
stackDepths.Pop()
if op == ops.Else {
stackDepths.Push(0)
blockIndices.Push(uint64(curIndex))
if !instr.Unreachable {
blockPolymorphicOps = append(blockPolymorphicOps, []int{})
}
}
case ops.Block, ops.Loop, ops.If:
sig, err := leb128.ReadVarint32(reader)
if err != nil {
return nil, err
}
logger.Printf("if, depth is %d", stackDepths.Top())
stackDepths.Push(stackDepths.Top())
// If this new block is unreachable, its
// entire instruction sequence is unreachable
// as well. To make sure that isInstrReachable
// returns the correct value, we don't push a new
// array to blockPolymorphicOps.
if !instr.Unreachable {
// Therefore, only push a new array if this instruction
// is reachable.
blockPolymorphicOps = append(blockPolymorphicOps, []int{})
}
instr.Block = &BlockInfo{
Start: true,
Signature: wasm.BlockType(sig),
}
blockIndices.Push(uint64(curIndex))
instr.Immediates = append(instr.Immediates, wasm.BlockType(sig))
case ops.Br, ops.BrIf:
depth, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, depth)
if int(depth) == blockIndices.Len() {
instr.IsReturn = true
} else {
curDepth := stackDepths.Top()
// whenever we take a branch, the stack is unwound
// to the height of stack of its *parent* block, which
// is why we subtract 2 instead of 1.
// prevDepth holds the height of the stack when
// the block that we branch to started.
prevDepth := stackDepths.Get(stackDepths.Len() - 2 - int(depth))
elemsDiscard := int(curDepth) - int(prevDepth)
if elemsDiscard < 0 {
return nil, ErrStackUnderflow
}
// No need to subtract 2 here, we are getting the block
// we need to branch to.
index := blockIndices.Get(blockIndices.Len() - 1 - int(depth))
instr.NewStack = &StackInfo{
StackTopDiff: int64(elemsDiscard),
PreserveTop: disas.Code[index].Block.Signature != wasm.BlockTypeEmpty,
}
}
if op == ops.Br {
pushPolymorphicOp(blockPolymorphicOps, curIndex)
}
case ops.BrTable:
if !instr.Unreachable {
stackDepths.SetTop(stackDepths.Top() - 1)
}
targetCount, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, targetCount)
for i := uint32(0); i < targetCount; i++ {
entry, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, entry)
var info StackInfo
if int(entry) == blockIndices.Len() {
info.IsReturn = true
} else {
curDepth := stackDepths.Top()
branchDepth := stackDepths.Get(stackDepths.Len() - 2 - int(entry))
elemsDiscard := int(curDepth) - int(branchDepth)
logger.Printf("Curdepth %d branchDepth %d discard %d", curDepth, branchDepth, elemsDiscard)
if elemsDiscard < 0 {
return nil, ErrStackUnderflow
}
index := blockIndices.Get(blockIndices.Len() - 1 - int(entry))
info.StackTopDiff = int64(elemsDiscard)
info.PreserveTop = disas.Code[index].Block.Signature != wasm.BlockTypeEmpty
}
instr.Branches = append(instr.Branches, info)
}
defaultTarget, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, defaultTarget)
var info StackInfo
if int(defaultTarget) == blockIndices.Len() {
info.IsReturn = true
} else {
curDepth := stackDepths.Top()
branchDepth := stackDepths.Get(stackDepths.Len() - 2 - int(defaultTarget))
elemsDiscard := int(curDepth) - int(branchDepth)
if elemsDiscard < 0 {
return nil, ErrStackUnderflow
}
index := blockIndices.Get(blockIndices.Len() - 1 - int(defaultTarget))
info.StackTopDiff = int64(elemsDiscard)
info.PreserveTop = disas.Code[index].Block.Signature != wasm.BlockTypeEmpty
}
instr.Branches = append(instr.Branches, info)
pushPolymorphicOp(blockPolymorphicOps, curIndex)
case ops.Call, ops.CallIndirect:
index, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, index)
if op == ops.CallIndirect {
reserved, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, reserved)
}
if !instr.Unreachable {
var sig *wasm.FunctionSig
top := int(stackDepths.Top())
if op == ops.CallIndirect {
if module.Types == nil {
return nil, errors.New("missing types section")
}
sig = &module.Types.Entries[index]
top--
} else {
sig = module.GetFunction(int(index)).Sig
}
top -= len(sig.ParamTypes)
top += len(sig.ReturnTypes)
stackDepths.SetTop(uint64(top))
disas.checkMaxDepth(top)
}
case ops.GetLocal, ops.SetLocal, ops.TeeLocal, ops.GetGlobal, ops.SetGlobal:
index, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, index)
if !instr.Unreachable {
top := stackDepths.Top()
switch op {
case ops.GetLocal, ops.GetGlobal:
top++
stackDepths.SetTop(top)
disas.checkMaxDepth(int(top))
case ops.SetLocal, ops.SetGlobal:
top--
stackDepths.SetTop(top)
case ops.TeeLocal:
// stack remains unchanged for tee_local
}
}
case ops.I32Const:
i, err := leb128.ReadVarint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, i)
case ops.I64Const:
i, err := leb128.ReadVarint64(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, i)
case ops.F32Const:
var i uint32
// TODO(vibhavp): Switch to a reflect-free method in the future
// for reading off the bytestream.
err := binary.Read(reader, binary.LittleEndian, &i)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, math.Float32frombits(i))
case ops.F64Const:
var i uint64
// TODO(vibhavp): Switch to a reflect-free method in the future
// for reading off the bytestream.
err := binary.Read(reader, binary.LittleEndian, &i)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, math.Float64frombits(i))
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
// read memory_immediate
flags, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, flags)
offset, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, offset)
case ops.CurrentMemory, ops.GrowMemory:
res, err := leb128.ReadVarUint32(reader)
if err != nil {
return nil, err
}
instr.Immediates = append(instr.Immediates, uint8(res))
}
if op != ops.Return {
lastOpReturn = false
}
disas.Code = append(disas.Code, instr)
curIndex++
}
if logging {
for _, instr := range disas.Code {
logger.Printf("%v %v", instr.Op.Name, instr.NewStack)
}
}
return disas, nil
}

33
vendor/github.com/go-interpreter/wagon/disasm/log.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package disasm
import (
"io/ioutil"
"log"
"os"
)
var (
logger *log.Logger
logging bool
)
func SetDebugMode(l bool) {
w := ioutil.Discard
logging = l
if l {
w = os.Stderr
}
logger = log.New(w, "", log.Lshortfile)
logger.SetFlags(log.Lshortfile)
}
func init() {
SetDebugMode(false)
}

57
vendor/github.com/go-interpreter/wagon/exec/call.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import "errors"
var (
// ErrSignatureMismatch is the error value used while trapping the VM when
// a signature mismatch between the table entry and the type entry is found
// in a call_indirect operation.
ErrSignatureMismatch = errors.New("exec: signature mismatch in call_indirect")
// ErrUndefinedElementIndex is the error value used while trapping the VM when
// an invalid index to the module's table space is used as an operand to
// call_indirect
ErrUndefinedElementIndex = errors.New("exec: undefined element index")
)
func (vm *VM) call() {
index := vm.fetchUint32()
vm.funcs[index].call(vm, int64(index))
}
func (vm *VM) callIndirect() {
index := vm.fetchUint32()
fnExpect := vm.module.Types.Entries[index]
_ = vm.fetchUint32() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#call-operators-described-here)
tableIndex := vm.popUint32()
if int(tableIndex) >= len(vm.module.TableIndexSpace[0]) {
panic(ErrUndefinedElementIndex)
}
elemIndex := vm.module.TableIndexSpace[0][tableIndex]
fnActual := vm.module.FunctionIndexSpace[elemIndex]
if len(fnExpect.ParamTypes) != len(fnActual.Sig.ParamTypes) {
panic(ErrSignatureMismatch)
}
if len(fnExpect.ReturnTypes) != len(fnActual.Sig.ReturnTypes) {
panic(ErrSignatureMismatch)
}
for i := range fnExpect.ParamTypes {
if fnExpect.ParamTypes[i] != fnActual.Sig.ParamTypes[i] {
panic(ErrSignatureMismatch)
}
}
for i := range fnExpect.ReturnTypes {
if fnExpect.ReturnTypes[i] != fnActual.Sig.ReturnTypes[i] {
panic(ErrSignatureMismatch)
}
}
vm.funcs[elemIndex].call(vm, int64(elemIndex))
}

View File

@ -0,0 +1,155 @@
// Copyright 2018 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"bytes"
"reflect"
"testing"
"github.com/go-interpreter/wagon/wasm"
)
func TestHostCall(t *testing.T) {
const secretValue = 0xdeadbeef
var secretVariable int = 0
// a host function that can be called by WASM code.
testHostFunction := func() {
secretVariable = secretValue
}
m := wasm.NewModule()
m.Start = &wasm.SectionStartFunction{Index: 0}
// A function signature. Both the host and WASM function
// have the same signature.
fsig := wasm.FunctionSig{
Form: 0,
ParamTypes: []wasm.ValueType{},
ReturnTypes: []wasm.ValueType{},
}
// List of all function types available in this module.
// There is only one: (func [] -> [])
m.Types = &wasm.SectionTypes{
Entries: []wasm.FunctionSig{fsig, fsig},
}
m.Function = &wasm.SectionFunctions{
Types: []uint32{0, 0},
}
// The body of the start function, that should only
// call the host function
fb := wasm.FunctionBody{
Module: m,
Locals: []wasm.LocalEntry{},
// code should disassemble to:
// call 1 (which is host)
// end
Code: []byte{0x02, 0x00, 0x10, 0x01, 0x0b},
}
// There was no call to `ReadModule` so this part emulates
// how the module object would look like if the function
// had been called.
m.FunctionIndexSpace = []wasm.Function{
{
Sig: &fsig,
Body: &fb,
},
{
Sig: &fsig,
Host: reflect.ValueOf(testHostFunction),
},
}
m.Code = &wasm.SectionCode{
Bodies: []wasm.FunctionBody{fb},
}
// Once called, NewVM will execute the module's main
// function.
vm, err := NewVM(m)
if err != nil {
t.Fatalf("Error creating VM: %v", vm)
}
if len(vm.funcs) < 1 {
t.Fatalf("Need at least a start function!")
}
// Only one entry, which should be a function
if secretVariable != secretValue {
t.Fatalf("x is %d instead of %d", secretVariable, secretValue)
}
}
var moduleCallHost = []byte{
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x1A, 0x06, 0x60, 0x01, 0x7F, 0x00, 0x60,
0x01, 0x7F, 0x01, 0x7F, 0x60, 0x00, 0x01, 0x7F, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7C, 0x60,
0x01, 0x7F, 0x01, 0x7F, 0x02, 0x0F, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6E, 0x61, 0x74,
0x69, 0x76, 0x65, 0x00, 0x05, 0x03, 0x02, 0x01, 0x02, 0x04, 0x04, 0x01, 0x70, 0x00, 0x02, 0x06,
0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x01,
0x0B, 0x07, 0x09, 0x01, 0x05, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x01, 0x09, 0x01, 0x00, 0x0A,
0x08, 0x01, 0x06, 0x00, 0x41, 0x00, 0x10, 0x00, 0x0B,
}
func add3(x int32) int32 {
return x + 3
}
func importer(name string) (*wasm.Module, error) {
m := wasm.NewModule()
m.Types = &wasm.SectionTypes{
// List of all function types available in this module.
// There is only one: (func [int32] -> [int32])
Entries: []wasm.FunctionSig{
{
Form: 0,
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
},
},
}
m.FunctionIndexSpace = []wasm.Function{
{
Sig: &m.Types.Entries[0],
Host: reflect.ValueOf(add3),
Body: &wasm.FunctionBody{},
},
}
m.Export = &wasm.SectionExports{
Entries: map[string]wasm.ExportEntry{
"_native": {
FieldStr: "_naive",
Kind: wasm.ExternalFunction,
Index: 0,
},
},
}
return m, nil
}
func TestHostSymbolCall(t *testing.T) {
m, err := wasm.ReadModule(bytes.NewReader(moduleCallHost), importer)
if err != nil {
t.Fatalf("Could not read module: %v", err)
}
vm, err := NewVM(m)
if err != nil {
t.Fatalf("Could not instantiate vm: %v", err)
}
rtrns, err := vm.ExecCode(1)
if err != nil {
t.Fatalf("Error executing the default function: %v", err)
}
if int(rtrns.(uint32)) != 3 {
t.Fatalf("Did not get the right value. Got %d, wanted %d", rtrns, 3)
}
}

21
vendor/github.com/go-interpreter/wagon/exec/const.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
func (vm *VM) i32Const() {
vm.pushUint32(vm.fetchUint32())
}
func (vm *VM) i64Const() {
vm.pushUint64(vm.fetchUint64())
}
func (vm *VM) f32Const() {
vm.pushFloat32(vm.fetchFloat32())
}
func (vm *VM) f64Const() {
vm.pushFloat64(vm.fetchFloat64())
}

17
vendor/github.com/go-interpreter/wagon/exec/control.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import "errors"
// ErrUnreachable is the error value used while trapping the VM when
// an unreachable operator is reached during execution.
var ErrUnreachable = errors.New("exec: reached unreachable")
func (vm *VM) unreachable() {
panic(ErrUnreachable)
}
func (vm *VM) nop() {}

93
vendor/github.com/go-interpreter/wagon/exec/conv.go generated vendored Normal file
View File

@ -0,0 +1,93 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"math"
)
func (vm *VM) i32Wrapi64() {
vm.pushUint32(uint32(vm.popUint64()))
}
func (vm *VM) i32TruncSF32() {
vm.pushInt32(int32(math.Trunc(float64(vm.popFloat32()))))
}
func (vm *VM) i32TruncUF32() {
vm.pushUint32(uint32(math.Trunc(float64(vm.popFloat32()))))
}
func (vm *VM) i32TruncSF64() {
vm.pushInt32(int32(math.Trunc(vm.popFloat64())))
}
func (vm *VM) i32TruncUF64() {
vm.pushUint32(uint32(math.Trunc(vm.popFloat64())))
}
func (vm *VM) i64ExtendSI32() {
vm.pushInt64(int64(vm.popInt32()))
}
func (vm *VM) i64ExtendUI32() {
vm.pushUint64(uint64(vm.popUint32()))
}
func (vm *VM) i64TruncSF32() {
vm.pushInt64(int64(math.Trunc(float64(vm.popFloat32()))))
}
func (vm *VM) i64TruncUF32() {
vm.pushUint64(uint64(math.Trunc(float64(vm.popFloat32()))))
}
func (vm *VM) i64TruncSF64() {
vm.pushInt64(int64(math.Trunc(vm.popFloat64())))
}
func (vm *VM) i64TruncUF64() {
vm.pushUint64(uint64(math.Trunc(vm.popFloat64())))
}
func (vm *VM) f32ConvertSI32() {
vm.pushFloat32(float32(vm.popInt32()))
}
func (vm *VM) f32ConvertUI32() {
vm.pushFloat32(float32(vm.popUint32()))
}
func (vm *VM) f32ConvertSI64() {
vm.pushFloat32(float32(vm.popInt64()))
}
func (vm *VM) f32ConvertUI64() {
vm.pushFloat32(float32(vm.popUint64()))
}
func (vm *VM) f32DemoteF64() {
vm.pushFloat32(float32(vm.popFloat64()))
}
func (vm *VM) f64ConvertSI32() {
vm.pushFloat64(float64(vm.popInt32()))
}
func (vm *VM) f64ConvertUI32() {
vm.pushFloat64(float64(vm.popUint32()))
}
func (vm *VM) f64ConvertSI64() {
vm.pushFloat64(float64(vm.popInt64()))
}
func (vm *VM) f64ConvertUI64() {
vm.pushFloat64(float64(vm.popUint64()))
}
func (vm *VM) f64PromoteF32() {
vm.pushFloat64(float64(vm.popFloat32()))
}

View File

@ -0,0 +1,402 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec_test
import (
"encoding/json"
"fmt"
"math"
"math/big"
"os"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
"testing"
"github.com/go-interpreter/wagon/exec"
"github.com/go-interpreter/wagon/validate"
"github.com/go-interpreter/wagon/wasm"
)
const (
nonSpecTestsDir = "./testdata"
specTestsDir = "./testdata/spec"
)
type testCase struct {
Function string `json:"function"`
Args []string `json:"args"`
Return string `json:"return"`
Trap string `json:"trap"`
RecoverPanic bool `json:"recoverpanic,omitempty"`
ErrorMsg string `json:"errormsg"`
}
type file struct {
FileName string `json:"file"`
Tests []testCase `json:"tests"`
}
var reValue = regexp.MustCompile(`(.+)\:(.+)`)
func parseFloat(str string, bitSize int) float64 {
hexFloat, err := regexp.MatchString("0x", str)
if err != nil {
panic(err)
}
isNanOrInf, err := regexp.MatchString("(nan|inf)", str)
if err != nil {
panic(err)
}
if isNanOrInf {
if strings.HasPrefix(str, "-") {
str = strings.TrimPrefix(str, "-")
if str == "inf" {
return math.Inf(-1)
} else if str == "nan" {
return math.NaN()
}
}
if str == "inf" {
return math.Inf(+1)
} else if str == "nan" {
return math.NaN()
}
}
if hexFloat {
if strings.HasPrefix(str, "-0x") {
str = strings.TrimPrefix(str, "-0x")
if str == "inf" {
return math.Inf(-1)
}
str = "-" + str
} else {
if str == "inf" {
return math.Inf(+1)
} else if str == "nan" {
return math.NaN()
}
str = strings.TrimPrefix(str, "0x")
}
f, _, err := big.ParseFloat(str, 16, big.MaxPrec, big.ToNearestEven)
if err != nil {
panic(err)
}
n, _ := f.Float64()
return n
}
n, err := strconv.ParseFloat(str, bitSize)
if err != nil {
panic(err)
}
return n
}
func TestParseFloat(t *testing.T) {
tests := []struct {
str string
want float64
}{
{"0xf32", 3890.0},
{"3.4", 3.4},
{"0.0", 0.0},
{"-123.0", -123.0},
{"0x1.fffffep+127", 340282346638528859811704183484516925440.0},
{"nan", math.NaN()},
{"inf", math.Inf(+1)},
{"-inf", math.Inf(-1)},
}
for _, test := range tests {
f := parseFloat(test.str, 64)
if f != test.want && !(math.IsNaN(test.want) && math.IsNaN(f)) {
t.Errorf("unexpected value returned by parseFloat: got=%f want=%f", f, test.want)
}
}
}
func parseInt(str string, bitSize int) uint64 {
isHex, _ := regexp.MatchString("0x", str)
base := 10
if isHex {
if strings.HasPrefix(str, "-") {
str = strings.TrimPrefix(str, "-0x")
str = "-" + str
} else {
str = strings.TrimPrefix(str, "0x")
}
base = 16
}
n, err := strconv.ParseUint(str, base, bitSize)
if err != nil {
// try parsing as an int
n2, err := strconv.ParseInt(str, base, bitSize)
if err != nil {
panic(err)
}
n = uint64(n2)
}
return n
}
func TestParseInt(t *testing.T) {
tests := []struct {
str string
want uint64
}{
{"45", 45},
{"0", 0},
{"0xABADCAFEDEAD1DEA", 0xABADCAFEDEAD1DEA},
{"-1", 18446744073709551615},
}
for _, test := range tests {
i := parseInt(test.str, 64)
if i != test.want {
t.Errorf("unexpected value returned by parseInt: got=%d want=%d", i, test.want)
}
}
}
func parseValue(str string) interface{} {
if str == "" {
return nil
}
matches := reValue.FindStringSubmatch(str)
if matches == nil {
panic("Invalid value expression: " + str)
}
switch matches[1] {
case "i32":
n := parseInt(matches[2], 32)
return uint32(n)
case "i64":
n := parseInt(matches[2], 64)
return n
case "f32":
return float32(parseFloat(matches[2], 32))
case "f64":
return parseFloat(matches[2], 64)
default:
panic("invalid value_type prefix " + matches[1])
}
}
func TestParseValue(t *testing.T) {
tests := []struct {
str string
want interface{}
}{
{"i64:45", uint64(45)},
{"f32:0.0", float32(0)},
{"f64:0x1.fffffep+127", float64(340282346638528859811704183484516925440.0)},
{"f64:inf", float64(math.Inf(+1))},
{"f32:inf", float32(math.Inf(+1))},
}
for _, test := range tests {
v := parseValue(test.str)
if !reflect.DeepEqual(test.want, v) {
t.Errorf("unexpected value returned by parseInt: got=%v want=%v", v, test.want)
}
}
}
func parseArgs(args []string) (arr []uint64) {
for _, str := range args {
v := parseValue(str)
var n uint64
switch v.(type) {
case uint64:
n = v.(uint64)
case uint32:
n = uint64(v.(uint32))
case float32:
n = uint64(math.Float32bits(v.(float32)))
case float64:
n = math.Float64bits(v.(float64))
default:
panic(fmt.Sprintf("invalid value type: %v(%v)", reflect.TypeOf(v), v))
}
arr = append(arr, n)
}
return arr
}
func fnString(fn string, args []string) string {
if len(args) == 0 {
return fn
}
return fmt.Sprintf("%s(%v)", fn, args)
}
func panics(fn func()) (panicked bool, msg string) {
defer func() {
r := recover()
panicked = r != nil
msg = fmt.Sprint(r)
}()
fn()
return
}
func runTest(fileName string, testCases []testCase, t testing.TB) {
file, err := os.Open(fileName)
if err != nil {
t.Fatal(err)
}
defer file.Close()
module, err := wasm.ReadModule(file, nil)
if err != nil {
t.Fatal(err)
}
if err = validate.VerifyModule(module); err != nil {
t.Fatalf("%s: %v", fileName, err)
}
vm, err := exec.NewVM(module)
if err != nil {
t.Fatalf("%s: %v", fileName, err)
}
b, ok := t.(*testing.B)
for _, testCase := range testCases {
var expected interface{}
index := module.Export.Entries[testCase.Function].Index
args := parseArgs(testCase.Args)
if testCase.Return != "" {
expected = parseValue(testCase.Return)
}
if testCase.Trap != "" {
// don't benchmark tests that involve trapping the VM
fn := func() {
_, err := vm.ExecCode(int64(index), args...)
if err != nil {
t.Fatalf("%s, %s: %v", fileName, testCase.Function, err)
}
}
if p, msg := panics(fn); p && msg != testCase.Trap {
t.Errorf("%s, %s: unexpected trap message: got=%s, want=%s", fileName, fnString(testCase.Function, testCase.Args), msg, testCase.Trap)
}
continue
}
vm.RecoverPanic = (testCase.RecoverPanic == true)
times := 1
if ok {
times = b.N
b.ResetTimer()
}
var res interface{}
var err error
for i := 0; i < times; i++ {
res, err = vm.ExecCode(int64(index), args...)
}
if ok {
b.StopTimer()
}
if err != nil && err.Error() != testCase.ErrorMsg {
t.Fatalf("%s, %s: %v", fileName, testCase.Function, err)
}
nanEq := false
if reflect.TypeOf(res) == reflect.TypeOf(expected) {
switch v := expected.(type) {
case float32:
nanEq = math.IsNaN(float64(v)) && math.IsNaN(float64(res.(float32)))
case float64:
nanEq = math.IsNaN(v) && math.IsNaN(res.(float64))
}
}
if nanEq {
continue
}
if !reflect.DeepEqual(res, expected) {
t.Fatalf("%s, %s (%d): unexpected return value: got=%v(%v), want=%v(%v) (%s)", fileName, fnString(testCase.Function, testCase.Args), index, reflect.TypeOf(res), res, reflect.TypeOf(expected), expected, testCase.Return)
}
}
}
func testModules(t *testing.T, dir string) {
files := []file{}
file, err := os.Open(filepath.Join(dir, "modules.json"))
if err != nil {
t.Fatal(err)
}
defer file.Close()
err = json.NewDecoder(file).Decode(&files)
if err != nil {
t.Fatal(err)
}
for _, file := range files {
fileName := filepath.Join(dir, file.FileName)
testCases := file.Tests
t.Run(fileName, func(t *testing.T) {
t.Parallel()
path, err := filepath.Abs(fileName)
if err != nil {
t.Fatal(err)
}
runTest(path, testCases, t)
})
}
}
func BenchmarkModules(b *testing.B) {
files := []file{}
file, err := os.Open(filepath.Join("testdata/spec", "modules.json"))
if err != nil {
b.Fatal(err)
}
defer file.Close()
err = json.NewDecoder(file).Decode(&files)
if err != nil {
b.Fatal(err)
}
for _, file := range files {
fileName := filepath.Join("testdata/spec", file.FileName)
testCases := file.Tests
b.Run(fileName, func(b *testing.B) {
path, err := filepath.Abs(fileName)
if err != nil {
b.Fatal(err)
}
runTest(path, testCases, b)
})
}
}
func TestNonSpec(t *testing.T) {
testModules(t, nonSpecTestsDir)
}
func TestSpec(t *testing.T) {
testModules(t, specTestsDir)
}

99
vendor/github.com/go-interpreter/wagon/exec/func.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"fmt"
"math"
"reflect"
"github.com/go-interpreter/wagon/exec/internal/compile"
)
type function interface {
call(vm *VM, index int64)
}
type compiledFunction struct {
code []byte
branchTables []*compile.BranchTable
maxDepth int // maximum stack depth reached while executing the function body
totalLocalVars int // number of local variables used by the function
args int // number of arguments the function accepts
returns bool // whether the function returns a value
}
type goFunction struct {
val reflect.Value
typ reflect.Type
}
func (fn goFunction) call(vm *VM, index int64) {
numIn := fn.typ.NumIn()
args := make([]reflect.Value, numIn)
for i := numIn - 1; i >= 0; i-- {
val := reflect.New(fn.typ.In(i)).Elem()
raw := vm.popUint64()
kind := fn.typ.In(i).Kind()
switch kind {
case reflect.Float64, reflect.Float32:
val.SetFloat(math.Float64frombits(raw))
case reflect.Uint32, reflect.Uint64:
val.SetUint(raw)
case reflect.Int32, reflect.Int64:
val.SetInt(int64(raw))
default:
panic(fmt.Sprintf("exec: args %d invalid kind=%v", i, kind))
}
args[i] = val
}
rtrns := fn.val.Call(args)
for i, out := range rtrns {
kind := out.Kind()
switch kind {
case reflect.Float64, reflect.Float32:
vm.pushFloat64(out.Float())
case reflect.Uint32, reflect.Uint64:
vm.pushUint64(out.Uint())
case reflect.Int32, reflect.Int64:
vm.pushInt64(out.Int())
default:
panic(fmt.Sprintf("exec: return value %d invalid kind=%v", i, kind))
}
}
}
func (compiled compiledFunction) call(vm *VM, index int64) {
newStack := make([]uint64, compiled.maxDepth)
locals := make([]uint64, compiled.totalLocalVars)
for i := compiled.args - 1; i >= 0; i-- {
locals[i] = vm.popUint64()
}
//save execution context
prevCtxt := vm.ctx
vm.ctx = context{
stack: newStack,
locals: locals,
code: compiled.code,
pc: 0,
curFunc: index,
}
rtrn := vm.execCode(compiled)
//restore execution context
vm.ctx = prevCtxt
if compiled.returns {
vm.pushUint64(rtrn)
}
}

View File

@ -0,0 +1,186 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
ops "github.com/go-interpreter/wagon/wasm/operators"
)
func (vm *VM) newFuncTable() {
vm.funcTable[ops.I32Clz] = vm.i32Clz
vm.funcTable[ops.I32Ctz] = vm.i32Ctz
vm.funcTable[ops.I32Popcnt] = vm.i32Popcnt
vm.funcTable[ops.I32Add] = vm.i32Add
vm.funcTable[ops.I32Sub] = vm.i32Sub
vm.funcTable[ops.I32Mul] = vm.i32Mul
vm.funcTable[ops.I32DivS] = vm.i32DivS
vm.funcTable[ops.I32DivU] = vm.i32DivU
vm.funcTable[ops.I32RemS] = vm.i32RemS
vm.funcTable[ops.I32RemU] = vm.i32RemU
vm.funcTable[ops.I32And] = vm.i32And
vm.funcTable[ops.I32Or] = vm.i32Or
vm.funcTable[ops.I32Xor] = vm.i32Xor
vm.funcTable[ops.I32Shl] = vm.i32Shl
vm.funcTable[ops.I32ShrS] = vm.i32ShrS
vm.funcTable[ops.I32ShrU] = vm.i32ShrU
vm.funcTable[ops.I32Rotl] = vm.i32Rotl
vm.funcTable[ops.I32Rotr] = vm.i32Rotr
vm.funcTable[ops.I32Eqz] = vm.i32Eqz
vm.funcTable[ops.I32Eq] = vm.i32Eq
vm.funcTable[ops.I32Ne] = vm.i32Ne
vm.funcTable[ops.I32LtS] = vm.i32LtS
vm.funcTable[ops.I32LtU] = vm.i32LtU
vm.funcTable[ops.I32GtS] = vm.i32GtS
vm.funcTable[ops.I32GtU] = vm.i32GtU
vm.funcTable[ops.I32LeS] = vm.i32LeS
vm.funcTable[ops.I32LeU] = vm.i32LeU
vm.funcTable[ops.I32GeS] = vm.i32GeS
vm.funcTable[ops.I32GeU] = vm.i32GeU
vm.funcTable[ops.I64Clz] = vm.i64Clz
vm.funcTable[ops.I64Ctz] = vm.i64Ctz
vm.funcTable[ops.I64Popcnt] = vm.i64Popcnt
vm.funcTable[ops.I64Add] = vm.i64Add
vm.funcTable[ops.I64Sub] = vm.i64Sub
vm.funcTable[ops.I64Mul] = vm.i64Mul
vm.funcTable[ops.I64DivS] = vm.i64DivS
vm.funcTable[ops.I64DivU] = vm.i64DivU
vm.funcTable[ops.I64RemS] = vm.i64RemS
vm.funcTable[ops.I64RemU] = vm.i64RemU
vm.funcTable[ops.I64And] = vm.i64And
vm.funcTable[ops.I64Or] = vm.i64Or
vm.funcTable[ops.I64Xor] = vm.i64Xor
vm.funcTable[ops.I64Shl] = vm.i64Shl
vm.funcTable[ops.I64ShrS] = vm.i64ShrS
vm.funcTable[ops.I64ShrU] = vm.i64ShrU
vm.funcTable[ops.I64Rotl] = vm.i64Rotl
vm.funcTable[ops.I64Rotr] = vm.i64Rotr
vm.funcTable[ops.I64Eqz] = vm.i64Eqz
vm.funcTable[ops.I64Eq] = vm.i64Eq
vm.funcTable[ops.I64Ne] = vm.i64Ne
vm.funcTable[ops.I64LtS] = vm.i64LtS
vm.funcTable[ops.I64LtU] = vm.i64LtU
vm.funcTable[ops.I64GtS] = vm.i64GtS
vm.funcTable[ops.I64GtU] = vm.i64GtU
vm.funcTable[ops.I64LeS] = vm.i64LeS
vm.funcTable[ops.I64LeU] = vm.i64LeU
vm.funcTable[ops.I64GeS] = vm.i64GeS
vm.funcTable[ops.I64GeU] = vm.i64GeU
vm.funcTable[ops.F32Eq] = vm.f32Eq
vm.funcTable[ops.F32Ne] = vm.f32Ne
vm.funcTable[ops.F32Lt] = vm.f32Lt
vm.funcTable[ops.F32Gt] = vm.f32Gt
vm.funcTable[ops.F32Le] = vm.f32Le
vm.funcTable[ops.F32Ge] = vm.f32Ge
vm.funcTable[ops.F32Abs] = vm.f32Abs
vm.funcTable[ops.F32Neg] = vm.f32Neg
vm.funcTable[ops.F32Ceil] = vm.f32Ceil
vm.funcTable[ops.F32Floor] = vm.f32Floor
vm.funcTable[ops.F32Trunc] = vm.f32Trunc
vm.funcTable[ops.F32Nearest] = vm.f32Nearest
vm.funcTable[ops.F32Sqrt] = vm.f32Sqrt
vm.funcTable[ops.F32Add] = vm.f32Add
vm.funcTable[ops.F32Sub] = vm.f32Sub
vm.funcTable[ops.F32Mul] = vm.f32Mul
vm.funcTable[ops.F32Div] = vm.f32Div
vm.funcTable[ops.F32Min] = vm.f32Min
vm.funcTable[ops.F32Max] = vm.f32Max
vm.funcTable[ops.F32Copysign] = vm.f32Copysign
vm.funcTable[ops.F64Eq] = vm.f64Eq
vm.funcTable[ops.F64Ne] = vm.f64Ne
vm.funcTable[ops.F64Lt] = vm.f64Lt
vm.funcTable[ops.F64Gt] = vm.f64Gt
vm.funcTable[ops.F64Le] = vm.f64Le
vm.funcTable[ops.F64Ge] = vm.f64Ge
vm.funcTable[ops.F64Abs] = vm.f64Abs
vm.funcTable[ops.F64Neg] = vm.f64Neg
vm.funcTable[ops.F64Ceil] = vm.f64Ceil
vm.funcTable[ops.F64Floor] = vm.f64Floor
vm.funcTable[ops.F64Trunc] = vm.f64Trunc
vm.funcTable[ops.F64Nearest] = vm.f64Nearest
vm.funcTable[ops.F64Sqrt] = vm.f64Sqrt
vm.funcTable[ops.F64Add] = vm.f64Add
vm.funcTable[ops.F64Sub] = vm.f64Sub
vm.funcTable[ops.F64Mul] = vm.f64Mul
vm.funcTable[ops.F64Div] = vm.f64Div
vm.funcTable[ops.F64Min] = vm.f64Min
vm.funcTable[ops.F64Max] = vm.f64Max
vm.funcTable[ops.F64Copysign] = vm.f64Copysign
vm.funcTable[ops.I32Const] = vm.i32Const
vm.funcTable[ops.I64Const] = vm.i64Const
vm.funcTable[ops.F32Const] = vm.f32Const
vm.funcTable[ops.F64Const] = vm.f64Const
vm.funcTable[ops.I32ReinterpretF32] = vm.i32ReinterpretF32
vm.funcTable[ops.I64ReinterpretF64] = vm.i64ReinterpretF64
vm.funcTable[ops.F32ReinterpretI32] = vm.f32ReinterpretI32
vm.funcTable[ops.F64ReinterpretI64] = vm.f64ReinterpretI64
vm.funcTable[ops.I32WrapI64] = vm.i32Wrapi64
vm.funcTable[ops.I32TruncSF32] = vm.i32TruncSF32
vm.funcTable[ops.I32TruncUF32] = vm.i32TruncUF32
vm.funcTable[ops.I32TruncSF64] = vm.i32TruncSF64
vm.funcTable[ops.I32TruncUF64] = vm.i32TruncUF64
vm.funcTable[ops.I64ExtendSI32] = vm.i64ExtendSI32
vm.funcTable[ops.I64ExtendUI32] = vm.i64ExtendUI32
vm.funcTable[ops.I64TruncSF32] = vm.i64TruncSF32
vm.funcTable[ops.I64TruncUF32] = vm.i64TruncUF32
vm.funcTable[ops.I64TruncSF64] = vm.i64TruncSF64
vm.funcTable[ops.I64TruncUF64] = vm.i64TruncUF64
vm.funcTable[ops.F32ConvertSI32] = vm.f32ConvertSI32
vm.funcTable[ops.F32ConvertUI32] = vm.f32ConvertUI32
vm.funcTable[ops.F32ConvertSI64] = vm.f32ConvertSI64
vm.funcTable[ops.F32ConvertUI64] = vm.f32ConvertUI64
vm.funcTable[ops.F32DemoteF64] = vm.f32DemoteF64
vm.funcTable[ops.F64ConvertSI32] = vm.f64ConvertSI32
vm.funcTable[ops.F64ConvertUI32] = vm.f64ConvertUI32
vm.funcTable[ops.F64ConvertSI64] = vm.f64ConvertSI64
vm.funcTable[ops.F64ConvertUI64] = vm.f64ConvertUI64
vm.funcTable[ops.F64PromoteF32] = vm.f64PromoteF32
vm.funcTable[ops.I32Load] = vm.i32Load
vm.funcTable[ops.I64Load] = vm.i64Load
vm.funcTable[ops.F32Load] = vm.f32Load
vm.funcTable[ops.F64Load] = vm.f64Load
vm.funcTable[ops.I32Load8s] = vm.i32Load8s
vm.funcTable[ops.I32Load8u] = vm.i32Load8u
vm.funcTable[ops.I32Load16s] = vm.i32Load16s
vm.funcTable[ops.I32Load16u] = vm.i32Load16u
vm.funcTable[ops.I64Load8s] = vm.i64Load8s
vm.funcTable[ops.I64Load8u] = vm.i64Load8u
vm.funcTable[ops.I64Load16s] = vm.i64Load16s
vm.funcTable[ops.I64Load16u] = vm.i64Load16u
vm.funcTable[ops.I64Load32s] = vm.i64Load32s
vm.funcTable[ops.I64Load32u] = vm.i64Load32u
vm.funcTable[ops.I32Store] = vm.i32Store
vm.funcTable[ops.I64Store] = vm.i64Store
vm.funcTable[ops.F32Store] = vm.f32Store
vm.funcTable[ops.F64Store] = vm.f64Store
vm.funcTable[ops.I32Store8] = vm.i32Store8
vm.funcTable[ops.I32Store16] = vm.i32Store16
vm.funcTable[ops.I64Store8] = vm.i64Store8
vm.funcTable[ops.I64Store16] = vm.i64Store16
vm.funcTable[ops.I64Store32] = vm.i64Store32
vm.funcTable[ops.CurrentMemory] = vm.currentMemory
vm.funcTable[ops.GrowMemory] = vm.growMemory
vm.funcTable[ops.Drop] = vm.drop
vm.funcTable[ops.Select] = vm.selectOp
vm.funcTable[ops.GetLocal] = vm.getLocal
vm.funcTable[ops.SetLocal] = vm.setLocal
vm.funcTable[ops.TeeLocal] = vm.teeLocal
vm.funcTable[ops.GetGlobal] = vm.getGlobal
vm.funcTable[ops.SetGlobal] = vm.setGlobal
vm.funcTable[ops.Unreachable] = vm.unreachable
vm.funcTable[ops.Nop] = vm.nop
vm.funcTable[ops.Call] = vm.call
vm.funcTable[ops.CallIndirect] = vm.callIndirect
}

View File

@ -0,0 +1,377 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package compile is used internally by wagon to convert standard structured
// WebAssembly bytecode into an unstructured form suitable for execution by
// it's VM.
// The conversion process consists of translating block instruction sequences
// and branch operators (br, br_if, br_table) to absolute jumps to PC values.
// For instance, an instruction sequence like:
// loop
// i32.const 1
// get_local 0
// i32.add
// set_local 0
// get_local 1
// i32.const 1
// i32.add
// tee_local 1
// get_local 2
// i32.eq
// br_if 0
// end
// Is "compiled" to:
// i32.const 1
// i32.add
// set_local 0
// get_local 1
// i32.const 1
// i32.add
// tee_local 1
// get_local 2
// i32.eq
// jmpnz <addr> <preserve> <discard>
// Where jmpnz is a jump-if-not-zero operator that takes certain arguments
// plus the jump address as immediates.
// This is in contrast with original WebAssembly bytecode, where the target
// of branch operators are relative block depths instead.
package compile
import (
"bytes"
"encoding/binary"
"github.com/go-interpreter/wagon/disasm"
ops "github.com/go-interpreter/wagon/wasm/operators"
)
// A small note on the usage of discard instructions:
// A control operator sequence isn't allowed to access nor modify (pop) operands
// that were pushed outside it. Therefore, each sequence has its own stack
// that may or may not push a value to the original stack, depending on the
// block's signature.
// Instead of creating a new stack every time we enter a control structure,
// we record the current stack height on encountering a control operator.
// After we leave the sequence, the stack height is restored using the discard
// operator. A block with a signature will push a value of that type on the parent
// stack (that is, the stack of the parent block where this block started). The
// OpDiscardPreserveTop operator allows us to preserve this value while
// discarding the remaining ones.
// Branches are rewritten as
// <jmp> <addr>
// Where the address is an 8 byte address, initially set to zero. It is
// later "patched" by patchOffset.
var (
// OpJmp unconditionally jumps to the provided address.
OpJmp byte = 0x0c
// OpJmpZ jumps to the given address if the value at the top of the stack is zero.
OpJmpZ byte = 0x03
// OpJmpNz jumps to the given address if the value at the top of the
// stack is not zero. It also discards elements and optionally preserves
// the topmost value on the stack
OpJmpNz byte = 0x0d
// OpDiscard discards a given number of elements from the execution stack.
OpDiscard byte = 0x0b
// OpDiscardPreserveTop discards a given number of elements from the
// execution stack, while preserving the value on the top of the stack.
OpDiscardPreserveTop byte = 0x05
)
// Target is the "target" of a br_table instruction.
// Unlike other control instructions, br_table does jumps and discarding all
// by itself.
type Target struct {
Addr int64 // The absolute address of the target
Discard int64 // The number of elements to discard
PreserveTop bool // Whether the top of the stack is to be preserved
Return bool // Whether to return in order to take this branch/target
}
// BranchTable is the structure pointed to by a rewritten br_table instruction.
// A rewritten br_table instruction is of the format:
// br_table <table_index>
// where <table_index> is the index to an array of
// BranchTable objects stored by the VM.
type BranchTable struct {
Targets []Target // A list of targets, br_table pops an int value, and jumps to Targets[val]
DefaultTarget Target // If val > len(Targets), the VM will jump here
patchedAddrs []int64 // A list of already patched addresses
blocksLen int // The length of the blocks map in Compile when this table was initialized
}
// block stores the information relevant for a block created by a control operator
// sequence (if...else...end, loop...end, and block...end)
type block struct {
// the byte offset to which the continuation of the label
// created by the block operator is located
// for 'loop', this is the offset of the loop operator itself
// for 'if', 'else', 'block', this is the 'end' operator
offset int64
// Whether this block is created by an 'if' operator
// in that case, the 'offset' field is set to the byte offset
// of the else branch, once the else operator is reached.
ifBlock bool
// if ... else ... end is compiled to
// jmpnz <else-addr> ... jmp <end-addr> ... <discard>
// elseAddrOffset is the byte offset of the else-addr address
// in the new/compiled byte buffer.
elseAddrOffset int64
// Whether this block is created by a 'loop' operator
// in that case, the 'offset' field is set at the end of the block
loopBlock bool
patchOffsets []int64 // A list of offsets in the bytecode stream that need to be patched with the correct jump addresses
discard disasm.StackInfo // Information about the stack created in this block, used while creating Discard instructions
branchTables []*BranchTable // All branch tables that were defined in this block.
}
// Compile rewrites WebAssembly bytecode from its disassembly.
// TODO(vibhavp): Add options for optimizing code. Operators like i32.reinterpret/f32
// are no-ops, and can be safely removed.
func Compile(disassembly []disasm.Instr) ([]byte, []*BranchTable) {
buffer := new(bytes.Buffer)
branchTables := []*BranchTable{}
curBlockDepth := -1
blocks := make(map[int]*block) // maps nesting depths (labels) to blocks
blocks[-1] = &block{}
for _, instr := range disassembly {
if instr.Unreachable {
continue
}
switch instr.Op.Code {
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
// memory_immediate has two fields, the alignment and the offset.
// The former is simply an optimization hint and can be safely
// discarded.
instr.Immediates = []interface{}{instr.Immediates[1].(uint32)}
case ops.If:
curBlockDepth++
buffer.WriteByte(OpJmpZ)
blocks[curBlockDepth] = &block{
ifBlock: true,
elseAddrOffset: int64(buffer.Len()),
}
// the address to jump to if the condition for `if` is false
// (i.e when the value on the top of the stack is 0)
binary.Write(buffer, binary.LittleEndian, int64(0))
continue
case ops.Loop:
// there is no condition for entering a loop block
curBlockDepth++
blocks[curBlockDepth] = &block{
offset: int64(buffer.Len()),
ifBlock: false,
loopBlock: true,
discard: *instr.NewStack,
}
continue
case ops.Block:
curBlockDepth++
blocks[curBlockDepth] = &block{
ifBlock: false,
discard: *instr.NewStack,
}
continue
case ops.Else:
ifInstr := disassembly[instr.Block.ElseIfIndex] // the corresponding `if` instruction for this else
if ifInstr.NewStack != nil && ifInstr.NewStack.StackTopDiff != 0 {
// add code for jumping out of a taken if branch
if ifInstr.NewStack.PreserveTop {
buffer.WriteByte(OpDiscardPreserveTop)
} else {
buffer.WriteByte(OpDiscard)
}
binary.Write(buffer, binary.LittleEndian, ifInstr.NewStack.StackTopDiff)
}
buffer.WriteByte(OpJmp)
ifBlockEndOffset := int64(buffer.Len())
binary.Write(buffer, binary.LittleEndian, int64(0))
curOffset := int64(buffer.Len())
ifBlock := blocks[curBlockDepth]
code := buffer.Bytes()
buffer = patchOffset(code, ifBlock.elseAddrOffset, curOffset)
// this is no longer an if block
ifBlock.ifBlock = false
ifBlock.patchOffsets = append(ifBlock.patchOffsets, ifBlockEndOffset)
continue
case ops.End:
depth := curBlockDepth
block := blocks[depth]
if instr.NewStack.StackTopDiff != 0 {
// when exiting a block, discard elements to
// restore stack height.
if instr.NewStack.PreserveTop {
// this is true when the block has a
// signature, and therefore pushes
// a value on to the stack
buffer.WriteByte(OpDiscardPreserveTop)
} else {
buffer.WriteByte(OpDiscard)
}
binary.Write(buffer, binary.LittleEndian, instr.NewStack.StackTopDiff)
}
if !block.loopBlock { // is a normal block
block.offset = int64(buffer.Len())
if block.ifBlock {
code := buffer.Bytes()
buffer = patchOffset(code, block.elseAddrOffset, int64(block.offset))
}
}
for _, offset := range block.patchOffsets {
code := buffer.Bytes()
buffer = patchOffset(code, offset, block.offset)
}
for _, table := range block.branchTables {
table.patchTable(table.blocksLen-depth-1, int64(block.offset))
}
delete(blocks, curBlockDepth)
curBlockDepth--
continue
case ops.Br:
if instr.NewStack != nil && instr.NewStack.StackTopDiff != 0 {
if instr.NewStack.PreserveTop {
buffer.WriteByte(OpDiscardPreserveTop)
} else {
buffer.WriteByte(OpDiscard)
}
binary.Write(buffer, binary.LittleEndian, instr.NewStack.StackTopDiff)
}
buffer.WriteByte(OpJmp)
label := int(instr.Immediates[0].(uint32))
block := blocks[curBlockDepth-int(label)]
block.patchOffsets = append(block.patchOffsets, int64(buffer.Len()))
// write the jump address
binary.Write(buffer, binary.LittleEndian, int64(0))
continue
case ops.BrIf:
buffer.WriteByte(OpJmpNz)
label := int(instr.Immediates[0].(uint32))
block := blocks[curBlockDepth-int(label)]
block.patchOffsets = append(block.patchOffsets, int64(buffer.Len()))
// write the jump address
binary.Write(buffer, binary.LittleEndian, int64(0))
var stackTopDiff int64
// write whether we need to preserve the top
if instr.NewStack == nil || !instr.NewStack.PreserveTop || instr.NewStack.StackTopDiff == 0 {
buffer.WriteByte(byte(0))
} else {
stackTopDiff = instr.NewStack.StackTopDiff
buffer.WriteByte(byte(1))
}
// write the number of elements on the stack we need to discard
binary.Write(buffer, binary.LittleEndian, stackTopDiff)
continue
case ops.BrTable:
branchTable := &BranchTable{
// we subtract one for the implicit block created by
// the function body
blocksLen: len(blocks) - 1,
}
targetCount := instr.Immediates[0].(uint32)
branchTable.Targets = make([]Target, targetCount)
for i := range branchTable.Targets {
// The first immediates is the number of targets, so we ignore that
label := int64(instr.Immediates[i+1].(uint32))
branchTable.Targets[i].Addr = label
branch := instr.Branches[i]
branchTable.Targets[i].Return = branch.IsReturn
branchTable.Targets[i].Discard = branch.StackTopDiff
branchTable.Targets[i].PreserveTop = branch.PreserveTop
}
defaultLabel := int64(instr.Immediates[len(instr.Immediates)-1].(uint32))
branchTable.DefaultTarget.Addr = defaultLabel
defaultBranch := instr.Branches[targetCount]
branchTable.DefaultTarget.Return = defaultBranch.IsReturn
branchTable.DefaultTarget.Discard = defaultBranch.StackTopDiff
branchTable.DefaultTarget.PreserveTop = defaultBranch.PreserveTop
branchTables = append(branchTables, branchTable)
for _, block := range blocks {
block.branchTables = append(block.branchTables, branchTable)
}
buffer.WriteByte(ops.BrTable)
binary.Write(buffer, binary.LittleEndian, int64(len(branchTables)-1))
}
buffer.WriteByte(instr.Op.Code)
for _, imm := range instr.Immediates {
err := binary.Write(buffer, binary.LittleEndian, imm)
if err != nil {
panic(err)
}
}
}
// writing nop as the last instructions allows us to branch out of the
// function (ie, return)
addr := buffer.Len()
buffer.WriteByte(ops.Nop)
// patch all references to the "root" block of the function body
for _, offset := range blocks[-1].patchOffsets {
code := buffer.Bytes()
buffer = patchOffset(code, offset, int64(addr))
}
for _, table := range branchTables {
table.patchedAddrs = nil
}
return buffer.Bytes(), branchTables
}
// replace the address starting at start with addr
func patchOffset(code []byte, start int64, addr int64) *bytes.Buffer {
var shift uint
for i := int64(0); i < 8; i++ {
code[start+i] = byte(addr >> shift)
shift += 8
}
buf := new(bytes.Buffer)
buf.Write(code)
return buf
}
func (table *BranchTable) patchTable(block int, addr int64) {
if block < 0 {
panic("Invalid block value")
}
for i, target := range table.Targets {
if !table.isAddr(target.Addr) && target.Addr == int64(block) {
table.Targets[i].Addr = addr
}
}
if table.DefaultTarget.Addr == int64(block) {
table.DefaultTarget.Addr = addr
}
table.patchedAddrs = append(table.patchedAddrs, addr)
}
// Whether the given value is an instruction (or the block depth)
func (table *BranchTable) isAddr(addr int64) bool {
for _, t := range table.patchedAddrs {
if t == addr {
return true
}
}
return false
}

214
vendor/github.com/go-interpreter/wagon/exec/memory.go generated vendored Normal file
View File

@ -0,0 +1,214 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"errors"
"math"
)
// ErrOutOfBoundsMemoryAccess is the error value used while trapping the VM
// when it detects an out of bounds access to the linear memory.
var ErrOutOfBoundsMemoryAccess = errors.New("exec: out of bounds memory access")
func (vm *VM) fetchBaseAddr() int {
return int(vm.fetchUint32() + uint32(vm.popInt32()))
}
// inBounds returns true when the next vm.fetchBaseAddr() + offset
// indices are in bounds accesses to the linear memory.
func (vm *VM) inBounds(offset int) bool {
addr := endianess.Uint32(vm.ctx.code[vm.ctx.pc:]) + uint32(vm.ctx.stack[len(vm.ctx.stack)-1])
return int(addr)+offset < len(vm.memory)
}
// curMem returns a slice to the memeory segment pointed to by
// the current base address on the bytecode stream.
func (vm *VM) curMem() []byte {
return vm.memory[vm.fetchBaseAddr():]
}
func (vm *VM) i32Load() {
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint32(endianess.Uint32(vm.curMem()))
}
func (vm *VM) i32Load8s() {
if !vm.inBounds(0) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushInt32(int32(int8(vm.memory[vm.fetchBaseAddr()])))
}
func (vm *VM) i32Load8u() {
if !vm.inBounds(0) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint32(uint32(uint8(vm.memory[vm.fetchBaseAddr()])))
}
func (vm *VM) i32Load16s() {
if !vm.inBounds(1) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushInt32(int32(int16(endianess.Uint16(vm.curMem()))))
}
func (vm *VM) i32Load16u() {
if !vm.inBounds(1) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint32(uint32(endianess.Uint16(vm.curMem())))
}
func (vm *VM) i64Load() {
if !vm.inBounds(7) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint64(endianess.Uint64(vm.curMem()))
}
func (vm *VM) i64Load8s() {
if !vm.inBounds(0) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushInt64(int64(int8(vm.memory[vm.fetchBaseAddr()])))
}
func (vm *VM) i64Load8u() {
if !vm.inBounds(0) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint64(uint64(uint8(vm.memory[vm.fetchBaseAddr()])))
}
func (vm *VM) i64Load16s() {
if !vm.inBounds(1) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushInt64(int64(int16(endianess.Uint16(vm.curMem()))))
}
func (vm *VM) i64Load16u() {
if !vm.inBounds(1) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint64(uint64(endianess.Uint16(vm.curMem())))
}
func (vm *VM) i64Load32s() {
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushInt64(int64(int32(endianess.Uint32(vm.curMem()))))
}
func (vm *VM) i64Load32u() {
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushUint64(uint64(endianess.Uint32(vm.curMem())))
}
func (vm *VM) f32Store() {
v := math.Float32bits(vm.popFloat32())
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint32(vm.curMem(), v)
}
func (vm *VM) f32Load() {
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushFloat32(math.Float32frombits(endianess.Uint32(vm.curMem())))
}
func (vm *VM) f64Store() {
v := math.Float64bits(vm.popFloat64())
if !vm.inBounds(7) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint64(vm.curMem(), v)
}
func (vm *VM) f64Load() {
if !vm.inBounds(7) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.pushFloat64(math.Float64frombits(endianess.Uint64(vm.curMem())))
}
func (vm *VM) i32Store() {
v := vm.popUint32()
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint32(vm.curMem(), v)
}
func (vm *VM) i32Store8() {
v := byte(uint8(vm.popUint32()))
if !vm.inBounds(0) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.memory[vm.fetchBaseAddr()] = v
}
func (vm *VM) i32Store16() {
v := uint16(vm.popUint32())
if !vm.inBounds(1) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint16(vm.curMem(), v)
}
func (vm *VM) i64Store() {
v := vm.popUint64()
if !vm.inBounds(7) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint64(vm.curMem(), v)
}
func (vm *VM) i64Store8() {
v := byte(uint8(vm.popUint64()))
if !vm.inBounds(0) {
panic(ErrOutOfBoundsMemoryAccess)
}
vm.memory[vm.fetchBaseAddr()] = v
}
func (vm *VM) i64Store16() {
v := uint16(vm.popUint64())
if !vm.inBounds(1) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint16(vm.curMem(), v)
}
func (vm *VM) i64Store32() {
v := uint32(vm.popUint64())
if !vm.inBounds(3) {
panic(ErrOutOfBoundsMemoryAccess)
}
endianess.PutUint32(vm.curMem(), v)
}
func (vm *VM) currentMemory() {
_ = vm.fetchInt8() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#memory-related-operators-described-here)
vm.pushInt32(int32(len(vm.memory) / wasmPageSize))
}
func (vm *VM) growMemory() {
_ = vm.fetchInt8() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#memory-related-operators-described-here)
curLen := len(vm.memory) / wasmPageSize
n := vm.popInt32()
vm.memory = append(vm.memory, make([]byte, n*wasmPageSize)...)
vm.pushInt32(int32(curLen))
}

508
vendor/github.com/go-interpreter/wagon/exec/num.go generated vendored Normal file
View File

@ -0,0 +1,508 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"math"
"math/bits"
)
// int32 operators
func (vm *VM) i32Clz() {
vm.pushUint64(uint64(bits.LeadingZeros32(vm.popUint32())))
}
func (vm *VM) i32Ctz() {
vm.pushUint64(uint64(bits.TrailingZeros32(vm.popUint32())))
}
func (vm *VM) i32Popcnt() {
vm.pushUint64(uint64(bits.OnesCount32(vm.popUint32())))
}
func (vm *VM) i32Add() {
vm.pushUint32(vm.popUint32() + vm.popUint32())
}
func (vm *VM) i32Mul() {
vm.pushUint32(vm.popUint32() * vm.popUint32())
}
func (vm *VM) i32DivS() {
v2 := vm.popInt32()
v1 := vm.popInt32()
vm.pushInt32(v1 / v2)
}
func (vm *VM) i32DivU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(v1 / v2)
}
func (vm *VM) i32RemS() {
v2 := vm.popInt32()
v1 := vm.popInt32()
vm.pushInt32(v1 % v2)
}
func (vm *VM) i32RemU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(v1 % v2)
}
func (vm *VM) i32Sub() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(v1 - v2)
}
func (vm *VM) i32And() {
vm.pushUint32(vm.popUint32() & vm.popUint32())
}
func (vm *VM) i32Or() {
vm.pushUint32(vm.popUint32() | vm.popUint32())
}
func (vm *VM) i32Xor() {
vm.pushUint32(vm.popUint32() ^ vm.popUint32())
}
func (vm *VM) i32Shl() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(v1 << v2)
}
func (vm *VM) i32ShrU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(v1 >> v2)
}
func (vm *VM) i32ShrS() {
v2 := vm.popUint32()
v1 := vm.popInt32()
vm.pushInt32(v1 >> v2)
}
func (vm *VM) i32Rotl() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(bits.RotateLeft32(v1, int(v2)))
}
func (vm *VM) i32Rotr() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushUint32(bits.RotateLeft32(v1, -int(v2)))
}
func (vm *VM) i32LeS() {
v2 := vm.popInt32()
v1 := vm.popInt32()
vm.pushBool(v1 <= v2)
}
func (vm *VM) i32LeU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushBool(v1 <= v2)
}
func (vm *VM) i32LtS() {
v2 := vm.popInt32()
v1 := vm.popInt32()
vm.pushBool(v1 < v2)
}
func (vm *VM) i32LtU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushBool(v1 < v2)
}
func (vm *VM) i32GtS() {
v2 := vm.popInt32()
v1 := vm.popInt32()
vm.pushBool(v1 > v2)
}
func (vm *VM) i32GeS() {
v2 := vm.popInt32()
v1 := vm.popInt32()
vm.pushBool(v1 >= v2)
}
func (vm *VM) i32GtU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushBool(v1 > v2)
}
func (vm *VM) i32GeU() {
v2 := vm.popUint32()
v1 := vm.popUint32()
vm.pushBool(v1 >= v2)
}
func (vm *VM) i32Eqz() {
vm.pushBool(vm.popUint32() == 0)
}
func (vm *VM) i32Eq() {
vm.pushBool(vm.popUint32() == vm.popUint32())
}
func (vm *VM) i32Ne() {
vm.pushBool(vm.popUint32() != vm.popUint32())
}
// int64 operators
func (vm *VM) i64Clz() {
vm.pushUint64(uint64(bits.LeadingZeros64(vm.popUint64())))
}
func (vm *VM) i64Ctz() {
vm.pushUint64(uint64(bits.TrailingZeros64(vm.popUint64())))
}
func (vm *VM) i64Popcnt() {
vm.pushUint64(uint64(bits.OnesCount64(vm.popUint64())))
}
func (vm *VM) i64Add() {
vm.pushUint64(vm.popUint64() + vm.popUint64())
}
func (vm *VM) i64Sub() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushUint64(v1 - v2)
}
func (vm *VM) i64Mul() {
vm.pushUint64(vm.popUint64() * vm.popUint64())
}
func (vm *VM) i64DivS() {
v2 := vm.popInt64()
v1 := vm.popInt64()
vm.pushInt64(v1 / v2)
}
func (vm *VM) i64DivU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushUint64(v1 / v2)
}
func (vm *VM) i64RemS() {
v2 := vm.popInt64()
v1 := vm.popInt64()
vm.pushInt64(v1 % v2)
}
func (vm *VM) i64RemU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushUint64(v1 % v2)
}
func (vm *VM) i64And() {
vm.pushUint64(vm.popUint64() & vm.popUint64())
}
func (vm *VM) i64Or() {
vm.pushUint64(vm.popUint64() | vm.popUint64())
}
func (vm *VM) i64Xor() {
vm.pushUint64(vm.popUint64() ^ vm.popUint64())
}
func (vm *VM) i64Shl() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushUint64(v1 << v2)
}
func (vm *VM) i64ShrS() {
v2 := vm.popUint64()
v1 := vm.popInt64()
vm.pushInt64(v1 >> v2)
}
func (vm *VM) i64ShrU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushUint64(v1 >> v2)
}
func (vm *VM) i64Rotl() {
v2 := vm.popInt64()
v1 := vm.popUint64()
vm.pushUint64(bits.RotateLeft64(v1, int(v2)))
}
func (vm *VM) i64Rotr() {
v2 := vm.popInt64()
v1 := vm.popUint64()
vm.pushUint64(bits.RotateLeft64(v1, -int(v2)))
}
func (vm *VM) i64Eq() {
vm.pushBool(vm.popUint64() == vm.popUint64())
}
func (vm *VM) i64Eqz() {
vm.pushBool(vm.popUint64() == 0)
}
func (vm *VM) i64Ne() {
vm.pushBool(vm.popUint64() != vm.popUint64())
}
func (vm *VM) i64LtS() {
v2 := vm.popInt64()
v1 := vm.popInt64()
vm.pushBool(v1 < v2)
}
func (vm *VM) i64LtU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushBool(v1 < v2)
}
func (vm *VM) i64GtS() {
v2 := vm.popInt64()
v1 := vm.popInt64()
vm.pushBool(v1 > v2)
}
func (vm *VM) i64GtU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushBool(v1 > v2)
}
func (vm *VM) i64LeU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushBool(v1 <= v2)
}
func (vm *VM) i64LeS() {
v2 := vm.popInt64()
v1 := vm.popInt64()
vm.pushBool(v1 <= v2)
}
func (vm *VM) i64GeS() {
v2 := vm.popInt64()
v1 := vm.popInt64()
vm.pushBool(v1 >= v2)
}
func (vm *VM) i64GeU() {
v2 := vm.popUint64()
v1 := vm.popUint64()
vm.pushBool(v1 >= v2)
}
// float32 operators
func (vm *VM) f32Abs() {
vm.pushFloat32(float32(math.Abs(float64(vm.popFloat32()))))
}
func (vm *VM) f32Neg() {
vm.pushFloat32(-vm.popFloat32())
}
func (vm *VM) f32Ceil() {
vm.pushFloat32(float32(math.Ceil(float64(vm.popFloat32()))))
}
func (vm *VM) f32Floor() {
vm.pushFloat32(float32(math.Floor(float64(vm.popFloat32()))))
}
func (vm *VM) f32Trunc() {
vm.pushFloat32(float32(math.Trunc(float64(vm.popFloat32()))))
}
func (vm *VM) f32Nearest() {
f := vm.popFloat32()
vm.pushFloat32(float32(int32(f + float32(math.Copysign(0.5, float64(f))))))
}
func (vm *VM) f32Sqrt() {
vm.pushFloat32(float32(math.Sqrt(float64(vm.popFloat32()))))
}
func (vm *VM) f32Add() {
vm.pushFloat32(vm.popFloat32() + vm.popFloat32())
}
func (vm *VM) f32Sub() {
v2 := vm.popFloat32()
v1 := vm.popFloat32()
vm.pushFloat32(v1 - v2)
}
func (vm *VM) f32Mul() {
vm.pushFloat32(vm.popFloat32() * vm.popFloat32())
}
func (vm *VM) f32Div() {
v2 := vm.popFloat32()
v1 := vm.popFloat32()
vm.pushFloat32(v1 / v2)
}
func (vm *VM) f32Min() {
vm.pushFloat32(float32(math.Min(float64(vm.popFloat32()), float64(vm.popFloat32()))))
}
func (vm *VM) f32Max() {
vm.pushFloat32(float32(math.Max(float64(vm.popFloat32()), float64(vm.popFloat32()))))
}
func (vm *VM) f32Copysign() {
vm.pushFloat32(float32(math.Copysign(float64(vm.popFloat32()), float64(vm.popFloat32()))))
}
func (vm *VM) f32Eq() {
vm.pushBool(vm.popFloat32() == vm.popFloat32())
}
func (vm *VM) f32Ne() {
vm.pushBool(vm.popFloat32() != vm.popFloat32())
}
func (vm *VM) f32Lt() {
v2 := vm.popFloat32()
v1 := vm.popFloat32()
vm.pushBool(v1 < v2)
}
func (vm *VM) f32Gt() {
v2 := vm.popFloat32()
v1 := vm.popFloat32()
vm.pushBool(v1 > v2)
}
func (vm *VM) f32Le() {
v2 := vm.popFloat32()
v1 := vm.popFloat32()
vm.pushBool(v1 <= v2)
}
func (vm *VM) f32Ge() {
v2 := vm.popFloat32()
v1 := vm.popFloat32()
vm.pushBool(v1 >= v2)
}
// float64 operators
func (vm *VM) f64Abs() {
vm.pushFloat64(math.Abs(vm.popFloat64()))
}
func (vm *VM) f64Neg() {
vm.pushFloat64(-vm.popFloat64())
}
func (vm *VM) f64Ceil() {
vm.pushFloat64(math.Ceil(vm.popFloat64()))
}
func (vm *VM) f64Floor() {
vm.pushFloat64(math.Floor(vm.popFloat64()))
}
func (vm *VM) f64Trunc() {
vm.pushFloat64(math.Trunc(vm.popFloat64()))
}
func (vm *VM) f64Nearest() {
f := vm.popFloat64()
vm.pushFloat64(float64(int64(f + math.Copysign(0.5, f))))
}
func (vm *VM) f64Sqrt() {
vm.pushFloat64(math.Sqrt(vm.popFloat64()))
}
func (vm *VM) f64Add() {
vm.pushFloat64(vm.popFloat64() + vm.popFloat64())
}
func (vm *VM) f64Sub() {
v2 := vm.popFloat64()
v1 := vm.popFloat64()
vm.pushFloat64(v1 - v2)
}
func (vm *VM) f64Mul() {
vm.pushFloat64(vm.popFloat64() * vm.popFloat64())
}
func (vm *VM) f64Div() {
v2 := vm.popFloat64()
v1 := vm.popFloat64()
vm.pushFloat64(v1 / v2)
}
func (vm *VM) f64Min() {
vm.pushFloat64(math.Min(vm.popFloat64(), vm.popFloat64()))
}
func (vm *VM) f64Max() {
vm.pushFloat64(math.Max(vm.popFloat64(), vm.popFloat64()))
}
func (vm *VM) f64Copysign() {
vm.pushFloat64(math.Copysign(vm.popFloat64(), vm.popFloat64()))
}
func (vm *VM) f64Eq() {
vm.pushBool(vm.popFloat64() == vm.popFloat64())
}
func (vm *VM) f64Ne() {
vm.pushBool(vm.popFloat64() != vm.popFloat64())
}
func (vm *VM) f64Lt() {
v2 := vm.popFloat64()
v1 := vm.popFloat64()
vm.pushBool(v1 < v2)
}
func (vm *VM) f64Gt() {
v2 := vm.popFloat64()
v1 := vm.popFloat64()
vm.pushBool(v1 > v2)
}
func (vm *VM) f64Le() {
v2 := vm.popFloat64()
v1 := vm.popFloat64()
vm.pushBool(v1 <= v2)
}
func (vm *VM) f64Ge() {
v2 := vm.popFloat64()
v1 := vm.popFloat64()
vm.pushBool(v1 >= v2)
}

View File

@ -0,0 +1,21 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
func (vm *VM) drop() {
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-1]
}
func (vm *VM) selectOp() {
c := vm.popUint32()
val2 := vm.popUint64()
val1 := vm.popUint64()
if c != 0 {
vm.pushUint64(val1)
} else {
vm.pushUint64(val2)
}
}

View File

@ -0,0 +1,29 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"math"
)
// these operations are essentially no-ops.
// TODO(vibhavp): Add optimisations to package compiles that
// removes them from the original bytecode.
func (vm *VM) i32ReinterpretF32() {
vm.pushUint32(math.Float32bits(vm.popFloat32()))
}
func (vm *VM) i64ReinterpretF64() {
vm.pushUint64(math.Float64bits(vm.popFloat64()))
}
func (vm *VM) f32ReinterpretI32() {
vm.pushFloat32(math.Float32frombits(vm.popUint32()))
}
func (vm *VM) f64ReinterpretI64() {
vm.pushFloat64(math.Float64frombits(vm.popUint64()))
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
(module
(memory 1)
(data (i32.const 0) "abcdefghijklmnopqrstuvwxyz")
(func (export "good1") (param $i i32) (result i32)
(i32.load8_u offset=0 (get_local $i)) ;; 97 'a'
)
(func (export "good2") (param $i i32) (result i32)
(i32.load8_u offset=1 (get_local $i)) ;; 98 'b'
)
(func (export "good3") (param $i i32) (result i32)
(i32.load8_u offset=2 (get_local $i)) ;; 99 'c'
)
(func (export "good4") (param $i i32) (result i32)
(i32.load8_u offset=25 (get_local $i)) ;; 122 'z'
)
(func (export "good5") (param $i i32) (result i32)
(i32.load16_u offset=0 (get_local $i)) ;; 25185 'ab'
)
(func (export "good6") (param $i i32) (result i32)
(i32.load16_u align=1 (get_local $i)) ;; 25185 'ab'
)
(func (export "good7") (param $i i32) (result i32)
(i32.load16_u offset=1 align=1 (get_local $i)) ;; 25442 'bc'
)
(func (export "good8") (param $i i32) (result i32)
(i32.load16_u offset=2 (get_local $i)) ;; 25699 'cd'
)
(func (export "good9") (param $i i32) (result i32)
(i32.load16_u offset=25 align=1 (get_local $i)) ;; 122 'z\0'
)
(func (export "good10") (param $i i32) (result i32)
(i32.load offset=0 (get_local $i)) ;; 1684234849 'abcd'
)
(func (export "good11") (param $i i32) (result i32)
(i32.load offset=1 align=1 (get_local $i)) ;; 1701077858 'bcde'
)
(func (export "good12") (param $i i32) (result i32)
(i32.load offset=2 align=2 (get_local $i)) ;; 1717920867 'cdef'
)
(func (export "good13") (param $i i32) (result i32)
(i32.load offset=25 align=1 (get_local $i)) ;; 122 'z\0\0\0'
)
(func (export "bad") (param $i i32)
(drop (i32.load offset=4294967295 (get_local $i)))
)
)

View File

@ -0,0 +1,136 @@
(module
;; Auxiliary definition
(func $dummy)
(func (export "empty")
(block)
(block $l)
)
(func (export "singular") (result i32)
(block (nop))
(block (result i32) (i32.const 7))
)
(func (export "multi") (result i32)
(block (call $dummy) (call $dummy) (call $dummy) (call $dummy))
(block (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8))
)
(func (export "nested") (result i32)
(block (result i32)
(block (call $dummy) (block) (nop))
(block (result i32) (call $dummy) (i32.const 9))
)
)
(func (export "deep") (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(block (result i32) (block (result i32)
(call $dummy) (i32.const 150)
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
)
(func (export "as-unary-operand") (result i32)
(i32.ctz (block (result i32) (call $dummy) (i32.const 13)))
)
(func (export "as-binary-operand") (result i32)
(i32.mul
(block (result i32) (call $dummy) (i32.const 3))
(block (result i32) (call $dummy) (i32.const 4))
)
)
(func (export "as-test-operand") (result i32)
(i32.eqz (block (result i32) (call $dummy) (i32.const 13)))
)
(func (export "as-compare-operand") (result i32)
(f32.gt
(block (result f32) (call $dummy) (f32.const 3))
(block (result f32) (call $dummy) (f32.const 3))
)
)
(func (export "break-bare") (result i32)
(block (br 0) (unreachable))
(block (br_if 0 (i32.const 1)) (unreachable))
(block (br_table 0 (i32.const 0)) (unreachable))
(block (br_table 0 0 0 (i32.const 1)) (unreachable))
(i32.const 19)
)
(func (export "break-value") (result i32)
(block (result i32) (br 0 (i32.const 18)) (i32.const 19))
)
(func (export "break-repeated") (result i32)
(block (result i32)
(br 0 (i32.const 18))
(br 0 (i32.const 19))
(drop (br_if 0 (i32.const 20) (i32.const 0)))
(drop (br_if 0 (i32.const 20) (i32.const 1)))
(br 0 (i32.const 21))
(br_table 0 (i32.const 22) (i32.const 4))
(br_table 0 0 0 (i32.const 23) (i32.const 1))
(i32.const 21)
)
)
(func (export "break-inner") (result i32)
(local i32)
(set_local 0 (i32.const 0))
(set_local 0 (i32.add (get_local 0) (block (result i32) (block (result i32) (br 1 (i32.const 0x1))))))
(set_local 0 (i32.add (get_local 0) (block (result i32) (block (br 0)) (i32.const 0x2))))
(set_local 0
(i32.add (get_local 0) (block (result i32) (i32.ctz (br 0 (i32.const 0x4)))))
)
(set_local 0
(i32.add (get_local 0) (block (result i32) (i32.ctz (block (result i32) (br 1 (i32.const 0x8))))))
)
(get_local 0)
)
(func (export "effects") (result i32)
(local i32)
(block
(set_local 0 (i32.const 1))
(set_local 0 (i32.mul (get_local 0) (i32.const 3)))
(set_local 0 (i32.sub (get_local 0) (i32.const 5)))
(set_local 0 (i32.mul (get_local 0) (i32.const 7)))
(br 0)
(set_local 0 (i32.mul (get_local 0) (i32.const 100)))
)
(i32.eq (get_local 0) (i32.const -14))
)
)

View File

@ -0,0 +1,323 @@
(module
;; Auxiliary definition
(func $dummy)
(func (export "type-i32") (block (drop (i32.ctz (br 0)))))
(func (export "type-i64") (block (drop (i64.ctz (br 0)))))
(func (export "type-f32") (block (drop (f32.neg (br 0)))))
(func (export "type-f64") (block (drop (f64.neg (br 0)))))
(func (export "type-i32-value") (result i32)
(block (result i32) (i32.ctz (br 0 (i32.const 1))))
)
(func (export "type-i64-value") (result i64)
(block (result i64) (i64.ctz (br 0 (i64.const 2))))
)
(func (export "type-f32-value") (result f32)
(block (result f32) (f32.neg (br 0 (f32.const 3))))
)
(func (export "type-f64-value") (result f64)
(block (result f64) (f64.neg (br 0 (f64.const 4))))
)
(func (export "as-block-first")
(block (br 0) (call $dummy))
)
(func (export "as-block-mid")
(block (call $dummy) (br 0) (call $dummy))
)
(func (export "as-block-last")
(block (nop) (call $dummy) (br 0))
)
(func (export "as-block-value") (result i32)
(block (result i32) (nop) (call $dummy) (br 0 (i32.const 2)))
)
(func (export "as-loop-first") (result i32)
(block (result i32) (loop (result i32) (br 1 (i32.const 3)) (i32.const 2)))
)
(func (export "as-loop-mid") (result i32)
(block (result i32)
(loop (result i32) (call $dummy) (br 1 (i32.const 4)) (i32.const 2))
)
)
(func (export "as-loop-last") (result i32)
(block (result i32)
(loop (result i32) (nop) (call $dummy) (br 1 (i32.const 5)))
)
)
(func (export "as-br-value") (result i32)
(block (result i32) (br 0 (br 0 (i32.const 9))))
)
(func (export "as-br_if-cond")
(block (br_if 0 (br 0)))
)
(func (export "as-br_if-value") (result i32)
(block (result i32)
(drop (br_if 0 (br 0 (i32.const 8)) (i32.const 1))) (i32.const 7)
)
)
(func (export "as-br_if-value-cond") (result i32)
(block (result i32)
(drop (br_if 0 (i32.const 6) (br 0 (i32.const 9)))) (i32.const 7)
)
)
(func (export "as-br_table-index")
(block (br_table 0 0 0 (br 0)))
)
(func (export "as-br_table-value") (result i32)
(block (result i32)
(br_table 0 0 0 (br 0 (i32.const 10)) (i32.const 1)) (i32.const 7)
)
)
(func (export "as-br_table-value-index") (result i32)
(block (result i32)
(br_table 0 0 (i32.const 6) (br 0 (i32.const 11))) (i32.const 7)
)
)
(func (export "as-return-value") (result i64)
(block (result i64) (return (br 0 (i64.const 7))))
)
(func (export "as-if-cond") (result i32)
(block (result i32)
(if (result i32) (br 0 (i32.const 2))
(then (i32.const 0))
(else (i32.const 1))
)
)
)
(func (export "as-if-then") (param i32 i32) (result i32)
(block (result i32)
(if (result i32) (get_local 0)
(then (br 1 (i32.const 3)))
(else (get_local 1))
)
)
)
(func (export "as-if-else") (param i32 i32) (result i32)
(block (result i32)
(if (result i32) (get_local 0)
(then (get_local 1))
(else (br 1 (i32.const 4)))
)
)
)
(func (export "as-select-first") (param i32 i32) (result i32)
(block (result i32)
(select (br 0 (i32.const 5)) (get_local 0) (get_local 1))
)
)
(func (export "as-select-second") (param i32 i32) (result i32)
(block (result i32)
(select (get_local 0) (br 0 (i32.const 6)) (get_local 1))
)
)
(func (export "as-select-cond") (result i32)
(block (result i32)
(select (i32.const 0) (i32.const 1) (br 0 (i32.const 7)))
)
)
(func $f (param i32 i32 i32) (result i32) (i32.const -1))
(func (export "as-call-first") (result i32)
(block (result i32)
(call $f (br 0 (i32.const 12)) (i32.const 2) (i32.const 3))
)
)
(func (export "as-call-mid") (result i32)
(block (result i32)
(call $f (i32.const 1) (br 0 (i32.const 13)) (i32.const 3))
)
)
(func (export "as-call-last") (result i32)
(block (result i32)
(call $f (i32.const 1) (i32.const 2) (br 0 (i32.const 14)))
)
)
(type $sig (func (param i32 i32 i32) (result i32)))
(table anyfunc (elem $f))
(func (export "as-call_indirect-func") (result i32)
(block (result i32)
(call_indirect $sig
(br 0 (i32.const 20))
(i32.const 1) (i32.const 2) (i32.const 3)
)
)
)
(func (export "as-call_indirect-first") (result i32)
(block (result i32)
(call_indirect $sig
(i32.const 0)
(br 0 (i32.const 21)) (i32.const 2) (i32.const 3)
)
)
)
(func (export "as-call_indirect-mid") (result i32)
(block (result i32)
(call_indirect $sig
(i32.const 0)
(i32.const 1) (br 0 (i32.const 22)) (i32.const 3)
)
)
)
(func (export "as-call_indirect-last") (result i32)
(block (result i32)
(call_indirect $sig
(i32.const 0)
(i32.const 1) (i32.const 2) (br 0 (i32.const 23))
)
)
)
(func (export "as-set_local-value") (result i32) (local f32)
(block (result i32) (set_local 0 (br 0 (i32.const 17))) (i32.const -1))
)
(memory 1)
(func (export "as-load-address") (result f32)
(block (result f32) (f32.load (br 0 (f32.const 1.7))))
)
(func (export "as-loadN-address") (result i64)
(block (result i64) (i64.load8_s (br 0 (i64.const 30))))
)
(func (export "as-store-address") (result i32)
(block (result i32)
(f64.store (br 0 (i32.const 30)) (f64.const 7)) (i32.const -1)
)
)
(func (export "as-store-value") (result i32)
(block (result i32)
(i64.store (i32.const 2) (br 0 (i32.const 31))) (i32.const -1)
)
)
(func (export "as-storeN-address") (result i32)
(block (result i32)
(i32.store8 (br 0 (i32.const 32)) (i32.const 7)) (i32.const -1)
)
)
(func (export "as-storeN-value") (result i32)
(block (result i32)
(i64.store16 (i32.const 2) (br 0 (i32.const 33))) (i32.const -1)
)
)
(func (export "as-unary-operand") (result f32)
(block (result f32) (f32.neg (br 0 (f32.const 3.4))))
)
(func (export "as-binary-left") (result i32)
(block (result i32) (i32.add (br 0 (i32.const 3)) (i32.const 10)))
)
(func (export "as-binary-right") (result i64)
(block (result i64) (i64.sub (i64.const 10) (br 0 (i64.const 45))))
)
(func (export "as-test-operand") (result i32)
(block (result i32) (i32.eqz (br 0 (i32.const 44))))
)
(func (export "as-compare-left") (result i32)
(block (result i32) (f64.le (br 0 (i32.const 43)) (f64.const 10)))
)
(func (export "as-compare-right") (result i32)
(block (result i32) (f32.ne (f32.const 10) (br 0 (i32.const 42))))
)
(func (export "as-convert-operand") (result i32)
(block (result i32) (i32.wrap/i64 (br 0 (i32.const 41))))
)
(func (export "as-grow_memory-size") (result i32)
(block (result i32) (grow_memory (br 0 (i32.const 40))))
)
(func (export "nested-block-value") (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(call $dummy)
(i32.add (i32.const 4) (br 0 (i32.const 8)))
)
)
)
(func (export "nested-br-value") (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(drop
(block (result i32)
(drop (i32.const 4))
(br 0 (br 1 (i32.const 8)))
)
)
(i32.const 16)
)
)
)
(func (export "nested-br_if-value") (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(drop
(block (result i32)
(drop (i32.const 4))
(drop (br_if 0 (br 1 (i32.const 8)) (i32.const 1)))
(i32.const 32)
)
)
(i32.const 16)
)
)
)
(func (export "nested-br_if-value-cond") (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(drop (br_if 0 (i32.const 4) (br 0 (i32.const 8))))
(i32.const 16)
)
)
)
(func (export "nested-br_table-value") (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(drop
(block (result i32)
(drop (i32.const 4))
(br_table 0 (br 1 (i32.const 8)) (i32.const 1))
)
)
(i32.const 16)
)
)
)
(func (export "nested-br_table-value-index") (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(br_table 0 (i32.const 4) (br 0 (i32.const 8)))
(i32.const 16)
)
)
)
)

View File

@ -0,0 +1,148 @@
(module
(func $dummy)
(func (export "as-block-first") (param i32) (result i32)
(block (br_if 0 (get_local 0)) (return (i32.const 2))) (i32.const 3)
)
(func (export "as-block-mid") (param i32) (result i32)
(block (call $dummy) (br_if 0 (get_local 0)) (return (i32.const 2)))
(i32.const 3)
)
(func (export "as-block-last") (param i32)
(block (call $dummy) (call $dummy) (br_if 0 (get_local 0)))
)
(func (export "as-block-first-value") (param i32) (result i32)
(block (result i32)
(drop (br_if 0 (i32.const 10) (get_local 0))) (return (i32.const 11))
)
)
(func (export "as-block-mid-value") (param i32) (result i32)
(block (result i32)
(call $dummy)
(drop (br_if 0 (i32.const 20) (get_local 0)))
(return (i32.const 21))
)
)
(func (export "as-block-last-value") (param i32) (result i32)
(block (result i32)
(call $dummy) (call $dummy) (br_if 0 (i32.const 11) (get_local 0))
)
)
(func (export "as-loop-first") (param i32) (result i32)
(block (loop (br_if 1 (get_local 0)) (return (i32.const 2)))) (i32.const 3)
)
(func (export "as-loop-mid") (param i32) (result i32)
(block (loop (call $dummy) (br_if 1 (get_local 0)) (return (i32.const 2))))
(i32.const 4)
)
(func (export "as-loop-last") (param i32)
(loop (call $dummy) (br_if 1 (get_local 0)))
)
(func (export "as-if-then") (param i32 i32)
(block
(if (get_local 0) (then (br_if 1 (get_local 1))) (else (call $dummy)))
)
)
(func (export "as-if-else") (param i32 i32)
(block
(if (get_local 0) (then (call $dummy)) (else (br_if 1 (get_local 1))))
)
)
(func (export "nested-block-value") (param i32) (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(i32.add
(i32.const 4)
(block (result i32)
(drop (br_if 1 (i32.const 8) (get_local 0)))
(i32.const 16)
)
)
)
)
)
(func (export "nested-br-value") (param i32) (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(br 0
(block (result i32)
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 4)
)
)
(i32.const 16)
)
)
)
(func (export "nested-br_if-value") (param i32) (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(drop (br_if 0
(block (result i32)
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 4)
)
(i32.const 1)
))
(i32.const 16)
)
)
)
(func (export "nested-br_if-value-cond") (param i32) (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(drop (br_if 0
(i32.const 4)
(block (result i32)
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 1)
)
))
(i32.const 16)
)
)
)
(func (export "nested-br_table-value") (param i32) (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(br_table 0
(block (result i32)
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 4)
)
(i32.const 1)
)
(i32.const 16)
)
)
)
(func (export "nested-br_table-value-index") (param i32) (result i32)
(i32.add
(i32.const 1)
(block (result i32)
(drop (i32.const 2))
(br_table 0
(i32.const 4)
(block (result i32)
(drop (br_if 1 (i32.const 8) (get_local 0))) (i32.const 1)
)
)
(i32.const 16)
)
)
)
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
(module
(func (export "br") (block (br 0)))
(func (export "br_if") (block (br_if 0 (i32.const 1))))
(func (export "br_table") (block (br_table 0 (i32.const 0))))
)

View File

@ -0,0 +1,171 @@
(module
;; Auxiliary definitions
(type $proc (func))
(type $out-i32 (func (result i32)))
(type $out-i64 (func (result i64)))
(type $out-f32 (func (result f32)))
(type $out-f64 (func (result f64)))
(type $over-i32 (func (param i32) (result i32)))
(type $over-i64 (func (param i64) (result i64)))
(type $over-f32 (func (param f32) (result f32)))
(type $over-f64 (func (param f64) (result f64)))
(type $f32-i32 (func (param f32 i32) (result i32)))
(type $i32-i64 (func (param i32 i64) (result i64)))
(type $f64-f32 (func (param f64 f32) (result f32)))
(type $i64-f64 (func (param i64 f64) (result f64)))
(type $over-i32-duplicate (func (param i32) (result i32)))
(type $over-i64-duplicate (func (param i64) (result i64)))
(type $over-f32-duplicate (func (param f32) (result f32)))
(type $over-f64-duplicate (func (param f64) (result f64)))
(func $const-i32 (type $out-i32) (i32.const 0x132))
(func $const-i64 (type $out-i64) (i64.const 0x164))
(func $const-f32 (type $out-f32) (f32.const 0xf32))
(func $const-f64 (type $out-f64) (f64.const 0xf64))
(func $id-i32 (type $over-i32) (get_local 0))
(func $id-i64 (type $over-i64) (get_local 0))
(func $id-f32 (type $over-f32) (get_local 0))
(func $id-f64 (type $over-f64) (get_local 0))
(func $i32-i64 (type $i32-i64) (get_local 1))
(func $i64-f64 (type $i64-f64) (get_local 1))
(func $f32-i32 (type $f32-i32) (get_local 1))
(func $f64-f32 (type $f64-f32) (get_local 1))
(func $over-i32-duplicate (type $over-i32-duplicate) (get_local 0))
(func $over-i64-duplicate (type $over-i64-duplicate) (get_local 0))
(func $over-f32-duplicate (type $over-f32-duplicate) (get_local 0))
(func $over-f64-duplicate (type $over-f64-duplicate) (get_local 0))
(table anyfunc
(elem
$const-i32 $const-i64 $const-f32 $const-f64
$id-i32 $id-i64 $id-f32 $id-f64
$f32-i32 $i32-i64 $f64-f32 $i64-f64
$fac $fib $even $odd
$runaway $mutual-runaway1 $mutual-runaway2
$over-i32-duplicate $over-i64-duplicate
$over-f32-duplicate $over-f64-duplicate
)
)
;; Typing
(func (export "type-i32") (result i32) (call_indirect $out-i32 (i32.const 0)))
(func (export "type-i64") (result i64) (call_indirect $out-i64 (i32.const 1)))
(func (export "type-f32") (result f32) (call_indirect $out-f32 (i32.const 2)))
(func (export "type-f64") (result f64) (call_indirect $out-f64 (i32.const 3)))
(func (export "type-index") (result i64)
(call_indirect $over-i64 (i64.const 100) (i32.const 5))
)
(func (export "type-first-i32") (result i32)
(call_indirect $over-i32 (i32.const 32) (i32.const 4))
)
(func (export "type-first-i64") (result i64)
(call_indirect $over-i64 (i64.const 64) (i32.const 5))
)
(func (export "type-first-f32") (result f32)
(call_indirect $over-f32 (f32.const 1.32) (i32.const 6))
)
(func (export "type-first-f64") (result f64)
(call_indirect $over-f64 (f64.const 1.64) (i32.const 7))
)
(func (export "type-second-i32") (result i32)
(call_indirect $f32-i32 (f32.const 32.1) (i32.const 32) (i32.const 8))
)
(func (export "type-second-i64") (result i64)
(call_indirect $i32-i64 (i32.const 32) (i64.const 64) (i32.const 9))
)
(func (export "type-second-f32") (result f32)
(call_indirect $f64-f32 (f64.const 64) (f32.const 32) (i32.const 10))
)
(func (export "type-second-f64") (result f64)
(call_indirect $i64-f64 (i64.const 64) (f64.const 64.1) (i32.const 11))
)
;; Dispatch
(func (export "dispatch") (param i32 i64) (result i64)
(call_indirect $over-i64 (get_local 1) (get_local 0))
)
(func (export "dispatch-structural") (param i32) (result i64)
(call_indirect $over-i64-duplicate (i64.const 9) (get_local 0))
)
;; Recursion
(func $fac (export "fac") (type $over-i64)
(if (result i64) (i64.eqz (get_local 0))
(then (i64.const 1))
(else
(i64.mul
(get_local 0)
(call_indirect $over-i64
(i64.sub (get_local 0) (i64.const 1))
(i32.const 12)
)
)
)
)
)
(func $fib (export "fib") (type $over-i64)
(if (result i64) (i64.le_u (get_local 0) (i64.const 1))
(then (i64.const 1))
(else
(i64.add
(call_indirect $over-i64
(i64.sub (get_local 0) (i64.const 2))
(i32.const 13)
)
(call_indirect $over-i64
(i64.sub (get_local 0) (i64.const 1))
(i32.const 13)
)
)
)
)
)
(func $even (export "even") (param i32) (result i32)
(if (result i32) (i32.eqz (get_local 0))
(then (i32.const 44))
(else
(call_indirect $over-i32
(i32.sub (get_local 0) (i32.const 1))
(i32.const 15)
)
)
)
)
(func $odd (export "odd") (param i32) (result i32)
(if (result i32) (i32.eqz (get_local 0))
(then (i32.const 99))
(else
(call_indirect $over-i32
(i32.sub (get_local 0) (i32.const 1))
(i32.const 14)
)
)
)
)
;; Stack exhaustion
;; Implementations are required to have every call consume some abstract
;; resource towards exhausting some abstract finite limit, such that
;; infinitely recursive test cases reliably trap in finite time. This is
;; because otherwise applications could come to depend on it on those
;; implementations and be incompatible with implementations that don't do
;; it (or don't do it under the same circumstances).
(func $runaway (export "runaway") (call_indirect $proc (i32.const 16)))
(func $mutual-runaway1 (export "mutual-runaway") (call_indirect $proc (i32.const 18)))
(func $mutual-runaway2 (call_indirect $proc (i32.const 17)))
)

View File

@ -0,0 +1,131 @@
(module
(memory 1)
;; Stores an i16 value in little-endian-format
(func $i16_store_little (param $address i32) (param $value i32)
(i32.store8 (get_local $address) (get_local $value))
(i32.store8 (i32.add (get_local $address) (i32.const 1)) (i32.shr_u (get_local $value) (i32.const 8)))
)
;; Stores an i32 value in little-endian format
(func $i32_store_little (param $address i32) (param $value i32)
(call $i16_store_little (get_local $address) (get_local $value))
(call $i16_store_little (i32.add (get_local $address) (i32.const 2)) (i32.shr_u (get_local $value) (i32.const 16)))
)
;; Stores an i64 value in little-endian format
(func $i64_store_little (param $address i32) (param $value i64)
(call $i32_store_little (get_local $address) (i32.wrap/i64 (get_local $value)))
(call $i32_store_little (i32.add (get_local $address) (i32.const 4)) (i32.wrap/i64 (i64.shr_u (get_local $value) (i64.const 32))))
)
;; Loads an i16 value in little-endian format
(func $i16_load_little (param $address i32) (result i32)
(i32.or
(i32.load8_u (get_local $address))
(i32.shl (i32.load8_u (i32.add (get_local $address) (i32.const 1))) (i32.const 8))
)
)
;; Loads an i32 value in little-endian format
(func $i32_load_little (param $address i32) (result i32)
(i32.or
(call $i16_load_little (get_local $address))
(i32.shl (call $i16_load_little (i32.add (get_local $address) (i32.const 2))) (i32.const 16))
)
)
;; Loads an i64 value in little-endian format
(func $i64_load_little (param $address i32) (result i64)
(i64.or
(i64.extend_u/i32 (call $i32_load_little (get_local $address)))
(i64.shl (i64.extend_u/i32 (call $i32_load_little (i32.add (get_local $address) (i32.const 4)))) (i64.const 32))
)
)
(func (export "i32_load16_s") (param $value i32) (result i32)
(call $i16_store_little (i32.const 0) (get_local $value))
(i32.load16_s (i32.const 0))
)
(func (export "i32_load16_u") (param $value i32) (result i32)
(call $i16_store_little (i32.const 0) (get_local $value))
(i32.load16_u (i32.const 0))
)
(func (export "i32_load") (param $value i32) (result i32)
(call $i32_store_little (i32.const 0) (get_local $value))
(i32.load (i32.const 0))
)
(func (export "i64_load16_s") (param $value i64) (result i64)
(call $i16_store_little (i32.const 0) (i32.wrap/i64 (get_local $value)))
(i64.load16_s (i32.const 0))
)
(func (export "i64_load16_u") (param $value i64) (result i64)
(call $i16_store_little (i32.const 0) (i32.wrap/i64 (get_local $value)))
(i64.load16_u (i32.const 0))
)
(func (export "i64_load32_s") (param $value i64) (result i64)
(call $i32_store_little (i32.const 0) (i32.wrap/i64 (get_local $value)))
(i64.load32_s (i32.const 0))
)
(func (export "i64_load32_u") (param $value i64) (result i64)
(call $i32_store_little (i32.const 0) (i32.wrap/i64 (get_local $value)))
(i64.load32_u (i32.const 0))
)
(func (export "i64_load") (param $value i64) (result i64)
(call $i64_store_little (i32.const 0) (get_local $value))
(i64.load (i32.const 0))
)
(func (export "f32_load") (param $value f32) (result f32)
(call $i32_store_little (i32.const 0) (i32.reinterpret/f32 (get_local $value)))
(f32.load (i32.const 0))
)
(func (export "f64_load") (param $value f64) (result f64)
(call $i64_store_little (i32.const 0) (i64.reinterpret/f64 (get_local $value)))
(f64.load (i32.const 0))
)
(func (export "i32_store16") (param $value i32) (result i32)
(i32.store16 (i32.const 0) (get_local $value))
(call $i16_load_little (i32.const 0))
)
(func (export "i32_store") (param $value i32) (result i32)
(i32.store (i32.const 0) (get_local $value))
(call $i32_load_little (i32.const 0))
)
(func (export "i64_store16") (param $value i64) (result i64)
(i64.store16 (i32.const 0) (get_local $value))
(i64.extend_u/i32 (call $i16_load_little (i32.const 0)))
)
(func (export "i64_store32") (param $value i64) (result i64)
(i64.store32 (i32.const 0) (get_local $value))
(i64.extend_u/i32 (call $i32_load_little (i32.const 0)))
)
(func (export "i64_store") (param $value i64) (result i64)
(i64.store (i32.const 0) (get_local $value))
(call $i64_load_little (i32.const 0))
)
(func (export "f32_store") (param $value f32) (result f32)
(f32.store (i32.const 0) (get_local $value))
(f32.reinterpret/i32 (call $i32_load_little (i32.const 0)))
)
(func (export "f64_store") (param $value f64) (result f64)
(f64.store (i32.const 0) (get_local $value))
(f64.reinterpret/i64 (call $i64_load_little (i32.const 0)))
)
)

View File

@ -0,0 +1,82 @@
(module
;; Recursive factorial
(func (export "fac-rec") (param i64) (result i64)
(if (result i64) (i64.eq (get_local 0) (i64.const 0))
(then (i64.const 1))
(else
(i64.mul (get_local 0) (call 0 (i64.sub (get_local 0) (i64.const 1))))
)
)
)
;; Recursive factorial named
(func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64)
(if (result i64) (i64.eq (get_local $n) (i64.const 0))
(then (i64.const 1))
(else
(i64.mul
(get_local $n)
(call $fac-rec-named (i64.sub (get_local $n) (i64.const 1)))
)
)
)
)
;; Iterative factorial
(func (export "fac-iter") (param i64) (result i64)
(local i64 i64)
(set_local 1 (get_local 0))
(set_local 2 (i64.const 1))
(block
(loop
(if
(i64.eq (get_local 1) (i64.const 0))
(then (br 2))
(else
(set_local 2 (i64.mul (get_local 1) (get_local 2)))
(set_local 1 (i64.sub (get_local 1) (i64.const 1)))
)
)
(br 0)
)
)
(get_local 2)
)
;; Iterative factorial named
(func (export "fac-iter-named") (param $n i64) (result i64)
(local $i i64)
(local $res i64)
(set_local $i (get_local $n))
(set_local $res (i64.const 1))
(block $done
(loop $loop
(if
(i64.eq (get_local $i) (i64.const 0))
(then (br $done))
(else
(set_local $res (i64.mul (get_local $i) (get_local $res)))
(set_local $i (i64.sub (get_local $i) (i64.const 1)))
)
)
(br $loop)
)
)
(get_local $res)
)
;; Optimized factorial.
(func (export "fac-opt") (param i64) (result i64)
(local i64)
(set_local 1 (i64.const 1))
(block
(br_if 0 (i64.lt_s (get_local 0) (i64.const 2)))
(loop
(set_local 1 (i64.mul (get_local 1) (get_local 0)))
(set_local 0 (i64.add (get_local 0) (i64.const -1)))
(br_if 0 (i64.gt_s (get_local 0) (i64.const 1)))
)
)
(get_local 1)
)
)

View File

@ -0,0 +1,15 @@
(module
(func $even (export "even") (param $n i32) (result i32)
(if (result i32) (i32.eq (get_local $n) (i32.const 0))
(then (i32.const 1))
(else (call $odd (i32.sub (get_local $n) (i32.const 1))))
)
)
(func $odd (export "odd") (param $n i32) (result i32)
(if (result i32) (i32.eq (get_local $n) (i32.const 0))
(then (i32.const 0))
(else (call $even (i32.sub (get_local $n) (i32.const 1))))
)
)
)

View File

@ -0,0 +1,62 @@
;; Test `get_local` operator
(module
;; Typing
(func (export "type-local-i32") (result i32) (local i32) (get_local 0))
(func (export "type-local-i64") (result i64) (local i64) (get_local 0))
(func (export "type-local-f32") (result f32) (local f32) (get_local 0))
(func (export "type-local-f64") (result f64) (local f64) (get_local 0))
(func (export "type-param-i32") (param i32) (result i32) (get_local 0))
(func (export "type-param-i64") (param i64) (result i64) (get_local 0))
(func (export "type-param-f32") (param f32) (result f32) (get_local 0))
(func (export "type-param-f64") (param f64) (result f64) (get_local 0))
(func (export "type-mixed") (param i64 f32 f64 i32 i32)
(local f32 i64 i64 f64)
(drop (i64.eqz (get_local 0)))
(drop (f32.neg (get_local 1)))
(drop (f64.neg (get_local 2)))
(drop (i32.eqz (get_local 3)))
(drop (i32.eqz (get_local 4)))
(drop (f32.neg (get_local 5)))
(drop (i64.eqz (get_local 6)))
(drop (i64.eqz (get_local 7)))
(drop (f64.neg (get_local 8)))
)
;; Reading
(func (export "read") (param i64 f32 f64 i32 i32) (result f64)
(local f32 i64 i64 f64)
(set_local 5 (f32.const 5.5))
(set_local 6 (i64.const 6))
(set_local 8 (f64.const 8))
(f64.add
(f64.convert_u/i64 (get_local 0))
(f64.add
(f64.promote/f32 (get_local 1))
(f64.add
(get_local 2)
(f64.add
(f64.convert_u/i32 (get_local 3))
(f64.add
(f64.convert_s/i32 (get_local 4))
(f64.add
(f64.promote/f32 (get_local 5))
(f64.add
(f64.convert_u/i64 (get_local 6))
(f64.add
(f64.convert_u/i64 (get_local 7))
(get_local 8)
)
)
)
)
)
)
)
)
)
)

View File

@ -0,0 +1,27 @@
;; Test globals
(module
(global $a i32 (i32.const -2))
(global (;1;) f32 (f32.const -3))
(global (;2;) f64 (f64.const -4))
(global $b i64 (i64.const -5))
(global $x (mut i32) (i32.const -12))
(global (;5;) (mut f32) (f32.const -13))
(global (;6;) (mut f64) (f64.const -14))
(global $y (mut i64) (i64.const -15))
(func (export "get-a") (result i32) (get_global $a))
(func (export "get-b") (result i64) (get_global $b))
(func (export "get-x") (result i32) (get_global $x))
(func (export "get-y") (result i64) (get_global $y))
(func (export "set-x") (param i32) (set_global $x (get_local 0)))
(func (export "set-y") (param i64) (set_global $y (get_local 0)))
(func (export "get-1") (result f32) (get_global 1))
(func (export "get-2") (result f64) (get_global 2))
(func (export "get-5") (result f32) (get_global 5))
(func (export "get-6") (result f64) (get_global 6))
(func (export "set-5") (param f32) (set_global 5 (get_local 0)))
(func (export "set-6") (param f64) (set_global 6 (get_local 0)))
)

View File

@ -0,0 +1,132 @@
;; Test `if` operator
(module
;; Auxiliary definition
(func $dummy)
(func (export "empty") (param i32)
(if (get_local 0) (then))
(if (get_local 0) (then) (else))
(if $l (get_local 0) (then))
(if $l (get_local 0) (then) (else))
)
(func (export "singular") (param i32) (result i32)
(if (get_local 0) (then (nop)))
(if (get_local 0) (then (nop)) (else (nop)))
(if (result i32) (get_local 0) (then (i32.const 7)) (else (i32.const 8)))
)
(func (export "multi") (param i32) (result i32)
(if (get_local 0) (then (call $dummy) (call $dummy) (call $dummy)))
(if (get_local 0) (then) (else (call $dummy) (call $dummy) (call $dummy)))
(if (result i32) (get_local 0)
(then (call $dummy) (call $dummy) (i32.const 8))
(else (call $dummy) (call $dummy) (i32.const 9))
)
)
(func (export "nested") (param i32 i32) (result i32)
(if (result i32) (get_local 0)
(then
(if (get_local 1) (then (call $dummy) (block) (nop)))
(if (get_local 1) (then) (else (call $dummy) (block) (nop)))
(if (result i32) (get_local 1)
(then (call $dummy) (i32.const 9))
(else (call $dummy) (i32.const 10))
)
)
(else
(if (get_local 1) (then (call $dummy) (block) (nop)))
(if (get_local 1) (then) (else (call $dummy) (block) (nop)))
(if (result i32) (get_local 1)
(then (call $dummy) (i32.const 10))
(else (call $dummy) (i32.const 11))
)
)
)
)
(func (export "as-unary-operand") (param i32) (result i32)
(i32.ctz
(if (result i32) (get_local 0)
(then (call $dummy) (i32.const 13))
(else (call $dummy) (i32.const -13))
)
)
)
(func (export "as-binary-operand") (param i32 i32) (result i32)
(i32.mul
(if (result i32) (get_local 0)
(then (call $dummy) (i32.const 3))
(else (call $dummy) (i32.const -3))
)
(if (result i32) (get_local 1)
(then (call $dummy) (i32.const 4))
(else (call $dummy) (i32.const -5))
)
)
)
(func (export "as-test-operand") (param i32) (result i32)
(i32.eqz
(if (result i32) (get_local 0)
(then (call $dummy) (i32.const 13))
(else (call $dummy) (i32.const 0))
)
)
)
(func (export "as-compare-operand") (param i32 i32) (result i32)
(f32.gt
(if (result f32) (get_local 0)
(then (call $dummy) (f32.const 3))
(else (call $dummy) (f32.const -3))
)
(if (result f32) (get_local 1)
(then (call $dummy) (f32.const 4))
(else (call $dummy) (f32.const -4))
)
)
)
(func (export "break-bare") (result i32)
(if (i32.const 1) (then (br 0) (unreachable)))
(if (i32.const 1) (then (br 0) (unreachable)) (else (unreachable)))
(if (i32.const 0) (then (unreachable)) (else (br 0) (unreachable)))
(if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable)))
(if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable)) (else (unreachable)))
(if (i32.const 0) (then (unreachable)) (else (br_if 0 (i32.const 1)) (unreachable)))
(if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable)))
(if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable)) (else (unreachable)))
(if (i32.const 0) (then (unreachable)) (else (br_table 0 (i32.const 0)) (unreachable)))
(i32.const 19)
)
(func (export "break-value") (param i32) (result i32)
(if (result i32) (get_local 0)
(then (br 0 (i32.const 18)) (i32.const 19))
(else (br 0 (i32.const 21)) (i32.const 20))
)
)
(func (export "effects") (param i32) (result i32)
(local i32)
(if
(block (result i32) (set_local 1 (i32.const 1)) (get_local 0))
(then
(set_local 1 (i32.mul (get_local 1) (i32.const 3)))
(set_local 1 (i32.sub (get_local 1) (i32.const 5)))
(set_local 1 (i32.mul (get_local 1) (i32.const 7)))
(br 0)
(set_local 1 (i32.mul (get_local 1) (i32.const 100)))
)
(else
(set_local 1 (i32.mul (get_local 1) (i32.const 5)))
(set_local 1 (i32.sub (get_local 1) (i32.const 7)))
(set_local 1 (i32.mul (get_local 1) (i32.const 3)))
(br 0)
(set_local 1 (i32.mul (get_local 1) (i32.const 1000)))
)
)
(get_local 1)
)
)

View File

@ -0,0 +1,201 @@
(module
(func $dummy)
(func (export "empty")
(loop)
(loop $l)
)
(func (export "singular") (result i32)
(loop (nop))
(loop (result i32) (i32.const 7))
)
(func (export "multi") (result i32)
(loop (call $dummy) (call $dummy) (call $dummy) (call $dummy))
(loop (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8))
)
(func (export "nested") (result i32)
(loop (result i32)
(loop (call $dummy) (block) (nop))
(loop (result i32) (call $dummy) (i32.const 9))
)
)
(func (export "deep") (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(loop (result i32) (block (result i32)
(call $dummy) (i32.const 150)
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
))
)
(func (export "as-unary-operand") (result i32)
(i32.ctz (loop (result i32) (call $dummy) (i32.const 13)))
)
(func (export "as-binary-operand") (result i32)
(i32.mul
(loop (result i32) (call $dummy) (i32.const 3))
(loop (result i32) (call $dummy) (i32.const 4))
)
)
(func (export "as-test-operand") (result i32)
(i32.eqz (loop (result i32) (call $dummy) (i32.const 13)))
)
(func (export "as-compare-operand") (result i32)
(f32.gt
(loop (result f32) (call $dummy) (f32.const 3))
(loop (result f32) (call $dummy) (f32.const 3))
)
)
(func (export "break-bare") (result i32)
(block (loop (br 1) (br 0) (unreachable)))
(block (loop (br_if 1 (i32.const 1)) (unreachable)))
(block (loop (br_table 1 (i32.const 0)) (unreachable)))
(block (loop (br_table 1 1 1 (i32.const 1)) (unreachable)))
(i32.const 19)
)
(func (export "break-value") (result i32)
(block (result i32)
(loop (result i32) (br 1 (i32.const 18)) (br 0) (i32.const 19))
)
)
(func (export "break-repeated") (result i32)
(block (result i32)
(loop (result i32)
(br 1 (i32.const 18))
(br 1 (i32.const 19))
(drop (br_if 1 (i32.const 20) (i32.const 0)))
(drop (br_if 1 (i32.const 20) (i32.const 1)))
(br 1 (i32.const 21))
(br_table 1 (i32.const 22) (i32.const 0))
(br_table 1 1 1 (i32.const 23) (i32.const 1))
(i32.const 21)
)
)
)
(func (export "break-inner") (result i32)
(local i32)
(set_local 0 (i32.const 0))
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (block (result i32) (br 2 (i32.const 0x1)))))))
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (loop (result i32) (br 2 (i32.const 0x2)))))))
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (block (result i32) (loop (result i32) (br 1 (i32.const 0x4))))))))
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (i32.ctz (br 1 (i32.const 0x8)))))))
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (i32.ctz (loop (result i32) (br 2 (i32.const 0x10))))))))
(get_local 0)
)
(func (export "cont-inner") (result i32)
(local i32)
(set_local 0 (i32.const 0))
(set_local 0 (i32.add (get_local 0) (loop (result i32) (loop (result i32) (br 1)))))
(set_local 0 (i32.add (get_local 0) (loop (result i32) (i32.ctz (br 0)))))
(set_local 0 (i32.add (get_local 0) (loop (result i32) (i32.ctz (loop (result i32) (br 1))))))
(get_local 0)
)
(func $fx (export "effects") (result i32)
(local i32)
(block
(loop
(set_local 0 (i32.const 1))
(set_local 0 (i32.mul (get_local 0) (i32.const 3)))
(set_local 0 (i32.sub (get_local 0) (i32.const 5)))
(set_local 0 (i32.mul (get_local 0) (i32.const 7)))
(br 1)
(set_local 0 (i32.mul (get_local 0) (i32.const 100)))
)
)
(i32.eq (get_local 0) (i32.const -14))
)
(func (export "while") (param i64) (result i64)
(local i64)
(set_local 1 (i64.const 1))
(block
(loop
(br_if 1 (i64.eqz (get_local 0)))
(set_local 1 (i64.mul (get_local 0) (get_local 1)))
(set_local 0 (i64.sub (get_local 0) (i64.const 1)))
(br 0)
)
)
(get_local 1)
)
(func (export "for") (param i64) (result i64)
(local i64 i64)
(set_local 1 (i64.const 1))
(set_local 2 (i64.const 2))
(block
(loop
(br_if 1 (i64.gt_u (get_local 2) (get_local 0)))
(set_local 1 (i64.mul (get_local 1) (get_local 2)))
(set_local 2 (i64.add (get_local 2) (i64.const 1)))
(br 0)
)
)
(get_local 1)
)
(func (export "nesting") (param f32 f32) (result f32)
(local f32 f32)
(block
(loop
(br_if 1 (f32.eq (get_local 0) (f32.const 0)))
(set_local 2 (get_local 1))
(block
(loop
(br_if 1 (f32.eq (get_local 2) (f32.const 0)))
(br_if 3 (f32.lt (get_local 2) (f32.const 0)))
(set_local 3 (f32.add (get_local 3) (get_local 2)))
(set_local 2 (f32.sub (get_local 2) (f32.const 2)))
(br 0)
)
)
(set_local 3 (f32.div (get_local 3) (get_local 0)))
(set_local 0 (f32.sub (get_local 0) (f32.const 1)))
(br 0)
)
)
(get_local 3)
)
)

View File

@ -0,0 +1,53 @@
(module
(memory 1 1)
(func (export "zero_everything")
(i32.store (i32.const 0) (i32.const 0))
(i32.store (i32.const 4) (i32.const 0))
(i32.store (i32.const 8) (i32.const 0))
(i32.store (i32.const 12) (i32.const 0))
)
(func (export "test_store_to_load") (result i32)
(i32.store (i32.const 8) (i32.const 0))
(f32.store (i32.const 5) (f32.const -0.0))
(i32.load (i32.const 8))
)
(func (export "test_redundant_load") (result i32)
(local $t i32)
(local $s i32)
(set_local $t (i32.load (i32.const 8)))
(i32.store (i32.const 5) (i32.const 0x80000000))
(set_local $s (i32.load (i32.const 8)))
(i32.add (get_local $t) (get_local $s))
)
(func (export "test_dead_store") (result f32)
(local $t f32)
(i32.store (i32.const 8) (i32.const 0x23232323))
(set_local $t (f32.load (i32.const 11)))
(i32.store (i32.const 8) (i32.const 0))
(get_local $t)
)
;; A function named "malloc" which implementations nonetheless shouldn't
;; assume behaves like C malloc.
(func $malloc (export "malloc")
(param $size i32)
(result i32)
(i32.const 16)
)
;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers.
(func (export "malloc_aliasing")
(result i32)
(local $x i32)
(local $y i32)
(set_local $x (call $malloc (i32.const 4)))
(set_local $y (call $malloc (i32.const 4)))
(i32.store (get_local $x) (i32.const 42))
(i32.store (get_local $y) (i32.const 43))
(i32.load (get_local $x))
)
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,582 @@
(module
;; Test that we can use the empty string as a symbol.
(func (export "") (result i32) (i32.const 0))
;; Test that we can use names beginning with a digit.
(func (export "0") (result i32) (i32.const 1))
;; Test that we can use names beginning with a dash.
(func (export "-0") (result i32) (i32.const 2))
;; Test that we can use names beginning with an underscore.
(func (export "_") (result i32) (i32.const 3))
;; Test that we can use names beginning with a dollar sign.
(func (export "$") (result i32) (i32.const 4))
;; Test that we can use names beginning with an at sign.
(func (export "@") (result i32) (i32.const 5))
;; Test that we can use non-alphanumeric names.
(func (export "~!@#$%^&*()_+`-={}|[]\\:\";'<>?,./ ") (result i32) (i32.const 6))
;; Test that we can use names that have special meaning in JS.
(func (export "NaN") (result i32) (i32.const 7))
(func (export "Infinity") (result i32) (i32.const 8))
(func (export "if") (result i32) (i32.const 9))
;; Test that we can use common libc names without conflict.
(func (export "malloc") (result i32) (i32.const 10))
;; Test that we can use some libc hidden names without conflict.
(func (export "_malloc") (result i32) (i32.const 11))
(func (export "__malloc") (result i32) (i32.const 12))
;; Test that names are case-sensitive.
(func (export "a") (result i32) (i32.const 13))
(func (export "A") (result i32) (i32.const 14))
;; Test that UTF-8 BOM code points can appear in identifiers.
(func (export "") (result i32) (i32.const 15))
;; Test that Unicode normalization is not applied. These function names
;; contain different codepoints which normalize to the same thing under
;; NFC or NFD.
(func (export "Å") (result i32) (i32.const 16))
(func (export "Å") (result i32) (i32.const 17))
(func (export "Å") (result i32) (i32.const 18))
;; Test that Unicode compatibility normalization is not applied. These
;; function names contain different codepoints which normalize to the
;; same thing under NFKC or NFKD.
(func (export "ffi") (result i32) (i32.const 19))
(func (export "ffi") (result i32) (i32.const 20))
(func (export "ffi") (result i32) (i32.const 21))
;; Test the C0 control codes.
(func (export "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f") (result i32) (i32.const 22))
(func (export "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f") (result i32) (i32.const 23))
;; Test miscellaneous control codes.
(func (export " \7f") (result i32) (i32.const 24))
;; Test the C1 control codes.
(func (export "\c2\80\c2\81\c2\82\c2\83\c2\84\c2\85\c2\86\c2\87\c2\88\c2\89\c2\8a\c2\8b\c2\8c\c2\8d\c2\8e\c2\8f") (result i32) (i32.const 25))
(func (export "\c2\90\c2\91\c2\92\c2\93\c2\94\c2\95\c2\96\c2\97\c2\98\c2\99\c2\9a\c2\9b\c2\9c\c2\9d\c2\9e\c2\9f") (result i32) (i32.const 26))
;; Test the Unicode Specials.
(func (export "\ef\bf\b0\ef\bf\b1\ef\bf\b2\ef\bf\b3\ef\bf\b4\ef\bf\b5\ef\bf\b6\ef\bf\b7") (result i32) (i32.const 27))
(func (export "\ef\bf\b8\ef\bf\b9\ef\bf\ba\ef\bf\bb\ef\bf\bc\ef\bf\bd\ef\bf\be\ef\bf\bf") (result i32) (i32.const 28))
;; Test that the control pictures are distinct from the control codes they
;; depict. These correspond to the C0 and miscellaneous control code tests
;; above.
(func (export "␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏") (result i32) (i32.const 29))
(func (export "␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟") (result i32) (i32.const 30))
(func (export "␠␡") (result i32) (i32.const 31))
;; Test the Unicode Specials in non-escaped form (excluding U+FFFE and
;; U+FFFF, so that generic tools don't detect this file as non-UTF-8).
(func (export "<EFBFBB>") (result i32) (i32.const 32))
;; Test a bare ZWJ code point.
(func (export "") (result i32) (i32.const 33))
;; Test a bare ZWNJ code point.
(func (export "") (result i32) (i32.const 34))
;; Test various bare joiner code points.
(func (export "͏") (result i32) (i32.const 35))
(func (export "") (result i32) (i32.const 36))
(func (export "⵿") (result i32) (i32.const 37))
(func (export "𑁿") (result i32) (i32.const 38))
(func (export "") (result i32) (i32.const 39))
;; Test various interesting code points: reverse BOM, zero-width space,
;; no-break space, soft hyphen, word joiner, ogham space mark,
;; right-to-left override, left-to-right override.
(func (export "￯​ ­") (result i32) (i32.const 40))
;; Test more interesting code points: left-to-right mark, right-to-left mark,
;; non-breaking hyphen, line separator, paragraph separator,
;; left-to-right embedding, right-to-left embedding,
;; pop directional formatting, narrow no-break space, left-to-right isolate,
;; right-to-left isolate, first strong isolate, pop directional isolate.
(func (export "") (result i32) (i32.const 41))
;; Test some deprecated code points: inhibit symmetric swapping,
;; activate symmetric swapping, inhibit arabic form shaping,
;; activate arabic form shaping, national digit shapes, nominal digit shapes.
(func (export "") (result i32) (i32.const 42))
;; Test "invisible" operator code points.
(func (export "") (result i32) (i32.const 43))
;; Test that code points outside the BMP are supported.
(func (export "𐀀󟿿􏿿") (result i32) (i32.const 44))
;; Test that WebAssembly implementations cope in the presence of Zalgo.
(func (export "Z̴͇̫̥̪͓͈͔͎̗̞̺̯̱̞̙̱̜̖̠̏͆̆͛͌͘͞ḁ̶̰̳̭͙̲̱̹̝͎̼͗ͨ̎̄̆͗̿̀́͟͡l̶̷͉̩̹̫̝͖̙̲̼͇͚͍̮͎̥̞̈́͊͗ͦ̈́ͫ̇́̚ͅͅg̶͕͔͚̩̓̐̅ͮ̔̐̎̂̏̾͊̍͋͊ͧ́̆ͦ͞o̡͋̔͐ͪͩ͏̢̧̫̙̤̮͖͙͓̺̜̩̼̘̠́") (result i32) (i32.const 45))
;; Test Hangul filler code points.
(func (export "") (result i32) (i32.const 46))
;; Test variation selectors (which are also ID_Continue code points).
(func (export "") (result i32) (i32.const 47))
(func (export "") (result i32) (i32.const 48))
(func (export "󠄀") (result i32) (i32.const 49))
(func (export "󠇯") (result i32) (i32.const 50))
;; Test an uncombined combining code point.
(func (export "̈") (result i32) (i32.const 51))
;; Test that numerous different present and historical representations of the
;; "newline" concept are distinct. Tests largely inspired by:
;; https://en.wikipedia.org/wiki/Newline#Representations
;; https://en.wikipedia.org/wiki/Newline#Unicode and
;; https://en.wikipedia.org/wiki/Newline#Reverse_and_partial_line_feeds
(func (export "\0a") (result i32) (i32.const 52))
(func (export "␤") (result i32) (i32.const 53))
(func (export "") (result i32) (i32.const 54))
(func (export "\0d") (result i32) (i32.const 55))
(func (export "\0d\0a") (result i32) (i32.const 56))
(func (export "\0a\0d") (result i32) (i32.const 57))
(func (export "\1e") (result i32) (i32.const 58))
(func (export "\0b") (result i32) (i32.const 59))
(func (export "\0c") (result i32) (i32.const 60))
(func (export "\c2\85") (result i32) (i32.const 61))
(func (export "") (result i32) (i32.const 62))
(func (export "…") (result i32) (i32.const 63))
(func (export "⏎") (result i32) (i32.const 64))
(func (export "\c2\8b") (result i32) (i32.const 65))
(func (export "\c2\8c") (result i32) (i32.const 66))
(func (export "\c2\8d") (result i32) (i32.const 67))
(func (export "↵") (result i32) (i32.const 68))
(func (export "↩") (result i32) (i32.const 69))
(func (export "⌤") (result i32) (i32.const 70))
(func (export "⤶") (result i32) (i32.const 71))
(func (export "↲") (result i32) (i32.const 72))
(func (export "⮨") (result i32) (i32.const 73))
(func (export "⮰") (result i32) (i32.const 74))
;; Test that non-characters are not replaced by the replacement character.
(func (export "<22>") (result i32) (i32.const 75))
(func (export "\ef\b7\90") (result i32) (i32.const 76))
(func (export "\ef\b7\91") (result i32) (i32.const 77))
(func (export "\ef\b7\92") (result i32) (i32.const 78))
(func (export "\ef\b7\93") (result i32) (i32.const 79))
(func (export "\ef\b7\94") (result i32) (i32.const 80))
(func (export "\ef\b7\95") (result i32) (i32.const 81))
(func (export "\ef\b7\96") (result i32) (i32.const 82))
(func (export "\ef\b7\97") (result i32) (i32.const 83))
(func (export "\ef\b7\98") (result i32) (i32.const 84))
(func (export "\ef\b7\99") (result i32) (i32.const 85))
(func (export "\ef\b7\9a") (result i32) (i32.const 86))
(func (export "\ef\b7\9b") (result i32) (i32.const 87))
(func (export "\ef\b7\9c") (result i32) (i32.const 88))
(func (export "\ef\b7\9d") (result i32) (i32.const 89))
(func (export "\ef\b7\9e") (result i32) (i32.const 90))
(func (export "\ef\b7\9f") (result i32) (i32.const 91))
(func (export "\ef\b7\a0") (result i32) (i32.const 92))
(func (export "\ef\b7\a1") (result i32) (i32.const 93))
(func (export "\ef\b7\a2") (result i32) (i32.const 94))
(func (export "\ef\b7\a3") (result i32) (i32.const 95))
(func (export "\ef\b7\a4") (result i32) (i32.const 96))
(func (export "\ef\b7\a5") (result i32) (i32.const 97))
(func (export "\ef\b7\a6") (result i32) (i32.const 98))
(func (export "\ef\b7\a7") (result i32) (i32.const 99))
(func (export "\ef\b7\a8") (result i32) (i32.const 100))
(func (export "\ef\b7\a9") (result i32) (i32.const 101))
(func (export "\ef\b7\aa") (result i32) (i32.const 102))
(func (export "\ef\b7\ab") (result i32) (i32.const 103))
(func (export "\ef\b7\ac") (result i32) (i32.const 104))
(func (export "\ef\b7\ad") (result i32) (i32.const 105))
(func (export "\ef\b7\ae") (result i32) (i32.const 106))
(func (export "\ef\b7\af") (result i32) (i32.const 107))
(func (export "\ef\bf\be") (result i32) (i32.const 108))
(func (export "\ef\bf\bf") (result i32) (i32.const 109))
(func (export "\f0\9f\bf\be") (result i32) (i32.const 110))
(func (export "\f0\9f\bf\bf") (result i32) (i32.const 111))
(func (export "\f0\af\bf\be") (result i32) (i32.const 112))
(func (export "\f0\af\bf\bf") (result i32) (i32.const 113))
(func (export "\f0\bf\bf\be") (result i32) (i32.const 114))
(func (export "\f0\bf\bf\bf") (result i32) (i32.const 115))
(func (export "\f1\8f\bf\be") (result i32) (i32.const 116))
(func (export "\f1\8f\bf\bf") (result i32) (i32.const 117))
(func (export "\f1\9f\bf\be") (result i32) (i32.const 118))
(func (export "\f1\9f\bf\bf") (result i32) (i32.const 119))
(func (export "\f1\af\bf\be") (result i32) (i32.const 120))
(func (export "\f1\af\bf\bf") (result i32) (i32.const 121))
(func (export "\f1\bf\bf\be") (result i32) (i32.const 122))
(func (export "\f1\bf\bf\bf") (result i32) (i32.const 123))
(func (export "\f2\8f\bf\be") (result i32) (i32.const 124))
(func (export "\f2\8f\bf\bf") (result i32) (i32.const 125))
(func (export "\f2\9f\bf\be") (result i32) (i32.const 126))
(func (export "\f2\9f\bf\bf") (result i32) (i32.const 127))
(func (export "\f2\af\bf\be") (result i32) (i32.const 128))
(func (export "\f2\af\bf\bf") (result i32) (i32.const 129))
(func (export "\f2\bf\bf\be") (result i32) (i32.const 130))
(func (export "\f2\bf\bf\bf") (result i32) (i32.const 131))
(func (export "\f3\8f\bf\be") (result i32) (i32.const 132))
(func (export "\f3\8f\bf\bf") (result i32) (i32.const 133))
(func (export "\f3\9f\bf\be") (result i32) (i32.const 134))
(func (export "\f3\9f\bf\bf") (result i32) (i32.const 135))
(func (export "\f3\af\bf\be") (result i32) (i32.const 136))
(func (export "\f3\af\bf\bf") (result i32) (i32.const 137))
(func (export "\f3\bf\bf\be") (result i32) (i32.const 138))
(func (export "\f3\bf\bf\bf") (result i32) (i32.const 139))
(func (export "\f4\8f\bf\be") (result i32) (i32.const 140))
(func (export "\f4\8f\bf\bf") (result i32) (i32.const 141))
;; Test an interrobang with combining diacritical marks above.
;; https://xkcd.com/1209/
(func (export "̈‽̈̉") (result i32) (i32.const 142))
;; Test that RLM/LRM don't change the logical byte order.
(func (export "abc") (result i32) (i32.const 143))
(func (export "abc") (result i32) (i32.const 144))
(func (export "cba") (result i32) (i32.const 145))
(func (export "abc") (result i32) (i32.const 146))
(func (export "cba") (result i32) (i32.const 147))
;; Test that Unicode font variations are preserved.
(func (export "𝑨") (result i32) (i32.const 148))
(func (export "𝐴") (result i32) (i32.const 149))
(func (export "𝘈") (result i32) (i32.const 150))
(func (export "𝘼") (result i32) (i32.const 151))
(func (export "𝐀") (result i32) (i32.const 152))
(func (export "𝓐") (result i32) (i32.const 153))
(func (export "𝕬") (result i32) (i32.const 154))
(func (export "𝗔") (result i32) (i32.const 155))
(func (export "𝒜") (result i32) (i32.const 156))
(func (export "𝔄") (result i32) (i32.const 157))
(func (export "𝔸") (result i32) (i32.const 158))
(func (export "𝖠") (result i32) (i32.const 159))
(func (export "𝙰") (result i32) (i32.const 160))
(func (export "ᴀ") (result i32) (i32.const 161))
;; Test that various additional letter variations are preserved.
;; (U+0040, U+0061, U+0041, U+00C5, U+0041 U+030A, U+212B, and the font
;; variations are covered above.)
(func (export "ᴬ") (result i32) (i32.const 162))
(func (export "Ⓐ") (result i32) (i32.const 163))
(func (export "") (result i32) (i32.const 164))
(func (export "🄐") (result i32) (i32.const 165))
(func (export "🄰") (result i32) (i32.const 166))
(func (export "󠁁") (result i32) (i32.const 167))
(func (export "U+0041") (result i32) (i32.const 168))
(func (export "A") (result i32) (i32.const 169))
(func (export "А") (result i32) (i32.const 170))
(func (export "Ꙗ") (result i32) (i32.const 171))
(func (export "ⷼ") (result i32) (i32.const 172))
(func (export "ⷶ") (result i32) (i32.const 173))
(func (export "Ɐ") (result i32) (i32.const 174))
(func (export "🅐") (result i32) (i32.const 175))
(func (export "🅰") (result i32) (i32.const 176))
(func (export "Ⱝ") (result i32) (i32.const 177))
(func (export "𐐂") (result i32) (i32.const 178))
(func (export "𐐈") (result i32) (i32.const 179))
(func (export "𐒰") (result i32) (i32.const 180))
(func (export "À") (result i32) (i32.const 181))
(func (export "Á") (result i32) (i32.const 182))
(func (export "Â") (result i32) (i32.const 183))
(func (export "Ã") (result i32) (i32.const 184))
(func (export "Ä") (result i32) (i32.const 185))
(func (export "Ā") (result i32) (i32.const 186))
(func (export "Ă") (result i32) (i32.const 187))
(func (export "Ą") (result i32) (i32.const 188))
(func (export "Ǎ") (result i32) (i32.const 189))
(func (export "Ǟ") (result i32) (i32.const 190))
(func (export "Ǡ") (result i32) (i32.const 191))
(func (export "Ǻ") (result i32) (i32.const 192))
(func (export "Ȁ") (result i32) (i32.const 193))
(func (export "Ȃ") (result i32) (i32.const 194))
(func (export "Ȧ") (result i32) (i32.const 195))
(func (export "Ⱥ") (result i32) (i32.const 196))
(func (export "Ӑ") (result i32) (i32.const 197))
(func (export "Ӓ") (result i32) (i32.const 198))
(func (export "ߊ") (result i32) (i32.const 199))
(func (export "ࠡ") (result i32) (i32.const 200))
(func (export "ࠢ") (result i32) (i32.const 201))
(func (export "ࠣ") (result i32) (i32.const 202))
(func (export "ࠤ") (result i32) (i32.const 203))
(func (export "ࠥ") (result i32) (i32.const 204))
(func (export "ऄ") (result i32) (i32.const 205))
(func (export "अ") (result i32) (i32.const 206))
(func (export "ॲ") (result i32) (i32.const 207))
(func (export "অ") (result i32) (i32.const 208))
(func (export "ਅ") (result i32) (i32.const 209))
(func (export "અ") (result i32) (i32.const 210))
(func (export "ଅ") (result i32) (i32.const 211))
(func (export "அ") (result i32) (i32.const 212))
(func (export "అ") (result i32) (i32.const 213))
(func (export "ಅ") (result i32) (i32.const 214))
(func (export "അ") (result i32) (i32.const 215))
(func (export "ะ") (result i32) (i32.const 216))
(func (export "ະ") (result i32) (i32.const 217))
(func (export "༁") (result i32) (i32.const 218))
(func (export "ཨ") (result i32) (i32.const 219))
(func (export "ྸ") (result i32) (i32.const 220))
(func (export "အ") (result i32) (i32.const 221))
(func (export "ဢ") (result i32) (i32.const 222))
(func (export "ႜ") (result i32) (i32.const 223))
(func (export "ᅡ") (result i32) (i32.const 224))
(func (export "አ") (result i32) (i32.const 225))
(func (export "") (result i32) (i32.const 226))
(func (export "") (result i32) (i32.const 227))
(func (export "ᐊ") (result i32) (i32.const 228))
(func (export "ᖳ") (result i32) (i32.const 229))
(func (export "ᚨ") (result i32) (i32.const 230))
(func (export "ᚪ") (result i32) (i32.const 231))
(func (export "ᛆ") (result i32) (i32.const 232))
(func (export "ᜀ") (result i32) (i32.const 233))
(func (export "ᜠ") (result i32) (i32.const 234))
(func (export "ᝀ") (result i32) (i32.const 235))
(func (export "ᝠ") (result i32) (i32.const 236))
(func (export "ᠠ") (result i32) (i32.const 237))
(func (export "ᢇ") (result i32) (i32.const 238))
(func (export "ᤠ") (result i32) (i32.const 239))
(func (export "ᥣ") (result i32) (i32.const 240))
(func (export "ᨕ") (result i32) (i32.const 241))
(func (export "ᩋ") (result i32) (i32.const 242))
(func (export "ᩡ") (result i32) (i32.const 243))
(func (export "ᮃ") (result i32) (i32.const 244))
(func (export "ᯀ") (result i32) (i32.const 245))
(func (export "ᯁ") (result i32) (i32.const 246))
(func (export "ᰣ") (result i32) (i32.const 247))
(func (export "Ḁ") (result i32) (i32.const 248))
(func (export "Ạ") (result i32) (i32.const 249))
(func (export "Ả") (result i32) (i32.const 250))
(func (export "Ấ") (result i32) (i32.const 251))
(func (export "Ầ") (result i32) (i32.const 252))
(func (export "Ẩ") (result i32) (i32.const 253))
(func (export "Ẫ") (result i32) (i32.const 254))
(func (export "Ậ") (result i32) (i32.const 255))
(func (export "Ắ") (result i32) (i32.const 256))
(func (export "Ằ") (result i32) (i32.const 257))
(func (export "Ẳ") (result i32) (i32.const 258))
(func (export "Ẵ") (result i32) (i32.const 259))
(func (export "Ặ") (result i32) (i32.const 260))
(func (export "あ") (result i32) (i32.const 261))
(func (export "ア") (result i32) (i32.const 262))
(func (export "ㄚ") (result i32) (i32.const 263))
(func (export "ㅏ") (result i32) (i32.const 264))
(func (export "㈎") (result i32) (i32.const 265))
(func (export "㈏") (result i32) (i32.const 266))
(func (export "㈐") (result i32) (i32.const 267))
(func (export "㈑") (result i32) (i32.const 268))
(func (export "㈒") (result i32) (i32.const 269))
(func (export "㈓") (result i32) (i32.const 270))
(func (export "㈔") (result i32) (i32.const 271))
(func (export "㈕") (result i32) (i32.const 272))
(func (export "㈖") (result i32) (i32.const 273))
(func (export "㈗") (result i32) (i32.const 274))
(func (export "㈘") (result i32) (i32.const 275))
(func (export "㈙") (result i32) (i32.const 276))
(func (export "㈚") (result i32) (i32.const 277))
(func (export "㈛") (result i32) (i32.const 278))
(func (export "㉮") (result i32) (i32.const 279))
(func (export "㉯") (result i32) (i32.const 280))
(func (export "㉰") (result i32) (i32.const 281))
(func (export "㉱") (result i32) (i32.const 282))
(func (export "㉲") (result i32) (i32.const 283))
(func (export "㉳") (result i32) (i32.const 284))
(func (export "㉴") (result i32) (i32.const 285))
(func (export "㉵") (result i32) (i32.const 286))
(func (export "㉶") (result i32) (i32.const 287))
(func (export "㉷") (result i32) (i32.const 288))
(func (export "㉸") (result i32) (i32.const 289))
(func (export "㉹") (result i32) (i32.const 290))
(func (export "㉺") (result i32) (i32.const 291))
(func (export "㉻") (result i32) (i32.const 292))
(func (export "㋐") (result i32) (i32.const 293))
(func (export "ꀊ") (result i32) (i32.const 294))
(func (export "") (result i32) (i32.const 295))
(func (export "ꕉ") (result i32) (i32.const 296))
(func (export "ꚠ") (result i32) (i32.const 297))
(func (export "ꠀ") (result i32) (i32.const 298))
(func (export "ꠣ") (result i32) (i32.const 299))
(func (export "ꡝ") (result i32) (i32.const 300))
(func (export "ꢂ") (result i32) (i32.const 301))
(func (export "꣪") (result i32) (i32.const 302))
(func (export "ꤢ") (result i32) (i32.const 303))
(func (export "ꥆ") (result i32) (i32.const 304))
(func (export "ꦄ") (result i32) (i32.const 305))
(func (export "ꨀ") (result i32) (i32.const 306))
(func (export "ア") (result i32) (i32.const 307))
(func (export "ᅡ") (result i32) (i32.const 308))
(func (export "𐀀") (result i32) (i32.const 309))
(func (export "𐊀") (result i32) (i32.const 310))
(func (export "𐊠") (result i32) (i32.const 311))
(func (export "𐌀") (result i32) (i32.const 312))
(func (export "𐎠") (result i32) (i32.const 313))
(func (export "𐒖") (result i32) (i32.const 314))
(func (export "𐔀") (result i32) (i32.const 315))
(func (export "𐝀") (result i32) (i32.const 316))
(func (export "𐠀") (result i32) (i32.const 317))
(func (export "𐤠") (result i32) (i32.const 318))
(func (export "𐦀") (result i32) (i32.const 319))
(func (export "𐦠") (result i32) (i32.const 320))
(func (export "𐨀") (result i32) (i32.const 321))
(func (export "𐬀") (result i32) (i32.const 322))
(func (export "𐰀") (result i32) (i32.const 323))
(func (export "𐰁") (result i32) (i32.const 324))
(func (export "𐲀") (result i32) (i32.const 325))
(func (export "𑀅") (result i32) (i32.const 326))
(func (export "𑂃") (result i32) (i32.const 327))
(func (export "𑄧") (result i32) (i32.const 328))
(func (export "𑅐") (result i32) (i32.const 329))
(func (export "𑆃") (result i32) (i32.const 330))
(func (export "𑈀") (result i32) (i32.const 331))
(func (export "𑊀") (result i32) (i32.const 332))
(func (export "𑊰") (result i32) (i32.const 333))
(func (export "𑌅") (result i32) (i32.const 334))
(func (export "𑍰") (result i32) (i32.const 335))
(func (export "𑐀") (result i32) (i32.const 336))
(func (export "𑒁") (result i32) (i32.const 337))
(func (export "𑖀") (result i32) (i32.const 338))
(func (export "𑘀") (result i32) (i32.const 339))
(func (export "𑚀") (result i32) (i32.const 340))
(func (export "𑜒") (result i32) (i32.const 341))
(func (export "𑜠") (result i32) (i32.const 342))
(func (export "𑢡") (result i32) (i32.const 343))
(func (export "𑫕") (result i32) (i32.const 344))
(func (export "𑰀") (result i32) (i32.const 345))
(func (export "𑲏") (result i32) (i32.const 346))
(func (export "𑲯") (result i32) (i32.const 347))
(func (export "𒀀") (result i32) (i32.const 348))
(func (export "𖧕") (result i32) (i32.const 349))
(func (export "𖩆") (result i32) (i32.const 350))
(func (export "𖫧") (result i32) (i32.const 351))
(func (export "𖽔") (result i32) (i32.const 352))
(func (export "𛱁") (result i32) (i32.const 353))
(func (export "𛱤") (result i32) (i32.const 354))
(func (export "𞠣") (result i32) (i32.const 355))
(func (export "🇦") (result i32) (i32.const 356))
(func (export "Ɑ") (result i32) (i32.const 357))
(func (export "Λ") (result i32) (i32.const 358))
(func (export "Ɒ") (result i32) (i32.const 359))
(func (export "ª") (result i32) (i32.const 360))
(func (export "∀") (result i32) (i32.const 361))
(func (export "₳") (result i32) (i32.const 362))
(func (export "𐤀") (result i32) (i32.const 363))
(func (export "Ⲁ") (result i32) (i32.const 364))
(func (export "𐌰") (result i32) (i32.const 365))
(func (export "Ά") (result i32) (i32.const 366))
(func (export "Α") (result i32) (i32.const 367))
(func (export "Ἀ") (result i32) (i32.const 368))
(func (export "Ἁ") (result i32) (i32.const 369))
(func (export "Ἂ") (result i32) (i32.const 370))
(func (export "Ἃ") (result i32) (i32.const 371))
(func (export "Ἄ") (result i32) (i32.const 372))
(func (export "Ἅ") (result i32) (i32.const 373))
(func (export "Ἆ") (result i32) (i32.const 374))
(func (export "Ἇ") (result i32) (i32.const 375))
(func (export "ᾈ") (result i32) (i32.const 376))
(func (export "ᾉ") (result i32) (i32.const 377))
(func (export "ᾊ") (result i32) (i32.const 378))
(func (export "ᾋ") (result i32) (i32.const 379))
(func (export "ᾌ") (result i32) (i32.const 380))
(func (export "ᾍ") (result i32) (i32.const 381))
(func (export "ᾎ") (result i32) (i32.const 382))
(func (export "ᾏ") (result i32) (i32.const 383))
(func (export "Ᾰ") (result i32) (i32.const 384))
(func (export "Ᾱ") (result i32) (i32.const 385))
(func (export "Ὰ") (result i32) (i32.const 386))
(func (export "Ά") (result i32) (i32.const 387))
(func (export "ᾼ") (result i32) (i32.const 388))
(func (export "𝚨") (result i32) (i32.const 389))
(func (export "𝛢") (result i32) (i32.const 390))
(func (export "𝜜") (result i32) (i32.const 391))
(func (export "𝝖") (result i32) (i32.const 392))
(func (export "𝞐") (result i32) (i32.const 393))
(func (export "⍶") (result i32) (i32.const 394))
(func (export "") (result i32) (i32.const 395))
(func (export "⩜") (result i32) (i32.const 396))
(func (export "") (result i32) (i32.const 397))
(func (export "") (result i32) (i32.const 398))
;; Test unmatched "closing" and "opening" code points.
(func (export ")˺˼𔗏𝅴𝅶𝅸𝅺⁾₎❩❫⟯﴿︶﹚)⦆󠀩❳❵⟧⟩⟫⟭⦈⦊⦖⸣⸥︘︸︺︼︾﹀﹂﹄﹈﹜﹞]}」󠁝󠁽»’”›❯") (result i32) (i32.const 399))
(func (export "(˹˻𔗎𝅳𝅵𝅷𝅹⁽₍❨❪⟮﴾︵﹙(⦅󠀨❲❴⟦⟨⟪⟬⦇⦉⦕⸢⸤︗︷︹︻︽︿﹁﹃﹇﹛﹝[{「󠁛󠁻«‘“‹❮") (result i32) (i32.const 400))
(func (export "𝪋𝪤") (result i32) (i32.const 401))
(func (export "𝪋") (result i32) (i32.const 402))
;; Test that Unicode fraction normalization is not applied.
(func (export "½") (result i32) (i32.const 403))
(func (export "12") (result i32) (i32.const 404))
(func (export "1/2") (result i32) (i32.const 405))
(func (export "୳") (result i32) (i32.const 406))
(func (export "൴") (result i32) (i32.const 407))
(func (export "⳽") (result i32) (i32.const 408))
(func (export "꠱") (result i32) (i32.const 409))
(func (export "𐅁") (result i32) (i32.const 410))
(func (export "𐅵") (result i32) (i32.const 411))
(func (export "𐅶") (result i32) (i32.const 412))
(func (export "𐦽") (result i32) (i32.const 413))
(func (export "𐹻") (result i32) (i32.const 414))
;; Test a full-width quote.
(func (export "") (result i32) (i32.const 415))
;; Test that different present and historical representations of the "delete"
;; concept are distinct.
(func (export "\7f") (result i32) (i32.const 416))
(func (export "\08") (result i32) (i32.const 417))
(func (export "⌫") (result i32) (i32.const 418))
(func (export "⌦") (result i32) (i32.const 419))
(func (export "␈") (result i32) (i32.const 420))
(func (export "␡") (result i32) (i32.const 421))
(func (export "᷻") (result i32) (i32.const 422))
(func (export "\0f") (result i32) (i32.const 423))
(func (export "←") (result i32) (i32.const 424))
(func (export "⌧") (result i32) (i32.const 425))
(func (export "⍒") (result i32) (i32.const 426))
(func (export "⍔") (result i32) (i32.const 427))
(func (export "⍢") (result i32) (i32.const 428))
(func (export "⍫") (result i32) (i32.const 429))
;; Test that different representations of the "substitute" concept are
;; distinct. (U+FFFD is covered above.)
(func (export "\1a") (result i32) (i32.const 430))
(func (export "␦") (result i32) (i32.const 431))
(func (export "␚") (result i32) (i32.const 432))
(func (export "") (result i32) (i32.const 433))
(func (export "?") (result i32) (i32.const 434))
(func (export "¿") (result i32) (i32.const 435))
(func (export "᥅") (result i32) (i32.const 436))
(func (export ";") (result i32) (i32.const 437))
(func (export "՞") (result i32) (i32.const 438))
(func (export "؟") (result i32) (i32.const 439))
(func (export "፧") (result i32) (i32.const 440))
(func (export "⁇") (result i32) (i32.const 441))
(func (export "⍰") (result i32) (i32.const 442))
(func (export "❓") (result i32) (i32.const 443))
(func (export "❔") (result i32) (i32.const 444))
(func (export "⳺") (result i32) (i32.const 445))
(func (export "⳻") (result i32) (i32.const 446))
(func (export "⸮") (result i32) (i32.const 447))
(func (export "㉄") (result i32) (i32.const 448))
(func (export "꘏") (result i32) (i32.const 449))
(func (export "꛷") (result i32) (i32.const 450))
(func (export "︖") (result i32) (i32.const 451))
(func (export "﹖") (result i32) (i32.const 452))
(func (export "") (result i32) (i32.const 453))
(func (export "𑅃") (result i32) (i32.const 454))
(func (export "𞥟") (result i32) (i32.const 455))
(func (export "󠀿") (result i32) (i32.const 456))
(func (export "𖡄") (result i32) (i32.const 457))
(func (export "⯑") (result i32) (i32.const 458))
;; Test that different present and historical representations of the
;; "paragraph" concept are distinct. (U+2029 is covered above).
(func (export "¶") (result i32) (i32.const 459))
(func (export "⁋") (result i32) (i32.const 460))
(func (export "܀") (result i32) (i32.const 461))
(func (export "჻") (result i32) (i32.const 462))
(func (export "፨") (result i32) (i32.const 463))
(func (export "〷") (result i32) (i32.const 464))
(func (export "❡") (result i32) (i32.const 465))
(func (export "⸏") (result i32) (i32.const 466))
(func (export "⸐") (result i32) (i32.const 467))
(func (export "⸑") (result i32) (i32.const 468))
(func (export "⸎") (result i32) (i32.const 469))
(func (export "\14") (result i32) (i32.const 470)) ;; ¶ in CP437
(func (export "☙") (result i32) (i32.const 471))
)

View File

@ -0,0 +1,181 @@
;; Test `nop` operator.
(module
;; Auxiliary definitions
(func $dummy)
(func $3-ary (param i32 i32 i32) (result i32)
get_local 0 get_local 1 get_local 2 i32.sub i32.add
)
(memory 1)
(func (export "as-func-first") (result i32)
(nop) (i32.const 1)
)
(func (export "as-func-mid") (result i32)
(call $dummy) (nop) (i32.const 2)
)
(func (export "as-func-last") (result i32)
(call $dummy) (i32.const 3) (nop)
)
(func (export "as-func-everywhere") (result i32)
(nop) (nop) (call $dummy) (nop) (i32.const 4) (nop) (nop)
)
(func (export "as-drop-last") (param i32)
(get_local 0) (nop) (drop)
)
(func (export "as-drop-everywhere") (param i32)
(nop) (nop) (get_local 0) (nop) (nop) (drop)
)
(func (export "as-select-mid1") (param i32) (result i32)
(get_local 0) (nop) (get_local 0) (get_local 0) (select)
)
(func (export "as-select-mid2") (param i32) (result i32)
(get_local 0) (get_local 0) (nop) (get_local 0) (select)
)
(func (export "as-select-last") (param i32) (result i32)
(get_local 0) (get_local 0) (get_local 0) (nop) (select)
)
(func (export "as-select-everywhere") (param i32) (result i32)
(nop) (get_local 0) (nop) (nop) (get_local 0)
(nop) (nop) (get_local 0) (nop) (nop) (select)
)
(func (export "as-block-first") (result i32)
(block (result i32) (nop) (i32.const 2))
)
(func (export "as-block-mid") (result i32)
(block (result i32) (call $dummy) (nop) (i32.const 2))
)
(func (export "as-block-last") (result i32)
(block (result i32) (nop) (call $dummy) (i32.const 3) (nop))
)
(func (export "as-block-everywhere") (result i32)
(block (result i32)
(nop) (nop) (call $dummy) (nop) (i32.const 4) (nop) (nop)
)
)
(func (export "as-loop-first") (result i32)
(loop (result i32) (nop) (i32.const 2))
)
(func (export "as-loop-mid") (result i32)
(loop (result i32) (call $dummy) (nop) (i32.const 2))
)
(func (export "as-loop-last") (result i32)
(loop (result i32) (call $dummy) (i32.const 3) (nop))
)
(func (export "as-loop-everywhere") (result i32)
(loop (result i32)
(nop) (nop) (call $dummy) (nop) (i32.const 4) (nop) (nop)
)
)
(func (export "as-if-condition") (param i32)
(get_local 0) (nop) (if (then (call $dummy)))
)
(func (export "as-if-then") (param i32)
(if (get_local 0) (then (nop)) (else (call $dummy)))
)
(func (export "as-if-else") (param i32)
(if (get_local 0) (then (call $dummy)) (else (nop)))
)
(func (export "as-br-last") (param i32) (result i32)
(block (result i32) (get_local 0) (nop) (br 0))
)
(func (export "as-br-everywhere") (param i32) (result i32)
(block (result i32) (nop) (nop) (get_local 0) (nop) (nop) (br 0))
)
(func (export "as-br_if-mid") (param i32) (result i32)
(block (result i32) (get_local 0) (nop) (get_local 0) (br_if 0))
)
(func (export "as-br_if-last") (param i32) (result i32)
(block (result i32) (get_local 0) (get_local 0) (nop) (br_if 0))
)
(func (export "as-br_if-everywhere") (param i32) (result i32)
(block (result i32)
(nop) (nop) (get_local 0) (nop) (nop) (get_local 0) (nop) (nop)
(br_if 0)
)
)
(func (export "as-br_table-mid") (param i32) (result i32)
(block (result i32) (get_local 0) (nop) (get_local 0) (br_table 0 0))
)
(func (export "as-br_table-last") (param i32) (result i32)
(block (result i32) (get_local 0) (get_local 0) (nop) (br_table 0 0))
)
(func (export "as-br_table-everywhere") (param i32) (result i32)
(block (result i32)
(nop) (nop) (get_local 0) (nop) (nop) (get_local 0) (nop) (nop)
(br_table 0 0)
)
)
(func (export "as-return-last") (param i32) (result i32)
(get_local 0) (nop) (return)
)
(func (export "as-return-everywhere") (param i32) (result i32)
(nop) (nop) (get_local 0) (nop) (nop) (return)
)
(func (export "as-call-mid1") (param i32 i32 i32) (result i32)
(get_local 0) (nop) (get_local 1) (get_local 2) (call $3-ary)
)
(func (export "as-call-mid2") (param i32 i32 i32) (result i32)
(get_local 0) (get_local 1) (nop) (get_local 2) (call $3-ary)
)
(func (export "as-call-last") (param i32 i32 i32) (result i32)
(get_local 0) (get_local 1) (get_local 2) (nop) (call $3-ary)
)
(func (export "as-call-everywhere") (param i32 i32 i32) (result i32)
(nop) (nop) (get_local 0) (nop) (nop) (get_local 1)
(nop) (nop) (get_local 2) (nop) (nop) (call $3-ary)
)
;; TODO(stack): call_indirect, *_local, load*, store*
(func (export "as-unary-last") (param i32) (result i32)
(get_local 0) (nop) (i32.ctz)
)
(func (export "as-unary-everywhere") (param i32) (result i32)
(nop) (nop) (get_local 0) (nop) (nop) (i32.ctz)
)
(func (export "as-binary-mid") (param i32) (result i32)
(get_local 0) (nop) (get_local 0) (i32.add)
)
(func (export "as-binary-last") (param i32) (result i32)
(get_local 0) (get_local 0) (nop) (i32.add)
)
(func (export "as-binary-everywhere") (param i32) (result i32)
(nop) (get_local 0) (nop) (nop) (get_local 0) (nop) (nop) (i32.add)
)
(func (export "as-test-last") (param i32) (result i32)
(get_local 0) (nop) (i32.eqz)
)
(func (export "as-test-everywhere") (param i32) (result i32)
(nop) (nop) (get_local 0) (nop) (nop) i32.eqz
)
(func (export "as-compare-mid") (param i32) (result i32)
(get_local 0) (nop) (get_local 0) (i32.ne)
)
(func (export "as-compare-last") (param i32) (result i32)
(get_local 0) (get_local 0) (nop) (i32.lt_u)
)
(func (export "as-compare-everywhere") (param i32) (result i32)
(nop) (get_local 0) (nop) (nop) (get_local 0) (nop) (nop) (i32.le_s)
)
(func (export "as-grow_memory-last") (param i32) (result i32)
(get_local 0) (nop) (grow_memory)
)
(func (export "as-grow_memory-everywhere") (param i32) (result i32)
(nop) (nop) (get_local 0) (nop) (nop) (grow_memory)
)
)

View File

@ -0,0 +1,12 @@
(module
(memory 0)
(func (export "load_at_zero") (result i32) (i32.load (i32.const 0)))
(func (export "store_at_zero") (i32.store (i32.const 0) (i32.const 2)))
(func (export "load_at_page_size") (result i32) (i32.load (i32.const 0x10000)))
(func (export "store_at_page_size") (i32.store (i32.const 0x10000) (i32.const 3)))
(func (export "grow") (param $sz i32) (result i32) (grow_memory (get_local $sz)))
(func (export "size") (result i32) (current_memory))
)

View File

@ -0,0 +1,194 @@
;; Test `return` operator
(module
;; Auxiliary definition
(func $dummy)
(func (export "type-i32") (drop (i32.ctz (return))))
(func (export "type-i64") (drop (i64.ctz (return))))
(func (export "type-f32") (drop (f32.neg (return))))
(func (export "type-f64") (drop (f64.neg (return))))
(func (export "nullary") (return))
(func (export "unary") (result f64) (return (f64.const 3)))
(func (export "as-func-first") (result i32)
(return (i32.const 1)) (i32.const 2)
)
(func (export "as-func-mid") (result i32)
(call $dummy) (return (i32.const 2)) (i32.const 3)
)
(func (export "as-func-last")
(nop) (call $dummy) (return)
)
(func (export "as-func-value") (result i32)
(nop) (call $dummy) (return (i32.const 3))
)
(func (export "as-block-first")
(block (return) (call $dummy))
)
(func (export "as-block-mid")
(block (call $dummy) (return) (call $dummy))
)
(func (export "as-block-last")
(block (nop) (call $dummy) (return))
)
(func (export "as-block-value") (result i32)
(block (result i32) (nop) (call $dummy) (return (i32.const 2)))
)
(func (export "as-loop-first") (result i32)
(loop (result i32) (return (i32.const 3)) (i32.const 2))
)
(func (export "as-loop-mid") (result i32)
(loop (result i32) (call $dummy) (return (i32.const 4)) (i32.const 2))
)
(func (export "as-loop-last") (result i32)
(loop (result i32) (nop) (call $dummy) (return (i32.const 5)))
)
(func (export "as-br-value") (result i32)
(block (result i32) (br 0 (return (i32.const 9))))
)
(func (export "as-br_if-cond")
(block (br_if 0 (return)))
)
(func (export "as-br_if-value") (result i32)
(block (result i32)
(drop (br_if 0 (return (i32.const 8)) (i32.const 1))) (i32.const 7)
)
)
(func (export "as-br_if-value-cond") (result i32)
(block (result i32)
(drop (br_if 0 (i32.const 6) (return (i32.const 9)))) (i32.const 7)
)
)
(func (export "as-br_table-index") (result i64)
(block (br_table 0 0 0 (return (i64.const 9)))) (i64.const -1)
)
(func (export "as-br_table-value") (result i32)
(block (result i32)
(br_table 0 0 0 (return (i32.const 10)) (i32.const 1)) (i32.const 7)
)
)
(func (export "as-br_table-value-index") (result i32)
(block (result i32)
(br_table 0 0 (i32.const 6) (return (i32.const 11))) (i32.const 7)
)
)
(func (export "as-return-value") (result i64)
(return (return (i64.const 7)))
)
(func (export "as-if-cond") (result i32)
(if (result i32)
(return (i32.const 2)) (then (i32.const 0)) (else (i32.const 1))
)
)
(func (export "as-if-then") (param i32 i32) (result i32)
(if (result i32)
(get_local 0) (then (return (i32.const 3))) (else (get_local 1))
)
)
(func (export "as-if-else") (param i32 i32) (result i32)
(if (result i32)
(get_local 0) (then (get_local 1)) (else (return (i32.const 4)))
)
)
(func (export "as-select-first") (param i32 i32) (result i32)
(select (return (i32.const 5)) (get_local 0) (get_local 1))
)
(func (export "as-select-second") (param i32 i32) (result i32)
(select (get_local 0) (return (i32.const 6)) (get_local 1))
)
(func (export "as-select-cond") (result i32)
(select (i32.const 0) (i32.const 1) (return (i32.const 7)))
)
(func $f (param i32 i32 i32) (result i32) (i32.const -1))
(func (export "as-call-first") (result i32)
(call $f (return (i32.const 12)) (i32.const 2) (i32.const 3))
)
(func (export "as-call-mid") (result i32)
(call $f (i32.const 1) (return (i32.const 13)) (i32.const 3))
)
(func (export "as-call-last") (result i32)
(call $f (i32.const 1) (i32.const 2) (return (i32.const 14)))
)
(type $sig (func (param i32 i32 i32) (result i32)))
(table anyfunc (elem $f))
(func (export "as-call_indirect-func") (result i32)
(call_indirect $sig (return (i32.const 20)) (i32.const 1) (i32.const 2) (i32.const 3))
)
(func (export "as-call_indirect-first") (result i32)
(call_indirect $sig (i32.const 0) (return (i32.const 21)) (i32.const 2) (i32.const 3))
)
(func (export "as-call_indirect-mid") (result i32)
(call_indirect $sig (i32.const 0) (i32.const 1) (return (i32.const 22)) (i32.const 3))
)
(func (export "as-call_indirect-last") (result i32)
(call_indirect $sig (i32.const 0) (i32.const 1) (i32.const 2) (return (i32.const 23)))
)
(func (export "as-set_local-value") (result i32) (local f32)
(set_local 0 (return (i32.const 17))) (i32.const -1)
)
(memory 1)
(func (export "as-load-address") (result f32)
(f32.load (return (f32.const 1.7)))
)
(func (export "as-loadN-address") (result i64)
(i64.load8_s (return (i64.const 30)))
)
(func (export "as-store-address") (result i32)
(f64.store (return (i32.const 30)) (f64.const 7)) (i32.const -1)
)
(func (export "as-store-value") (result i32)
(i64.store (i32.const 2) (return (i32.const 31))) (i32.const -1)
)
(func (export "as-storeN-address") (result i32)
(i32.store8 (return (i32.const 32)) (i32.const 7)) (i32.const -1)
)
(func (export "as-storeN-value") (result i32)
(i64.store16 (i32.const 2) (return (i32.const 33))) (i32.const -1)
)
(func (export "as-unary-operand") (result f32)
(f32.neg (return (f32.const 3.4)))
)
(func (export "as-binary-left") (result i32)
(i32.add (return (i32.const 3)) (i32.const 10))
)
(func (export "as-binary-right") (result i64)
(i64.sub (i64.const 10) (return (i64.const 45)))
)
(func (export "as-test-operand") (result i32)
(i32.eqz (return (i32.const 44)))
)
(func (export "as-compare-left") (result i32)
(f64.le (return (i32.const 43)) (f64.const 10))
)
(func (export "as-compare-right") (result i32)
(f32.ne (f32.const 10) (return (i32.const 42)))
)
(func (export "as-convert-operand") (result i32)
(i32.wrap/i64 (return (i32.const 41)))
)
(func (export "as-grow_memory-size") (result i32)
(grow_memory (return (i32.const 40)))
)
)

View File

@ -0,0 +1,29 @@
(module
(func (export "select_i32") (param $lhs i32) (param $rhs i32) (param $cond i32) (result i32)
(select (get_local $lhs) (get_local $rhs) (get_local $cond)))
(func (export "select_i64") (param $lhs i64) (param $rhs i64) (param $cond i32) (result i64)
(select (get_local $lhs) (get_local $rhs) (get_local $cond)))
(func (export "select_f32") (param $lhs f32) (param $rhs f32) (param $cond i32) (result f32)
(select (get_local $lhs) (get_local $rhs) (get_local $cond)))
(func (export "select_f64") (param $lhs f64) (param $rhs f64) (param $cond i32) (result f64)
(select (get_local $lhs) (get_local $rhs) (get_local $cond)))
;; Check that both sides of the select are evaluated
(func (export "select_trap_l") (param $cond i32) (result i32)
(select (unreachable) (i32.const 0) (get_local $cond))
)
(func (export "select_trap_r") (param $cond i32) (result i32)
(select (i32.const 0) (unreachable) (get_local $cond))
)
(func (export "select_unreached")
(unreachable) (select)
(unreachable) (i32.const 0) (select)
(unreachable) (i32.const 0) (i32.const 0) (select)
(unreachable) (f32.const 0) (i32.const 0) (select)
(unreachable)
)
)

View File

@ -0,0 +1,118 @@
(module
;; Statement switch
(func (export "stmt") (param $i i32) (result i32)
(local $j i32)
(set_local $j (i32.const 100))
(block $switch
(block $7
(block $default
(block $6
(block $5
(block $4
(block $3
(block $2
(block $1
(block $0
(br_table $0 $1 $2 $3 $4 $5 $6 $7 $default
(get_local $i)
)
) ;; 0
(return (get_local $i))
) ;; 1
(nop)
;; fallthrough
) ;; 2
;; fallthrough
) ;; 3
(set_local $j (i32.sub (i32.const 0) (get_local $i)))
(br $switch)
) ;; 4
(br $switch)
) ;; 5
(set_local $j (i32.const 101))
(br $switch)
) ;; 6
(set_local $j (i32.const 101))
;; fallthrough
) ;; default
(set_local $j (i32.const 102))
) ;; 7
;; fallthrough
)
(return (get_local $j))
)
;; Expression switch
(func (export "expr") (param $i i64) (result i64)
(local $j i64)
(set_local $j (i64.const 100))
(return
(block $switch (result i64)
(block $7
(block $default
(block $4
(block $5
(block $6
(block $3
(block $2
(block $1
(block $0
(br_table $0 $1 $2 $3 $4 $5 $6 $7 $default
(i32.wrap/i64 (get_local $i))
)
) ;; 0
(return (get_local $i))
) ;; 1
(nop)
;; fallthrough
) ;; 2
;; fallthrough
) ;; 3
(br $switch (i64.sub (i64.const 0) (get_local $i)))
) ;; 6
(set_local $j (i64.const 101))
;; fallthrough
) ;; 4
;; fallthrough
) ;; 5
;; fallthrough
) ;; default
(br $switch (get_local $j))
) ;; 7
(i64.const -5)
)
)
)
;; Argument switch
(func (export "arg") (param $i i32) (result i32)
(return
(block $2 (result i32)
(i32.add (i32.const 10)
(block $1 (result i32)
(i32.add (i32.const 100)
(block $0 (result i32)
(i32.add (i32.const 1000)
(block $default (result i32)
(br_table $0 $1 $2 $default
(i32.mul (i32.const 2) (get_local $i))
(i32.and (i32.const 3) (get_local $i))
)
)
)
)
)
)
)
)
)
)
;; Corner cases
(func (export "corner") (result i32)
(block
(br_table 0 (i32.const 0))
)
(i32.const 1)
)
)

View File

@ -0,0 +1,94 @@
(module
;; Typing
(func (export "type-local-i32") (result i32) (local i32) (tee_local 0 (i32.const 0)))
(func (export "type-local-i64") (result i64) (local i64) (tee_local 0 (i64.const 0)))
(func (export "type-local-f32") (result f32) (local f32) (tee_local 0 (f32.const 0)))
(func (export "type-local-f64") (result f64) (local f64) (tee_local 0 (f64.const 0)))
(func (export "type-param-i32") (param i32) (result i32) (tee_local 0 (i32.const 10)))
(func (export "type-param-i64") (param i64) (result i64) (tee_local 0 (i64.const 11)))
(func (export "type-param-f32") (param f32) (result f32) (tee_local 0 (f32.const 11.1)))
(func (export "type-param-f64") (param f64) (result f64) (tee_local 0 (f64.const 12.2)))
(func (export "type-mixed") (param i64 f32 f64 i32 i32) (local f32 i64 i64 f64)
(drop (i64.eqz (tee_local 0 (i64.const 0))))
(drop (f32.neg (tee_local 1 (f32.const 0))))
(drop (f64.neg (tee_local 2 (f64.const 0))))
(drop (i32.eqz (tee_local 3 (i32.const 0))))
(drop (i32.eqz (tee_local 4 (i32.const 0))))
(drop (f32.neg (tee_local 5 (f32.const 0))))
(drop (i64.eqz (tee_local 6 (i64.const 0))))
(drop (i64.eqz (tee_local 7 (i64.const 0))))
(drop (f64.neg (tee_local 8 (f64.const 0))))
)
;; Writing
(func (export "write") (param i64 f32 f64 i32 i32) (result i64) (local f32 i64 i64 f64)
(drop (tee_local 1 (f32.const -0.3)))
(drop (tee_local 3 (i32.const 40)))
(drop (tee_local 4 (i32.const -7)))
(drop (tee_local 5 (f32.const 5.5)))
(drop (tee_local 6 (i64.const 6)))
(drop (tee_local 8 (f64.const 8)))
(i64.trunc_s/f64
(f64.add
(f64.convert_u/i64 (get_local 0))
(f64.add
(f64.promote/f32 (get_local 1))
(f64.add
(get_local 2)
(f64.add
(f64.convert_u/i32 (get_local 3))
(f64.add
(f64.convert_s/i32 (get_local 4))
(f64.add
(f64.promote/f32 (get_local 5))
(f64.add
(f64.convert_u/i64 (get_local 6))
(f64.add
(f64.convert_u/i64 (get_local 7))
(get_local 8)
)
)
)
)
)
)
)
)
)
)
;; Result
(func (export "result") (param i64 f32 f64 i32 i32) (result f64)
(local f32 i64 i64 f64)
(f64.add
(f64.convert_u/i64 (tee_local 0 (i64.const 1)))
(f64.add
(f64.promote/f32 (tee_local 1 (f32.const 2)))
(f64.add
(tee_local 2 (f64.const 3.3))
(f64.add
(f64.convert_u/i32 (tee_local 3 (i32.const 4)))
(f64.add
(f64.convert_s/i32 (tee_local 4 (i32.const 5)))
(f64.add
(f64.promote/f32 (tee_local 5 (f32.const 5.5)))
(f64.add
(f64.convert_u/i64 (tee_local 6 (i64.const 6)))
(f64.add
(f64.convert_u/i64 (tee_local 7 (i64.const 0)))
(tee_local 8 (f64.const 8))
)
)
)
)
)
)
)
)
)
)

View File

@ -0,0 +1,10 @@
(module
(func (export "no_dce.i32.div_s") (param $x i32) (param $y i32)
(drop (i32.div_s (get_local $x) (get_local $y))))
(func (export "no_dce.i32.div_u") (param $x i32) (param $y i32)
(drop (i32.div_u (get_local $x) (get_local $y))))
(func (export "no_dce.i64.div_s") (param $x i64) (param $y i64)
(drop (i64.div_s (get_local $x) (get_local $y))))
(func (export "no_dce.i64.div_u") (param $x i64) (param $y i64)
(drop (i64.div_u (get_local $x) (get_local $y))))
)

View File

@ -0,0 +1,10 @@
(module
(func (export "no_dce.i32.rem_s") (param $x i32) (param $y i32)
(drop (i32.rem_s (get_local $x) (get_local $y))))
(func (export "no_dce.i32.rem_u") (param $x i32) (param $y i32)
(drop (i32.rem_u (get_local $x) (get_local $y))))
(func (export "no_dce.i64.rem_s") (param $x i64) (param $y i64)
(drop (i64.rem_s (get_local $x) (get_local $y))))
(func (export "no_dce.i64.rem_u") (param $x i64) (param $y i64)
(drop (i64.rem_u (get_local $x) (get_local $y))))
)

View File

@ -0,0 +1,18 @@
(module
(memory 1)
(func (export "no_dce.i32.load") (param $i i32) (drop (i32.load (get_local $i))))
(func (export "no_dce.i32.load16_s") (param $i i32) (drop (i32.load16_s (get_local $i))))
(func (export "no_dce.i32.load16_u") (param $i i32) (drop (i32.load16_u (get_local $i))))
(func (export "no_dce.i32.load8_s") (param $i i32) (drop (i32.load8_s (get_local $i))))
(func (export "no_dce.i32.load8_u") (param $i i32) (drop (i32.load8_u (get_local $i))))
(func (export "no_dce.i64.load") (param $i i32) (drop (i64.load (get_local $i))))
(func (export "no_dce.i64.load32_s") (param $i i32) (drop (i64.load32_s (get_local $i))))
(func (export "no_dce.i64.load32_u") (param $i i32) (drop (i64.load32_u (get_local $i))))
(func (export "no_dce.i64.load16_s") (param $i i32) (drop (i64.load16_s (get_local $i))))
(func (export "no_dce.i64.load16_u") (param $i i32) (drop (i64.load16_u (get_local $i))))
(func (export "no_dce.i64.load8_s") (param $i i32) (drop (i64.load8_s (get_local $i))))
(func (export "no_dce.i64.load8_u") (param $i i32) (drop (i64.load8_u (get_local $i))))
(func (export "no_dce.f32.load") (param $i i32) (drop (f32.load (get_local $i))))
(func (export "no_dce.f64.load") (param $i i32) (drop (f64.load (get_local $i))))
)

View File

@ -0,0 +1,157 @@
;; Test that control-flow transfer unwinds stack and it can be anything after.
(module
(func (export "func-unwind-by-unreachable")
(i32.const 3) (i64.const 1) (unreachable)
)
(func (export "func-unwind-by-br")
(i32.const 3) (i64.const 1) (br 0)
)
(func (export "func-unwind-by-br-value") (result i32)
(i32.const 3) (i64.const 1) (br 0 (i32.const 9))
)
(func (export "func-unwind-by-br_table")
(i32.const 3) (i64.const 1) (br_table 0 (i32.const 0))
)
(func (export "func-unwind-by-br_table-value") (result i32)
(i32.const 3) (i64.const 1) (br_table 0 (i32.const 9) (i32.const 0))
)
(func (export "func-unwind-by-return") (result i32)
(i32.const 3) (i64.const 1) (return (i32.const 9))
)
(func (export "block-unwind-by-unreachable")
(block (i32.const 3) (i64.const 1) (unreachable))
)
(func (export "block-unwind-by-br") (result i32)
(block (i32.const 3) (i64.const 1) (br 0)) (i32.const 9)
)
(func (export "block-unwind-by-br-value") (result i32)
(block (result i32) (i32.const 3) (i64.const 1) (br 0 (i32.const 9)))
)
(func (export "block-unwind-by-br_table") (result i32)
(block (i32.const 3) (i64.const 1) (br_table 0 (i32.const 0))) (i32.const 9)
)
(func (export "block-unwind-by-br_table-value") (result i32)
(block (result i32)
(i32.const 3) (i64.const 1) (br_table 0 (i32.const 9) (i32.const 0))
)
)
(func (export "block-unwind-by-return") (result i32)
(block (result i32) (i32.const 3) (i64.const 1) (return (i32.const 9)))
)
(func (export "block-nested-unwind-by-unreachable") (result i32)
(block (result i32) (i32.const 3) (block (i64.const 1) (unreachable)))
)
(func (export "block-nested-unwind-by-br") (result i32)
(block (i32.const 3) (block (i64.const 1) (br 1)) (drop)) (i32.const 9)
)
(func (export "block-nested-unwind-by-br-value") (result i32)
(block (result i32)
(i32.const 3) (block (i64.const 1) (br 1 (i32.const 9)))
)
)
(func (export "block-nested-unwind-by-br_table") (result i32)
(block
(i32.const 3) (block (i64.const 1) (br_table 1 (i32.const 1)))
(drop)
)
(i32.const 9)
)
(func (export "block-nested-unwind-by-br_table-value") (result i32)
(block (result i32)
(i32.const 3)
(block (i64.const 1) (br_table 1 (i32.const 9) (i32.const 1)))
)
)
(func (export "block-nested-unwind-by-return") (result i32)
(block (result i32)
(i32.const 3) (block (i64.const 1) (return (i32.const 9)))
)
)
(func (export "unary-after-unreachable") (result i32)
(f32.const 0) (unreachable) (i64.eqz)
)
(func (export "unary-after-br") (result i32)
(block (result i32) (f32.const 0) (br 0 (i32.const 9)) (i64.eqz))
)
(func (export "unary-after-br_table") (result i32)
(block (result i32)
(f32.const 0) (br_table 0 0 (i32.const 9) (i32.const 0)) (i64.eqz)
)
)
(func (export "unary-after-return") (result i32)
(f32.const 0) (return (i32.const 9)) (i64.eqz)
)
(func (export "binary-after-unreachable") (result i32)
(f32.const 0) (f64.const 1) (unreachable) (i64.eq)
)
(func (export "binary-after-br") (result i32)
(block (result i32)
(f32.const 0) (f64.const 1) (br 0 (i32.const 9)) (i64.eq)
)
)
(func (export "binary-after-br_table") (result i32)
(block (result i32)
(f32.const 0) (f64.const 1) (br_table 0 (i32.const 9) (i32.const 0))
(i64.eq)
)
)
(func (export "binary-after-return") (result i32)
(f32.const 0) (f64.const 1) (return (i32.const 9)) (i64.eq)
)
(func (export "select-after-unreachable") (result i32)
(f32.const 0) (f64.const 1) (i64.const 0) (unreachable) (select)
)
(func (export "select-after-br") (result i32)
(block (result i32)
(f32.const 0) (f64.const 1) (i64.const 0) (br 0 (i32.const 9)) (select)
)
)
(func (export "select-after-br_table") (result i32)
(block (result i32)
(f32.const 0) (f64.const 1) (i64.const 0)
(br_table 0 (i32.const 9) (i32.const 0))
(select)
)
)
(func (export "select-after-return") (result i32)
(f32.const 0) (f64.const 1) (i64.const 1) (return (i32.const 9)) (select)
)
(func (export "block-value-after-unreachable") (result i32)
(block (result i32) (f32.const 0) (unreachable))
)
(func (export "block-value-after-br") (result i32)
(block (result i32) (f32.const 0) (br 0 (i32.const 9)))
)
(func (export "block-value-after-br_table") (result i32)
(block (result i32)
(f32.const 0) (br_table 0 0 (i32.const 9) (i32.const 0))
)
)
(func (export "block-value-after-return") (result i32)
(block (result i32) (f32.const 0) (return (i32.const 9)))
)
(func (export "loop-value-after-unreachable") (result i32)
(loop (result i32) (f32.const 0) (unreachable))
)
(func (export "loop-value-after-br") (result i32)
(block (result i32) (loop (result i32) (f32.const 0) (br 1 (i32.const 9))))
)
(func (export "loop-value-after-br_table") (result i32)
(block (result i32)
(loop (result i32)
(f32.const 0) (br_table 1 1 (i32.const 9) (i32.const 0))
)
)
)
(func (export "loop-value-after-return") (result i32)
(loop (result i32) (f32.const 0) (return (i32.const 9)))
)
)

31
vendor/github.com/go-interpreter/wagon/exec/var.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
func (vm *VM) getLocal() {
index := vm.fetchUint32()
vm.pushUint64(vm.ctx.locals[int(index)])
}
func (vm *VM) setLocal() {
index := vm.fetchUint32()
vm.ctx.locals[int(index)] = vm.popUint64()
}
func (vm *VM) teeLocal() {
index := vm.fetchUint32()
val := vm.ctx.stack[len(vm.ctx.stack)-1]
vm.ctx.locals[int(index)] = val
}
func (vm *VM) getGlobal() {
index := vm.fetchUint32()
vm.pushUint64(vm.globals[int(index)])
}
func (vm *VM) setGlobal() {
index := vm.fetchUint32()
vm.globals[int(index)] = vm.popUint64()
}

399
vendor/github.com/go-interpreter/wagon/exec/vm.go generated vendored Normal file
View File

@ -0,0 +1,399 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package exec provides functions for executing WebAssembly bytecode.
package exec
import (
"encoding/binary"
"errors"
"fmt"
"math"
"github.com/go-interpreter/wagon/disasm"
"github.com/go-interpreter/wagon/exec/internal/compile"
"github.com/go-interpreter/wagon/wasm"
ops "github.com/go-interpreter/wagon/wasm/operators"
)
var (
// ErrMultipleLinearMemories is returned by (*VM).NewVM when the module
// has more then one entries in the linear memory space.
ErrMultipleLinearMemories = errors.New("exec: more than one linear memories in module")
// ErrInvalidArgumentCount is returned by (*VM).ExecCode when an invalid
// number of arguments to the WebAssembly function are passed to it.
ErrInvalidArgumentCount = errors.New("exec: invalid number of arguments to function")
)
// InvalidReturnTypeError is returned by (*VM).ExecCode when the module
// specifies an invalid return type value for the executed function.
type InvalidReturnTypeError int8
func (e InvalidReturnTypeError) Error() string {
return fmt.Sprintf("Function has invalid return value_type: %d", int8(e))
}
// InvalidFunctionIndexError is returned by (*VM).ExecCode when the function
// index provided is invalid.
type InvalidFunctionIndexError int64
func (e InvalidFunctionIndexError) Error() string {
return fmt.Sprintf("Invalid index to function index space: %d", int64(e))
}
type context struct {
stack []uint64
locals []uint64
code []byte
pc int64
curFunc int64
}
// VM is the execution context for executing WebAssembly bytecode.
type VM struct {
ctx context
module *wasm.Module
globals []uint64
memory []byte
funcs []function
funcTable [256]func()
// RecoverPanic controls whether the `ExecCode` method
// recovers from a panic and returns it as an error
// instead.
// A panic can occur either when executing an invalid VM
// or encountering an invalid instruction, e.g. `unreachable`.
RecoverPanic bool
}
// As per the WebAssembly spec: https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/Semantics.md#linear-memory
const wasmPageSize = 65536 // (64 KB)
var endianess = binary.LittleEndian
// NewVM creates a new VM from a given module. If the module defines a
// start function, it will be executed.
func NewVM(module *wasm.Module) (*VM, error) {
var vm VM
if module.Memory != nil && len(module.Memory.Entries) != 0 {
if len(module.Memory.Entries) > 1 {
return nil, ErrMultipleLinearMemories
}
vm.memory = make([]byte, uint(module.Memory.Entries[0].Limits.Initial)*wasmPageSize)
copy(vm.memory, module.LinearMemoryIndexSpace[0])
}
vm.funcs = make([]function, len(module.FunctionIndexSpace))
vm.globals = make([]uint64, len(module.GlobalIndexSpace))
vm.newFuncTable()
vm.module = module
nNatives := 0
for i, fn := range module.FunctionIndexSpace {
// Skip native methods as they need not be
// disassembled; simply add them at the end
// of the `funcs` array as is, as specified
// in the spec. See the "host functions"
// section of:
// https://webassembly.github.io/spec/core/exec/modules.html#allocation
if fn.IsHost() {
vm.funcs[i] = goFunction{
typ: fn.Host.Type(),
val: fn.Host,
}
nNatives++
continue
}
disassembly, err := disasm.Disassemble(fn, module)
if err != nil {
return nil, err
}
totalLocalVars := 0
totalLocalVars += len(fn.Sig.ParamTypes)
for _, entry := range fn.Body.Locals {
totalLocalVars += int(entry.Count)
}
code, table := compile.Compile(disassembly.Code)
vm.funcs[i] = compiledFunction{
code: code,
branchTables: table,
maxDepth: disassembly.MaxDepth,
totalLocalVars: totalLocalVars,
args: len(fn.Sig.ParamTypes),
returns: len(fn.Sig.ReturnTypes) != 0,
}
}
for i, global := range module.GlobalIndexSpace {
val, err := module.ExecInitExpr(global.Init)
if err != nil {
return nil, err
}
switch v := val.(type) {
case int32:
vm.globals[i] = uint64(v)
case int64:
vm.globals[i] = uint64(v)
case float32:
vm.globals[i] = uint64(math.Float32bits(v))
case float64:
vm.globals[i] = uint64(math.Float64bits(v))
}
}
if module.Start != nil {
_, err := vm.ExecCode(int64(module.Start.Index))
if err != nil {
return nil, err
}
}
return &vm, nil
}
// Memory returns the linear memory space for the VM.
func (vm *VM) Memory() []byte {
return vm.memory
}
func (vm *VM) pushBool(v bool) {
if v {
vm.pushUint64(1)
} else {
vm.pushUint64(0)
}
}
func (vm *VM) fetchBool() bool {
return vm.fetchInt8() != 0
}
func (vm *VM) fetchInt8() int8 {
i := int8(vm.ctx.code[vm.ctx.pc])
vm.ctx.pc++
return i
}
func (vm *VM) fetchUint32() uint32 {
v := endianess.Uint32(vm.ctx.code[vm.ctx.pc:])
vm.ctx.pc += 4
return v
}
func (vm *VM) fetchInt32() int32 {
return int32(vm.fetchUint32())
}
func (vm *VM) fetchFloat32() float32 {
return math.Float32frombits(vm.fetchUint32())
}
func (vm *VM) fetchUint64() uint64 {
v := endianess.Uint64(vm.ctx.code[vm.ctx.pc:])
vm.ctx.pc += 8
return v
}
func (vm *VM) fetchInt64() int64 {
return int64(vm.fetchUint64())
}
func (vm *VM) fetchFloat64() float64 {
return math.Float64frombits(vm.fetchUint64())
}
func (vm *VM) popUint64() uint64 {
i := vm.ctx.stack[len(vm.ctx.stack)-1]
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-1]
return i
}
func (vm *VM) popInt64() int64 {
return int64(vm.popUint64())
}
func (vm *VM) popFloat64() float64 {
return math.Float64frombits(vm.popUint64())
}
func (vm *VM) popUint32() uint32 {
return uint32(vm.popUint64())
}
func (vm *VM) popInt32() int32 {
return int32(vm.popUint32())
}
func (vm *VM) popFloat32() float32 {
return math.Float32frombits(vm.popUint32())
}
func (vm *VM) pushUint64(i uint64) {
vm.ctx.stack = append(vm.ctx.stack, i)
}
func (vm *VM) pushInt64(i int64) {
vm.pushUint64(uint64(i))
}
func (vm *VM) pushFloat64(f float64) {
vm.pushUint64(math.Float64bits(f))
}
func (vm *VM) pushUint32(i uint32) {
vm.pushUint64(uint64(i))
}
func (vm *VM) pushInt32(i int32) {
vm.pushUint64(uint64(i))
}
func (vm *VM) pushFloat32(f float32) {
vm.pushUint32(math.Float32bits(f))
}
// ExecCode calls the function with the given index and arguments.
// fnIndex should be a valid index into the function index space of
// the VM's module.
func (vm *VM) ExecCode(fnIndex int64, args ...uint64) (rtrn interface{}, err error) {
// If used as a library, client code should set vm.RecoverPanic to true
// in order to have an error returned.
if vm.RecoverPanic {
defer func() {
if r := recover(); r != nil {
switch e := r.(type) {
case error:
err = e
default:
err = fmt.Errorf("exec: %v", e)
}
}
}()
}
if int(fnIndex) > len(vm.funcs) {
return nil, InvalidFunctionIndexError(fnIndex)
}
if len(vm.module.GetFunction(int(fnIndex)).Sig.ParamTypes) != len(args) {
return nil, ErrInvalidArgumentCount
}
compiled, ok := vm.funcs[fnIndex].(compiledFunction)
if !ok {
panic(fmt.Sprintf("exec: function at index %d is not a compiled function", fnIndex))
}
if len(vm.ctx.stack) < compiled.maxDepth {
vm.ctx.stack = make([]uint64, 0, compiled.maxDepth)
}
vm.ctx.locals = make([]uint64, compiled.totalLocalVars)
vm.ctx.pc = 0
vm.ctx.code = compiled.code
vm.ctx.curFunc = fnIndex
for i, arg := range args {
vm.ctx.locals[i] = arg
}
res := vm.execCode(compiled)
if compiled.returns {
rtrnType := vm.module.GetFunction(int(fnIndex)).Sig.ReturnTypes[0]
switch rtrnType {
case wasm.ValueTypeI32:
rtrn = uint32(res)
case wasm.ValueTypeI64:
rtrn = uint64(res)
case wasm.ValueTypeF32:
rtrn = math.Float32frombits(uint32(res))
case wasm.ValueTypeF64:
rtrn = math.Float64frombits(res)
default:
return nil, InvalidReturnTypeError(rtrnType)
}
}
return rtrn, nil
}
func (vm *VM) execCode(compiled compiledFunction) uint64 {
outer:
for int(vm.ctx.pc) < len(vm.ctx.code) {
op := vm.ctx.code[vm.ctx.pc]
vm.ctx.pc++
switch op {
case ops.Return:
break outer
case compile.OpJmp:
vm.ctx.pc = vm.fetchInt64()
continue
case compile.OpJmpZ:
target := vm.fetchInt64()
if vm.popUint32() == 0 {
vm.ctx.pc = target
continue
}
case compile.OpJmpNz:
target := vm.fetchInt64()
preserveTop := vm.fetchBool()
discard := vm.fetchInt64()
if vm.popUint32() != 0 {
vm.ctx.pc = target
var top uint64
if preserveTop {
top = vm.ctx.stack[len(vm.ctx.stack)-1]
}
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(discard)]
if preserveTop {
vm.pushUint64(top)
}
continue
}
case ops.BrTable:
index := vm.fetchInt64()
label := vm.popInt32()
cf, ok := vm.funcs[vm.ctx.curFunc].(compiledFunction)
if !ok {
panic(fmt.Sprintf("exec: function at index %d is not a compiled function", vm.ctx.curFunc))
}
table := cf.branchTables[index]
var target compile.Target
if label >= 0 && label < int32(len(table.Targets)) {
target = table.Targets[int32(label)]
} else {
target = table.DefaultTarget
}
if target.Return {
break outer
}
vm.ctx.pc = target.Addr
var top uint64
if target.PreserveTop {
top = vm.ctx.stack[len(vm.ctx.stack)-1]
}
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(target.Discard)]
if target.PreserveTop {
vm.pushUint64(top)
}
continue
case compile.OpDiscard:
place := vm.fetchInt64()
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(place)]
case compile.OpDiscardPreserveTop:
top := vm.ctx.stack[len(vm.ctx.stack)-1]
place := vm.fetchInt64()
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(place)]
vm.pushUint64(top)
default:
vm.funcTable[op]()
}
}
if compiled.returns {
return vm.ctx.stack[len(vm.ctx.stack)-1]
}
return 0
}

View File

@ -0,0 +1,40 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package stack implements a growable uint64 stack
package stack
type Stack struct {
slice []uint64
}
func (s *Stack) Push(b uint64) {
s.slice = append(s.slice, b)
}
func (s *Stack) Pop() uint64 {
v := s.Top()
s.slice = s.slice[:len(s.slice)-1]
return v
}
func (s *Stack) SetTop(v uint64) {
s.slice[len(s.slice)-1] = v
}
func (s *Stack) Top() uint64 {
return s.slice[len(s.slice)-1]
}
func (s *Stack) Get(i int) uint64 {
return s.slice[i]
}
func (s *Stack) Set(i int, v uint64) {
s.slice[i] = v
}
func (s *Stack) Len() int {
return len(s.slice)
}

View File

@ -0,0 +1,74 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package validate
import (
"errors"
"fmt"
"github.com/go-interpreter/wagon/wasm"
ops "github.com/go-interpreter/wagon/wasm/operators"
)
type Error struct {
Offset int // Byte offset in the bytecode vector where the error occurs.
Function int // Index into the function index space for the offending function.
Err error
}
func (e Error) Error() string {
return fmt.Sprintf("error while validating function %d at offset %d: %v", e.Function, e.Offset, e.Err)
}
var ErrStackUnderflow = errors.New("validate: stack underflow")
type InvalidImmediateError struct {
ImmType string
OpName string
}
func (e InvalidImmediateError) Error() string {
return fmt.Sprintf("invalid immediate for op %s at (should be %s)", e.OpName, e.ImmType)
}
type UnmatchedOpError byte
func (e UnmatchedOpError) Error() string {
n1, _ := ops.New(byte(e))
return fmt.Sprintf("encountered unmatched %s", n1.Name)
}
type InvalidLabelError uint32
func (e InvalidLabelError) Error() string {
return fmt.Sprintf("invalid nesting depth %d", uint32(e))
}
type InvalidLocalIndexError uint32
func (e InvalidLocalIndexError) Error() string {
return fmt.Sprintf("invalid index for local variable %d", uint32(e))
}
type InvalidTypeError struct {
Wanted wasm.ValueType
Got wasm.ValueType
}
func (e InvalidTypeError) Error() string {
return fmt.Sprintf("invalid type, got: %v, wanted: %v", e.Got, e.Wanted)
}
type InvalidElementIndexError uint32
func (e InvalidElementIndexError) Error() string {
return fmt.Sprintf("invalid element index %d", uint32(e))
}
type NoSectionError wasm.SectionID
func (e NoSectionError) Error() string {
return fmt.Sprintf("reference to non existant section (id %d) in module", wasm.SectionID(e))
}

26
vendor/github.com/go-interpreter/wagon/validate/log.go generated vendored Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package validate
import (
"io/ioutil"
"log"
"os"
)
var PrintDebugInfo = false
var logger *log.Logger
func init() {
w := ioutil.Discard
if PrintDebugInfo {
w = os.Stderr
}
logger = log.New(w, "", log.Lshortfile)
log.SetFlags(log.Lshortfile)
}

View File

@ -0,0 +1,13 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package validate
import (
"github.com/go-interpreter/wagon/wasm"
)
type operand struct {
Type wasm.ValueType
}

View File

@ -0,0 +1,374 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// package validate provides functions for validating WebAssembly modules.
package validate
import (
"bytes"
"io"
"github.com/go-interpreter/wagon/wasm"
ops "github.com/go-interpreter/wagon/wasm/operators"
)
// vibhavp: TODO: We do not verify whether blocks don't access for the parent block, do that.
func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Module) (*mockVM, error) {
vm := &mockVM{
stack: []operand{},
stackTop: 0,
code: bytes.NewReader(body.Code),
origLength: len(body.Code),
polymorphic: false,
blocks: []block{},
curFunc: fn,
}
localVariables := []operand{}
// Paramters count as local variables too
// This comment explains how local variables work: https://github.com/WebAssembly/design/issues/1037#issuecomment-293505798
for _, entry := range fn.ParamTypes {
localVariables = append(localVariables, operand{entry})
}
for _, entry := range body.Locals {
vars := make([]operand, entry.Count)
for i := uint32(0); i < entry.Count; i++ {
vars[i].Type = entry.Type
logger.Printf("Var %v", entry.Type)
}
localVariables = append(localVariables, vars...)
}
for {
op, err := vm.code.ReadByte()
if err == io.EOF {
break
} else if err != nil {
return vm, err
}
opStruct, err := ops.New(op)
if err != nil {
return vm, err
}
logger.Printf("PC: %d OP: %s polymorphic: %v", vm.pc(), opStruct.Name, vm.isPolymorphic())
if !opStruct.Polymorphic {
if err := vm.adjustStack(opStruct); err != nil {
return vm, err
}
}
switch op {
case ops.If, ops.Block, ops.Loop:
sig, err := vm.fetchVarInt()
if err != nil {
return vm, err
}
switch wasm.ValueType(sig) {
case wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueType(wasm.BlockTypeEmpty):
vm.pushBlock(op, wasm.BlockType(sig))
default:
if !vm.isPolymorphic() {
return vm, InvalidImmediateError{"block_type", opStruct.Name}
}
}
case ops.Else:
block := vm.topBlock()
if block == nil || block.op != ops.If {
return vm, UnmatchedOpError(op)
}
if block.blockType != wasm.BlockTypeEmpty {
top, under := vm.topOperand()
if !vm.isPolymorphic() && (under || top.Type != wasm.ValueType(block.blockType)) {
return vm, InvalidTypeError{wasm.ValueType(block.blockType), top.Type}
}
vm.pushOperand(wasm.ValueType(block.blockType))
}
vm.stackTop = block.stackTop
case ops.End:
isPolymorphic := vm.isPolymorphic()
block := vm.popBlock()
if block == nil {
return vm, UnmatchedOpError(op)
}
if block.blockType != wasm.BlockTypeEmpty {
top, under := vm.topOperand()
if !isPolymorphic && (under || top.Type != wasm.ValueType(block.blockType)) {
return vm, InvalidTypeError{wasm.ValueType(block.blockType), top.Type}
}
vm.stackTop = block.stackTop
vm.pushOperand(wasm.ValueType(block.blockType))
vm.stackTop = block.stackTop + 1 // as we pushed an element
} else {
vm.stackTop = block.stackTop
}
case ops.BrIf, ops.Br:
depth, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
if err = vm.canBranch(int(depth)); !vm.isPolymorphic() && err != nil {
return vm, err
}
if op == ops.Br {
vm.setPolymorphic()
}
case ops.BrTable:
operand, under := vm.popOperand()
if !vm.isPolymorphic() && (under || operand.Type != wasm.ValueTypeI32) {
return vm, InvalidTypeError{wasm.ValueTypeI32, operand.Type}
}
// read table entries
targetCount, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
var targetTable []uint32
for i := uint32(0); i < targetCount; i++ {
entry, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
if err = vm.canBranch(int(entry)); !vm.isPolymorphic() && err != nil {
return vm, err
}
targetTable = append(targetTable, entry)
}
defaultTarget, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
if err = vm.canBranch(int(defaultTarget)); !vm.isPolymorphic() && err != nil {
return vm, err
}
vm.setPolymorphic()
case ops.Return:
if len(fn.ReturnTypes) > 1 {
panic("not implemented")
}
if len(fn.ReturnTypes) != 0 {
// only single returns supported for now
top, under := vm.popOperand()
if !vm.isPolymorphic() && (under || top.Type != fn.ReturnTypes[0]) {
return vm, InvalidTypeError{fn.ReturnTypes[0], top.Type}
}
}
vm.setPolymorphic()
case ops.Unreachable:
vm.setPolymorphic()
case ops.I32Const:
_, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
case ops.I64Const:
_, err := vm.fetchVarInt64()
if err != nil {
return vm, err
}
case ops.F32Const:
_, err := vm.fetchUint32()
if err != nil {
return vm, err
}
case ops.F64Const:
_, err := vm.fetchUint64()
if err != nil {
return vm, err
}
case ops.GetLocal, ops.SetLocal, ops.TeeLocal:
i, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
if int(i) >= len(localVariables) {
return vm, InvalidLocalIndexError(i)
}
v := localVariables[i]
if op == ops.GetLocal {
vm.pushOperand(v.Type)
} else { // == set_local or tee_local
top, under := vm.popOperand()
if !vm.isPolymorphic() && (under || top.Type != v.Type) {
return vm, InvalidTypeError{v.Type, top.Type}
}
if op == ops.TeeLocal {
vm.pushOperand(v.Type)
}
}
case ops.GetGlobal, ops.SetGlobal:
index, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
gv := module.GetGlobal(int(index))
if gv == nil {
return vm, wasm.InvalidGlobalIndexError(index)
}
if op == ops.GetGlobal {
vm.pushOperand(gv.Type.Type)
} else {
val, under := vm.popOperand()
if !vm.isPolymorphic() && (under || val.Type != gv.Type.Type) {
return vm, InvalidTypeError{gv.Type.Type, val.Type}
}
}
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
// read memory_immediate
// flags
_, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
// offset
_, err = vm.fetchVarUint()
if err != nil {
return vm, err
}
case ops.CurrentMemory, ops.GrowMemory:
_, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
case ops.Call:
index, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
fn := module.GetFunction(int(index))
if fn == nil {
return vm, wasm.InvalidFunctionIndexError(index)
}
logger.Printf("Function being called: %v", fn)
for index := range fn.Sig.ParamTypes {
argType := fn.Sig.ParamTypes[len(fn.Sig.ParamTypes)-index-1]
operand, under := vm.popOperand()
if !vm.isPolymorphic() && (under || operand.Type != argType) {
return vm, InvalidTypeError{argType, operand.Type}
}
}
if len(fn.Sig.ReturnTypes) > 0 {
vm.pushOperand(fn.Sig.ReturnTypes[0])
}
case ops.CallIndirect:
if module.Table == nil || len(module.Table.Entries) == 0 {
return vm, NoSectionError(wasm.SectionIDTable)
}
// The call_indirect process consists of getting two i32 values
// off (first from the bytecode stream, and the second from
// the stack) and using first as an index into the "Types" section
// of the module, while the the second one into the function index
// space. The signature of the two elements are then compared
// to see if they match, and the call proceeds as normal if they
// do.
// Since this is possible only during program execution, we only
// perform the static check for the function index mentioned
// in the bytecode stream here.
// type index
index, err := vm.fetchVarUint()
if err != nil {
return vm, err
}
fnExpectSig := module.Types.Entries[index]
if operand, under := vm.popOperand(); !vm.isPolymorphic() && (under || operand.Type != wasm.ValueTypeI32) {
return vm, InvalidTypeError{wasm.ValueTypeI32, operand.Type}
}
for index := range fnExpectSig.ParamTypes {
argType := fnExpectSig.ParamTypes[len(fnExpectSig.ParamTypes)-index-1]
operand, under := vm.popOperand()
if !vm.isPolymorphic() && (under || (operand.Type != argType)) {
return vm, InvalidTypeError{argType, operand.Type}
}
}
if len(fnExpectSig.ReturnTypes) > 0 {
vm.pushOperand(fnExpectSig.ReturnTypes[0])
}
case ops.Drop:
if _, under := vm.popOperand(); !vm.isPolymorphic() && under {
return vm, ErrStackUnderflow
}
case ops.Select:
if vm.isPolymorphic() {
continue
}
operands := make([]operand, 2)
c, under := vm.popOperand()
if under || c.Type != wasm.ValueTypeI32 {
return vm, InvalidTypeError{wasm.ValueTypeI32, c.Type}
}
for i := 0; i < 2; i++ {
operand, under := vm.popOperand()
if !vm.isPolymorphic() && under {
return vm, ErrStackUnderflow
}
operands[i] = operand
}
// last 2 popped values should be of the same type
if operands[0].Type != operands[1].Type {
return vm, InvalidTypeError{operands[1].Type, operands[2].Type}
}
vm.pushOperand(operands[1].Type)
}
}
return vm, nil
}
// VerifyModule verifies the given module according to WebAssembly verification
// specs.
func VerifyModule(module *wasm.Module) error {
if module.Function == nil || module.Types == nil || len(module.Types.Entries) == 0 {
return nil
}
if module.Code == nil {
return NoSectionError(wasm.SectionIDCode)
}
logger.Printf("There are %d functions", len(module.Function.Types))
for i, fn := range module.FunctionIndexSpace {
if vm, err := verifyBody(fn.Sig, fn.Body, module); err != nil {
return Error{vm.pc(), i, err}
}
logger.Printf("No errors in function %d", i)
}
return nil
}

222
vendor/github.com/go-interpreter/wagon/validate/vm.go generated vendored Normal file
View File

@ -0,0 +1,222 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package validate
import (
"bytes"
"encoding/binary"
"io"
"github.com/go-interpreter/wagon/wasm"
"github.com/go-interpreter/wagon/wasm/leb128"
ops "github.com/go-interpreter/wagon/wasm/operators"
)
// mockVM is a minimal implementation of a virtual machine to
// validate WebAssembly code
type mockVM struct {
stack []operand
stackTop int // the top of the operand stack
origLength int // the original length of the bytecode stream
code *bytes.Reader
polymorphic bool // whether the base implict block has a polymorphic stack
blocks []block // a stack of encountered blocks
curFunc *wasm.FunctionSig
}
// a block reprsents an instruction sequence preceeded by a control flow operator
// it is used to verify that the block signature set by the operator is the correct
// one when the block ends
type block struct {
pc int // the pc where the control flow operator starting the block is located
stackTop int // stack top when the block started
blockType wasm.BlockType // block_type signature of the control operator
op byte // opcode for the operator starting the new block
polymorphic bool // whether the block has a polymorphic stack
loop bool // whether the block is the body of a loop instruction
}
func (vm *mockVM) fetchVarUint() (uint32, error) {
return leb128.ReadVarUint32(vm.code)
}
func (vm *mockVM) fetchVarInt() (int32, error) {
return leb128.ReadVarint32(vm.code)
}
func (vm *mockVM) fetchVarInt64() (int64, error) {
return leb128.ReadVarint64(vm.code)
}
func (vm *mockVM) fetchUint32() (uint32, error) {
var buf [4]byte
_, err := io.ReadFull(vm.code, buf[:])
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(buf[:]), nil
}
func (vm *mockVM) fetchUint64() (uint64, error) {
var buf [8]byte
_, err := io.ReadFull(vm.code, buf[:])
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint64(buf[:]), nil
}
func (vm *mockVM) pushBlock(op byte, blockType wasm.BlockType) {
logger.Printf("Pushing block %v", blockType)
vm.blocks = append(vm.blocks, block{
pc: vm.pc(),
stackTop: vm.stackTop,
blockType: blockType,
polymorphic: vm.isPolymorphic(),
op: op,
loop: op == ops.Loop,
})
}
// Get a block from it's relative nesting depth
func (vm *mockVM) getBlockFromDepth(depth int) *block {
if depth >= len(vm.blocks) {
return nil
}
return &vm.blocks[len(vm.blocks)-1-depth]
}
// Returns nil if depth is a valid nesting depth value that can be
// branched to.
func (vm *mockVM) canBranch(depth int) error {
blockType := wasm.BlockTypeEmpty
block := vm.getBlockFromDepth(depth)
// jumping to the start of a loop block doesn't push a value
// on the stack.
if block == nil {
if depth == len(vm.blocks) {
//equivalent to a `return', as the function
//body is an "implicit" block
if len(vm.curFunc.ReturnTypes) != 0 {
blockType = wasm.BlockType(vm.curFunc.ReturnTypes[0])
}
} else {
return InvalidLabelError(uint32(depth))
}
} else if !block.loop {
blockType = block.blockType
}
if blockType != wasm.BlockTypeEmpty {
top, under := vm.topOperand()
if under || top.Type != wasm.ValueType(blockType) {
return InvalidTypeError{wasm.ValueType(blockType), top.Type}
}
}
return nil
}
// returns nil in case of an underflow
func (vm *mockVM) popBlock() *block {
if len(vm.blocks) == 0 {
return nil
}
stackTop := len(vm.blocks) - 1
block := vm.blocks[stackTop]
vm.blocks = append(vm.blocks[:stackTop], vm.blocks[stackTop+1:]...)
return &block
}
func (vm *mockVM) topBlock() *block {
if len(vm.blocks) == 0 {
return nil
}
return &vm.blocks[len(vm.blocks)-1]
}
func (vm *mockVM) topOperand() (o operand, under bool) {
stackTop := vm.stackTop - 1
if stackTop == -1 {
under = true
return
}
o = vm.stack[stackTop]
return
}
func (vm *mockVM) popOperand() (operand, bool) {
var o operand
stackTop := vm.stackTop - 1
if stackTop == -1 {
return o, true
}
o = vm.stack[stackTop]
vm.stackTop--
logger.Printf("Stack after pop is %v. Popped %v", vm.stack[:vm.stackTop], o)
return o, false
}
func (vm *mockVM) pushOperand(t wasm.ValueType) {
o := operand{t}
logger.Printf("Stack top: %d, Len of stack :%d", vm.stackTop, len(vm.stack))
if vm.stackTop == len(vm.stack) {
vm.stack = append(vm.stack, o)
} else {
vm.stack[vm.stackTop] = o
}
vm.stackTop++
logger.Printf("Stack after push is %v. Pushed %v", vm.stack[:vm.stackTop], o)
}
func (vm *mockVM) adjustStack(op ops.Op) error {
for _, t := range op.Args {
op, under := vm.popOperand()
if !vm.isPolymorphic() && (under || op.Type != t) {
return InvalidTypeError{t, op.Type}
}
}
if op.Returns != wasm.ValueType(wasm.BlockTypeEmpty) {
vm.pushOperand(op.Returns)
}
return nil
}
// setPolymorphic sets the current block as having a polymorphic stack
// blocks created under it will be polymorphic too. All type-checking
// is ignored in a polymorhpic stack.
// (See https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/Semantics.md#validation)
func (vm *mockVM) setPolymorphic() {
if len(vm.blocks) == 0 {
vm.polymorphic = true
} else {
vm.blocks[len(vm.blocks)-1].polymorphic = true
}
}
func (vm *mockVM) isPolymorphic() bool {
if len(vm.blocks) == 0 {
return vm.polymorphic
}
return vm.topBlock().polymorphic
}
func (vm *mockVM) pc() int {
return vm.origLength - vm.code.Len()
}

6
vendor/github.com/go-interpreter/wagon/wasm/doc.go generated vendored Normal file
View File

@ -0,0 +1,6 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package wasm provides functions for reading and parsing WebAssembly modules.
package wasm

171
vendor/github.com/go-interpreter/wagon/wasm/imports.go generated vendored Normal file
View File

@ -0,0 +1,171 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"errors"
"fmt"
)
// Import is an intreface implemented by types that can be imported by a WebAssembly module.
type Import interface {
isImport()
}
// ImportEntry describes an import statement in a Wasm module.
type ImportEntry struct {
ModuleName string // module name string
FieldName string // field name string
Kind External
// If Kind is Function, Type is a FuncImport containing the type index of the function signature
// If Kind is Table, Type is a TableImport containing the type of the imported table
// If Kind is Memory, Type is a MemoryImport containing the type of the imported memory
// If the Kind is Global, Type is a GlobalVarImport
Type Import
}
type FuncImport struct {
Type uint32
}
func (FuncImport) isImport() {}
type TableImport struct {
Type Table
}
func (TableImport) isImport() {}
type MemoryImport struct {
Type Memory
}
func (MemoryImport) isImport() {}
type GlobalVarImport struct {
Type GlobalVar
}
func (GlobalVarImport) isImport() {}
var (
ErrImportMutGlobal = errors.New("wasm: cannot import global mutable variable")
ErrNoExportsInImportedModule = errors.New("wasm: imported module has no exports")
)
type InvalidExternalError uint8
func (e InvalidExternalError) Error() string {
return fmt.Sprintf("wasm: invalid external_kind value %d", uint8(e))
}
type ExportNotFoundError struct {
ModuleName string
FieldName string
}
type KindMismatchError struct {
ModuleName string
FieldName string
Import External
Export External
}
func (e KindMismatchError) Error() string {
return fmt.Sprintf("wasm: Mismatching import and export external kind values for %s.%s (%v, %v)", e.FieldName, e.ModuleName, e.Import, e.Export)
}
func (e ExportNotFoundError) Error() string {
return fmt.Sprintf("wasm: couldn't find export with name %s in module %s", e.FieldName, e.ModuleName)
}
type InvalidFunctionIndexError uint32
func (e InvalidFunctionIndexError) Error() string {
return fmt.Sprintf("wasm: Invalid index to function index space: %#x", uint32(e))
}
func (module *Module) resolveImports(resolve ResolveFunc) error {
if module.Import == nil {
return nil
}
modules := make(map[string]*Module)
var funcs uint32
for _, importEntry := range module.Import.Entries {
importedModule, ok := modules[importEntry.ModuleName]
if !ok {
var err error
importedModule, err = resolve(importEntry.ModuleName)
if err != nil {
return err
}
modules[importEntry.ModuleName] = importedModule
}
if importedModule.Export == nil {
return ErrNoExportsInImportedModule
}
exportEntry, ok := importedModule.Export.Entries[importEntry.FieldName]
if !ok {
return ExportNotFoundError{importEntry.ModuleName, importEntry.FieldName}
}
if exportEntry.Kind != importEntry.Kind {
return KindMismatchError{
FieldName: importEntry.FieldName,
ModuleName: importEntry.ModuleName,
Import: importEntry.Kind,
Export: exportEntry.Kind,
}
}
index := exportEntry.Index
switch exportEntry.Kind {
case ExternalFunction:
fn := importedModule.GetFunction(int(index))
if fn == nil {
return InvalidFunctionIndexError(index)
}
module.FunctionIndexSpace = append(module.FunctionIndexSpace, *fn)
module.Code.Bodies = append(module.Code.Bodies, *fn.Body)
module.imports.Funcs = append(module.imports.Funcs, funcs)
funcs++
case ExternalGlobal:
glb := importedModule.GetGlobal(int(index))
if glb == nil {
return InvalidGlobalIndexError(index)
}
if glb.Type.Mutable {
return ErrImportMutGlobal
}
module.GlobalIndexSpace = append(module.GlobalIndexSpace, *glb)
module.imports.Globals++
// In both cases below, index should be always 0 (according to the MVP)
// We check it against the length of the index space anyway.
case ExternalTable:
if int(index) >= len(importedModule.TableIndexSpace) {
return InvalidTableIndexError(index)
}
module.TableIndexSpace[0] = importedModule.TableIndexSpace[0]
module.imports.Tables++
case ExternalMemory:
if int(index) >= len(importedModule.LinearMemoryIndexSpace) {
return InvalidLinearMemoryIndexError(index)
}
module.LinearMemoryIndexSpace[0] = importedModule.LinearMemoryIndexSpace[0]
module.imports.Memories++
default:
return InvalidExternalError(exportEntry.Kind)
}
}
return nil
}

180
vendor/github.com/go-interpreter/wagon/wasm/index.go generated vendored Normal file
View File

@ -0,0 +1,180 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"fmt"
"reflect"
)
type InvalidTableIndexError uint32
func (e InvalidTableIndexError) Error() string {
return fmt.Sprintf("wasm: Invalid table to table index space: %d", uint32(e))
}
type InvalidValueTypeInitExprError struct {
Wanted reflect.Kind
Got reflect.Kind
}
func (e InvalidValueTypeInitExprError) Error() string {
return fmt.Sprintf("wasm: Wanted initializer expression to return %v value, got %v", e.Wanted, e.Got)
}
type InvalidLinearMemoryIndexError uint32
func (e InvalidLinearMemoryIndexError) Error() string {
return fmt.Sprintf("wasm: Invalid linear memory index: %d", uint32(e))
}
// Functions for populating and looking up entries in a module's index space.
// More info: http://webassembly.org/docs/modules/#function-index-space
func (m *Module) populateFunctions() error {
if m.Types == nil || m.Function == nil {
return nil
}
for codeIndex, typeIndex := range m.Function.Types {
if int(typeIndex) >= len(m.Types.Entries) {
return InvalidFunctionIndexError(typeIndex)
}
fn := Function{
Sig: &m.Types.Entries[typeIndex],
Body: &m.Code.Bodies[codeIndex],
}
m.FunctionIndexSpace = append(m.FunctionIndexSpace, fn)
}
funcs := make([]uint32, 0, len(m.Function.Types)+len(m.imports.Funcs))
funcs = append(funcs, m.imports.Funcs...)
funcs = append(funcs, m.Function.Types...)
m.Function.Types = funcs
return nil
}
// GetFunction returns a *Function, based on the function's index in
// the function index space. Returns nil when the index is invalid
func (m *Module) GetFunction(i int) *Function {
if i >= len(m.FunctionIndexSpace) || i < 0 {
return nil
}
return &m.FunctionIndexSpace[i]
}
func (m *Module) populateGlobals() error {
if m.Global == nil {
return nil
}
m.GlobalIndexSpace = append(m.GlobalIndexSpace, m.Global.Globals...)
logger.Printf("There are %d entries in the global index spaces.", len(m.GlobalIndexSpace))
return nil
}
// GetGlobal returns a *GlobalEntry, based on the global index space.
// Returns nil when the index is invalid
func (m *Module) GetGlobal(i int) *GlobalEntry {
if i >= len(m.GlobalIndexSpace) || i < 0 {
return nil
}
return &m.GlobalIndexSpace[i]
}
func (m *Module) populateTables() error {
if m.Table == nil || len(m.Table.Entries) == 0 || m.Elements == nil || len(m.Elements.Entries) == 0 {
return nil
}
for _, elem := range m.Elements.Entries {
// the MVP dictates that index should always be zero, we shuold
// probably check this
if int(elem.Index) >= len(m.TableIndexSpace) {
return InvalidTableIndexError(elem.Index)
}
val, err := m.ExecInitExpr(elem.Offset)
if err != nil {
return err
}
offset, ok := val.(int32)
if !ok {
return InvalidValueTypeInitExprError{reflect.Int32, reflect.TypeOf(offset).Kind()}
}
table := m.TableIndexSpace[int(elem.Index)]
if int(offset)+len(elem.Elems) > len(table) {
data := make([]uint32, int(offset)+len(elem.Elems))
copy(data[offset:], elem.Elems)
copy(data, table)
m.TableIndexSpace[int(elem.Index)] = data
} else {
copy(table[int(offset):], elem.Elems)
m.TableIndexSpace[int(elem.Index)] = table
}
}
logger.Printf("There are %d entries in the table index space.", len(m.TableIndexSpace))
return nil
}
// GetTableElement returns an element from the tableindex space indexed
// by the integer index. It returns an error if index is invalid.
func (m *Module) GetTableElement(index int) (uint32, error) {
if index >= len(m.TableIndexSpace[0]) {
return 0, InvalidTableIndexError(index)
}
return m.TableIndexSpace[0][index], nil
}
func (m *Module) populateLinearMemory() error {
if m.Data == nil || len(m.Data.Entries) == 0 {
return nil
}
// each module can only have a single linear memory in the MVP
for _, entry := range m.Data.Entries {
if entry.Index != 0 {
return InvalidLinearMemoryIndexError(entry.Index)
}
val, err := m.ExecInitExpr(entry.Offset)
if err != nil {
return err
}
offset, ok := val.(int32)
if !ok {
return InvalidValueTypeInitExprError{reflect.Int32, reflect.TypeOf(offset).Kind()}
}
memory := m.LinearMemoryIndexSpace[int(entry.Index)]
if int(offset)+len(entry.Data) > len(memory) {
data := make([]byte, int(offset)+len(entry.Data))
copy(data[offset:], entry.Data)
copy(data, memory)
m.LinearMemoryIndexSpace[int(entry.Index)] = data
} else {
copy(memory[int(offset):], entry.Data)
m.LinearMemoryIndexSpace[int(entry.Index)] = memory
}
}
return nil
}
func (m *Module) GetLinearMemoryData(index int) (byte, error) {
if index >= len(m.LinearMemoryIndexSpace[0]) {
return 0, InvalidLinearMemoryIndexError(uint32(index))
}
return m.LinearMemoryIndexSpace[0][index], nil
}

View File

@ -0,0 +1,172 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"github.com/go-interpreter/wagon/wasm/leb128"
)
const (
i32Const byte = 0x41
i64Const byte = 0x42
f32Const byte = 0x43
f64Const byte = 0x44
getGlobal byte = 0x23
end byte = 0x0b
)
var ErrEmptyInitExpr = errors.New("wasm: Initializer expression produces no value")
type InvalidInitExprOpError byte
func (e InvalidInitExprOpError) Error() string {
return fmt.Sprintf("wasm: Invalid opcode in initializer expression: %#x", byte(e))
}
type InvalidGlobalIndexError uint32
func (e InvalidGlobalIndexError) Error() string {
return fmt.Sprintf("wasm: Invalid index to global index space: %#x", uint32(e))
}
func readInitExpr(r io.Reader) ([]byte, error) {
b := make([]byte, 1)
buf := new(bytes.Buffer)
r = io.TeeReader(r, buf)
outer:
for {
_, err := io.ReadFull(r, b)
if err != nil {
return nil, err
}
switch b[0] {
case i32Const:
_, err := leb128.ReadVarint32(r)
if err != nil {
return nil, err
}
case i64Const:
_, err := leb128.ReadVarint64(r)
if err != nil {
return nil, err
}
case f32Const:
if _, err := readU32(r); err != nil {
return nil, err
}
case f64Const:
if _, err := readU64(r); err != nil {
return nil, err
}
case getGlobal:
_, err := leb128.ReadVarUint32(r)
if err != nil {
return nil, err
}
case end:
break outer
default:
return nil, InvalidInitExprOpError(b[0])
}
}
if buf.Len() == 0 {
return nil, ErrEmptyInitExpr
}
return buf.Bytes(), nil
}
// ExecInitExpr executes an initializer expression and returns an interface{} value
// which can either be int32, int64, float32 or float64.
// It returns an error if the expression is invalid, and nil when the expression
// yields no value.
func (m *Module) ExecInitExpr(expr []byte) (interface{}, error) {
var stack []uint64
var lastVal ValueType
r := bytes.NewReader(expr)
if r.Len() == 0 {
return nil, ErrEmptyInitExpr
}
for {
b, err := r.ReadByte()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
switch b {
case i32Const:
i, err := leb128.ReadVarint32(r)
if err != nil {
return nil, err
}
stack = append(stack, uint64(i))
lastVal = ValueTypeI32
case i64Const:
i, err := leb128.ReadVarint64(r)
if err != nil {
return nil, err
}
stack = append(stack, uint64(i))
lastVal = ValueTypeI64
case f32Const:
i, err := readU32(r)
if err != nil {
return nil, err
}
stack = append(stack, uint64(i))
lastVal = ValueTypeF32
case f64Const:
i, err := readU64(r)
if err != nil {
return nil, err
}
stack = append(stack, i)
lastVal = ValueTypeF64
case getGlobal:
index, err := leb128.ReadVarUint32(r)
if err != nil {
return nil, err
}
globalVar := m.GetGlobal(int(index))
if globalVar == nil {
return nil, InvalidGlobalIndexError(index)
}
lastVal = globalVar.Type.Type
case end:
break
default:
return nil, InvalidInitExprOpError(b)
}
}
if len(stack) == 0 {
return nil, nil
}
v := stack[len(stack)-1]
switch lastVal {
case ValueTypeI32:
return int32(v), nil
case ValueTypeI64:
return int64(v), nil
case ValueTypeF32:
return math.Float32frombits(uint32(v)), nil
case ValueTypeF64:
return math.Float64frombits(uint64(v)), nil
default:
panic(fmt.Sprintf("Invalid value type produced by initializer expression: %d", int8(lastVal)))
}
}

View File

@ -0,0 +1,30 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package readpos
import (
"io"
)
// ReadPos implements io.Reader and stores the current number of bytes read from
// the reader
type ReadPos struct {
R io.Reader
CurPos int64
}
// Read implements the io.Reader interface
func (r *ReadPos) Read(p []byte) (int, error) {
n, err := r.R.Read(p)
r.CurPos += int64(n)
return n, err
}
// ReadByte implements the io.ByteReader interface
func (r *ReadPos) ReadByte() (byte, error) {
p := make([]byte, 1)
_, err := r.R.Read(p)
return p[0], err
}

View File

@ -0,0 +1,66 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package readpos_test
import (
"bytes"
"io"
"testing"
"github.com/go-interpreter/wagon/wasm/internal/readpos"
)
func TestRead(t *testing.T) {
data := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
for i, test := range []struct {
r io.Reader
data []byte
want int
err error
}{
{
r: bytes.NewReader(data),
data: nil,
want: 0,
err: nil,
},
{
r: bytes.NewReader(nil),
data: nil,
want: 0,
err: io.EOF,
},
{
r: bytes.NewReader(nil),
data: make([]byte, 2),
want: 0,
err: io.EOF,
},
{
r: bytes.NewReader(data),
data: data,
want: len(data),
err: nil,
},
{
r: bytes.NewReader(data[:1]),
data: make([]byte, 2),
want: 1,
err: nil,
},
} {
r := readpos.ReadPos{R: test.r}
n, err := r.Read(test.data)
switch {
case err != test.err:
t.Errorf("test-#%d: got err=%v. want=%v", i, err, test.err)
continue
case n != test.want:
t.Errorf("test-#%d: got n=%v. want=%v", i, n, test.want)
case int(r.CurPos) != test.want:
t.Errorf("test-#%d: got pos=%v. want=%v", i, r.CurPos, test.want)
}
}
}

View File

@ -0,0 +1,92 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package leb128 provides functions for reading integer values encoded in the
// Little Endian Base 128 (LEB128) format: https://en.wikipedia.org/wiki/LEB128
package leb128
import (
"io"
)
// ReadVarUint32Size reads a LEB128 encoded unsigned 32-bit integer from r.
// It returns the integer value, the size of the encoded value (in bytes), and
// the error (if any).
func ReadVarUint32Size(r io.Reader) (res uint32, size uint, err error) {
b := make([]byte, 1)
var shift uint
for {
if _, err = io.ReadFull(r, b); err != nil {
return
}
size++
cur := uint32(b[0])
res |= (cur & 0x7f) << (shift)
if cur&0x80 == 0 {
return res, size, nil
}
shift += 7
}
}
// ReadVarUint32 reads a LEB128 encoded unsigned 32-bit integer from r, and
// returns the integer value, and the error (if any).
func ReadVarUint32(r io.Reader) (uint32, error) {
n, _, err := ReadVarUint32Size(r)
return n, err
}
// ReadVarint32Size reads a LEB128 encoded signed 32-bit integer from r, and
// returns the integer value, the size of the encoded value, and the error
// (if any)
func ReadVarint32Size(r io.Reader) (res int32, size uint, err error) {
res64, size, err := ReadVarint64Size(r)
res = int32(res64)
return
}
// ReadVarint32 reads a LEB128 encoded signed 32-bit integer from r, and
// returns the integer value, and the error (if any).
func ReadVarint32(r io.Reader) (int32, error) {
n, _, err := ReadVarint32Size(r)
return n, err
}
// ReadVarint64Size reads a LEB128 encoded signed 64-bit integer from r, and
// returns the integer value, the size of the encoded value, and the error
// (if any)
func ReadVarint64Size(r io.Reader) (res int64, size uint, err error) {
var shift uint
var sign int64 = -1
b := make([]byte, 1)
for {
if _, err = io.ReadFull(r, b); err != nil {
return
}
size++
cur := int64(b[0])
res |= (cur & 0x7f) << shift
shift += 7
sign <<= 7
if cur&0x80 == 0 {
break
}
}
if ((sign >> 1) & res) != 0 {
res |= sign
}
return res, size, nil
}
// ReadVarint64 reads a LEB128 encoded signed 64-bit integer from r, and
// returns the integer value, and the error (if any).
func ReadVarint64(r io.Reader) (int64, error) {
n, _, err := ReadVarint64Size(r)
return n, err
}

View File

@ -0,0 +1,31 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package leb128
import (
"bytes"
"testing"
)
func TestReadVarUint32(t *testing.T) {
n, err := ReadVarUint32(bytes.NewReader([]byte{0x80, 0x7f}))
if err != nil {
t.Fatal(err)
}
if n != uint32(16256) {
t.Fatalf("got = %d; want = %d", n, 16256)
}
}
func TestReadVarint32(t *testing.T) {
n, err := ReadVarint32(bytes.NewReader([]byte{0xFF, 0x7e}))
if err != nil {
t.Fatal(err)
}
if n != int32(-129) {
t.Fatalf("got = %d; want = %d", n, -129)
}
}

25
vendor/github.com/go-interpreter/wagon/wasm/log.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"io/ioutil"
"log"
"os"
)
var logger *log.Logger
func init() {
SetDebugMode(false)
}
func SetDebugMode(dbg bool) {
w := ioutil.Discard
if dbg {
w = os.Stderr
}
logger = log.New(w, "", log.Lshortfile)
}

144
vendor/github.com/go-interpreter/wagon/wasm/module.go generated vendored Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"errors"
"io"
"reflect"
"github.com/go-interpreter/wagon/wasm/internal/readpos"
)
var ErrInvalidMagic = errors.New("wasm: Invalid magic number")
const (
Magic uint32 = 0x6d736100
Version uint32 = 0x1
)
// Function represents an entry in the function index space of a module.
type Function struct {
Sig *FunctionSig
Body *FunctionBody
Host reflect.Value
}
// IsHost indicates whether this function is a host function as defined in:
// https://webassembly.github.io/spec/core/exec/modules.html#host-functions
func (fct *Function) IsHost() bool {
return fct.Host != reflect.Value{}
}
// Module represents a parsed WebAssembly module:
// http://webassembly.org/docs/modules/
type Module struct {
Version uint32
Types *SectionTypes
Import *SectionImports
Function *SectionFunctions
Table *SectionTables
Memory *SectionMemories
Global *SectionGlobals
Export *SectionExports
Start *SectionStartFunction
Elements *SectionElements
Code *SectionCode
Data *SectionData
// The function index space of the module
FunctionIndexSpace []Function
GlobalIndexSpace []GlobalEntry
// function indices into the global function space
// the limit of each table is its capacity (cap)
TableIndexSpace [][]uint32
LinearMemoryIndexSpace [][]byte
Other []Section // Other holds the custom sections if any
imports struct {
Funcs []uint32
Globals int
Tables int
Memories int
}
}
// NewModule creates a new empty module
func NewModule() *Module {
return &Module{
Types: &SectionTypes{},
Import: &SectionImports{},
Table: &SectionTables{},
Memory: &SectionMemories{},
Global: &SectionGlobals{},
Export: &SectionExports{},
Start: &SectionStartFunction{},
Elements: &SectionElements{},
Data: &SectionData{},
}
}
// ResolveFunc is a function that takes a module name and
// returns a valid resolved module.
type ResolveFunc func(name string) (*Module, error)
// ReadModule reads a module from the reader r. resolvePath must take a string
// and a return a reader to the module pointed to by the string.
func ReadModule(r io.Reader, resolvePath ResolveFunc) (*Module, error) {
reader := &readpos.ReadPos{
R: r,
CurPos: 0,
}
m := &Module{}
magic, err := readU32(reader)
if err != nil {
return nil, err
}
if magic != Magic {
return nil, ErrInvalidMagic
}
if m.Version, err = readU32(reader); err != nil {
return nil, err
}
for {
done, err := m.readSection(reader)
if err != nil {
return nil, err
} else if done {
break
}
}
m.LinearMemoryIndexSpace = make([][]byte, 1)
if m.Table != nil {
m.TableIndexSpace = make([][]uint32, int(len(m.Table.Entries)))
}
if m.Import != nil && resolvePath != nil {
err := m.resolveImports(resolvePath)
if err != nil {
return nil, err
}
}
for _, fn := range []func() error{
m.populateGlobals,
m.populateFunctions,
m.populateTables,
m.populateLinearMemory,
} {
if err := fn(); err != nil {
return nil, err
}
}
logger.Printf("There are %d entries in the function index space.", len(m.FunctionIndexSpace))
return m, nil
}

View File

@ -0,0 +1,39 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm_test
import (
"bytes"
"io/ioutil"
"path/filepath"
"testing"
"github.com/go-interpreter/wagon/wasm"
)
func TestReadModule(t *testing.T) {
fnames, err := filepath.Glob(filepath.Join("testdata", "*.wasm"))
if err != nil {
t.Fatal(err)
}
for _, fname := range fnames {
name := fname
t.Run(filepath.Base(name), func(t *testing.T) {
raw, err := ioutil.ReadFile(name)
if err != nil {
t.Fatal(err)
}
r := bytes.NewReader(raw)
m, err := wasm.ReadModule(r, nil)
if err != nil {
t.Fatalf("error reading module %v", err)
}
if m == nil {
t.Fatalf("error reading module: (nil *Module)")
}
})
}
}

View File

@ -0,0 +1,10 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
var (
Call = newPolymorphicOp(0x10, "call")
CallIndirect = newPolymorphicOp(0x11, "call_indirect")
)

View File

@ -0,0 +1,46 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"github.com/go-interpreter/wagon/wasm"
)
var (
I32Eqz = newOp(0x45, "i32.eqz", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Eq = newOp(0x46, "i32.eq", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Ne = newOp(0x47, "i32.ne", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32LtS = newOp(0x48, "i32.lt_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32LtU = newOp(0x49, "i32.lt_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32GtS = newOp(0x4a, "i32.gt_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32GtU = newOp(0x4b, "i32.gt_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32LeS = newOp(0x4c, "i32.le_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32LeU = newOp(0x4d, "i32.le_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32GeS = newOp(0x4e, "i32.ge_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32GeU = newOp(0x4f, "i32.ge_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I64Eqz = newOp(0x50, "i64.eqz", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64Eq = newOp(0x51, "i64.eq", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64Ne = newOp(0x52, "i64.ne", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64LtS = newOp(0x53, "i64.lt_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64LtU = newOp(0x54, "i64.lt_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64GtS = newOp(0x55, "i64.gt_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64GtU = newOp(0x56, "i64.gt_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64LeS = newOp(0x57, "i64.le_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64LeU = newOp(0x58, "i64.le_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64GeS = newOp(0x59, "i64.ge_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
I64GeU = newOp(0x5a, "i64.ge_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
F32Eq = newOp(0x5b, "f32.eq", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
F32Ne = newOp(0x5c, "f32.ne", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
F32Lt = newOp(0x5d, "f32.lt", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
F32Gt = newOp(0x5e, "f32.gt", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
F32Le = newOp(0x5f, "f32.le", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
F32Ge = newOp(0x60, "f32.ge", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
F64Eq = newOp(0x61, "f64.eq", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
F64Ne = newOp(0x62, "f64.ne", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
F64Lt = newOp(0x63, "f64.lt", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
F64Gt = newOp(0x64, "f64.gt", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
F64Le = newOp(0x65, "f64.le", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
F64Ge = newOp(0x66, "f64.ge", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
)

View File

@ -0,0 +1,16 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"github.com/go-interpreter/wagon/wasm"
)
var (
I32Const = newOp(0x41, "i32.const", nil, wasm.ValueTypeI32)
I64Const = newOp(0x42, "i64.const", nil, wasm.ValueTypeI64)
F32Const = newOp(0x43, "f32.const", nil, wasm.ValueTypeF32)
F64Const = newOp(0x44, "f64.const", nil, wasm.ValueTypeF64)
)

View File

@ -0,0 +1,23 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"github.com/go-interpreter/wagon/wasm"
)
var (
Unreachable = newOp(0x00, "unreachable", nil, noReturn)
Nop = newOp(0x01, "nop", nil, noReturn)
Block = newOp(0x02, "block", nil, noReturn)
Loop = newOp(0x03, "loop", nil, noReturn)
If = newOp(0x04, "if", []wasm.ValueType{wasm.ValueTypeI32}, noReturn)
Else = newOp(0x05, "else", nil, noReturn)
End = newOp(0x0b, "end", nil, noReturn)
Br = newPolymorphicOp(0x0c, "br")
BrIf = newOp(0x0d, "br_if", []wasm.ValueType{wasm.ValueTypeI32}, noReturn)
BrTable = newPolymorphicOp(0x0e, "br_table")
Return = newPolymorphicOp(0x0f, "return")
)

View File

@ -0,0 +1,64 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"regexp"
"github.com/go-interpreter/wagon/wasm"
)
var reCvrtOp = regexp.MustCompile(`(.+)\.(?:[a-z]|\_)+\/(.+)`)
func valType(s string) wasm.ValueType {
switch s {
case "i32":
return wasm.ValueTypeI32
case "i64":
return wasm.ValueTypeI64
case "f32":
return wasm.ValueTypeF32
case "f64":
return wasm.ValueTypeF64
default:
panic("Invalid value type string: " + s)
}
}
func newConversionOp(code byte, name string) byte {
matches := reCvrtOp.FindStringSubmatch(name)
if len(matches) == 0 {
panic(name + " is not a conversion operator")
}
returns := valType(matches[1])
param := valType(matches[2])
return newOp(code, name, []wasm.ValueType{param}, returns)
}
var (
I32WrapI64 = newConversionOp(0xa7, "i32.wrap/i64")
I32TruncSF32 = newConversionOp(0xa8, "i32.trunc_s/f32")
I32TruncUF32 = newConversionOp(0xa9, "i32.trunc_u/f32")
I32TruncSF64 = newConversionOp(0xaa, "i32.trunc_s/f64")
I32TruncUF64 = newConversionOp(0xab, "i32.trunc_u/f64")
I64ExtendSI32 = newConversionOp(0xac, "i64.extend_s/i32")
I64ExtendUI32 = newConversionOp(0xad, "i64.extend_u/i32")
I64TruncSF32 = newConversionOp(0xae, "i64.trunc_s/f32")
I64TruncUF32 = newConversionOp(0xaf, "i64.trunc_u/f32")
I64TruncSF64 = newConversionOp(0xb0, "i64.trunc_s/f64")
I64TruncUF64 = newConversionOp(0xb1, "i64.trunc_u/f64")
F32ConvertSI32 = newConversionOp(0xb2, "f32.convert_s/i32")
F32ConvertUI32 = newConversionOp(0xb3, "f32.convert_u/i32")
F32ConvertSI64 = newConversionOp(0xb4, "f32.convert_s/i64")
F32ConvertUI64 = newConversionOp(0xb5, "f32.convert_u/i64")
F32DemoteF64 = newConversionOp(0xb6, "f32.demote/f64")
F64ConvertSI32 = newConversionOp(0xb7, "f64.convert_s/i32")
F64ConvertUI32 = newConversionOp(0xb8, "f64.convert_u/i32")
F64ConvertSI64 = newConversionOp(0xb9, "f64.convert_s/i64")
F64ConvertUI64 = newConversionOp(0xba, "f64.convert_u/i64")
F64PromoteF32 = newConversionOp(0xbb, "f64.promote/f32")
)

View File

@ -0,0 +1,44 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"reflect"
"testing"
"github.com/go-interpreter/wagon/wasm"
)
func TestNewConversionOp(t *testing.T) {
origOps := ops
defer func() {
ops = origOps
}()
ops = [256]Op{}
testCases := []struct {
name string
args []wasm.ValueType
returns wasm.ValueType
}{
{"i32.wrap/i64", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI32},
{"i32.trunc_s/f32", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeI32},
}
for i, testCase := range testCases {
op, err := New(newConversionOp(byte(i), testCase.name))
if err != nil {
t.Fatalf("%s: unexpected error from New: %v", testCase.name, err)
}
if !reflect.DeepEqual(op.Args, testCase.args) {
t.Fatalf("%s: unexpected param types: got=%v, want=%v", testCase.name, op.Args, testCase.args)
}
if op.Returns != testCase.returns {
t.Fatalf("%s: unexpected return type: got=%v, want=%v", testCase.name, op.Returns, testCase.returns)
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"github.com/go-interpreter/wagon/wasm"
)
var (
I32Load = newOp(0x28, "i32.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I64Load = newOp(0x29, "i64.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
F32Load = newOp(0x2a, "f32.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeF32)
F64Load = newOp(0x2b, "f64.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeF64)
I32Load8s = newOp(0x2c, "i32.load8_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Load8u = newOp(0x2d, "i32.load8_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Load16s = newOp(0x2e, "i32.load16_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Load16u = newOp(0x2f, "i32.load16_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I64Load8s = newOp(0x30, "i64.load8_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
I64Load8u = newOp(0x31, "i64.load8_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
I64Load16s = newOp(0x32, "i64.load16_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
I64Load16u = newOp(0x33, "i64.load16_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
I64Load32s = newOp(0x34, "i64.load32_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
I64Load32u = newOp(0x35, "i64.load32_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
I32Store = newOp(0x36, "i32.store", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, noReturn)
I64Store = newOp(0x37, "i64.store", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
F32Store = newOp(0x38, "f32.store", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeI32}, noReturn)
F64Store = newOp(0x39, "f64.store", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeI32}, noReturn)
I32Store8 = newOp(0x3a, "i32.store8", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, noReturn)
I32Store16 = newOp(0x3b, "i32.store16", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, noReturn)
I64Store8 = newOp(0x3c, "i64.store8", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
I64Store16 = newOp(0x3d, "i64.store16", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
I64Store32 = newOp(0x3e, "i64.store32", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
CurrentMemory = newOp(0x3f, "current_memory", nil, wasm.ValueTypeI32)
GrowMemory = newOp(0x40, "grow_memory", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
)

View File

@ -0,0 +1,76 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"github.com/go-interpreter/wagon/wasm"
)
var (
I32Clz = newOp(0x67, "i32.clz", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Ctz = newOp(0x68, "i32.ctz", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Popcnt = newOp(0x69, "i32.popcnt", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Add = newOp(0x6a, "i32.add", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Sub = newOp(0x6b, "i32.sub", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Mul = newOp(0x6c, "i32.mul", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32DivS = newOp(0x6d, "i32.div_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32DivU = newOp(0x6e, "i32.div_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32RemS = newOp(0x6f, "i32.rem_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32RemU = newOp(0x70, "i32.rem_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32And = newOp(0x71, "i32.and", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Or = newOp(0x72, "i32.or", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Xor = newOp(0x73, "i32.xor", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Shl = newOp(0x74, "i32.shl", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32ShrS = newOp(0x75, "i32.shr_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32ShrU = newOp(0x76, "i32.shr_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Rotl = newOp(0x77, "i32.rotl", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I32Rotr = newOp(0x78, "i32.rotr", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
I64Clz = newOp(0x79, "i64.clz", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Ctz = newOp(0x7a, "i64.ctz", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Popcnt = newOp(0x7b, "i64.popcnt", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Add = newOp(0x7c, "i64.add", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Sub = newOp(0x7d, "i64.sub", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Mul = newOp(0x7e, "i64.mul", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64DivS = newOp(0x7f, "i64.div_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64DivU = newOp(0x80, "i64.div_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64RemS = newOp(0x81, "i64.div_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64RemU = newOp(0x82, "i64.rem_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64And = newOp(0x83, "i64.and", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Or = newOp(0x84, "i64.or", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Xor = newOp(0x85, "i64.xor", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Shl = newOp(0x86, "i64.shl", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64ShrS = newOp(0x87, "i64.shr_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64ShrU = newOp(0x88, "i64.shr_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Rotl = newOp(0x89, "i64.rotl", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
I64Rotr = newOp(0x8a, "i64.rotr", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
F32Abs = newOp(0x8b, "f32.abs", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Neg = newOp(0x8c, "f32.neg", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Ceil = newOp(0x8d, "f32.ceil", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Floor = newOp(0x8e, "f32.floor", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Trunc = newOp(0x8f, "f32.trunc", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Nearest = newOp(0x90, "f32.nearest", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Sqrt = newOp(0x91, "f32.sqrt", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Add = newOp(0x92, "f32.add", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Sub = newOp(0x93, "f32.sub", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Mul = newOp(0x94, "f32.mul", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Div = newOp(0x95, "f32.div", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Min = newOp(0x96, "f32.min", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Max = newOp(0x97, "f32.max", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F32Copysign = newOp(0x98, "f32.copysign", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
F64Abs = newOp(0x99, "f64.abs", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Neg = newOp(0x9a, "f64.neg", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Ceil = newOp(0x9b, "f64.ceil", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Floor = newOp(0x9c, "f64.floor", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Trunc = newOp(0x9d, "f64.trunc", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Nearest = newOp(0x9e, "f64.nearest", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Sqrt = newOp(0x9f, "f64.sqrt", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Add = newOp(0xa0, "f64.add", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Sub = newOp(0xa1, "f64.sub", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Mul = newOp(0xa2, "f64.mul", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Div = newOp(0xa3, "f64.div", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Min = newOp(0xa4, "f64.min", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Max = newOp(0xa5, "f64.max", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
F64Copysign = newOp(0xa6, "f64.copysign", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
)

View File

@ -0,0 +1,86 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package operators provides all operators used by WebAssembly bytecode,
// together with their parameter and return type(s).
package operators
import (
"fmt"
"github.com/go-interpreter/wagon/wasm"
)
var (
ops [256]Op // an array of Op values mapped by wasm opcodes, used by New().
noReturn = wasm.ValueType(wasm.BlockTypeEmpty)
)
// Op describes a WASM operator.
type Op struct {
Code byte // The single-byte opcode
Name string // The name of the operator
// Whether this operator is polymorphic.
// A polymorphic operator has a variable arity. call, call_indirect, and
// drop are examples of polymorphic operators.
Polymorphic bool
Args []wasm.ValueType // an array of value types used by the operator as arguments, is nil for polymorphic operators
Returns wasm.ValueType // the value returned (pushed) by the operator, is 0 for polymorphic operators
}
func (o Op) IsValid() bool {
return o.Name != ""
}
func newOp(code byte, name string, args []wasm.ValueType, returns wasm.ValueType) byte {
if ops[code].IsValid() {
panic(fmt.Errorf("Opcode %#x is already assigned to %s", code, ops[code].Name))
}
op := Op{
Code: code,
Name: name,
Polymorphic: false,
Args: args,
Returns: returns,
}
ops[code] = op
return code
}
func newPolymorphicOp(code byte, name string) byte {
if ops[code].IsValid() {
panic(fmt.Errorf("Opcode %#x is already assigned to %s", code, ops[code].Name))
}
op := Op{
Code: code,
Name: name,
Polymorphic: true,
}
ops[code] = op
return code
}
type InvalidOpcodeError byte
func (e InvalidOpcodeError) Error() string {
return fmt.Sprintf("Invalid opcode: %#x", byte(e))
}
// New returns the Op object for a valid given opcode.
// If code is invalid, an ErrInvalidOpcode is returned.
func New(code byte) (Op, error) {
var op Op
if int(code) >= len(ops) {
return op, InvalidOpcodeError(code)
}
op = ops[code]
if !op.IsValid() {
return op, InvalidOpcodeError(code)
}
return op, nil
}

View File

@ -0,0 +1,30 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"testing"
)
func TestNew(t *testing.T) {
op1, err := New(Unreachable)
if err != nil {
t.Fatalf("unexpected error from New: %v", err)
}
if op1.Name != "unreachable" {
t.Fatalf("0x00: unexpected Op name. got=%s, want=unrechable", op1.Name)
}
if !op1.IsValid() {
t.Fatalf("0x00: operator %v is invalid (should be valid)", op1)
}
op2, err := New(0xff)
if err == nil {
t.Fatalf("0xff: expected error while getting Op value")
}
if op2.IsValid() {
t.Fatalf("0xff: operator %v is valid (should be invalid)", op2)
}
}

View File

@ -0,0 +1,10 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
var (
Drop = newPolymorphicOp(0x1a, "drop")
Select = newPolymorphicOp(0x1b, "select")
)

View File

@ -0,0 +1,16 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
import (
"github.com/go-interpreter/wagon/wasm"
)
var (
I32ReinterpretF32 = newOp(0xbc, "i32.reinterpret/f32", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeI32)
I64ReinterpretF64 = newOp(0xbd, "i64.reinterpret/f64", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeI64)
F32ReinterpretI32 = newOp(0xbe, "f32.reinterpret/i32", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeF32)
F64ReinterpretI64 = newOp(0xbf, "f64.reinterpret/i64", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeF64)
)

View File

@ -0,0 +1,13 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package operators
var (
GetLocal = newPolymorphicOp(0x20, "get_local")
SetLocal = newPolymorphicOp(0x21, "set_local")
TeeLocal = newPolymorphicOp(0x22, "tee_local")
GetGlobal = newPolymorphicOp(0x23, "get_global")
SetGlobal = newPolymorphicOp(0x24, "set_global")
)

46
vendor/github.com/go-interpreter/wagon/wasm/read.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"encoding/binary"
"io"
)
func readBytes(r io.Reader, n int) ([]byte, error) {
bytes := make([]byte, n)
_, err := io.ReadFull(r, bytes)
if err != nil {
return bytes, err
}
return bytes, nil
}
func readString(r io.Reader, n int) (string, error) {
bytes, err := readBytes(r, n)
if err != nil {
return "", err
}
return string(bytes), nil
}
func readU32(r io.Reader) (uint32, error) {
var buf [4]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(buf[:]), nil
}
func readU64(r io.Reader) (uint64, error) {
var buf [8]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint64(buf[:]), nil
}

774
vendor/github.com/go-interpreter/wagon/wasm/section.go generated vendored Normal file
View File

@ -0,0 +1,774 @@
// Copyright 2017 The go-interpreter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wasm
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"github.com/go-interpreter/wagon/wasm/internal/readpos"
"github.com/go-interpreter/wagon/wasm/leb128"
)
// SectionID is a 1-byte code that encodes the section code of both known and custom sections.
type SectionID uint8
const (
SectionIDCustom SectionID = 0
SectionIDType SectionID = 1
SectionIDImport SectionID = 2
SectionIDFunction SectionID = 3
SectionIDTable SectionID = 4
SectionIDMemory SectionID = 5
SectionIDGlobal SectionID = 6
SectionIDExport SectionID = 7
SectionIDStart SectionID = 8
SectionIDElement SectionID = 9
SectionIDCode SectionID = 10
SectionIDData SectionID = 11
)
func (s SectionID) String() string {
n, ok := map[SectionID]string{
SectionIDCustom: "custom",
SectionIDType: "type",
SectionIDImport: "import",
SectionIDFunction: "function",
SectionIDTable: "table",
SectionIDMemory: "memory",
SectionIDGlobal: "global",
SectionIDExport: "export",
SectionIDStart: "start",
SectionIDElement: "element",
SectionIDCode: "code",
SectionIDData: "data",
}[s]
if !ok {
return "unknown"
}
return n
}
// Section is a declared section in a WASM module.
type Section struct {
Start int64
End int64
ID SectionID
// Size of this section in bytes
PayloadLen uint32
// Section name, empty if id != 0
Name string
Bytes []byte
}
type InvalidSectionIDError SectionID
func (e InvalidSectionIDError) Error() string {
return fmt.Sprintf("wasm: invalid section ID %d", e)
}
type InvalidCodeIndexError int
func (e InvalidCodeIndexError) Error() string {
return fmt.Sprintf("wasm: invalid index to code section: %d", int(e))
}
var ErrUnsupportedSection = errors.New("wasm: unsupported section")
type MissingSectionError SectionID
func (e MissingSectionError) Error() string {
return fmt.Sprintf("wasm: missing section %s", SectionID(e).String())
}
// reads a valid section from r. The first return value is true if and only if
// the module has been completely read.
func (m *Module) readSection(r *readpos.ReadPos) (bool, error) {
var err error
var id uint32
logger.Println("Reading section ID")
if id, err = leb128.ReadVarUint32(r); err != nil {
if err == io.EOF { // no bytes were read, the reader is empty
return true, nil
}
return false, err
}
s := Section{ID: SectionID(id)}
logger.Println("Reading payload length")
if s.PayloadLen, err = leb128.ReadVarUint32(r); err != nil {
return false, nil
}
payloadDataLen := s.PayloadLen
if s.ID == SectionIDCustom {
nameLen, nameLenSize, err := leb128.ReadVarUint32Size(r)
if err != nil {
return false, err
}
payloadDataLen -= uint32(nameLenSize)
if s.Name, err = readString(r, int(nameLen)); err != nil {
return false, err
}
payloadDataLen -= uint32(len(s.Name))
}
logger.Printf("Section payload length: %d", payloadDataLen)
s.Start = r.CurPos
sectionBytes := new(bytes.Buffer)
sectionBytes.Grow(int(payloadDataLen))
sectionReader := io.LimitReader(io.TeeReader(r, sectionBytes), int64(payloadDataLen))
switch s.ID {
case SectionIDCustom:
logger.Println("section custom")
if err = m.readSectionCustom(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Other = append(m.Other, s)
}
case SectionIDType:
logger.Println("section type")
if err = m.readSectionTypes(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Types.Section = s
}
case SectionIDImport:
logger.Println("section import")
if err = m.readSectionImports(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Import.Section = s
}
case SectionIDFunction:
logger.Println("section function")
if err = m.readSectionFunctions(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Function.Section = s
}
case SectionIDTable:
logger.Println("section table")
if err = m.readSectionTables(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Table.Section = s
}
case SectionIDMemory:
logger.Println("section memory")
if err = m.readSectionMemories(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Memory.Section = s
}
case SectionIDGlobal:
logger.Println("section global")
if err = m.readSectionGlobals(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Global.Section = s
}
case SectionIDExport:
logger.Println("section export")
if err = m.readSectionExports(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Export.Section = s
}
case SectionIDStart:
logger.Println("section start")
if err = m.readSectionStart(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Start.Section = s
}
case SectionIDElement:
logger.Println("section element")
if err = m.readSectionElements(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Elements.Section = s
}
case SectionIDCode:
logger.Println("section code")
if err = m.readSectionCode(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Code.Section = s
}
case SectionIDData:
logger.Println("section data")
if err = m.readSectionData(sectionReader); err == nil {
s.End = r.CurPos
s.Bytes = sectionBytes.Bytes()
m.Data.Section = s
}
default:
return false, InvalidSectionIDError(s.ID)
}
logger.Println(err)
return false, err
}
func (m *Module) readSectionCustom(r io.Reader) error {
_, err := io.Copy(ioutil.Discard, r)
return err
}
// SectionTypes declares all function signatures that will be used in a module.
type SectionTypes struct {
Section
Entries []FunctionSig
}
func (m *Module) readSectionTypes(r io.Reader) error {
s := &SectionTypes{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make([]FunctionSig, int(count))
for i := range s.Entries {
if s.Entries[i], err = readFunction(r); err != nil {
return err
}
}
m.Types = s
return nil
}
// SectionImports declares all imports that will be used in the module.
type SectionImports struct {
Section
Entries []ImportEntry
}
func (m *Module) readSectionImports(r io.Reader) error {
s := &SectionImports{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make([]ImportEntry, count)
for i := range s.Entries {
s.Entries[i], err = readImportEntry(r)
if err != nil {
return err
}
}
m.Import = s
return nil
}
func readImportEntry(r io.Reader) (ImportEntry, error) {
i := ImportEntry{}
modLen, err := leb128.ReadVarUint32(r)
if err != nil {
return i, err
}
if i.ModuleName, err = readString(r, int(modLen)); err != nil {
return i, err
}
fieldLen, err := leb128.ReadVarUint32(r)
if err != nil {
return i, err
}
if i.FieldName, err = readString(r, int(fieldLen)); err != nil {
return i, err
}
if i.Kind, err = readExternal(r); err != nil {
return i, err
}
switch i.Kind {
case ExternalFunction:
logger.Println("importing function")
var t uint32
t, err = leb128.ReadVarUint32(r)
i.Type = FuncImport{t}
case ExternalTable:
logger.Println("importing table")
var table *Table
table, err = readTable(r)
if table != nil {
i.Type = TableImport{*table}
}
case ExternalMemory:
logger.Println("importing memory")
var mem *Memory
mem, err = readMemory(r)
if mem != nil {
i.Type = MemoryImport{*mem}
}
case ExternalGlobal:
logger.Println("importing global var")
var gl *GlobalVar
gl, err = readGlobalVar(r)
if gl != nil {
i.Type = GlobalVarImport{*gl}
}
default:
return i, InvalidExternalError(i.Kind)
}
return i, err
}
// SectionFunction declares the signature of all functions defined in the module (in the code section)
type SectionFunctions struct {
Section
// Sequences of indices into (FunctionSignatues).Entries
Types []uint32
}
func (m *Module) readSectionFunctions(r io.Reader) error {
s := &SectionFunctions{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Types = make([]uint32, count)
for i := range s.Types {
t, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Types[i] = t
}
m.Function = s
return nil
}
// SectionTables describes all tables declared by a module.
type SectionTables struct {
Section
Entries []Table
}
func (m *Module) readSectionTables(r io.Reader) error {
s := &SectionTables{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make([]Table, count)
for i := range s.Entries {
t, err := readTable(r)
if err != nil {
return err
}
s.Entries[i] = *t
}
m.Table = s
return err
}
// SectionMemories describes all linaer memories used by a module.
type SectionMemories struct {
Section
Entries []Memory
}
func (m *Module) readSectionMemories(r io.Reader) error {
s := &SectionMemories{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make([]Memory, count)
for i := range s.Entries {
m, err := readMemory(r)
if err != nil {
return err
}
s.Entries[i] = *m
}
m.Memory = s
return err
}
// SectionGlobals defines the value of all global variables declared in a module.
type SectionGlobals struct {
Section
Globals []GlobalEntry
}
func (m *Module) readSectionGlobals(r io.Reader) error {
s := &SectionGlobals{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Globals = make([]GlobalEntry, count)
logger.Printf("%d global entries\n", count)
for i := range s.Globals {
s.Globals[i], err = readGlobalEntry(r)
if err != nil {
return err
}
}
m.Global = s
return nil
}
// GlobalEntry declares a global variable.
type GlobalEntry struct {
Type *GlobalVar // Type holds information about the value type and mutability of the variable
Init []byte // Init is an initializer expression that computes the initial value of the variable
}
func readGlobalEntry(r io.Reader) (e GlobalEntry, err error) {
logger.Println("reading global_type")
e.Type, err = readGlobalVar(r)
if err != nil {
logger.Println("Error!")
return
}
logger.Println("reading init expr")
// init_expr is delimited by opcode "end" (0x0b)
e.Init, err = readInitExpr(r)
logger.Println("Value:", e.Init)
return e, err
}
// SectionExports declares the export section of a module
type SectionExports struct {
Section
Entries map[string]ExportEntry
}
type DuplicateExportError string
func (e DuplicateExportError) Error() string {
return fmt.Sprintf("Duplicate export entry: %s", e)
}
func (m *Module) readSectionExports(r io.Reader) error {
s := &SectionExports{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make(map[string]ExportEntry, count)
for i := uint32(0); i < count; i++ {
entry, err := readExportEntry(r)
if err != nil {
return err
}
if _, exists := s.Entries[entry.FieldStr]; exists {
return DuplicateExportError(entry.FieldStr)
}
s.Entries[entry.FieldStr] = entry
}
m.Export = s
return nil
}
// ExportEntry represents an exported entry by the module
type ExportEntry struct {
FieldStr string
Kind External
Index uint32
}
func readExportEntry(r io.Reader) (ExportEntry, error) {
e := ExportEntry{}
fieldLen, err := leb128.ReadVarUint32(r)
if e.FieldStr, err = readString(r, int(fieldLen)); err != nil {
return e, err
}
if e.Kind, err = readExternal(r); err != nil {
return e, err
}
e.Index, err = leb128.ReadVarUint32(r)
return e, err
}
// SectionStartFunction represents the start function section.
type SectionStartFunction struct {
Section
Index uint32 // The index of the start function into the global index space.
}
func (m *Module) readSectionStart(r io.Reader) error {
s := &SectionStartFunction{}
var err error
s.Index, err = leb128.ReadVarUint32(r)
if err != nil {
return err
}
m.Start = s
return nil
}
// SectionElements describes the initial contents of a table's elements.
type SectionElements struct {
Section
Entries []ElementSegment
}
func (m *Module) readSectionElements(r io.Reader) error {
s := &SectionElements{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make([]ElementSegment, count)
for i := range s.Entries {
s.Entries[i], err = readElementSegment(r)
if err != nil {
return err
}
}
m.Elements = s
return nil
}
// ElementSegment describes a group of repeated elements that begin at a specified offset
type ElementSegment struct {
Index uint32 // The index into the global table space, should always be 0 in the MVP.
Offset []byte // initializer expression for computing the offset for placing elements, should return an i32 value
Elems []uint32
}
func readElementSegment(r io.Reader) (ElementSegment, error) {
s := ElementSegment{}
var err error
if s.Index, err = leb128.ReadVarUint32(r); err != nil {
return s, err
}
if s.Offset, err = readInitExpr(r); err != nil {
return s, err
}
numElems, err := leb128.ReadVarUint32(r)
if err != nil {
return s, err
}
s.Elems = make([]uint32, numElems)
for i := range s.Elems {
e, err := leb128.ReadVarUint32(r)
if err != nil {
return s, err
}
s.Elems[i] = e
}
return s, nil
}
// SectionCode describes the body for every function declared inside a module.
type SectionCode struct {
Section
Bodies []FunctionBody
}
func (m *Module) readSectionCode(r io.Reader) error {
s := &SectionCode{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Bodies = make([]FunctionBody, count)
logger.Printf("%d function bodies\n", count)
for i := range s.Bodies {
logger.Printf("Reading function %d\n", i)
if s.Bodies[i], err = readFunctionBody(r); err != nil {
return err
}
s.Bodies[i].Module = m
}
m.Code = s
if m.Function == nil || len(m.Function.Types) == 0 {
return MissingSectionError(SectionIDFunction)
}
if len(m.Function.Types) != len(s.Bodies) {
return errors.New("The number of entries in the function and code section are unequal")
}
if m.Types == nil {
return MissingSectionError(SectionIDType)
}
return nil
}
var ErrFunctionNoEnd = errors.New("Function body does not end with 0x0b (end)")
type FunctionBody struct {
Module *Module // The parent module containing this function body, for execution purposes
Locals []LocalEntry
Code []byte
}
func readFunctionBody(r io.Reader) (FunctionBody, error) {
f := FunctionBody{}
bodySize, err := leb128.ReadVarUint32(r)
if err != nil {
return f, err
}
body := make([]byte, bodySize)
if _, err = io.ReadFull(r, body); err != nil {
return f, err
}
bytesReader := bytes.NewBuffer(body)
localCount, err := leb128.ReadVarUint32(bytesReader)
if err != nil {
return f, err
}
f.Locals = make([]LocalEntry, localCount)
for i := range f.Locals {
if f.Locals[i], err = readLocalEntry(bytesReader); err != nil {
return f, err
}
}
logger.Printf("bodySize: %d, localCount: %d\n", bodySize, localCount)
code := bytesReader.Bytes()
logger.Printf("Read %d bytes for function body", len(code))
if code[len(code)-1] != end {
return f, ErrFunctionNoEnd
}
f.Code = code[:len(code)-1]
return f, nil
}
type LocalEntry struct {
Count uint32 // The total number of local variables of the given Type used in the function body
Type ValueType // The type of value stored by the variable
}
func readLocalEntry(r io.Reader) (LocalEntry, error) {
l := LocalEntry{}
var err error
l.Count, err = leb128.ReadVarUint32(r)
if err != nil {
return l, err
}
l.Type, err = readValueType(r)
if err != nil {
return l, err
}
return l, nil
}
// SectionData describes the intial values of a module's linear memory
type SectionData struct {
Section
Entries []DataSegment
}
func (m *Module) readSectionData(r io.Reader) error {
s := &SectionData{}
count, err := leb128.ReadVarUint32(r)
if err != nil {
return err
}
s.Entries = make([]DataSegment, count)
for i := range s.Entries {
if s.Entries[i], err = readDataSegment(r); err != nil {
return err
}
}
m.Data = s
return err
}
// DataSegment describes a group of repeated elements that begin at a specified offset in the linear memory
type DataSegment struct {
Index uint32 // The index into the global linear memory space, should always be 0 in the MVP.
Offset []byte // initializer expression for computing the offset for placing elements, should return an i32 value
Data []byte
}
func readDataSegment(r io.Reader) (DataSegment, error) {
s := DataSegment{}
var err error
if s.Index, err = leb128.ReadVarUint32(r); err != nil {
return s, err
}
if s.Offset, err = readInitExpr(r); err != nil {
return s, err
}
size, err := leb128.ReadVarUint32(r)
if err != nil {
return s, err
}
s.Data, err = readBytes(r, int(size))
return s, err
}

View File

@ -0,0 +1,2 @@
(module
)

View File

@ -0,0 +1,13 @@
(module
(func (export "add") (param $x f64) (param $y f64) (result f64) (f64.add (get_local $x) (get_local $y)))
(func (export "sub") (param $x f64) (param $y f64) (result f64) (f64.sub (get_local $x) (get_local $y)))
(func (export "mul") (param $x f64) (param $y f64) (result f64) (f64.mul (get_local $x) (get_local $y)))
(func (export "div") (param $x f64) (param $y f64) (result f64) (f64.div (get_local $x) (get_local $y)))
(func (export "sqrt") (param $x f64) (result f64) (f64.sqrt (get_local $x)))
(func (export "min") (param $x f64) (param $y f64) (result f64) (f64.min (get_local $x) (get_local $y)))
(func (export "max") (param $x f64) (param $y f64) (result f64) (f64.max (get_local $x) (get_local $y)))
(func (export "ceil") (param $x f64) (result f64) (f64.ceil (get_local $x)))
(func (export "floor") (param $x f64) (result f64) (f64.floor (get_local $x)))
(func (export "trunc") (param $x f64) (result f64) (f64.trunc (get_local $x)))
(func (export "nearest") (param $x f64) (result f64) (f64.nearest (get_local $x)))
)

Some files were not shown because too many files have changed in this diff Show More