vgo
This commit is contained in:
parent
6aa9a6d149
commit
7b073fd67e
|
@ -1,3 +1,3 @@
|
|||
include_rules
|
||||
|
||||
: *.go |> go build -o %o |> ../../bin/land
|
||||
: *.go |> vgo build -o %o |> ../../bin/land
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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=
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
npm install
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- "1.x"
|
||||
- "1.9"
|
||||
|
||||
script:
|
||||
- go test -v -race -cover ./...
|
|
@ -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.
|
|
@ -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.
|
|
@ -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}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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 })
|
|
@ -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
|
||||
}
|
|
@ -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...)
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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...)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -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() {}
|
|
@ -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()))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
377
vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go
generated
vendored
Normal file
377
vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go
generated
vendored
Normal 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
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()))
|
||||
}
|
1120
vendor/github.com/go-interpreter/wagon/exec/testdata/modules.json
generated
vendored
Normal file
1120
vendor/github.com/go-interpreter/wagon/exec/testdata/modules.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
50
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/address.wast
generated
vendored
Normal file
50
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/address.wast
generated
vendored
Normal 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)))
|
||||
)
|
||||
)
|
136
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/block.wast
generated
vendored
Normal file
136
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/block.wast
generated
vendored
Normal 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))
|
||||
)
|
||||
)
|
323
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/br.wast
generated
vendored
Normal file
323
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/br.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
148
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/br_if.wast
generated
vendored
Normal file
148
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/br_if.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
1229
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/br_table.wast
generated
vendored
Normal file
1229
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/br_table.wast
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/break-drop.wast
generated
vendored
Normal file
5
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/break-drop.wast
generated
vendored
Normal 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))))
|
||||
)
|
171
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/call_indirect.wast
generated
vendored
Normal file
171
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/call_indirect.wast
generated
vendored
Normal 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)))
|
||||
)
|
131
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/endianness.wast
generated
vendored
Normal file
131
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/endianness.wast
generated
vendored
Normal 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)))
|
||||
)
|
||||
)
|
82
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/fac.wast
generated
vendored
Normal file
82
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/fac.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
15
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/forward.wast
generated
vendored
Normal file
15
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/forward.wast
generated
vendored
Normal 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))))
|
||||
)
|
||||
)
|
||||
)
|
62
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/get_local.wast
generated
vendored
Normal file
62
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/get_local.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
27
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/globals.wast
generated
vendored
Normal file
27
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/globals.wast
generated
vendored
Normal 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)))
|
||||
)
|
132
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/if.wast
generated
vendored
Normal file
132
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/if.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
201
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/loop.wast
generated
vendored
Normal file
201
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/loop.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
53
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/memory_redundancy.wast
generated
vendored
Normal file
53
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/memory_redundancy.wast
generated
vendored
Normal 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))
|
||||
)
|
||||
)
|
5097
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/modules.json
generated
vendored
Normal file
5097
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/modules.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
582
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/names.wast
generated
vendored
Normal file
582
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/names.wast
generated
vendored
Normal 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 "A") (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 "1⁄2") (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))
|
||||
)
|
181
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/nop.wast
generated
vendored
Normal file
181
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/nop.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
12
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/resizing.wast
generated
vendored
Normal file
12
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/resizing.wast
generated
vendored
Normal 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))
|
||||
)
|
194
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/return.wast
generated
vendored
Normal file
194
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/return.wast
generated
vendored
Normal 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)))
|
||||
)
|
||||
)
|
29
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/select.wast
generated
vendored
Normal file
29
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/select.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
118
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/switch.wast
generated
vendored
Normal file
118
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/switch.wast
generated
vendored
Normal 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)
|
||||
)
|
||||
)
|
94
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/tee_local.wast
generated
vendored
Normal file
94
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/tee_local.wast
generated
vendored
Normal 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))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
10
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/traps_int_div.wast
generated
vendored
Normal file
10
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/traps_int_div.wast
generated
vendored
Normal 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))))
|
||||
)
|
10
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/traps_int_rem.wast
generated
vendored
Normal file
10
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/traps_int_rem.wast
generated
vendored
Normal 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))))
|
||||
)
|
18
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/traps_mem.wast
generated
vendored
Normal file
18
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/traps_mem.wast
generated
vendored
Normal 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))))
|
||||
)
|
157
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/unwind.wast
generated
vendored
Normal file
157
vendor/github.com/go-interpreter/wagon/exec/testdata/spec/unwind.wast
generated
vendored
Normal 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)))
|
||||
)
|
||||
)
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)))
|
||||
}
|
||||
}
|
30
vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go
generated
vendored
Normal file
30
vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go
generated
vendored
Normal 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
|
||||
}
|
66
vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos_test.go
generated
vendored
Normal file
66
vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
)
|
|
@ -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)
|
||||
)
|
|
@ -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)
|
||||
)
|
|
@ -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")
|
||||
)
|
|
@ -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")
|
||||
)
|
44
vendor/github.com/go-interpreter/wagon/wasm/operators/conv_test.go
generated
vendored
Normal file
44
vendor/github.com/go-interpreter/wagon/wasm/operators/conv_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
)
|
|
@ -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)
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
10
vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go
generated
vendored
Normal file
10
vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go
generated
vendored
Normal 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")
|
||||
)
|
|
@ -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)
|
||||
)
|
|
@ -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")
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
(module
|
||||
)
|
|
@ -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
Loading…
Reference in New Issue