Makes CacheNumInUint64 lazy and stops crashing in assemblyscript (#712)
* Makes CacheNumInUint64 lazy and stops crashing in assemblyscript This makes CacheNumInUint64 lazy so that all tests for function types don't need to handle it. This also changes the assemblyscript special functions so they don't crash when attempting to log. Finally, this refactors `wasm.Func` so that it can enclose the parameter names as it is more sensible than defining them elsewhere. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -38,6 +38,12 @@ import (
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
functionAbort = "abort"
|
||||
functionTrace = "trace"
|
||||
functionSeed = "seed"
|
||||
)
|
||||
|
||||
// Instantiate instantiates the "env" module used by AssemblyScript into the
|
||||
// runtime default namespace.
|
||||
//
|
||||
@@ -77,65 +83,33 @@ type FunctionExporter interface {
|
||||
|
||||
// NewFunctionExporter returns a FunctionExporter object with trace disabled.
|
||||
func NewFunctionExporter() FunctionExporter {
|
||||
return &functionExporter{traceMode: traceDisabled}
|
||||
return &functionExporter{abortFn: abortMessageEnabled, traceFn: traceDisabled}
|
||||
}
|
||||
|
||||
type traceMode int
|
||||
|
||||
const (
|
||||
traceDisabled traceMode = 0
|
||||
traceStdout traceMode = 1
|
||||
traceStderr traceMode = 2
|
||||
)
|
||||
|
||||
type functionExporter struct {
|
||||
abortMessageDisabled bool
|
||||
traceMode traceMode
|
||||
abortFn, traceFn *wasm.Func
|
||||
}
|
||||
|
||||
// WithAbortMessageDisabled implements FunctionExporter.WithAbortMessageDisabled
|
||||
func (e *functionExporter) WithAbortMessageDisabled() FunctionExporter {
|
||||
ret := *e // copy
|
||||
ret.abortMessageDisabled = true
|
||||
return &ret
|
||||
return &functionExporter{abortFn: abortMessageDisabled, traceFn: e.traceFn}
|
||||
}
|
||||
|
||||
// WithTraceToStdout implements FunctionExporter.WithTraceToStdout
|
||||
func (e *functionExporter) WithTraceToStdout() FunctionExporter {
|
||||
ret := *e // copy
|
||||
ret.traceMode = traceStdout
|
||||
return &ret
|
||||
return &functionExporter{abortFn: e.abortFn, traceFn: traceStdout}
|
||||
}
|
||||
|
||||
// WithTraceToStderr implements FunctionExporter.WithTraceToStderr
|
||||
func (e *functionExporter) WithTraceToStderr() FunctionExporter {
|
||||
ret := *e // copy
|
||||
ret.traceMode = traceStderr
|
||||
return &ret
|
||||
return &functionExporter{abortFn: e.abortFn, traceFn: traceStderr}
|
||||
}
|
||||
|
||||
// ExportFunctions implements FunctionExporter.ExportFunctions
|
||||
func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
|
||||
var abortFn fnAbort
|
||||
if e.abortMessageDisabled {
|
||||
abortFn = abort
|
||||
} else {
|
||||
abortFn = abortWithMessage
|
||||
}
|
||||
var traceFn interface{}
|
||||
switch e.traceMode {
|
||||
case traceDisabled:
|
||||
traceFn = traceNoop
|
||||
case traceStdout:
|
||||
traceFn = traceToStdout
|
||||
case traceStderr:
|
||||
traceFn = traceToStderr
|
||||
}
|
||||
builder.ExportFunction("abort", abortFn, "~lib/builtins/abort",
|
||||
"message", "fileName", "lineNumber", "columnNumber")
|
||||
builder.ExportFunction("trace", traceFn, "~lib/builtins/trace",
|
||||
"message", "nArgs", "arg0", "arg1", "arg2", "arg3", "arg4")
|
||||
builder.ExportFunction("seed", seed, "~lib/builtins/seed")
|
||||
builder.ExportFunction(functionAbort, e.abortFn)
|
||||
builder.ExportFunction(functionTrace, e.traceFn)
|
||||
builder.ExportFunction(functionSeed, seed)
|
||||
}
|
||||
|
||||
// abort is called on unrecoverable errors. This is typically present in Wasm
|
||||
@@ -150,18 +124,26 @@ func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
|
||||
// (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
|
||||
//
|
||||
// See https://github.com/AssemblyScript/assemblyscript/blob/fa14b3b03bd4607efa52aaff3132bea0c03a7989/std/assembly/wasi/index.ts#L18
|
||||
type fnAbort func(
|
||||
ctx context.Context, mod api.Module, message, fileName, lineNumber, columnNumber uint32,
|
||||
var abortMessageEnabled = wasm.NewGoFunc(
|
||||
"abort", "~lib/builtins/abort",
|
||||
[]string{"message", "fileName", "lineNumber", "columnNumber"},
|
||||
abortWithMessage,
|
||||
)
|
||||
|
||||
var abortMessageDisabled = abortMessageEnabled.WithGoFunc(abort)
|
||||
|
||||
// abortWithMessage implements fnAbort
|
||||
func abortWithMessage(
|
||||
ctx context.Context, mod api.Module, message, fileName, lineNumber, columnNumber uint32,
|
||||
) {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
msg := requireAssemblyScriptString(ctx, mod, "message", message)
|
||||
fn := requireAssemblyScriptString(ctx, mod, "fileName", fileName)
|
||||
_, _ = fmt.Fprintf(sysCtx.Stderr(), "%s at %s:%d:%d\n", msg, fn, lineNumber, columnNumber)
|
||||
mem := mod.Memory()
|
||||
// Don't panic if there was a problem reading the message
|
||||
if msg, msgOk := readAssemblyScriptString(ctx, mem, message); msgOk {
|
||||
if fn, fnOk := readAssemblyScriptString(ctx, mem, fileName); fnOk {
|
||||
_, _ = fmt.Fprintf(sysCtx.Stderr(), "%s at %s:%d:%d\n", msg, fn, lineNumber, columnNumber)
|
||||
}
|
||||
}
|
||||
abort(ctx, mod, message, fileName, lineNumber, columnNumber)
|
||||
}
|
||||
|
||||
@@ -180,25 +162,25 @@ func abort(
|
||||
panic(sys.NewExitError(mod.Name(), exitCode))
|
||||
}
|
||||
|
||||
// traceNoop implements trace in wasm to avoid host call overhead on no-op.
|
||||
var traceNoop = &wasm.Func{
|
||||
Type: wasm.MustFunctionType(traceToStdout),
|
||||
Code: &wasm.Code{Body: []byte{wasm.OpcodeEnd}},
|
||||
}
|
||||
// traceDisabled ignores the input.
|
||||
var traceDisabled = traceStdout.WithWasm([]byte{wasm.OpcodeEnd})
|
||||
|
||||
// traceToStdout implements trace to the configured Stdout.
|
||||
func traceToStdout(
|
||||
ctx context.Context, mod api.Module, message uint32, nArgs uint32, arg0, arg1, arg2, arg3, arg4 float64,
|
||||
) {
|
||||
traceTo(ctx, mod, message, nArgs, arg0, arg1, arg2, arg3, arg4, mod.(*wasm.CallContext).Sys.Stdout())
|
||||
}
|
||||
// traceStdout implements trace to the configured Stdout.
|
||||
var traceStdout = wasm.NewGoFunc(functionTrace, "~lib/builtins/trace",
|
||||
[]string{"message", "nArgs", "arg0", "arg1", "arg2", "arg3", "arg4"},
|
||||
func(
|
||||
ctx context.Context, mod api.Module, message uint32, nArgs uint32, arg0, arg1, arg2, arg3, arg4 float64,
|
||||
) {
|
||||
traceTo(ctx, mod, message, nArgs, arg0, arg1, arg2, arg3, arg4, mod.(*wasm.CallContext).Sys.Stdout())
|
||||
},
|
||||
)
|
||||
|
||||
// traceToStdout implements trace to the configured Stderr.
|
||||
func traceToStderr(
|
||||
// traceStderr implements trace to the configured Stderr.
|
||||
var traceStderr = traceStdout.WithGoFunc(func(
|
||||
ctx context.Context, mod api.Module, message uint32, nArgs uint32, arg0, arg1, arg2, arg3, arg4 float64,
|
||||
) {
|
||||
traceTo(ctx, mod, message, nArgs, arg0, arg1, arg2, arg3, arg4, mod.(*wasm.CallContext).Sys.Stderr())
|
||||
}
|
||||
})
|
||||
|
||||
// traceTo implements the function "trace" in AssemblyScript. Ex.
|
||||
// trace('Hello World!')
|
||||
@@ -212,9 +194,13 @@ func traceTo(
|
||||
ctx context.Context, mod api.Module, message uint32, nArgs uint32, arg0, arg1, arg2, arg3, arg4 float64,
|
||||
writer io.Writer,
|
||||
) {
|
||||
msg, ok := readAssemblyScriptString(ctx, mod.Memory(), message)
|
||||
if !ok {
|
||||
return // don't panic if unable to trace
|
||||
}
|
||||
var ret strings.Builder
|
||||
ret.WriteString("trace: ")
|
||||
ret.WriteString(requireAssemblyScriptString(ctx, mod, "message", message))
|
||||
ret.WriteString(msg)
|
||||
if nArgs >= 1 {
|
||||
ret.WriteString(" ")
|
||||
ret.WriteString(formatFloat(arg0))
|
||||
@@ -236,9 +222,7 @@ func traceTo(
|
||||
ret.WriteString(formatFloat(arg4))
|
||||
}
|
||||
ret.WriteByte('\n')
|
||||
if _, err := writer.Write([]byte(ret.String())); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, _ = writer.Write([]byte(ret.String())) // don't crash if trace logging fails
|
||||
}
|
||||
|
||||
func formatFloat(f float64) string {
|
||||
@@ -253,30 +237,29 @@ func formatFloat(f float64) string {
|
||||
// (import "env" "seed" (func $~lib/builtins/seed (result f64)))
|
||||
//
|
||||
// See https://github.com/AssemblyScript/assemblyscript/blob/fa14b3b03bd4607efa52aaff3132bea0c03a7989/std/assembly/wasi/index.ts#L111
|
||||
func seed(mod api.Module) float64 {
|
||||
randSource := mod.(*wasm.CallContext).Sys.RandSource()
|
||||
v, err := ieee754.DecodeFloat64(randSource)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error reading random seed: %w", err))
|
||||
}
|
||||
return v
|
||||
}
|
||||
var seed = wasm.NewGoFunc(functionSeed, "~lib/builtins/seed", []string{},
|
||||
func(mod api.Module) float64 {
|
||||
randSource := mod.(*wasm.CallContext).Sys.RandSource()
|
||||
v, err := ieee754.DecodeFloat64(randSource)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error reading random seed: %w", err))
|
||||
}
|
||||
return v
|
||||
},
|
||||
)
|
||||
|
||||
// requireAssemblyScriptString reads a UTF-16 string created by AssemblyScript.
|
||||
func requireAssemblyScriptString(ctx context.Context, mod api.Module, fieldName string, offset uint32) string {
|
||||
// readAssemblyScriptString reads a UTF-16 string created by AssemblyScript.
|
||||
func readAssemblyScriptString(ctx context.Context, mem api.Memory, offset uint32) (string, bool) {
|
||||
// Length is four bytes before pointer.
|
||||
byteCount, ok := mod.Memory().ReadUint32Le(ctx, offset-4)
|
||||
byteCount, ok := mem.ReadUint32Le(ctx, offset-4)
|
||||
if !ok || byteCount%2 != 0 {
|
||||
return "", false
|
||||
}
|
||||
buf, ok := mem.Read(ctx, offset, byteCount)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("out of memory reading %s", fieldName))
|
||||
return "", false
|
||||
}
|
||||
if byteCount%2 != 0 {
|
||||
panic(fmt.Errorf("invalid UTF-16 reading %s", fieldName))
|
||||
}
|
||||
buf, ok := mod.Memory().Read(ctx, offset, byteCount)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("out of memory reading %s", fieldName))
|
||||
}
|
||||
return decodeUTF16(buf)
|
||||
return decodeUTF16(buf), true
|
||||
}
|
||||
|
||||
func decodeUTF16(b []byte) string {
|
||||
|
||||
@@ -15,7 +15,8 @@ import (
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
. "github.com/tetratelabs/wazero/experimental"
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/watzero"
|
||||
"github.com/tetratelabs/wazero/internal/u64"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
@@ -23,25 +24,18 @@ import (
|
||||
var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
|
||||
|
||||
func TestAbort(t *testing.T) {
|
||||
var stderr bytes.Buffer
|
||||
mod, r := requireModule(t, wazero.NewModuleConfig().WithStderr(&stderr))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
abortFn fnAbort
|
||||
exporter FunctionExporter
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "enabled",
|
||||
abortFn: abortWithMessage,
|
||||
exporter: NewFunctionExporter(),
|
||||
expected: "message at filename:1:2\n",
|
||||
},
|
||||
{
|
||||
name: "disabled",
|
||||
abortFn: abort,
|
||||
exporter: NewFunctionExporter().WithAbortMessageDisabled(),
|
||||
expected: "",
|
||||
},
|
||||
@@ -51,15 +45,19 @@ func TestAbort(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer stderr.Reset()
|
||||
var stderr bytes.Buffer
|
||||
mod, r, log := requireModule(t, tc.exporter, wazero.NewModuleConfig().WithStderr(&stderr))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
messageOff, filenameOff := writeAbortMessageAndFileName(t, mod.Memory(), encodeUTF16("message"), encodeUTF16("filename"))
|
||||
|
||||
err := require.CapturePanic(func() {
|
||||
tc.abortFn(testCtx, mod, messageOff, filenameOff, 1, 2)
|
||||
})
|
||||
_, err := mod.ExportedFunction(functionAbort).
|
||||
Call(testCtx, uint64(messageOff), uint64(filenameOff), uint64(1), uint64(2))
|
||||
require.Error(t, err)
|
||||
require.Equal(t, uint32(255), err.(*sys.ExitError).ExitCode())
|
||||
require.Equal(t, `
|
||||
==> env.~lib/builtins/abort(message=4,fileName=22,lineNumber=1,columnNumber=2)
|
||||
`, "\n"+log.String())
|
||||
|
||||
require.Equal(t, tc.expected, stderr.String())
|
||||
})
|
||||
@@ -68,7 +66,7 @@ func TestAbort(t *testing.T) {
|
||||
|
||||
func TestAbort_Error(t *testing.T) {
|
||||
var stderr bytes.Buffer
|
||||
mod, r := requireModule(t, wazero.NewModuleConfig().WithStderr(&stderr))
|
||||
mod, r, log := requireModule(t, NewFunctionExporter(), wazero.NewModuleConfig().WithStderr(&stderr))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
tests := []struct {
|
||||
@@ -81,16 +79,16 @@ func TestAbort_Error(t *testing.T) {
|
||||
name: "bad message",
|
||||
messageUTF16: encodeUTF16("message")[:5],
|
||||
fileNameUTF16: encodeUTF16("filename"),
|
||||
expectedLog: `==> env.~lib/builtins/abort(message=4,fileName=13,lineNumber=1,columnNumber=2)
|
||||
<== ()
|
||||
expectedLog: `
|
||||
==> env.~lib/builtins/abort(message=4,fileName=13,lineNumber=1,columnNumber=2)
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "bad filename",
|
||||
messageUTF16: encodeUTF16("message"),
|
||||
fileNameUTF16: encodeUTF16("filename")[:5],
|
||||
expectedLog: `==> env.~lib/builtins/abort(message=4,fileName=22,lineNumber=1,columnNumber=2)
|
||||
<== ()
|
||||
expectedLog: `
|
||||
==> env.~lib/builtins/abort(message=4,fileName=22,lineNumber=1,columnNumber=2)
|
||||
`,
|
||||
},
|
||||
}
|
||||
@@ -99,25 +97,35 @@ func TestAbort_Error(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
defer stderr.Reset()
|
||||
|
||||
messageOff, filenameOff := writeAbortMessageAndFileName(t, mod.Memory(), tc.messageUTF16, tc.fileNameUTF16)
|
||||
|
||||
// Since abort panics, any opcodes afterwards cannot be reached.
|
||||
_ = require.CapturePanic(func() {
|
||||
abortWithMessage(testCtx, mod, messageOff, filenameOff, 1, 2)
|
||||
})
|
||||
_, err := mod.ExportedFunction(functionAbort).
|
||||
Call(testCtx, uint64(messageOff), uint64(filenameOff), uint64(1), uint64(2))
|
||||
require.Error(t, err)
|
||||
require.Equal(t, uint32(255), err.(*sys.ExitError).ExitCode())
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
|
||||
require.Equal(t, "", stderr.String()) // nothing output if strings fail to read.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeed(t *testing.T) {
|
||||
mod, r := requireModule(t, wazero.NewModuleConfig().
|
||||
WithRandSource(bytes.NewReader([]byte{0, 1, 2, 3, 4, 5, 6, 7})))
|
||||
b := []byte{0, 1, 2, 3, 4, 5, 6, 7}
|
||||
mod, r, log := requireModule(t, NewFunctionExporter(), wazero.NewModuleConfig().WithRandSource(bytes.NewReader(b)))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
require.Equal(t, 7.949928895127363e-275, seed(mod))
|
||||
ret, err := mod.ExportedFunction(functionSeed).Call(testCtx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `
|
||||
==> env.~lib/builtins/seed()
|
||||
<== (7.949928895127363e-275)
|
||||
`, "\n"+log.String())
|
||||
|
||||
require.Equal(t, b, u64.LeBytes(ret[0]))
|
||||
}
|
||||
|
||||
func TestSeed_error(t *testing.T) {
|
||||
@@ -127,14 +135,18 @@ func TestSeed_error(t *testing.T) {
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "not 8 bytes",
|
||||
source: bytes.NewReader([]byte{0, 1}),
|
||||
expectedErr: `error reading random seed: unexpected EOF`,
|
||||
name: "not 8 bytes",
|
||||
source: bytes.NewReader([]byte{0, 1}),
|
||||
expectedErr: `error reading random seed: unexpected EOF (recovered by wazero)
|
||||
wasm stack trace:
|
||||
env.~lib/builtins/seed() f64`,
|
||||
},
|
||||
{
|
||||
name: "error reading",
|
||||
source: iotest.ErrReader(errors.New("ice cream")),
|
||||
expectedErr: `error reading random seed: ice cream`,
|
||||
name: "error reading",
|
||||
source: iotest.ErrReader(errors.New("ice cream")),
|
||||
expectedErr: `error reading random seed: ice cream (recovered by wazero)
|
||||
wasm stack trace:
|
||||
env.~lib/builtins/seed() f64`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -142,11 +154,14 @@ func TestSeed_error(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mod, r := requireModule(t, wazero.NewModuleConfig().WithRandSource(tc.source))
|
||||
mod, r, log := requireModule(t, NewFunctionExporter(), wazero.NewModuleConfig().WithRandSource(tc.source))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
err := require.CapturePanic(func() { seed(mod) })
|
||||
_, err := mod.ExportedFunction(functionSeed).Call(testCtx)
|
||||
require.EqualError(t, err, tc.expectedErr)
|
||||
require.Equal(t, `
|
||||
==> env.~lib/builtins/seed()
|
||||
`, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -154,13 +169,17 @@ func TestSeed_error(t *testing.T) {
|
||||
// TestFunctionExporter_Trace ensures the trace output is according to configuration.
|
||||
func TestFunctionExporter_Trace(t *testing.T) {
|
||||
noArgs := []uint64{4, 0, 0, 0, 0, 0, 0}
|
||||
noArgsLog := `==> env.~lib/builtins/trace(message=4,nArgs=0,arg0=0,arg1=0,arg2=0,arg3=0,arg4=0)
|
||||
noArgsLog := `
|
||||
==> env.~lib/builtins/trace(message=4,nArgs=0,arg0=0,arg1=0,arg2=0,arg3=0,arg4=0)
|
||||
<== ()
|
||||
`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
exporter FunctionExporter
|
||||
params []uint64
|
||||
message []byte
|
||||
outErr bool
|
||||
expected, expectedLog string
|
||||
}{
|
||||
{
|
||||
@@ -190,7 +209,8 @@ func TestFunctionExporter_Trace(t *testing.T) {
|
||||
exporter: NewFunctionExporter().WithTraceToStdout(),
|
||||
params: []uint64{4, 1, api.EncodeF64(1), 0, 0, 0, 0},
|
||||
expected: "trace: hello 1\n",
|
||||
expectedLog: `==> env.~lib/builtins/trace(message=4,nArgs=1,arg0=1,arg1=0,arg2=0,arg3=0,arg4=0)
|
||||
expectedLog: `
|
||||
==> env.~lib/builtins/trace(message=4,nArgs=1,arg0=1,arg1=0,arg2=0,arg3=0,arg4=0)
|
||||
<== ()
|
||||
`,
|
||||
},
|
||||
@@ -199,7 +219,8 @@ func TestFunctionExporter_Trace(t *testing.T) {
|
||||
exporter: NewFunctionExporter().WithTraceToStdout(),
|
||||
params: []uint64{4, 2, api.EncodeF64(1), api.EncodeF64(2), 0, 0, 0},
|
||||
expected: "trace: hello 1,2\n",
|
||||
expectedLog: `==> env.~lib/builtins/trace(message=4,nArgs=2,arg0=1,arg1=2,arg2=0,arg3=0,arg4=0)
|
||||
expectedLog: `
|
||||
==> env.~lib/builtins/trace(message=4,nArgs=2,arg0=1,arg1=2,arg2=0,arg3=0,arg4=0)
|
||||
<== ()
|
||||
`,
|
||||
},
|
||||
@@ -216,81 +237,24 @@ func TestFunctionExporter_Trace(t *testing.T) {
|
||||
api.EncodeF64(5),
|
||||
},
|
||||
expected: "trace: hello 1,2,3.3,4.4,5\n",
|
||||
expectedLog: `==> env.~lib/builtins/trace(message=4,nArgs=5,arg0=1,arg1=2,arg2=3.3,arg3=4.4,arg4=5)
|
||||
expectedLog: `
|
||||
==> env.~lib/builtins/trace(message=4,nArgs=5,arg0=1,arg1=2,arg2=3.3,arg3=4.4,arg4=5)
|
||||
<== ()
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var stderr, functionLog bytes.Buffer
|
||||
|
||||
// Set context to one that has an experimental listener
|
||||
ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, NewLoggingListenerFactory(&functionLog))
|
||||
|
||||
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter())
|
||||
defer r.Close(ctx)
|
||||
|
||||
envBuilder := r.NewModuleBuilder("env")
|
||||
tc.exporter.ExportFunctions(envBuilder)
|
||||
_, err := envBuilder.Instantiate(ctx, r)
|
||||
require.NoError(t, err)
|
||||
|
||||
traceWasm, err := watzero.Wat2Wasm(`(module
|
||||
(import "env" "trace" (func $~lib/builtins/trace (param i32 i32 f64 f64 f64 f64 f64)))
|
||||
(memory 1 1)
|
||||
(export "trace" (func 0))
|
||||
)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
code, err := r.CompileModule(ctx, traceWasm, wazero.NewCompileConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
config := wazero.NewModuleConfig()
|
||||
if strings.Contains("ToStderr", tc.name) {
|
||||
config = config.WithStderr(&stderr)
|
||||
} else {
|
||||
config = config.WithStdout(&stderr)
|
||||
}
|
||||
|
||||
mod, err := r.InstantiateModule(ctx, code, config)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := encodeUTF16("hello")
|
||||
ok := mod.Memory().WriteUint32Le(ctx, 0, uint32(len(message)))
|
||||
require.True(t, ok)
|
||||
ok = mod.Memory().Write(ctx, uint32(4), message)
|
||||
require.True(t, ok)
|
||||
|
||||
_, err = mod.ExportedFunction("trace").Call(ctx, tc.params...)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, stderr.String())
|
||||
require.Equal(t, tc.expectedLog, functionLog.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrace_error(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
message []byte
|
||||
stderr io.Writer
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "not 8 bytes",
|
||||
exporter: NewFunctionExporter().WithTraceToStderr(),
|
||||
message: encodeUTF16("hello")[:5],
|
||||
stderr: &bytes.Buffer{},
|
||||
expectedErr: `invalid UTF-16 reading message`,
|
||||
params: noArgs,
|
||||
expectedLog: noArgsLog,
|
||||
},
|
||||
{
|
||||
name: "error writing",
|
||||
message: encodeUTF16("hello"),
|
||||
stderr: &errWriter{err: errors.New("ice cream")},
|
||||
expectedErr: `ice cream`,
|
||||
exporter: NewFunctionExporter().WithTraceToStderr(),
|
||||
outErr: true,
|
||||
params: noArgs,
|
||||
expectedLog: noArgsLog,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -298,32 +262,45 @@ func TestTrace_error(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mod, r := requireModule(t, wazero.NewModuleConfig().WithStderr(tc.stderr))
|
||||
var out bytes.Buffer
|
||||
|
||||
config := wazero.NewModuleConfig()
|
||||
if strings.Contains("ToStderr", tc.name) {
|
||||
config = config.WithStderr(&out)
|
||||
} else {
|
||||
config = config.WithStdout(&out)
|
||||
}
|
||||
if tc.outErr {
|
||||
config = config.WithStderr(&errWriter{err: errors.New("ice cream")})
|
||||
}
|
||||
|
||||
mod, r, log := requireModule(t, tc.exporter, config)
|
||||
defer r.Close(testCtx)
|
||||
|
||||
ok := mod.Memory().WriteUint32Le(testCtx, 0, uint32(len(tc.message)))
|
||||
message := tc.message
|
||||
if message == nil {
|
||||
message = encodeUTF16("hello")
|
||||
}
|
||||
ok := mod.Memory().WriteUint32Le(testCtx, 0, uint32(len(message)))
|
||||
require.True(t, ok)
|
||||
ok = mod.Memory().Write(testCtx, uint32(4), tc.message)
|
||||
ok = mod.Memory().Write(testCtx, uint32(4), message)
|
||||
require.True(t, ok)
|
||||
|
||||
err := require.CapturePanic(func() {
|
||||
traceToStderr(testCtx, mod, 4, 0, 0, 0, 0, 0, 0)
|
||||
})
|
||||
require.EqualError(t, err, tc.expectedErr)
|
||||
_, err := mod.ExportedFunction(functionTrace).Call(testCtx, tc.params...)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, out.String())
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_requireAssemblyScriptString(t *testing.T) {
|
||||
var stderr bytes.Buffer
|
||||
mod, r := requireModule(t, wazero.NewModuleConfig().WithStderr(&stderr))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
func Test_readAssemblyScriptString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
memory func(context.Context, api.Memory)
|
||||
offset int
|
||||
expected, expectedErr string
|
||||
name string
|
||||
memory func(context.Context, api.Memory)
|
||||
offset int
|
||||
expected string
|
||||
expectedOk bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
@@ -332,8 +309,9 @@ func Test_requireAssemblyScriptString(t *testing.T) {
|
||||
b := encodeUTF16("hello")
|
||||
memory.Write(testCtx, 4, b)
|
||||
},
|
||||
offset: 4,
|
||||
expected: "hello",
|
||||
offset: 4,
|
||||
expected: "hello",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
name: "can't read size",
|
||||
@@ -341,8 +319,8 @@ func Test_requireAssemblyScriptString(t *testing.T) {
|
||||
b := encodeUTF16("hello")
|
||||
memory.Write(testCtx, 0, b)
|
||||
},
|
||||
offset: 0, // will attempt to read size from offset -4
|
||||
expectedErr: "out of memory reading message",
|
||||
offset: 0, // will attempt to read size from offset -4
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
name: "odd size",
|
||||
@@ -351,8 +329,8 @@ func Test_requireAssemblyScriptString(t *testing.T) {
|
||||
b := encodeUTF16("hello")
|
||||
memory.Write(testCtx, 4, b)
|
||||
},
|
||||
offset: 4,
|
||||
expectedErr: "invalid UTF-16 reading message",
|
||||
offset: 4,
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
name: "can't read string",
|
||||
@@ -361,8 +339,8 @@ func Test_requireAssemblyScriptString(t *testing.T) {
|
||||
b := encodeUTF16("hello")
|
||||
memory.Write(testCtx, 4, b)
|
||||
},
|
||||
offset: 4,
|
||||
expectedErr: "out of memory reading message",
|
||||
offset: 4,
|
||||
expectedOk: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -370,17 +348,12 @@ func Test_requireAssemblyScriptString(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.memory(testCtx, mod.Memory())
|
||||
mem := wasm.NewMemoryInstance(&wasm.Memory{Min: 1, Cap: 1, Max: 1})
|
||||
tc.memory(testCtx, mem)
|
||||
|
||||
if tc.expectedErr != "" {
|
||||
err := require.CapturePanic(func() {
|
||||
_ = requireAssemblyScriptString(testCtx, mod, "message", uint32(tc.offset))
|
||||
})
|
||||
require.EqualError(t, err, tc.expectedErr)
|
||||
} else {
|
||||
s := requireAssemblyScriptString(testCtx, mod, "message", uint32(tc.offset))
|
||||
require.Equal(t, tc.expected, s)
|
||||
}
|
||||
s, ok := readAssemblyScriptString(testCtx, mem, uint32(tc.offset))
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -421,15 +394,21 @@ func (w *errWriter) Write([]byte) (int, error) {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
func requireModule(t *testing.T, config wazero.ModuleConfig) (api.Module, api.Closer) {
|
||||
func requireModule(t *testing.T, fns FunctionExporter, config wazero.ModuleConfig) (api.Module, api.Closer, *bytes.Buffer) {
|
||||
var log bytes.Buffer
|
||||
|
||||
// Set context to one that has an experimental listener
|
||||
ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, NewLoggingListenerFactory(&log))
|
||||
|
||||
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter())
|
||||
|
||||
compiled, err := r.NewModuleBuilder(t.Name()).
|
||||
ExportMemoryWithMax("memory", 1, 1).
|
||||
Compile(testCtx, wazero.NewCompileConfig())
|
||||
builder := r.NewModuleBuilder("env").
|
||||
ExportMemoryWithMax("memory", 1, 1)
|
||||
fns.ExportFunctions(builder)
|
||||
compiled, err := builder.Compile(ctx, wazero.NewCompileConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
mod, err := r.InstantiateModule(testCtx, compiled, config)
|
||||
mod, err := r.InstantiateModule(ctx, compiled, config)
|
||||
require.NoError(t, err)
|
||||
return mod, r
|
||||
return mod, r, &log
|
||||
}
|
||||
|
||||
@@ -324,6 +324,8 @@ func (b *moduleBuilder) Compile(ctx context.Context, cConfig CompileConfig) (Com
|
||||
module, err := wasm.NewHostModule(b.moduleName, b.nameToGoFunc, b.funcToNames, b.nameToMemory, b.nameToGlobal, b.r.enabledFeatures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if err = module.Validate(b.r.enabledFeatures); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &compiledModule{module: module, compiledEngine: b.r.store.Engine}
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{GoFunc: &fnUint32_uint32}},
|
||||
@@ -70,7 +70,7 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{GoFunc: &fnUint32_uint32}},
|
||||
@@ -90,7 +90,7 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{GoFunc: &fnUint64_uint32}},
|
||||
@@ -110,8 +110,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0, 1},
|
||||
CodeSection: []*wasm.Code{{GoFunc: &fnUint32_uint32}, {GoFunc: &fnUint64_uint32}},
|
||||
@@ -134,8 +134,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0, 1},
|
||||
CodeSection: []*wasm.Code{{GoFunc: &fnUint32_uint32}, {GoFunc: &fnUint64_uint32}},
|
||||
@@ -159,8 +159,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0, 1},
|
||||
CodeSection: []*wasm.Code{{GoFunc: &fnUint32_uint32}, {GoFunc: &fnUint64_uint32}},
|
||||
@@ -448,6 +448,9 @@ func TestNewModuleBuilder_Instantiate_Errors(t *testing.T) {
|
||||
// requireHostModuleEquals is redefined from internal/wasm/host_test.go to avoid an import cycle extracting it.
|
||||
func requireHostModuleEquals(t *testing.T, expected, actual *wasm.Module) {
|
||||
// `require.Equal(t, expected, actual)` fails reflect pointers don't match, so brute compare:
|
||||
for _, tp := range expected.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
require.Equal(t, expected.TypeSection, actual.TypeSection)
|
||||
require.Equal(t, expected.ImportSection, actual.ImportSection)
|
||||
require.Equal(t, expected.FunctionSection, actual.FunctionSection)
|
||||
|
||||
@@ -51,8 +51,7 @@ type functionExporter struct{}
|
||||
|
||||
// ExportFunctions implements FunctionExporter.ExportFunctions
|
||||
func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
|
||||
builder.ExportFunction("emscripten_notify_memory_growth", emscriptenNotifyMemoryGrowth,
|
||||
"emscripten_notify_memory_growth", "memory_index")
|
||||
builder.ExportFunction(notifyMemoryGrowth.Name, notifyMemoryGrowth)
|
||||
}
|
||||
|
||||
// emscriptenNotifyMemoryGrowth is called when wasm is compiled with
|
||||
@@ -70,7 +69,12 @@ func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
|
||||
//
|
||||
// See https://github.com/emscripten-core/emscripten/blob/3.1.16/system/lib/standalone/standalone.c#L118
|
||||
// and https://emscripten.org/docs/api_reference/emscripten.h.html#abi-functions
|
||||
var emscriptenNotifyMemoryGrowth = &wasm.Func{
|
||||
Type: &wasm.FunctionType{Params: []wasm.ValueType{wasm.ValueTypeI32}},
|
||||
Code: &wasm.Code{Body: []byte{wasm.OpcodeEnd}},
|
||||
const functionNotifyMemoryGrowth = "emscripten_notify_memory_growth"
|
||||
|
||||
var notifyMemoryGrowth = &wasm.Func{
|
||||
ExportNames: []string{functionNotifyMemoryGrowth},
|
||||
Name: functionNotifyMemoryGrowth,
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
ParamNames: []string{"memory_index"},
|
||||
Code: &wasm.Code{Body: []byte{wasm.OpcodeEnd}},
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ func newExample() *wasm.Module {
|
||||
f32, i32, i64 := wasm.ValueTypeF32, wasm.ValueTypeI32, wasm.ValueTypeI64
|
||||
return &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{ParamNumInUint64: 0, ResultNumInUint64: 0},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i64}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{f32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i64}},
|
||||
{Params: []wasm.ValueType{f32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}},
|
||||
},
|
||||
ImportSection: []*wasm.Import{
|
||||
{
|
||||
|
||||
@@ -30,14 +30,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
input: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 1,
|
||||
},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 4,
|
||||
ResultNumInUint64: 1,
|
||||
},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -45,14 +39,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
name: "type and import section",
|
||||
input: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 1,
|
||||
},
|
||||
{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 1,
|
||||
},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
|
||||
},
|
||||
ImportSection: []*wasm.Import{
|
||||
{
|
||||
|
||||
@@ -96,7 +96,5 @@ func decodeFunctionType(enabledFeatures wasm.Features, r *bytes.Reader) (*wasm.F
|
||||
Params: paramTypes,
|
||||
Results: resultTypes,
|
||||
}
|
||||
|
||||
ret.CacheNumInUint64()
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -23,52 +23,52 @@ func TestFunctionType(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "one param no result",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32}},
|
||||
expected: []byte{0x60, 1, i32, 0},
|
||||
},
|
||||
{
|
||||
name: "no param one result",
|
||||
input: &wasm.FunctionType{Results: []wasm.ValueType{i32}, ResultNumInUint64: 1},
|
||||
input: &wasm.FunctionType{Results: []wasm.ValueType{i32}},
|
||||
expected: []byte{0x60, 0, 1, i32},
|
||||
},
|
||||
{
|
||||
name: "one param one result",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i32}},
|
||||
expected: []byte{0x60, 1, i64, 1, i32},
|
||||
},
|
||||
{
|
||||
name: "two params no result",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, ParamNumInUint64: 2},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}},
|
||||
expected: []byte{0x60, 2, i32, i64, 0},
|
||||
},
|
||||
{
|
||||
name: "two param one result",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{i32}},
|
||||
expected: []byte{0x60, 2, i32, i64, 1, i32},
|
||||
},
|
||||
{
|
||||
name: "no param two results",
|
||||
input: &wasm.FunctionType{Results: []wasm.ValueType{i32, i64}, ResultNumInUint64: 2},
|
||||
input: &wasm.FunctionType{Results: []wasm.ValueType{i32, i64}},
|
||||
expected: []byte{0x60, 0, 2, i32, i64},
|
||||
},
|
||||
{
|
||||
name: "one param two results",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i32, i64}, ParamNumInUint64: 1, ResultNumInUint64: 2},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i32, i64}},
|
||||
expected: []byte{0x60, 1, i64, 2, i32, i64},
|
||||
},
|
||||
{
|
||||
name: "two param two results",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{i32, i64}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{i32, i64}},
|
||||
expected: []byte{0x60, 2, i32, i64, 2, i32, i64},
|
||||
},
|
||||
{
|
||||
name: "two param two results with funcrefs",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, funcRef}, Results: []wasm.ValueType{funcRef, i64}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, funcRef}, Results: []wasm.ValueType{funcRef, i64}},
|
||||
expected: []byte{0x60, 2, i32, funcRef, 2, funcRef, i64},
|
||||
},
|
||||
{
|
||||
name: "two param two results with externrefs",
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, externRef}, Results: []wasm.ValueType{externRef, i64}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
input: &wasm.FunctionType{Params: []wasm.ValueType{i32, externRef}, Results: []wasm.ValueType{externRef, i64}},
|
||||
expected: []byte{0x60, 2, i32, externRef, 2, externRef, i64},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -148,17 +148,6 @@ func newModuleVal(m api.Module) reflect.Value {
|
||||
return val
|
||||
}
|
||||
|
||||
// MustFunctionType returns the function type corresponding to the function
|
||||
// signature or panics if invalid.
|
||||
func MustFunctionType(fn interface{}) *FunctionType {
|
||||
fnV := reflect.ValueOf(fn)
|
||||
_, ft, err := getFunctionType(&fnV)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ft
|
||||
}
|
||||
|
||||
// getFunctionType returns the function type corresponding to the function signature or errs if invalid.
|
||||
func getFunctionType(fn *reflect.Value) (fk FunctionKind, ft *FunctionType, err error) {
|
||||
p := fn.Type()
|
||||
@@ -181,8 +170,6 @@ func getFunctionType(fn *reflect.Value) (fk FunctionKind, ft *FunctionType, err
|
||||
rCount := p.NumOut()
|
||||
|
||||
ft = &FunctionType{Params: make([]ValueType, p.NumIn()-pOffset), Results: make([]ValueType, rCount)}
|
||||
ft.CacheNumInUint64()
|
||||
|
||||
for i := 0; i < len(ft.Params); i++ {
|
||||
pI := p.In(i + pOffset)
|
||||
if t, ok := getTypeOf(pI.Kind()); ok {
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestGetFunctionType(t *testing.T) {
|
||||
name: "all supported params and i32 result",
|
||||
inputFunc: func(uint32, uint64, float32, float64, uintptr) uint32 { return 0 },
|
||||
expectedKind: FunctionKindGoNoContext,
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}, ParamNumInUint64: 5, ResultNumInUint64: 1},
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}},
|
||||
},
|
||||
{
|
||||
name: "all supported params and all supported results",
|
||||
@@ -58,28 +58,27 @@ func TestGetFunctionType(t *testing.T) {
|
||||
},
|
||||
expectedKind: FunctionKindGoNoContext,
|
||||
expectedType: &FunctionType{
|
||||
Params: []ValueType{i32, i64, f32, f64, externref},
|
||||
Results: []ValueType{i32, i64, f32, f64, externref},
|
||||
ParamNumInUint64: 5, ResultNumInUint64: 5,
|
||||
Params: []ValueType{i32, i64, f32, f64, externref},
|
||||
Results: []ValueType{i32, i64, f32, f64, externref},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all supported params and i32 result - wasm.Module",
|
||||
inputFunc: func(api.Module, uint32, uint64, float32, float64, uintptr) uint32 { return 0 },
|
||||
expectedKind: FunctionKindGoModule,
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}, ParamNumInUint64: 5, ResultNumInUint64: 1},
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}},
|
||||
},
|
||||
{
|
||||
name: "all supported params and i32 result - context.Context",
|
||||
inputFunc: func(context.Context, uint32, uint64, float32, float64, uintptr) uint32 { return 0 },
|
||||
expectedKind: FunctionKindGoContext,
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}, ParamNumInUint64: 5, ResultNumInUint64: 1},
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}},
|
||||
},
|
||||
{
|
||||
name: "all supported params and i32 result - context.Context and api.Module",
|
||||
inputFunc: func(context.Context, api.Module, uint32, uint64, float32, float64, uintptr) uint32 { return 0 },
|
||||
expectedKind: FunctionKindGoContextModule,
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}, ParamNumInUint64: 5, ResultNumInUint64: 1},
|
||||
expectedType: &FunctionType{Params: []ValueType{i32, i64, f32, f64, externref}, Results: []ValueType{i32}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -10,15 +10,59 @@ import (
|
||||
)
|
||||
|
||||
// Func is a function with an inlined type, typically used for NewHostModule.
|
||||
// Any corresponding FunctionType will be reused or added to the Module.
|
||||
type Func struct {
|
||||
// Type is the equivalent function in the SectionIDType.
|
||||
// This will resolve to an existing or new element.
|
||||
Type *FunctionType
|
||||
// ExportNames is equivalent to the same method on api.FunctionDefinition.
|
||||
ExportNames []string
|
||||
|
||||
// Name is equivalent to the same method on api.FunctionDefinition.
|
||||
Name string
|
||||
|
||||
// ParamTypes is equivalent to the same method on api.FunctionDefinition.
|
||||
ParamTypes []ValueType
|
||||
|
||||
// ParamNames is equivalent to the same method on api.FunctionDefinition.
|
||||
ParamNames []string
|
||||
|
||||
// ResultTypes is equivalent to the same method on api.FunctionDefinition.
|
||||
ResultTypes []ValueType
|
||||
|
||||
// Code is the equivalent function in the SectionIDCode.
|
||||
Code *Code
|
||||
}
|
||||
|
||||
// NewGoFunc returns a Func for the given parameters or panics.
|
||||
func NewGoFunc(exportName string, name string, paramNames []string, fn interface{}) *Func {
|
||||
fnV := reflect.ValueOf(fn)
|
||||
_, ft, err := getFunctionType(&fnV)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &Func{
|
||||
ExportNames: []string{exportName},
|
||||
Name: name,
|
||||
ParamTypes: ft.Params,
|
||||
ResultTypes: ft.Results,
|
||||
ParamNames: paramNames,
|
||||
Code: &Code{GoFunc: &fnV},
|
||||
}
|
||||
}
|
||||
|
||||
// WithGoFunc returns a copy of the function, replacing its Code.GoFunc.
|
||||
func (f *Func) WithGoFunc(fn interface{}) *Func {
|
||||
ret := *f
|
||||
fnV := reflect.ValueOf(fn)
|
||||
ret.Code = &Code{GoFunc: &fnV}
|
||||
return &ret
|
||||
}
|
||||
|
||||
// WithWasm returns a copy of the function, replacing its Code.Body.
|
||||
func (f *Func) WithWasm(body []byte) *Func {
|
||||
ret := *f
|
||||
ret.Code = &Code{Body: body}
|
||||
return &ret
|
||||
}
|
||||
|
||||
// NewHostModule is defined internally for use in WASI tests and to keep the code size in the root directory small.
|
||||
func NewHostModule(
|
||||
moduleName string,
|
||||
@@ -90,67 +134,73 @@ func addFuncs(
|
||||
funcToNames map[string][]string,
|
||||
enabledFeatures Features,
|
||||
) (err error) {
|
||||
funcCount := uint32(len(nameToGoFunc))
|
||||
funcNames := make([]string, 0, funcCount)
|
||||
if m.NameSection == nil {
|
||||
m.NameSection = &NameSection{}
|
||||
}
|
||||
moduleName := m.NameSection.ModuleName
|
||||
nameToFunc := make(map[string]*Func, len(nameToGoFunc))
|
||||
funcNames := make([]string, len(nameToFunc))
|
||||
for k, v := range nameToGoFunc {
|
||||
if hf, ok := v.(*Func); !ok {
|
||||
fn := reflect.ValueOf(v)
|
||||
_, ft, ftErr := getFunctionType(&fn)
|
||||
if ftErr != nil {
|
||||
return fmt.Errorf("func[%s.%s] %w", moduleName, k, ftErr)
|
||||
}
|
||||
hf = &Func{
|
||||
ExportNames: []string{k},
|
||||
Name: k,
|
||||
ParamTypes: ft.Params,
|
||||
ResultTypes: ft.Results,
|
||||
Code: &Code{GoFunc: &fn},
|
||||
}
|
||||
if names := funcToNames[k]; names != nil {
|
||||
namesLen := len(names)
|
||||
if namesLen > 1 && namesLen-1 != len(ft.Params) {
|
||||
return fmt.Errorf("func[%s.%s] has %d params, but %d param names", moduleName, k, namesLen-1, len(ft.Params))
|
||||
}
|
||||
hf.Name = names[0]
|
||||
hf.ParamNames = names[1:]
|
||||
}
|
||||
nameToFunc[k] = hf
|
||||
funcNames = append(funcNames, k)
|
||||
} else {
|
||||
nameToFunc[hf.Name] = hf
|
||||
funcNames = append(funcNames, hf.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort names for consistent iteration
|
||||
sort.Strings(funcNames)
|
||||
|
||||
funcCount := uint32(len(nameToFunc))
|
||||
m.NameSection.FunctionNames = make([]*NameAssoc, 0, funcCount)
|
||||
m.FunctionSection = make([]Index, 0, funcCount)
|
||||
m.CodeSection = make([]*Code, 0, funcCount)
|
||||
m.FunctionDefinitionSection = make([]*FunctionDefinition, 0, funcCount)
|
||||
|
||||
// Sort names for consistent iteration
|
||||
for k := range nameToGoFunc {
|
||||
funcNames = append(funcNames, k)
|
||||
}
|
||||
sort.Strings(funcNames)
|
||||
|
||||
for idx := Index(0); idx < funcCount; idx++ {
|
||||
exportName := funcNames[idx]
|
||||
debugName := wasmdebug.FuncName(moduleName, exportName, idx)
|
||||
|
||||
gf := nameToGoFunc[exportName]
|
||||
var ft *FunctionType
|
||||
if hf, ok := gf.(*Func); ok {
|
||||
ft = hf.Type
|
||||
m.CodeSection = append(m.CodeSection, hf.Code)
|
||||
} else {
|
||||
fn := reflect.ValueOf(gf)
|
||||
_, ft, err = getFunctionType(&fn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("func[%s] %w", debugName, err)
|
||||
}
|
||||
m.CodeSection = append(m.CodeSection, &Code{GoFunc: &fn})
|
||||
idx := Index(0)
|
||||
for _, name := range funcNames {
|
||||
hf := nameToFunc[name]
|
||||
debugName := wasmdebug.FuncName(moduleName, name, idx)
|
||||
typeIdx, typeErr := m.maybeAddType(hf.ParamTypes, hf.ResultTypes, enabledFeatures)
|
||||
if typeErr != nil {
|
||||
return fmt.Errorf("func[%s] %v", debugName, typeErr)
|
||||
}
|
||||
|
||||
m.FunctionSection = append(m.FunctionSection, m.maybeAddType(ft))
|
||||
|
||||
names := funcToNames[exportName]
|
||||
namesLen := len(names)
|
||||
if namesLen > 1 && namesLen-1 != len(ft.Params) {
|
||||
return fmt.Errorf("func[%s] has %d params, but %d param names", debugName, namesLen-1, len(ft.Params))
|
||||
m.FunctionSection = append(m.FunctionSection, typeIdx)
|
||||
m.CodeSection = append(m.CodeSection, hf.Code)
|
||||
for _, export := range hf.ExportNames {
|
||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeFunc, Name: export, Index: idx})
|
||||
}
|
||||
if len(ft.Results) > 1 {
|
||||
// Guard >1.0 feature multi-value
|
||||
if err = enabledFeatures.Require(FeatureMultiValue); err != nil {
|
||||
err = fmt.Errorf("func[%s] multiple result types invalid as %v", debugName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeFunc, Name: exportName, Index: idx})
|
||||
if namesLen > 0 {
|
||||
m.NameSection.FunctionNames = append(m.NameSection.FunctionNames, &NameAssoc{Index: idx, Name: names[0]})
|
||||
m.NameSection.FunctionNames = append(m.NameSection.FunctionNames, &NameAssoc{Index: idx, Name: hf.Name})
|
||||
if len(hf.ParamNames) > 0 {
|
||||
localNames := &NameMapAssoc{Index: idx}
|
||||
for i, n := range names[1:] {
|
||||
for i, n := range hf.ParamNames {
|
||||
localNames.NameMap = append(localNames.NameMap, &NameAssoc{Index: Index(i), Name: n})
|
||||
}
|
||||
m.NameSection.LocalNames = append(m.NameSection.LocalNames, localNames)
|
||||
} else {
|
||||
m.NameSection.FunctionNames = append(m.NameSection.FunctionNames, &NameAssoc{Index: idx, Name: exportName})
|
||||
}
|
||||
idx++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -199,14 +249,21 @@ func addGlobals(m *Module, globals map[string]*Global) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Module) maybeAddType(ft *FunctionType) Index {
|
||||
func (m *Module) maybeAddType(params, results []ValueType, enabledFeatures Features) (Index, error) {
|
||||
if len(results) > 1 {
|
||||
// Guard >1.0 feature multi-value
|
||||
if err := enabledFeatures.Require(FeatureMultiValue); err != nil {
|
||||
return 0, fmt.Errorf("multiple result types invalid as %v", err)
|
||||
}
|
||||
}
|
||||
for i, t := range m.TypeSection {
|
||||
if t.EqualsSignature(ft.Params, ft.Results) {
|
||||
return Index(i)
|
||||
if t.EqualsSignature(params, results) {
|
||||
return Index(i), nil
|
||||
}
|
||||
}
|
||||
|
||||
result := m.SectionElementCount(SectionIDType)
|
||||
m.TypeSection = append(m.TypeSection, ft)
|
||||
return result
|
||||
toAdd := &FunctionType{Params: params, Results: results}
|
||||
m.TypeSection = append(m.TypeSection, toAdd)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ func swap(x, y uint32) (uint32, uint32) {
|
||||
}
|
||||
|
||||
func TestNewHostModule(t *testing.T) {
|
||||
i32 := ValueTypeI32
|
||||
|
||||
a := wasiAPI{}
|
||||
functionArgsSizesGet := "args_sizes_get"
|
||||
fnArgsSizesGet := reflect.ValueOf(a.ArgsSizesGet)
|
||||
@@ -65,8 +63,8 @@ func TestNewHostModule(t *testing.T) {
|
||||
},
|
||||
expected: &Module{
|
||||
TypeSection: []*FunctionType{
|
||||
{Params: []ValueType{i32, i32}, Results: []ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{Params: []ValueType{i32, i32, i32, i32}, Results: []ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []ValueType{i32, i32}, Results: []ValueType{i32}},
|
||||
{Params: []ValueType{i32, i32, i32, i32}, Results: []ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []Index{0, 1},
|
||||
CodeSection: []*Code{{GoFunc: &fnArgsSizesGet}, {GoFunc: &fnFdWrite}},
|
||||
@@ -90,7 +88,7 @@ func TestNewHostModule(t *testing.T) {
|
||||
functionSwap: swap,
|
||||
},
|
||||
expected: &Module{
|
||||
TypeSection: []*FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}, ParamNumInUint64: 2, ResultNumInUint64: 2}},
|
||||
TypeSection: []*FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}},
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{{GoFunc: &fnSwap}},
|
||||
ExportSection: []*Export{{Name: "swap", Type: ExternTypeFunc, Index: 0}},
|
||||
@@ -151,7 +149,7 @@ func TestNewHostModule(t *testing.T) {
|
||||
},
|
||||
expected: &Module{
|
||||
TypeSection: []*FunctionType{
|
||||
{Params: []ValueType{i32, i32}, Results: []ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{Params: []ValueType{i32, i32}, Results: []ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{{GoFunc: &fnArgsSizesGet}},
|
||||
|
||||
@@ -227,6 +227,10 @@ func (m *Module) TypeOfFunction(funcIdx Index) *FunctionType {
|
||||
}
|
||||
|
||||
func (m *Module) Validate(enabledFeatures Features) error {
|
||||
for _, tp := range m.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
|
||||
if err := m.validateStartSection(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -325,7 +329,9 @@ func (m *Module) validateFunctions(enabledFeatures Features, functions []Index,
|
||||
if typeIndex >= typeCount {
|
||||
return fmt.Errorf("invalid %s: type section index %d out of range", m.funcDesc(SectionIDFunction, Index(idx)), typeIndex)
|
||||
}
|
||||
|
||||
if m.CodeSection[idx].GoFunc != nil {
|
||||
continue
|
||||
}
|
||||
if err = m.validateFunction(enabledFeatures, Index(idx), functions, globals, memory, tables, declaredFuncIndexes); err != nil {
|
||||
return fmt.Errorf("invalid %s: %w", m.funcDesc(SectionIDFunction, Index(idx)), err)
|
||||
}
|
||||
@@ -667,17 +673,21 @@ type FunctionType struct {
|
||||
}
|
||||
|
||||
func (f *FunctionType) CacheNumInUint64() {
|
||||
for _, tp := range f.Params {
|
||||
f.ParamNumInUint64++
|
||||
if tp == ValueTypeV128 {
|
||||
if f.ParamNumInUint64 == 0 {
|
||||
for _, tp := range f.Params {
|
||||
f.ParamNumInUint64++
|
||||
if tp == ValueTypeV128 {
|
||||
f.ParamNumInUint64++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tp := range f.Results {
|
||||
f.ResultNumInUint64++
|
||||
if tp == ValueTypeV128 {
|
||||
if f.ResultNumInUint64 == 0 {
|
||||
for _, tp := range f.Results {
|
||||
f.ResultNumInUint64++
|
||||
if tp == ValueTypeV128 {
|
||||
f.ResultNumInUint64++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,10 +143,6 @@ func DecodeModule(
|
||||
if names.ModuleName == "" && names.FunctionNames == nil && names.LocalNames == nil {
|
||||
module.NameSection = nil
|
||||
}
|
||||
|
||||
for _, tp := range module.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
input: "(module (type (func (param i32 i32) (result i32 i32))))",
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -72,7 +72,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
input: "(module (type (func (param i32) (param i32) (result i32) (result i32))))",
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -362,8 +362,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
v_v,
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
},
|
||||
ImportSection: []*wasm.Import{
|
||||
{
|
||||
@@ -549,7 +549,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
input: "(module (import \"\" \"\" (func (param i32 i32) (param $v i32) (param i64) (param $t f32))))",
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i64, f32}, ParamNumInUint64: 5},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i64, f32}},
|
||||
},
|
||||
ImportSection: []*wasm.Import{{Type: wasm.ExternTypeFunc, DescFunc: 0}},
|
||||
NameSection: &wasm.NameSection{
|
||||
@@ -569,8 +569,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
v_v,
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 9, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
},
|
||||
ImportSection: []*wasm.Import{
|
||||
{
|
||||
@@ -598,7 +598,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(import "foo" "bar" (func (type 0) (param i32)))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
ImportSection: []*wasm.Import{{
|
||||
Module: "foo", Name: "bar",
|
||||
Type: wasm.ExternTypeFunc,
|
||||
@@ -613,7 +613,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(type $i32 (func (param i32)))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
ImportSection: []*wasm.Import{{
|
||||
Module: "foo", Name: "bar",
|
||||
Type: wasm.ExternTypeFunc,
|
||||
@@ -628,7 +628,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(import "foo" "bar" (func (type $i32) (param i32)))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
ImportSection: []*wasm.Import{{
|
||||
Module: "foo", Name: "bar",
|
||||
Type: wasm.ExternTypeFunc,
|
||||
@@ -643,7 +643,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(type $i32 (func (param i32)))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
ImportSection: []*wasm.Import{{
|
||||
Module: "foo", Name: "bar",
|
||||
Type: wasm.ExternTypeFunc,
|
||||
@@ -655,7 +655,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
name: "import func multiple abbreviated results",
|
||||
input: `(module (import "misc" "swap" (func $swap (param i32 i32) (result i32 i32))))`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}, ParamNumInUint64: 2, ResultNumInUint64: 2}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}}},
|
||||
ImportSection: []*wasm.Import{{
|
||||
Module: "misc", Name: "swap",
|
||||
Type: wasm.ExternTypeFunc,
|
||||
@@ -856,8 +856,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
v_v,
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 9, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{1, 2},
|
||||
CodeSection: []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
|
||||
@@ -881,8 +881,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
v_v,
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{1, 2},
|
||||
CodeSection: []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
|
||||
@@ -922,7 +922,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(func (type 0) (param i32))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{codeEnd},
|
||||
},
|
||||
@@ -934,7 +934,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(type $i32 (func (param i32)))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{codeEnd},
|
||||
},
|
||||
@@ -946,7 +946,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(func (type $i32) (param i32))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{codeEnd},
|
||||
},
|
||||
@@ -958,7 +958,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(type $i32 (func (param i32)))
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}, ParamNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{codeEnd},
|
||||
},
|
||||
@@ -1101,7 +1101,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
name: "func param IDs",
|
||||
input: "(module (func $one (param $x i32) (param $y i32) (result i32) local.get 0))",
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: localGet0End}},
|
||||
NameSection: &wasm.NameSection{
|
||||
@@ -1119,7 +1119,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
(func (param $l i32) (param $r i32) (result i32) local.get 0)
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}},
|
||||
FunctionSection: []wasm.Index{0, 0},
|
||||
CodeSection: []*wasm.Code{{Body: localGet0End}, {Body: localGet0End}},
|
||||
NameSection: &wasm.NameSection{
|
||||
@@ -1134,7 +1134,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
name: "func mixed param IDs", // Verifies we can handle less param fields than Params
|
||||
input: "(module (func (param i32 i32) (param $v i32) (param i64) (param $t f32)))",
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32, i32, i64, f32}, ParamNumInUint64: 5}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32, i32, i64, f32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: end}},
|
||||
NameSection: &wasm.NameSection{
|
||||
@@ -1148,7 +1148,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
name: "func multiple abbreviated results",
|
||||
input: "(module (func $swap (param i32 i32) (result i32 i32) local.get 1 local.get 0))",
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}, ParamNumInUint64: 2, ResultNumInUint64: 2}},
|
||||
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32, i32}}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: []byte{wasm.OpcodeLocalGet, 0x01, wasm.OpcodeLocalGet, 0x00, wasm.OpcodeEnd}}},
|
||||
NameSection: &wasm.NameSection{
|
||||
@@ -1393,7 +1393,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
|
||||
},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{
|
||||
|
||||
@@ -9,44 +9,19 @@ import (
|
||||
|
||||
var (
|
||||
f32, f64, i32, i64 = wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeI32, wasm.ValueTypeI64
|
||||
i32_v = &wasm.FunctionType{Params: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 1,
|
||||
}
|
||||
v_i32 = &wasm.FunctionType{Results: []wasm.ValueType{i32},
|
||||
ResultNumInUint64: 1,
|
||||
}
|
||||
v_i32i64 = &wasm.FunctionType{Results: []wasm.ValueType{i32, i64},
|
||||
ResultNumInUint64: 2,
|
||||
}
|
||||
f32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{f32}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 1,
|
||||
ResultNumInUint64: 1,
|
||||
}
|
||||
i64_i64 = &wasm.FunctionType{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i64},
|
||||
ParamNumInUint64: 1,
|
||||
ResultNumInUint64: 1,
|
||||
}
|
||||
i32i64_v = &wasm.FunctionType{Params: []wasm.ValueType{i32, i64},
|
||||
ParamNumInUint64: 2,
|
||||
}
|
||||
i32i32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 1,
|
||||
}
|
||||
i32i64_i32 = &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 1,
|
||||
}
|
||||
i32i32i32i32_i32 = &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 4,
|
||||
ResultNumInUint64: 1,
|
||||
}
|
||||
i32_v = &wasm.FunctionType{Params: []wasm.ValueType{i32}}
|
||||
v_i32 = &wasm.FunctionType{Results: []wasm.ValueType{i32}}
|
||||
v_i32i64 = &wasm.FunctionType{Results: []wasm.ValueType{i32, i64}}
|
||||
f32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{f32}, Results: []wasm.ValueType{i32}}
|
||||
i64_i64 = &wasm.FunctionType{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i64}}
|
||||
i32i64_v = &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}}
|
||||
i32i32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}
|
||||
i32i64_i32 = &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{i32}}
|
||||
i32i32i32i32_i32 = &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}}
|
||||
i32i32i32i32i32i64i64i32i32_i32 = &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32},
|
||||
Results: []wasm.ValueType{i32},
|
||||
ParamNumInUint64: 9,
|
||||
ResultNumInUint64: 1,
|
||||
Params: []wasm.ValueType{i32, i32, i32, i32, i32, i64, i64, i32, i32},
|
||||
Results: []wasm.ValueType{i32},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -125,7 +100,7 @@ func TestTypeParser(t *testing.T) {
|
||||
{
|
||||
name: "mixed param abbreviation", // Verifies we can handle less param fields than param types
|
||||
input: "(type (func (param i32 i32) (param i32) (param i64) (param f32)))",
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i32, i32, i64, f32}, ParamNumInUint64: 5},
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i32, i32, i64, f32}},
|
||||
},
|
||||
|
||||
// Below are changes to test/core/br.wast from the commit that added "multi-value" support.
|
||||
@@ -134,58 +109,55 @@ func TestTypeParser(t *testing.T) {
|
||||
{
|
||||
name: "multi-value - v_i64f32 abbreviated",
|
||||
input: "(type (func (result i64 f32)))",
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}, ResultNumInUint64: 2},
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i64_f32f64 abbreviated",
|
||||
input: "(type (func (param i32 i64) (result f32 f64)))",
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - v_i64f32",
|
||||
input: "(type (func (result i64) (result f32)))",
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}, ResultNumInUint64: 2},
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i64_f32f64",
|
||||
input: "(type (func (param i32) (param i64) (result f32) (result f64)))",
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i64_f32f64 named",
|
||||
input: "(type (func (param $x i32) (param $y i64) (result f32) (result f64)))",
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}, ParamNumInUint64: 2, ResultNumInUint64: 2},
|
||||
expected: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i64i64f32_f32i32 results abbreviated in groups",
|
||||
input: "(type (func (result i64 i64 f32) (result f32 i32)))",
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32, f32, i32}, ResultNumInUint64: 5},
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32, f32, i32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i32i64i32_f32f64f64i32 params and results abbreviated in groups",
|
||||
input: "(type (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)))",
|
||||
expected: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 4, ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i32i64i32_f32f64f64i32 abbreviated in groups",
|
||||
input: "(type (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)))",
|
||||
expected: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 4, ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i32i64i32_f32f64f64i32 abbreviated in groups",
|
||||
input: "(type (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)))",
|
||||
expected: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 4, ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -193,7 +165,7 @@ func TestTypeParser(t *testing.T) {
|
||||
input: "(type (func (result) (result) (result i64 i64) (result) (result f32) (result)))",
|
||||
// Abbreviations have min length zero, which implies no-op results are ok.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#abbreviations%E2%91%A2
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32}, ResultNumInUint64: 3},
|
||||
expected: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - empty abbreviated params and results",
|
||||
@@ -204,9 +176,8 @@ func TestTypeParser(t *testing.T) {
|
||||
// Abbreviations have min length zero, which implies no-op results are ok.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#abbreviations%E2%91%A2
|
||||
expected: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 5, ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -408,9 +379,6 @@ func parseFunctionType(
|
||||
tp := newTypeParser(enabledFeatures, typeNamespace, setFunc)
|
||||
// typeParser starts after the '(type', so we need to eat it first!
|
||||
_, _, err := lex(skipTokens(2, tp.begin), []byte(input))
|
||||
if parsed != nil {
|
||||
parsed.CacheNumInUint64()
|
||||
}
|
||||
return parsed, tp, err
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestTypeUseParser_InlinesTypesWhenNotYetAdded(t *testing.T) {
|
||||
{
|
||||
name: "mixed param abbreviation", // Verifies we can handle less param fields than param types
|
||||
input: "((param i32 i32) (param i32) (param i64) (param f32))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i32, i32, i64, f32}, ParamNumInUint64: 5},
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i32, i32, i64, f32}},
|
||||
},
|
||||
|
||||
// Below are changes to test/core/br.wast from the commit that added "multi-value" support.
|
||||
@@ -83,73 +83,56 @@ func TestTypeUseParser_InlinesTypesWhenNotYetAdded(t *testing.T) {
|
||||
{
|
||||
name: "multi-value - v_i64f32 abbreviated",
|
||||
input: "((result i64 f32))",
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}, ResultNumInUint64: 2},
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i64_f32f64 abbreviated",
|
||||
input: "((param i32 i64) (result f32 f64))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 2,
|
||||
},
|
||||
name: "multi-value - i32i64_f32f64 abbreviated",
|
||||
input: "((param i32 i64) (result f32 f64))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - v_i64f32",
|
||||
input: "((result i64) (result f32))",
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}, ResultNumInUint64: 2},
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, f32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i64_f32f64",
|
||||
input: "((param i32) (param i64) (result f32) (result f64))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 2,
|
||||
},
|
||||
name: "multi-value - i32i64_f32f64",
|
||||
input: "((param i32) (param i64) (result f32) (result f64))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i64_f32f64 named",
|
||||
input: "((param $x i32) (param $y i64) (result f32) (result f64))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64},
|
||||
ParamNumInUint64: 2,
|
||||
ResultNumInUint64: 2,
|
||||
},
|
||||
expectedParamNames: wasm.NameMap{&wasm.NameAssoc{Index: 0, Name: "x"}, &wasm.NameAssoc{Index: 1, Name: "y"}},
|
||||
name: "multi-value - i32i64_f32f64 named",
|
||||
input: "((param $x i32) (param $y i64) (result f32) (result f64))",
|
||||
expectedInlinedType: &wasm.FunctionType{Params: []wasm.ValueType{i32, i64}, Results: []wasm.ValueType{f32, f64}},
|
||||
expectedParamNames: wasm.NameMap{&wasm.NameAssoc{Index: 0, Name: "x"}, &wasm.NameAssoc{Index: 1, Name: "y"}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i64i64f32_f32i32 results abbreviated in groups",
|
||||
input: "((result i64 i64 f32) (result f32 i32))",
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32, f32, i32},
|
||||
ResultNumInUint64: 5,
|
||||
},
|
||||
name: "multi-value - i64i64f32_f32i32 results abbreviated in groups",
|
||||
input: "((result i64 i64 f32) (result f32 i32))",
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32, f32, i32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i32i64i32_f32f64f64i32 params and results abbreviated in groups",
|
||||
input: "((param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32))",
|
||||
expectedInlinedType: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 4,
|
||||
ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i32i64i32_f32f64f64i32 abbreviated in groups",
|
||||
input: "((param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32))",
|
||||
expectedInlinedType: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 4,
|
||||
ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-value - i32i32i64i32_f32f64f64i32 abbreviated in groups",
|
||||
input: "((param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32))",
|
||||
expectedInlinedType: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 4,
|
||||
ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -157,9 +140,7 @@ func TestTypeUseParser_InlinesTypesWhenNotYetAdded(t *testing.T) {
|
||||
input: "((result) (result) (result i64 i64) (result) (result f32) (result))",
|
||||
// Abbreviations have min length zero, which implies no-op results are ok.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#abbreviations%E2%91%A2
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32},
|
||||
ResultNumInUint64: 3,
|
||||
},
|
||||
expectedInlinedType: &wasm.FunctionType{Results: []wasm.ValueType{i64, i64, f32}},
|
||||
},
|
||||
{
|
||||
name: "multi-value - empty abbreviated params and results",
|
||||
@@ -170,10 +151,8 @@ func TestTypeUseParser_InlinesTypesWhenNotYetAdded(t *testing.T) {
|
||||
// Abbreviations have min length zero, which implies no-op results are ok.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#abbreviations%E2%91%A2
|
||||
expectedInlinedType: &wasm.FunctionType{
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
ParamNumInUint64: 5,
|
||||
ResultNumInUint64: 4,
|
||||
Params: []wasm.ValueType{i32, i32, i64, i32, i32},
|
||||
Results: []wasm.ValueType{f32, f64, f64, i32},
|
||||
},
|
||||
expectedParamNames: wasm.NameMap{&wasm.NameAssoc{Index: 4, Name: "x"}},
|
||||
},
|
||||
@@ -185,8 +164,6 @@ func TestTypeUseParser_InlinesTypesWhenNotYetAdded(t *testing.T) {
|
||||
return tp, func(t *testing.T) {
|
||||
// We should have inlined the type, and it is the first type use, which means the inlined index is zero
|
||||
require.Zero(t, tp.inlinedTypeIndices[0].inlinedIdx)
|
||||
exp := tp.inlinedTypes[0]
|
||||
exp.CacheNumInUint64()
|
||||
require.Equal(t, []*wasm.FunctionType{tc.expectedInlinedType}, tp.inlinedTypes)
|
||||
}
|
||||
})
|
||||
@@ -225,9 +202,7 @@ func TestTypeUseParser_UnresolvedType(t *testing.T) {
|
||||
if tc.expectedInlinedType == nil {
|
||||
require.Zero(t, len(tp.inlinedTypes), "expected no inlinedTypes")
|
||||
} else {
|
||||
exp := tp.inlinedTypes[0]
|
||||
exp.CacheNumInUint64()
|
||||
require.Equal(t, tc.expectedInlinedType, exp)
|
||||
require.Equal(t, tc.expectedInlinedType, tp.inlinedTypes[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -372,9 +347,6 @@ func TestTypeUseParser_ReuseExistingInlinedType(t *testing.T) {
|
||||
require.NoError(t, parseTypeUse(tp, tc.input, ignoreTypeUse))
|
||||
|
||||
return tp, func(t *testing.T) {
|
||||
for _, it := range tp.inlinedTypes {
|
||||
it.CacheNumInUint64()
|
||||
}
|
||||
// verify it wasn't duplicated
|
||||
require.Equal(t, []*wasm.FunctionType{i32i64_v, tc.expectedInlinedType}, tp.inlinedTypes)
|
||||
// last two inlined types are the same
|
||||
@@ -420,9 +392,6 @@ func TestTypeUseParser_BeginResets(t *testing.T) {
|
||||
require.NoError(t, parseTypeUse(tp, tc.input, ignoreTypeUse))
|
||||
|
||||
return tp, func(t *testing.T) {
|
||||
for _, it := range tp.inlinedTypes {
|
||||
it.CacheNumInUint64()
|
||||
}
|
||||
// this is the second inlined type
|
||||
require.Equal(t, []*wasm.FunctionType{i32i64_i32, tc.expectedInlinedType}, tp.inlinedTypes)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func newExample() *wasm.Module {
|
||||
return &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 2, ResultNumInUint64: 1},
|
||||
{ParamNumInUint64: 0, ResultNumInUint64: 0},
|
||||
{},
|
||||
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 4, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{i64}, Results: []wasm.ValueType{i64}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
{Params: []wasm.ValueType{f32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1},
|
||||
|
||||
@@ -119,7 +119,9 @@ func TestCompile(t *testing.T) {
|
||||
if enabledFeatures == 0 {
|
||||
enabledFeatures = wasm.Features20220419
|
||||
}
|
||||
|
||||
for _, tp := range tc.module.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
res, err := CompileFunctions(ctx, enabledFeatures, tc.module)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, res[0])
|
||||
@@ -535,6 +537,9 @@ func TestCompile_MultiValue(t *testing.T) {
|
||||
if enabledFeatures == 0 {
|
||||
enabledFeatures = wasm.Features20220419
|
||||
}
|
||||
for _, tp := range tc.module.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
res, err := CompileFunctions(ctx, enabledFeatures, tc.module)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, res[0])
|
||||
@@ -565,7 +570,9 @@ func TestCompile_NonTrappingFloatToIntConversion(t *testing.T) {
|
||||
Types: []*wasm.FunctionType{f32_i32},
|
||||
TableTypes: []wasm.RefType{},
|
||||
}
|
||||
|
||||
for _, tp := range module.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
res, err := CompileFunctions(ctx, wasm.FeatureNonTrappingFloatToIntConversion, module)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, res[0])
|
||||
@@ -590,7 +597,9 @@ func TestCompile_SignExtensionOps(t *testing.T) {
|
||||
Types: []*wasm.FunctionType{i32_i32},
|
||||
TableTypes: []wasm.RefType{},
|
||||
}
|
||||
|
||||
for _, tp := range module.TypeSection {
|
||||
tp.CacheNumInUint64()
|
||||
}
|
||||
res, err := CompileFunctions(ctx, wasm.FeatureSignExtensionOps, module)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, res[0])
|
||||
|
||||
@@ -44,10 +44,14 @@ const (
|
||||
// See argsSizesGet
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_get
|
||||
// See https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
func argsGet(ctx context.Context, mod api.Module, argv, argvBuf uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), sysCtx.Args(), argv, argvBuf)
|
||||
}
|
||||
var argsGet = wasm.NewGoFunc(
|
||||
functionArgsGet, functionArgsGet,
|
||||
[]string{"argv", "argv_buf"},
|
||||
func(ctx context.Context, mod api.Module, argv, argvBuf uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), sysCtx.Args(), argv, argvBuf)
|
||||
},
|
||||
)
|
||||
|
||||
// argsSizesGet is the WASI function named functionArgsSizesGet that reads
|
||||
// command-line argument sizes.
|
||||
@@ -78,15 +82,19 @@ func argsGet(ctx context.Context, mod api.Module, argv, argvBuf uint32) Errno {
|
||||
// See argsGet
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_sizes_get
|
||||
// See https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
func argsSizesGet(ctx context.Context, mod api.Module, resultArgc, resultArgvBufSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
mem := mod.Memory()
|
||||
var argsSizesGet = wasm.NewGoFunc(
|
||||
functionArgsSizesGet, functionArgsSizesGet,
|
||||
[]string{"result.argc", "result.argv_buf_size"},
|
||||
func(ctx context.Context, mod api.Module, resultArgc, resultArgvBufSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
mem := mod.Memory()
|
||||
|
||||
if !mem.WriteUint32Le(ctx, resultArgc, uint32(len(sysCtx.Args()))) {
|
||||
return ErrnoFault
|
||||
}
|
||||
if !mem.WriteUint32Le(ctx, resultArgvBufSize, sysCtx.ArgsSize()) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
if !mem.WriteUint32Le(ctx, resultArgc, uint32(len(sysCtx.Args()))) {
|
||||
return ErrnoFault
|
||||
}
|
||||
if !mem.WriteUint32Le(ctx, resultArgvBufSize, sysCtx.ArgsSize()) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
@@ -53,28 +53,32 @@ const (
|
||||
// Note: This is similar to `clock_getres` in POSIX.
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_res_getid-clockid---errno-timestamp
|
||||
// See https://linux.die.net/man/3/clock_getres
|
||||
func clockResGet(ctx context.Context, mod api.Module, id uint32, resultResolution uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
var clockResGet = wasm.NewGoFunc(
|
||||
functionClockResGet, functionClockResGet,
|
||||
[]string{"id", "result.resolution"},
|
||||
func(ctx context.Context, mod api.Module, id uint32, resultResolution uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
|
||||
var resolution uint64 // ns
|
||||
switch id {
|
||||
case clockIDRealtime:
|
||||
resolution = uint64(sysCtx.WalltimeResolution())
|
||||
case clockIDMonotonic:
|
||||
resolution = uint64(sysCtx.NanotimeResolution())
|
||||
case clockIDProcessCputime, clockIDThreadCputime:
|
||||
// Similar to many other runtimes, we only support realtime and
|
||||
// monotonic clocks. Other types are slated to be removed from the next
|
||||
// version of WASI.
|
||||
return ErrnoNotsup
|
||||
default:
|
||||
return ErrnoInval
|
||||
}
|
||||
if !mod.Memory().WriteUint64Le(ctx, resultResolution, resolution) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
var resolution uint64 // ns
|
||||
switch id {
|
||||
case clockIDRealtime:
|
||||
resolution = uint64(sysCtx.WalltimeResolution())
|
||||
case clockIDMonotonic:
|
||||
resolution = uint64(sysCtx.NanotimeResolution())
|
||||
case clockIDProcessCputime, clockIDThreadCputime:
|
||||
// Similar to many other runtimes, we only support realtime and
|
||||
// monotonic clocks. Other types are slated to be removed from the next
|
||||
// version of WASI.
|
||||
return ErrnoNotsup
|
||||
default:
|
||||
return ErrnoInval
|
||||
}
|
||||
if !mod.Memory().WriteUint64Le(ctx, resultResolution, resolution) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// clockTimeGet is the WASI function named functionClockTimeGet that returns
|
||||
// the time value of a name (time.Now).
|
||||
@@ -107,28 +111,32 @@ func clockResGet(ctx context.Context, mod api.Module, id uint32, resultResolutio
|
||||
// Note: This is similar to `clock_gettime` in POSIX.
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
|
||||
// See https://linux.die.net/man/3/clock_gettime
|
||||
func clockTimeGet(ctx context.Context, mod api.Module, id uint32, precision uint64, resultTimestamp uint32) Errno {
|
||||
// TODO: precision is currently ignored.
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
var clockTimeGet = wasm.NewGoFunc(
|
||||
functionClockTimeGet, functionClockTimeGet,
|
||||
[]string{"id", "precision", "result.timestamp"},
|
||||
func(ctx context.Context, mod api.Module, id uint32, precision uint64, resultTimestamp uint32) Errno {
|
||||
// TODO: precision is currently ignored.
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
|
||||
var val uint64
|
||||
switch id {
|
||||
case clockIDRealtime:
|
||||
sec, nsec := sysCtx.Walltime(ctx)
|
||||
val = (uint64(sec) * uint64(time.Second.Nanoseconds())) + uint64(nsec)
|
||||
case clockIDMonotonic:
|
||||
val = uint64(sysCtx.Nanotime(ctx))
|
||||
case clockIDProcessCputime, clockIDThreadCputime:
|
||||
// Similar to many other runtimes, we only support realtime and
|
||||
// monotonic clocks. Other types are slated to be removed from the next
|
||||
// version of WASI.
|
||||
return ErrnoNotsup
|
||||
default:
|
||||
return ErrnoInval
|
||||
}
|
||||
var val uint64
|
||||
switch id {
|
||||
case clockIDRealtime:
|
||||
sec, nsec := sysCtx.Walltime(ctx)
|
||||
val = (uint64(sec) * uint64(time.Second.Nanoseconds())) + uint64(nsec)
|
||||
case clockIDMonotonic:
|
||||
val = uint64(sysCtx.Nanotime(ctx))
|
||||
case clockIDProcessCputime, clockIDThreadCputime:
|
||||
// Similar to many other runtimes, we only support realtime and
|
||||
// monotonic clocks. Other types are slated to be removed from the next
|
||||
// version of WASI.
|
||||
return ErrnoNotsup
|
||||
default:
|
||||
return ErrnoInval
|
||||
}
|
||||
|
||||
if !mod.Memory().WriteUint64Le(ctx, resultTimestamp, val) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
if !mod.Memory().WriteUint64Le(ctx, resultTimestamp, val) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
@@ -191,16 +191,28 @@ func Test_clockTimeGet_Unsupported(t *testing.T) {
|
||||
name: "process cputime",
|
||||
clockID: 2,
|
||||
expectedErrno: ErrnoNotsup,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.clock_time_get(id=2,precision=0,result.timestamp=1)
|
||||
<== ENOTSUP
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "thread cputime",
|
||||
clockID: 3,
|
||||
expectedErrno: ErrnoNotsup,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.clock_time_get(id=3,precision=0,result.timestamp=1)
|
||||
<== ENOTSUP
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "undefined",
|
||||
clockID: 100,
|
||||
expectedErrno: ErrnoInval,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.clock_time_get(id=100,precision=0,result.timestamp=1)
|
||||
<== EINVAL
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -211,9 +223,9 @@ func Test_clockTimeGet_Unsupported(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
resultTimestamp := uint32(1) // arbitrary offset
|
||||
errno := clockTimeGet(testCtx, mod, tc.clockID, 0 /* TODO: precision */, resultTimestamp)
|
||||
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
|
||||
requireErrno(t, tc.expectedErrno, mod, functionClockTimeGet, uint64(tc.clockID), uint64(0) /* TODO: precision */, uint64(resultTimestamp))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -232,11 +244,18 @@ func Test_clockTimeGet_Errors(t *testing.T) {
|
||||
{
|
||||
name: "resultTimestamp out-of-memory",
|
||||
resultTimestamp: memorySize,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.clock_time_get(id=0,precision=0,result.timestamp=65536)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "resultTimestamp exceeds the maximum valid address by 1",
|
||||
resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.clock_time_get(id=0,precision=0,result.timestamp=65533)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -246,9 +265,8 @@ func Test_clockTimeGet_Errors(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
errno := clockTimeGet(testCtx, mod, 0 /* TODO: id */, 0 /* TODO: precision */, tc.resultTimestamp)
|
||||
require.Equal(t, ErrnoFault, errno, ErrnoName(errno))
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
requireErrno(t, ErrnoFault, mod, functionClockTimeGet, uint64(0) /* TODO: id */, uint64(0) /* TODO: precision */, uint64(tc.resultTimestamp))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,14 @@ const (
|
||||
// See environSizesGet
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#environ_get
|
||||
// See https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
func environGet(ctx context.Context, mod api.Module, environ uint32, environBuf uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), sysCtx.Environ(), environ, environBuf)
|
||||
}
|
||||
var environGet = wasm.NewGoFunc(
|
||||
functionEnvironGet, functionEnvironGet,
|
||||
[]string{"environ", "environ_buf"},
|
||||
func(ctx context.Context, mod api.Module, environ uint32, environBuf uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), sysCtx.Environ(), environ, environBuf)
|
||||
},
|
||||
)
|
||||
|
||||
// environSizesGet is the WASI function named functionEnvironSizesGet that
|
||||
// reads environment variable sizes.
|
||||
@@ -80,16 +84,20 @@ func environGet(ctx context.Context, mod api.Module, environ uint32, environBuf
|
||||
// See environGet
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#environ_sizes_get
|
||||
// and https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
func environSizesGet(ctx context.Context, mod api.Module, resultEnvironc uint32, resultEnvironBufSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
mem := mod.Memory()
|
||||
var environSizesGet = wasm.NewGoFunc(
|
||||
functionEnvironSizesGet, functionEnvironSizesGet,
|
||||
[]string{"result.environc", "result.environBufSize"},
|
||||
func(ctx context.Context, mod api.Module, resultEnvironc uint32, resultEnvironBufSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
mem := mod.Memory()
|
||||
|
||||
if !mem.WriteUint32Le(ctx, resultEnvironc, uint32(len(sysCtx.Environ()))) {
|
||||
return ErrnoFault
|
||||
}
|
||||
if !mem.WriteUint32Le(ctx, resultEnvironBufSize, sysCtx.EnvironSize()) {
|
||||
return ErrnoFault
|
||||
}
|
||||
if !mem.WriteUint32Le(ctx, resultEnvironc, uint32(len(sysCtx.Environ()))) {
|
||||
return ErrnoFault
|
||||
}
|
||||
if !mem.WriteUint32Le(ctx, resultEnvironBufSize, sysCtx.EnvironSize()) {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
@@ -55,23 +55,39 @@ func Test_environGet_Errors(t *testing.T) {
|
||||
name: "out-of-memory environPtr",
|
||||
environ: memorySize,
|
||||
environBuf: validAddress,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_get(environ=65536,environ_buf=0)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "out-of-memory environBufPtr",
|
||||
environ: validAddress,
|
||||
environBuf: memorySize,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_get(environ=0,environ_buf=65536)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "environPtr exceeds the maximum valid address by 1",
|
||||
// 4*envCount is the expected length for environPtr, 4 is the size of uint32
|
||||
environ: memorySize - 4*2 + 1,
|
||||
environBuf: validAddress,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_get(environ=65529,environ_buf=0)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "environBufPtr exceeds the maximum valid address by 1",
|
||||
environ: validAddress,
|
||||
// "a=bc", "b=cd" size = size of "a=bc0b=cd0" = 10
|
||||
environBuf: memorySize - 10 + 1,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_get(environ=0,environ_buf=65527)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -81,9 +97,8 @@ func Test_environGet_Errors(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
errno := environGet(testCtx, mod, tc.environ, tc.environBuf)
|
||||
require.Equal(t, ErrnoFault, errno, ErrnoName(errno))
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
requireErrno(t, ErrnoFault, mod, functionEnvironGet, uint64(tc.environ), uint64(tc.environBuf))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -134,21 +149,37 @@ func Test_environSizesGet_Errors(t *testing.T) {
|
||||
name: "out-of-memory environCountPtr",
|
||||
environc: memorySize,
|
||||
environBufSize: validAddress,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_sizes_get(result.environc=65536,result.environBufSize=0)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "out-of-memory environBufSizePtr",
|
||||
environc: validAddress,
|
||||
environBufSize: memorySize,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_sizes_get(result.environc=0,result.environBufSize=65536)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "environCountPtr exceeds the maximum valid address by 1",
|
||||
environc: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of environ
|
||||
environBufSize: validAddress,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_sizes_get(result.environc=65533,result.environBufSize=0)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "environBufSizePtr exceeds the maximum valid size by 1",
|
||||
environc: validAddress,
|
||||
environBufSize: memorySize - 4 + 1, // 4 is count of bytes to encode uint32le
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.environ_sizes_get(result.environc=0,result.environBufSize=65533)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -156,9 +187,10 @@ func Test_environSizesGet_Errors(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errno := environSizesGet(testCtx, mod, tc.environc, tc.environBufSize)
|
||||
require.Equal(t, ErrnoFault, errno, ErrnoName(errno))
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
defer log.Reset()
|
||||
|
||||
requireErrno(t, ErrnoFault, mod, functionEnvironSizesGet, uint64(tc.environc), uint64(tc.environBufSize))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,21 @@ const (
|
||||
// advisory information on a file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_advisefd-fd-offset-filesize-len-filesize-advice-advice---errno
|
||||
var fdAdvise = stubFunction(i32, i64, i64, i32) // stubbed for GrainLang per #271.
|
||||
var fdAdvise = stubFunction(
|
||||
functionFdAdvise,
|
||||
[]wasm.ValueType{i32, i64, i64, i32},
|
||||
[]string{"fd", "offset", "len", "result.advice"},
|
||||
)
|
||||
|
||||
// fdAllocate is the WASI function named functionFdAllocate which forces the
|
||||
// allocation of space in a file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_allocatefd-fd-offset-filesize-len-filesize---errno
|
||||
var fdAllocate = stubFunction(i32, i64, i64) // stubbed for GrainLang per #271.
|
||||
var fdAllocate = stubFunction(
|
||||
functionFdAllocate,
|
||||
[]wasm.ValueType{i32, i64, i64},
|
||||
[]string{"fd", "offset", "len"},
|
||||
)
|
||||
|
||||
// fdClose is the WASI function named functionFdClose which closes a file
|
||||
// descriptor.
|
||||
@@ -73,20 +81,28 @@ var fdAllocate = stubFunction(i32, i64, i64) // stubbed for GrainLang per #271.
|
||||
// Note: This is similar to `close` in POSIX.
|
||||
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_close
|
||||
// and https://linux.die.net/man/3/close
|
||||
func fdClose(ctx context.Context, mod api.Module, fd uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
if ok := sysCtx.FS(ctx).CloseFile(ctx, fd); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
var fdClose = wasm.NewGoFunc(
|
||||
functionFdClose, functionFdClose,
|
||||
[]string{"fd"},
|
||||
func(ctx context.Context, mod api.Module, fd uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
if ok := sysCtx.FS(ctx).CloseFile(ctx, fd); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// fdDatasync is the WASI function named functionFdDatasync which synchronizes
|
||||
// the data of a file to disk.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_datasyncfd-fd---errno
|
||||
var fdDatasync = stubFunction(i32) // stubbed for GrainLang per #271.
|
||||
var fdDatasync = stubFunction(
|
||||
functionFdDatasync,
|
||||
[]wasm.ValueType{i32},
|
||||
[]string{"fd"},
|
||||
)
|
||||
|
||||
// fdFdstatGet is the WASI function named functionFdFdstatGet which returns the
|
||||
// attributes of a file descriptor.
|
||||
@@ -125,20 +141,28 @@ var fdDatasync = stubFunction(i32) // stubbed for GrainLang per #271.
|
||||
// well as additional fields.
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fdstat
|
||||
// and https://linux.die.net/man/3/fsync
|
||||
func fdFdstatGet(ctx context.Context, mod api.Module, fd uint32, resultStat uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
if _, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
// TODO: actually write the fdstat!
|
||||
return ErrnoSuccess
|
||||
}
|
||||
var fdFdstatGet = wasm.NewGoFunc(
|
||||
functionFdFdstatGet, functionFdFdstatGet,
|
||||
[]string{"fd", "result.stat"},
|
||||
func(ctx context.Context, mod api.Module, fd uint32, resultStat uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
if _, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
// TODO: actually write the fdstat!
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// fdFdstatSetFlags is the WASI function named functionFdFdstatSetFlags which
|
||||
// adjusts the flags associated with a file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_fdstat_set_flagsfd-fd-flags-fdflags---errnoand is stubbed for GrainLang per #271
|
||||
var fdFdstatSetFlags = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
var fdFdstatSetFlags = stubFunction(
|
||||
functionFdFdstatSetFlags,
|
||||
[]wasm.ValueType{i32, i32},
|
||||
[]string{"fd", "flags"},
|
||||
)
|
||||
|
||||
// fdFdstatSetRights is the WASI function named functionFdFdstatSetRights which
|
||||
// adjusts the rights associated with a file descriptor.
|
||||
@@ -146,31 +170,51 @@ var fdFdstatSetFlags = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_fdstat_set_rightsfd-fd-fs_rights_base-rights-fs_rights_inheriting-rights---errno
|
||||
//
|
||||
// Note: This will never be implemented per https://github.com/WebAssembly/WASI/issues/469#issuecomment-1045251844
|
||||
var fdFdstatSetRights = stubFunction(i32, i64, i64) // stubbed for GrainLang per #271.
|
||||
var fdFdstatSetRights = stubFunction(
|
||||
functionFdFdstatSetRights,
|
||||
[]wasm.ValueType{i32, i64, i64},
|
||||
[]string{"fd", "fs_rights_base", "fs_rights_inheriting"},
|
||||
)
|
||||
|
||||
// fdFilestatGet is the WASI function named functionFdFilestatGet which returns
|
||||
// the attributes of an open file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_getfd-fd---errno-filestat
|
||||
var fdFilestatGet = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
var fdFilestatGet = stubFunction(
|
||||
functionFdFilestatGet,
|
||||
[]wasm.ValueType{i32, i32},
|
||||
[]string{"fd", "result.buf"},
|
||||
)
|
||||
|
||||
// fdFilestatSetSize is the WASI function named functionFdFilestatSetSize which
|
||||
// adjusts the size of an open file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---errno
|
||||
var fdFilestatSetSize = stubFunction(i32, i64) // stubbed for GrainLang per #271.
|
||||
var fdFilestatSetSize = stubFunction(
|
||||
functionFdFilestatSetSize,
|
||||
[]wasm.ValueType{i32, i64},
|
||||
[]string{"fd", "size"},
|
||||
)
|
||||
|
||||
// fdFilestatSetTimes is the WASI function named functionFdFilestatSetTimes
|
||||
// which adjusts the times of an open file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_timesfd-fd-atim-timestamp-mtim-timestamp-fst_flags-fstflags---errno
|
||||
var fdFilestatSetTimes = stubFunction(i32, i64, i64, i32) // stubbed for GrainLang per #271.
|
||||
var fdFilestatSetTimes = stubFunction(
|
||||
functionFdFilestatSetTimes,
|
||||
[]wasm.ValueType{i32, i64, i64, i32},
|
||||
[]string{"fd", "atim", "mtim", "fst_flags"},
|
||||
)
|
||||
|
||||
// fdPread is the WASI function named functionFdPread which reads from a file
|
||||
// descriptor, without using and updating the file descriptor's offset.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_preadfd-fd-iovs-iovec_array-offset-filesize---errno-size
|
||||
var fdPread = stubFunction(i32, i32, i32, i64, i32) // stubbed for GrainLang per #271.
|
||||
var fdPread = stubFunction(
|
||||
functionFdPread,
|
||||
[]wasm.ValueType{i32, i32, i32, i64, i32},
|
||||
[]string{"fd", "iovs", "iovs_len", "offset", "result.nread"},
|
||||
)
|
||||
|
||||
// fdPrestatGet is the WASI function named functionFdPrestatGet which returns
|
||||
// the prestat data of a file descriptor.
|
||||
@@ -203,24 +247,28 @@ var fdPread = stubFunction(i32, i32, i32, i64, i32) // stubbed for GrainLang per
|
||||
//
|
||||
// See fdPrestatDirName and
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#prestat
|
||||
func fdPrestatGet(ctx context.Context, mod api.Module, fd uint32, resultPrestat uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
entry, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
|
||||
if !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
var fdPrestatGet = wasm.NewGoFunc(
|
||||
functionFdPrestatGet, functionFdPrestatGet,
|
||||
[]string{"fd", "result.prestat"},
|
||||
func(ctx context.Context, mod api.Module, fd uint32, resultPrestat uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
entry, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
|
||||
if !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
// Zero-value 8-bit tag, and 3-byte zero-value paddings, which is uint32le(0) in short.
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultPrestat, uint32(0)) {
|
||||
return ErrnoFault
|
||||
}
|
||||
// Write the length of the directory name at offset 4.
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultPrestat+4, uint32(len(entry.Path))) {
|
||||
return ErrnoFault
|
||||
}
|
||||
// Zero-value 8-bit tag, and 3-byte zero-value paddings, which is uint32le(0) in short.
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultPrestat, uint32(0)) {
|
||||
return ErrnoFault
|
||||
}
|
||||
// Write the length of the directory name at offset 4.
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultPrestat+4, uint32(len(entry.Path))) {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// fdPrestatDirName is the WASI function named functionFdPrestatDirName which
|
||||
// returns the path of the pre-opened directory of a file descriptor.
|
||||
@@ -252,30 +300,37 @@ func fdPrestatGet(ctx context.Context, mod api.Module, fd uint32, resultPrestat
|
||||
//
|
||||
// See fdPrestatGet
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_prestat_dir_name
|
||||
func fdPrestatDirName(ctx context.Context, mod api.Module, fd uint32, pathPtr uint32, pathLen uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
|
||||
if !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
var fdPrestatDirName = wasm.NewGoFunc(
|
||||
functionFdPrestatDirName, functionFdPrestatDirName,
|
||||
[]string{"fd", "path", "path_len"},
|
||||
func(ctx context.Context, mod api.Module, fd uint32, pathPtr uint32, pathLen uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
|
||||
if !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
// Some runtimes may have another semantics. See /RATIONALE.md
|
||||
if uint32(len(f.Path)) < pathLen {
|
||||
return ErrnoNametoolong
|
||||
}
|
||||
// Some runtimes may have another semantics. See /RATIONALE.md
|
||||
if uint32(len(f.Path)) < pathLen {
|
||||
return ErrnoNametoolong
|
||||
}
|
||||
|
||||
// TODO: fdPrestatDirName may have to return ErrnoNotdir if the type of the prestat data of `fd` is not a PrestatDir.
|
||||
if !mod.Memory().Write(ctx, pathPtr, []byte(f.Path)[:pathLen]) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
// TODO: fdPrestatDirName may have to return ErrnoNotdir if the type of the prestat data of `fd` is not a PrestatDir.
|
||||
if !mod.Memory().Write(ctx, pathPtr, []byte(f.Path)[:pathLen]) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// fdPwrite is the WASI function named functionFdPwrite which writes to a file
|
||||
// descriptor, without using and updating the file descriptor's offset.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_pwritefd-fd-iovs-ciovec_array-offset-filesize---errno-size
|
||||
var fdPwrite = stubFunction(i32, i32, i32, i64, i32) // stubbed for GrainLang per #271.
|
||||
var fdPwrite = stubFunction(functionFdPwrite,
|
||||
[]wasm.ValueType{i32, i32, i32, i64, i32},
|
||||
[]string{"fd", "iovs", "iovs_len", "offset", "result.nwritten"},
|
||||
)
|
||||
|
||||
// fdRead is the WASI function named functionFdRead which reads from a file
|
||||
// descriptor.
|
||||
@@ -326,53 +381,65 @@ var fdPwrite = stubFunction(i32, i32, i32, i64, i32) // stubbed for GrainLang pe
|
||||
//
|
||||
// See fdWrite
|
||||
// and https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_read
|
||||
func fdRead(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
reader := internalsys.FdReader(ctx, sysCtx, fd)
|
||||
if reader == nil {
|
||||
return ErrnoBadf
|
||||
}
|
||||
var fdRead = wasm.NewGoFunc(
|
||||
functionFdRead, functionFdRead,
|
||||
[]string{"fd", "iovs", "iovs_len", "result.size"},
|
||||
func(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
reader := internalsys.FdReader(ctx, sysCtx, fd)
|
||||
if reader == nil {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
var nread uint32
|
||||
for i := uint32(0); i < iovsCount; i++ {
|
||||
iovPtr := iovs + i*8
|
||||
offset, ok := mod.Memory().ReadUint32Le(ctx, iovPtr)
|
||||
if !ok {
|
||||
var nread uint32
|
||||
for i := uint32(0); i < iovsCount; i++ {
|
||||
iovPtr := iovs + i*8
|
||||
offset, ok := mod.Memory().ReadUint32Le(ctx, iovPtr)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
l, ok := mod.Memory().ReadUint32Le(ctx, iovPtr+4)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
b, ok := mod.Memory().Read(ctx, offset, l)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
n, err := reader.Read(b) // Note: n <= l
|
||||
nread += uint32(n)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
}
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultSize, nread) {
|
||||
return ErrnoFault
|
||||
}
|
||||
l, ok := mod.Memory().ReadUint32Le(ctx, iovPtr+4)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
b, ok := mod.Memory().Read(ctx, offset, l)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
n, err := reader.Read(b) // Note: n <= l
|
||||
nread += uint32(n)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
}
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultSize, nread) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// fdReaddir is the WASI function named functionFdReaddir which reads directory
|
||||
// entries from a directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_readdirfd-fd-buf-pointeru8-buf_len-size-cookie-dircookie---errno-size
|
||||
var fdReaddir = stubFunction(i32, i32, i32, i64, i32) // stubbed for GrainLang per #271.
|
||||
var fdReaddir = stubFunction(
|
||||
functionFdReaddir,
|
||||
[]wasm.ValueType{i32, i32, i32, i64, i32},
|
||||
[]string{"fd", "buf", "buf_len", "cookie", "result.bufused"},
|
||||
)
|
||||
|
||||
// fdRenumber is the WASI function named functionFdRenumber which atomically
|
||||
// replaces a file descriptor by renumbering another file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_renumberfd-fd-to-fd---errno
|
||||
var fdRenumber = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
var fdRenumber = stubFunction(
|
||||
functionFdRenumber,
|
||||
[]wasm.ValueType{i32, i32},
|
||||
[]string{"fd", "to"},
|
||||
)
|
||||
|
||||
// fdSeek is the WASI function named functionFdSeek which moves the offset of a
|
||||
// file descriptor.
|
||||
@@ -411,43 +478,55 @@ var fdRenumber = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
//
|
||||
// See io.Seeker
|
||||
// and https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_seek
|
||||
func fdSeek(ctx context.Context, mod api.Module, fd uint32, offset uint64, whence uint32, resultNewoffset uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
var seeker io.Seeker
|
||||
// Check to see if the file descriptor is available
|
||||
if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok || f.File == nil {
|
||||
return ErrnoBadf
|
||||
// fs.FS doesn't declare io.Seeker, but implementations such as os.File implement it.
|
||||
} else if seeker, ok = f.File.(io.Seeker); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
var fdSeek = wasm.NewGoFunc(
|
||||
functionFdSeek, functionFdSeek,
|
||||
[]string{"fd", "offset", "whence", "result.newoffset"},
|
||||
func(ctx context.Context, mod api.Module, fd uint32, offset uint64, whence uint32, resultNewoffset uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
var seeker io.Seeker
|
||||
// Check to see if the file descriptor is available
|
||||
if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok || f.File == nil {
|
||||
return ErrnoBadf
|
||||
// fs.FS doesn't declare io.Seeker, but implementations such as os.File implement it.
|
||||
} else if seeker, ok = f.File.(io.Seeker); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
if whence > io.SeekEnd /* exceeds the largest valid whence */ {
|
||||
return ErrnoInval
|
||||
}
|
||||
newOffset, err := seeker.Seek(int64(offset), int(whence))
|
||||
if err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
if whence > io.SeekEnd /* exceeds the largest valid whence */ {
|
||||
return ErrnoInval
|
||||
}
|
||||
newOffset, err := seeker.Seek(int64(offset), int(whence))
|
||||
if err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultNewoffset, uint32(newOffset)) {
|
||||
return ErrnoFault
|
||||
}
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultNewoffset, uint32(newOffset)) {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// fdSync is the WASI function named functionFdSync which synchronizes the data
|
||||
// and metadata of a file to disk.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_syncfd-fd---errno
|
||||
var fdSync = stubFunction(i32) // stubbed for GrainLang per #271.
|
||||
var fdSync = stubFunction(
|
||||
functionFdSync,
|
||||
[]wasm.ValueType{i32},
|
||||
[]string{"fd"},
|
||||
)
|
||||
|
||||
// fdTell is the WASI function named functionFdTell which returns the current
|
||||
// offset of a file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_tellfd-fd---errno-filesize
|
||||
var fdTell = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
var fdTell = stubFunction(
|
||||
functionFdTell,
|
||||
[]wasm.ValueType{i32, i32},
|
||||
[]string{"fd", "result.offset"},
|
||||
)
|
||||
|
||||
// fdWrite is the WASI function named functionFdWrite which writes to a file
|
||||
// descriptor.
|
||||
@@ -506,65 +585,85 @@ var fdTell = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
// See fdRead
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#ciovec
|
||||
// and https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_write
|
||||
func fdWrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
writer := internalsys.FdWriter(ctx, sysCtx, fd)
|
||||
if writer == nil {
|
||||
return ErrnoBadf
|
||||
}
|
||||
var fdWrite = wasm.NewGoFunc(
|
||||
functionFdWrite, functionFdWrite,
|
||||
[]string{"fd", "iovs", "iovs_len", "result.size"},
|
||||
func(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
writer := internalsys.FdWriter(ctx, sysCtx, fd)
|
||||
if writer == nil {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
var nwritten uint32
|
||||
for i := uint32(0); i < iovsCount; i++ {
|
||||
iovPtr := iovs + i*8
|
||||
offset, ok := mod.Memory().ReadUint32Le(ctx, iovPtr)
|
||||
if !ok {
|
||||
var nwritten uint32
|
||||
for i := uint32(0); i < iovsCount; i++ {
|
||||
iovPtr := iovs + i*8
|
||||
offset, ok := mod.Memory().ReadUint32Le(ctx, iovPtr)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
// Note: emscripten has been known to write zero length iovec. However,
|
||||
// it is not common in other compilers, so we don't optimize for it.
|
||||
l, ok := mod.Memory().ReadUint32Le(ctx, iovPtr+4)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
b, ok := mod.Memory().Read(ctx, offset, l)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
n, err := writer.Write(b)
|
||||
if err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
nwritten += uint32(n)
|
||||
}
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultSize, nwritten) {
|
||||
return ErrnoFault
|
||||
}
|
||||
// Note: emscripten has been known to write zero length iovec. However,
|
||||
// it is not common in other compilers, so we don't optimize for it.
|
||||
l, ok := mod.Memory().ReadUint32Le(ctx, iovPtr+4)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
b, ok := mod.Memory().Read(ctx, offset, l)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
n, err := writer.Write(b)
|
||||
if err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
nwritten += uint32(n)
|
||||
}
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultSize, nwritten) {
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// pathCreateDirectory is the WASI function named functionPathCreateDirectory
|
||||
// which creates a directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_create_directoryfd-fd-path-string---errno
|
||||
var pathCreateDirectory = stubFunction(i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathCreateDirectory = stubFunction(
|
||||
functionPathCreateDirectory,
|
||||
[]wasm.ValueType{i32, i32, i32},
|
||||
[]string{"fd", "path", "path_len"},
|
||||
)
|
||||
|
||||
// pathFilestatGet is the WASI function named functionPathFilestatGet which
|
||||
// returns the attributes of a file or directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_filestat_getfd-fd-flags-lookupflags-path-string---errno-filestat
|
||||
var pathFilestatGet = stubFunction(i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathFilestatGet = stubFunction(
|
||||
functionPathFilestatGet,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32},
|
||||
[]string{"fd", "flags", "path", "path_len", "result.buf"},
|
||||
)
|
||||
|
||||
// pathFilestatSetTimes is the WASI function named functionPathFilestatSetTimes
|
||||
// which adjusts the timestamps of a file or directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_filestat_set_timesfd-fd-flags-lookupflags-path-string-atim-timestamp-mtim-timestamp-fst_flags-fstflags---errno
|
||||
var pathFilestatSetTimes = stubFunction(i32, i32, i32, i32, i64, i64, i32) // stubbed for GrainLang per #271.
|
||||
var pathFilestatSetTimes = stubFunction(
|
||||
functionPathFilestatSetTimes,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i64, i64, i32},
|
||||
[]string{"fd", "flags", "path", "path_len", "atim", "mtim", "fst_flags"},
|
||||
)
|
||||
|
||||
// pathLink is the WASI function named functionPathLink which adjusts the
|
||||
// timestamps of a file or directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#path_link
|
||||
var pathLink = stubFunction(i32, i32, i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathLink = stubFunction(
|
||||
functionPathLink,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32, i32, i32},
|
||||
[]string{"old_fd", "old_flags", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len"},
|
||||
)
|
||||
|
||||
// pathOpen is the WASI function named functionPathOpen which opens a file or
|
||||
// directory. This returns ErrnoBadf if the fd is invalid.
|
||||
@@ -620,59 +719,83 @@ var pathLink = stubFunction(i32, i32, i32, i32, i32, i32, i32) // stubbed for Gr
|
||||
// * Rights will never be implemented per https://github.com/WebAssembly/WASI/issues/469#issuecomment-1045251844
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#path_open
|
||||
func pathOpen(ctx context.Context, mod api.Module, fd, dirflags, pathPtr, pathLen, oflags uint32, fsRightsBase,
|
||||
fsRightsInheriting uint64, fdflags, resultOpenedFd uint32) (errno Errno) {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
fsc := sysCtx.FS(ctx)
|
||||
if _, ok := fsc.OpenedFile(ctx, fd); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
b, ok := mod.Memory().Read(ctx, pathPtr, pathLen)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
if newFD, err := fsc.OpenFile(ctx, string(b)); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrNotExist):
|
||||
return ErrnoNoent
|
||||
case errors.Is(err, fs.ErrExist):
|
||||
return ErrnoExist
|
||||
default:
|
||||
return ErrnoIo
|
||||
var pathOpen = wasm.NewGoFunc(
|
||||
functionPathOpen, functionPathOpen,
|
||||
[]string{"fd", "dirflags", "path", "path_len", "oflags", "fs_rights_base", "fs_rights_inheriting", "fdflags", "result.opened_fd"},
|
||||
func(ctx context.Context, mod api.Module, fd, dirflags, pathPtr, pathLen, oflags uint32, fsRightsBase,
|
||||
fsRightsInheriting uint64, fdflags, resultOpenedFd uint32) (errno Errno) {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
fsc := sysCtx.FS(ctx)
|
||||
if _, ok := fsc.OpenedFile(ctx, fd); !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
} else if !mod.Memory().WriteUint32Le(ctx, resultOpenedFd, newFD) {
|
||||
_ = fsc.CloseFile(ctx, newFD)
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
b, ok := mod.Memory().Read(ctx, pathPtr, pathLen)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
if newFD, err := fsc.OpenFile(ctx, string(b)); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrNotExist):
|
||||
return ErrnoNoent
|
||||
case errors.Is(err, fs.ErrExist):
|
||||
return ErrnoExist
|
||||
default:
|
||||
return ErrnoIo
|
||||
}
|
||||
} else if !mod.Memory().WriteUint32Le(ctx, resultOpenedFd, newFD) {
|
||||
_ = fsc.CloseFile(ctx, newFD)
|
||||
return ErrnoFault
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// pathReadlink is the WASI function named functionPathReadlink that reads the
|
||||
// contents of a symbolic link.
|
||||
//
|
||||
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_readlinkfd-fd-path-string-buf-pointeru8-buf_len-size---errno-size
|
||||
var pathReadlink = stubFunction(i32, i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathReadlink = stubFunction(
|
||||
functionPathReadlink,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32, i32},
|
||||
[]string{"fd", "path", "path_len", "buf", "buf_len", "result.bufused"},
|
||||
)
|
||||
|
||||
// pathRemoveDirectory is the WASI function named functionPathRemoveDirectory
|
||||
// which removes a directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_remove_directoryfd-fd-path-string---errno
|
||||
var pathRemoveDirectory = stubFunction(i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathRemoveDirectory = stubFunction(
|
||||
functionPathRemoveDirectory,
|
||||
[]wasm.ValueType{i32, i32, i32},
|
||||
[]string{"fd", "path", "path_len"},
|
||||
)
|
||||
|
||||
// pathRename is the WASI function named functionPathRename which renames a
|
||||
// file or directory.
|
||||
var pathRename = stubFunction(i32, i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathRename = stubFunction(
|
||||
functionPathRename,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32, i32},
|
||||
[]string{"fd", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len"},
|
||||
)
|
||||
|
||||
// pathSymlink is the WASI function named functionPathSymlink which creates a
|
||||
// symbolic link.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#path_symlink
|
||||
var pathSymlink = stubFunction(i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathSymlink = stubFunction(
|
||||
functionPathSymlink,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32},
|
||||
[]string{"old_path", "old_path_len", "fd", "new_path", "new_path_len"},
|
||||
)
|
||||
|
||||
// pathUnlinkFile is the WASI function named functionPathUnlinkFile which
|
||||
// unlinks a file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_unlink_filefd-fd-path-string---errno
|
||||
var pathUnlinkFile = stubFunction(i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var pathUnlinkFile = stubFunction(
|
||||
functionPathUnlinkFile,
|
||||
[]wasm.ValueType{i32, i32, i32},
|
||||
[]string{"fd", "path", "path_len"},
|
||||
)
|
||||
|
||||
@@ -113,22 +113,38 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
name: "file",
|
||||
fd: fileFd,
|
||||
// TODO: expectedMem for a file
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=4,result.stat=0)
|
||||
<== ESUCCESS
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "dir",
|
||||
fd: dirFd,
|
||||
// TODO: expectedMem for a dir
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=5,result.stat=0)
|
||||
<== ESUCCESS
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "bad FD",
|
||||
fd: math.MaxUint32,
|
||||
expectedErrno: ErrnoBadf,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=4294967295,result.stat=0)
|
||||
<== EBADF
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "resultStat exceeds the maximum valid address by 1",
|
||||
fd: dirFd,
|
||||
resultStat: memorySize - 24 + 1,
|
||||
// TODO: ErrnoFault
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=5,result.stat=65513)
|
||||
<== ESUCCESS
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -138,9 +154,8 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
errno := fdFdstatGet(testCtx, mod, tc.fd, tc.resultStat)
|
||||
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
requireErrno(t, tc.expectedErrno, mod, functionFdFdstatGet, uint64(tc.fd), uint64(tc.resultStat))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -238,18 +253,27 @@ func Test_fdPrestatGet_Errors(t *testing.T) {
|
||||
fd uint32
|
||||
resultPrestat uint32
|
||||
expectedErrno Errno
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
name: "invalid FD",
|
||||
fd: 42, // arbitrary invalid FD
|
||||
resultPrestat: 0, // valid offset
|
||||
expectedErrno: ErrnoBadf,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_prestat_get(fd=42,result.prestat=0)
|
||||
<== EBADF
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "out-of-memory resultPrestat",
|
||||
fd: fd,
|
||||
resultPrestat: memorySize,
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_prestat_get(fd=4,result.prestat=65536)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
// TODO: non pre-opened file == api.ErrnoBadf
|
||||
}
|
||||
@@ -260,8 +284,8 @@ func Test_fdPrestatGet_Errors(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
errno := fdPrestatGet(testCtx, mod, tc.fd, tc.resultPrestat)
|
||||
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
|
||||
requireErrno(t, tc.expectedErrno, mod, functionFdPrestatGet, uint64(tc.fd), uint64(tc.resultPrestat))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -307,6 +331,7 @@ func Test_fdPrestatDirName_Errors(t *testing.T) {
|
||||
path uint32
|
||||
pathLen uint32
|
||||
expectedErrno Errno
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
name: "out-of-memory path",
|
||||
@@ -314,6 +339,10 @@ func Test_fdPrestatDirName_Errors(t *testing.T) {
|
||||
path: memorySize,
|
||||
pathLen: pathLen,
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_prestat_dir_name(fd=4,path=65536,path_len=4)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "path exceeds the maximum valid address by 1",
|
||||
@@ -321,6 +350,10 @@ func Test_fdPrestatDirName_Errors(t *testing.T) {
|
||||
path: memorySize - pathLen + 1,
|
||||
pathLen: pathLen,
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_prestat_dir_name(fd=4,path=65533,path_len=4)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "pathLen exceeds the length of the dir name",
|
||||
@@ -328,6 +361,10 @@ func Test_fdPrestatDirName_Errors(t *testing.T) {
|
||||
path: validAddress,
|
||||
pathLen: pathLen + 1,
|
||||
expectedErrno: ErrnoNametoolong,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_prestat_dir_name(fd=4,path=0,path_len=5)
|
||||
<== ENAMETOOLONG
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "invalid fd",
|
||||
@@ -335,6 +372,10 @@ func Test_fdPrestatDirName_Errors(t *testing.T) {
|
||||
path: validAddress,
|
||||
pathLen: pathLen,
|
||||
expectedErrno: ErrnoBadf,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_prestat_dir_name(fd=42,path=0,path_len=4)
|
||||
<== EBADF
|
||||
`,
|
||||
},
|
||||
// TODO: non pre-opened file == ErrnoBadf
|
||||
}
|
||||
@@ -345,8 +386,8 @@ func Test_fdPrestatDirName_Errors(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
errno := fdPrestatDirName(testCtx, mod, tc.fd, tc.path, tc.pathLen)
|
||||
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
|
||||
requireErrno(t, tc.expectedErrno, mod, functionFdPrestatDirName, uint64(tc.fd), uint64(tc.path), uint64(tc.pathLen))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -410,11 +451,16 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
fd, iovs, iovsCount, resultSize uint32
|
||||
memory []byte
|
||||
expectedErrno Errno
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
name: "invalid fd",
|
||||
fd: 42, // arbitrary invalid fd
|
||||
expectedErrno: ErrnoBadf,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_read(fd=42,iovs=65536,iovs_len=65536,result.size=65536)
|
||||
<== EBADF
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "out-of-memory reading iovs[0].offset",
|
||||
@@ -422,6 +468,10 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
iovs: 1,
|
||||
memory: []byte{'?'},
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_read(fd=4,iovs=65536,iovs_len=65535,result.size=65535)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "out-of-memory reading iovs[0].length",
|
||||
@@ -432,6 +482,10 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
9, 0, 0, 0, // = iovs[0].offset
|
||||
},
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_read(fd=4,iovs=65532,iovs_len=65532,result.size=65531)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "iovs[0].offset is outside memory",
|
||||
@@ -443,6 +497,10 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
1, 0, 0, 0, // = iovs[0].length
|
||||
},
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_read(fd=4,iovs=65528,iovs_len=65528,result.size=65527)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "length to read exceeds memory by 1",
|
||||
@@ -455,6 +513,10 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
'?',
|
||||
},
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_read(fd=4,iovs=65527,iovs_len=65527,result.size=65526)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "resultSize offset is outside memory",
|
||||
@@ -468,6 +530,10 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
'?',
|
||||
},
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_read(fd=4,iovs=65527,iovs_len=65527,result.size=65536)
|
||||
<== EFAULT
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -481,8 +547,8 @@ func Test_fdRead_Errors(t *testing.T) {
|
||||
memoryWriteOK := mod.Memory().Write(testCtx, offset, tc.memory)
|
||||
require.True(t, memoryWriteOK)
|
||||
|
||||
errno := fdRead(testCtx, mod, tc.fd, tc.iovs+offset, tc.iovsCount+offset, tc.resultSize+offset)
|
||||
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
|
||||
requireErrno(t, tc.expectedErrno, mod, functionFdRead, uint64(tc.fd), uint64(tc.iovs+offset), uint64(tc.iovsCount+offset), uint64(tc.resultSize+offset))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,57 +46,61 @@ const (
|
||||
// * This is similar to `poll` in POSIX.
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#poll_oneoff
|
||||
// See https://linux.die.net/man/3/poll
|
||||
func pollOneoff(ctx context.Context, mod api.Module, in, out, nsubscriptions, resultNevents uint32) Errno {
|
||||
if nsubscriptions == 0 {
|
||||
return ErrnoInval
|
||||
}
|
||||
|
||||
mem := mod.Memory()
|
||||
|
||||
// Ensure capacity prior to the read loop to reduce error handling.
|
||||
inBuf, ok := mem.Read(ctx, in, nsubscriptions*48)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
outBuf, ok := mem.Read(ctx, out, nsubscriptions*32)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// Eagerly write the number of events which will equal subscriptions unless
|
||||
// there's a fault in parsing (not processing).
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultNevents, nsubscriptions) {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// Loop through all subscriptions and write their output.
|
||||
for sub := uint32(0); sub < nsubscriptions; sub++ {
|
||||
inOffset := sub * 48
|
||||
outOffset := sub * 32
|
||||
|
||||
var errno Errno
|
||||
eventType := inBuf[inOffset+8] // +8 past userdata
|
||||
switch eventType {
|
||||
case eventTypeClock: // handle later
|
||||
// +8 past userdata +8 name alignment
|
||||
errno = processClockEvent(ctx, mod, inBuf[inOffset+8+8:])
|
||||
case eventTypeFdRead, eventTypeFdWrite:
|
||||
// +8 past userdata +4 FD alignment
|
||||
errno = processFDEvent(ctx, mod, eventType, inBuf[inOffset+8+4:])
|
||||
default:
|
||||
var pollOneoff = wasm.NewGoFunc(
|
||||
functionPollOneoff, functionPollOneoff,
|
||||
[]string{"in", "out", "nsubscriptions", "result.nevents"},
|
||||
func(ctx context.Context, mod api.Module, in, out, nsubscriptions, resultNevents uint32) Errno {
|
||||
if nsubscriptions == 0 {
|
||||
return ErrnoInval
|
||||
}
|
||||
|
||||
// Write the event corresponding to the processed subscription.
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct
|
||||
copy(outBuf, inBuf[inOffset:inOffset+8]) // userdata
|
||||
outBuf[outOffset+8] = byte(errno) // uint16, but safe as < 255
|
||||
outBuf[outOffset+9] = 0
|
||||
binary.LittleEndian.PutUint32(outBuf[outOffset+10:], uint32(eventType))
|
||||
// TODO: When FD events are supported, write outOffset+16
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
mem := mod.Memory()
|
||||
|
||||
// Ensure capacity prior to the read loop to reduce error handling.
|
||||
inBuf, ok := mem.Read(ctx, in, nsubscriptions*48)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
outBuf, ok := mem.Read(ctx, out, nsubscriptions*32)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// Eagerly write the number of events which will equal subscriptions unless
|
||||
// there's a fault in parsing (not processing).
|
||||
if !mod.Memory().WriteUint32Le(ctx, resultNevents, nsubscriptions) {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// Loop through all subscriptions and write their output.
|
||||
for sub := uint32(0); sub < nsubscriptions; sub++ {
|
||||
inOffset := sub * 48
|
||||
outOffset := sub * 32
|
||||
|
||||
var errno Errno
|
||||
eventType := inBuf[inOffset+8] // +8 past userdata
|
||||
switch eventType {
|
||||
case eventTypeClock: // handle later
|
||||
// +8 past userdata +8 name alignment
|
||||
errno = processClockEvent(ctx, mod, inBuf[inOffset+8+8:])
|
||||
case eventTypeFdRead, eventTypeFdWrite:
|
||||
// +8 past userdata +4 FD alignment
|
||||
errno = processFDEvent(ctx, mod, eventType, inBuf[inOffset+8+4:])
|
||||
default:
|
||||
return ErrnoInval
|
||||
}
|
||||
|
||||
// Write the event corresponding to the processed subscription.
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct
|
||||
copy(outBuf, inBuf[inOffset:inOffset+8]) // userdata
|
||||
outBuf[outOffset+8] = byte(errno) // uint16, but safe as < 255
|
||||
outBuf[outOffset+9] = 0
|
||||
binary.LittleEndian.PutUint32(outBuf[outOffset+10:], uint32(eventType))
|
||||
// TODO: When FD events are supported, write outOffset+16
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
// processClockEvent supports only relative name events, as that's what's used
|
||||
// to implement sleep in various compilers including Rust, Zig and TinyGo.
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
@@ -21,18 +22,22 @@ const (
|
||||
// * exitCode: exit code.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
|
||||
func procExit(ctx context.Context, mod api.Module, exitCode uint32) {
|
||||
// Ensure other callers see the exit code.
|
||||
_ = mod.CloseWithExitCode(ctx, exitCode)
|
||||
var procExit = wasm.NewGoFunc(
|
||||
functionProcExit, functionProcExit,
|
||||
[]string{"rval"},
|
||||
func(ctx context.Context, mod api.Module, exitCode uint32) {
|
||||
// Ensure other callers see the exit code.
|
||||
_ = mod.CloseWithExitCode(ctx, exitCode)
|
||||
|
||||
// Prevent any code from executing after this function. For example, LLVM
|
||||
// inserts unreachable instructions after calls to exit.
|
||||
// See: https://github.com/emscripten-core/emscripten/issues/12322
|
||||
panic(sys.NewExitError(mod.Name(), exitCode))
|
||||
}
|
||||
// Prevent any code from executing after this function. For example, LLVM
|
||||
// inserts unreachable instructions after calls to exit.
|
||||
// See: https://github.com/emscripten-core/emscripten/issues/12322
|
||||
panic(sys.NewExitError(mod.Name(), exitCode))
|
||||
},
|
||||
)
|
||||
|
||||
// procRaise is the WASI function named functionProcRaise which sends a signal
|
||||
// to the process of the calling thread.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-proc_raisesig-signal---errno
|
||||
var procRaise = stubFunction(i32) // stubbed for GrainLang per #271.
|
||||
var procRaise = stubFunction(functionProcRaise, []wasm.ValueType{i32}, []string{"sig"})
|
||||
|
||||
@@ -20,10 +20,16 @@ func Test_procExit(t *testing.T) {
|
||||
{
|
||||
name: "success (exitcode 0)",
|
||||
exitCode: 0,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.proc_exit(rval=0)
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "arbitrary non-zero exitcode",
|
||||
exitCode: 42,
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.proc_exit(rval=42)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -34,9 +40,9 @@ func Test_procExit(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
// Since procExit panics, any opcodes afterwards cannot be reached.
|
||||
err := require.CapturePanic(func() { procExit(testCtx, mod, tc.exitCode) })
|
||||
_, err := mod.ExportedFunction(functionProcExit).Call(testCtx, uint64(tc.exitCode))
|
||||
require.Equal(t, tc.exitCode, err.(*sys.ExitError).ExitCode())
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,19 +34,23 @@ const functionRandomGet = "random_get"
|
||||
// buf --^
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-bufLen-size---errno
|
||||
func randomGet(ctx context.Context, mod api.Module, buf uint32, bufLen uint32) (errno Errno) {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
randSource := sysCtx.RandSource()
|
||||
var randomGet = wasm.NewGoFunc(
|
||||
functionRandomGet, functionRandomGet,
|
||||
[]string{"buf", "buf_len"},
|
||||
func(ctx context.Context, mod api.Module, buf uint32, bufLen uint32) (errno Errno) {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
randSource := sysCtx.RandSource()
|
||||
|
||||
randomBytes, ok := mod.Memory().Read(ctx, buf, bufLen)
|
||||
if !ok { // out-of-range
|
||||
return ErrnoFault
|
||||
}
|
||||
randomBytes, ok := mod.Memory().Read(ctx, buf, bufLen)
|
||||
if !ok { // out-of-range
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// We can ignore the returned n as it only != byteCount on error
|
||||
if _, err := io.ReadAtLeast(randSource, randomBytes, int(bufLen)); err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
// We can ignore the returned n as it only != byteCount on error
|
||||
if _, err := io.ReadAtLeast(randSource, randomBytes, int(bufLen)); err != nil {
|
||||
return ErrnoIo
|
||||
}
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
return ErrnoSuccess
|
||||
},
|
||||
)
|
||||
|
||||
@@ -92,10 +92,18 @@ func Test_randomGet_SourceError(t *testing.T) {
|
||||
{
|
||||
name: "error",
|
||||
randSource: iotest.ErrReader(errors.New("RandSource error")),
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.random_get(buf=1,buf_len=5)
|
||||
<== EIO
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "incomplete",
|
||||
randSource: bytes.NewReader([]byte{1, 2}),
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.random_get(buf=1,buf_len=5)
|
||||
<== EIO
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -106,9 +114,8 @@ func Test_randomGet_SourceError(t *testing.T) {
|
||||
WithRandSource(tc.randSource))
|
||||
defer r.Close(testCtx)
|
||||
|
||||
errno := randomGet(testCtx, mod, uint32(1), uint32(5)) // arbitrary offset and length
|
||||
require.Equal(t, ErrnoIo, errno, ErrnoName(errno))
|
||||
require.Equal(t, tc.expectedLog, log.String())
|
||||
requireErrno(t, ErrnoIo, mod, functionRandomGet, uint64(1), uint64(5)) // arbitrary offset and length
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ const functionSchedYield = "sched_yield"
|
||||
// yields execution of the calling thread.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sched_yield---errno
|
||||
var schedYield = stubFunction() // stubbed for GrainLang per #271.
|
||||
var schedYield = stubFunction(functionSchedYield, nil, nil)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package wasi_snapshot_preview1
|
||||
|
||||
import "github.com/tetratelabs/wazero/internal/wasm"
|
||||
|
||||
const (
|
||||
functionSockRecv = "sock_recv"
|
||||
functionSockSend = "sock_send"
|
||||
@@ -10,16 +12,28 @@ const (
|
||||
// message from a socket.
|
||||
//
|
||||
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_recvfd-fd-ri_data-iovec_array-ri_flags-riflags---errno-size-roflags
|
||||
var sockRecv = stubFunction(i32, i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var sockRecv = stubFunction(
|
||||
functionSockRecv,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32, i32},
|
||||
[]string{"fd", "ri_data", "ri_data_count", "ri_flags", "result.ro_datalen", "result.ro_flags"},
|
||||
)
|
||||
|
||||
// sockSend is the WASI function named functionSockSend which sends a message
|
||||
// on a socket.
|
||||
//
|
||||
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_sendfd-fd-si_data-ciovec_array-si_flags-siflags---errno-size
|
||||
var sockSend = stubFunction(i32, i32, i32, i32, i32) // stubbed for GrainLang per #271.
|
||||
var sockSend = stubFunction(
|
||||
functionSockSend,
|
||||
[]wasm.ValueType{i32, i32, i32, i32, i32},
|
||||
[]string{"fd", "si_data", "si_data_count", "si_flags", "result.so_datalen"},
|
||||
)
|
||||
|
||||
// sockShutdown is the WASI function named functionSockShutdown which shuts
|
||||
// down socket send and receive channels.
|
||||
//
|
||||
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno
|
||||
var sockShutdown = stubFunction(i32, i32) // stubbed for GrainLang per #271.
|
||||
var sockShutdown = stubFunction(
|
||||
functionSockShutdown,
|
||||
[]wasm.ValueType{i32, i32},
|
||||
[]string{"fd", "how"},
|
||||
)
|
||||
|
||||
@@ -111,96 +111,51 @@ func (b *builder) Instantiate(ctx context.Context, ns wazero.Namespace) (api.Clo
|
||||
func exportFunctions(builder wazero.ModuleBuilder) {
|
||||
// Note:se are ordered per spec for consistency even if the resulting map can't guarantee that.
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#functions
|
||||
builder.ExportFunction(functionArgsGet, argsGet,
|
||||
functionArgsGet, "argv", "argv_buf")
|
||||
builder.ExportFunction(functionArgsSizesGet, argsSizesGet,
|
||||
functionArgsSizesGet, "result.argc", "result.argv_buf_size")
|
||||
builder.ExportFunction(functionEnvironGet, environGet,
|
||||
functionEnvironGet, "environ", "environ_buf")
|
||||
builder.ExportFunction(functionEnvironSizesGet, environSizesGet,
|
||||
functionEnvironSizesGet, "result.environc", "result.environBufSize")
|
||||
builder.ExportFunction(functionClockResGet, clockResGet,
|
||||
functionClockResGet, "id", "result.resolution")
|
||||
builder.ExportFunction(functionClockTimeGet, clockTimeGet,
|
||||
functionClockTimeGet, "id", "precision", "result.timestamp")
|
||||
builder.ExportFunction(functionFdAdvise, fdAdvise,
|
||||
functionFdAdvise, "fd", "offset", "len", "result.advice")
|
||||
builder.ExportFunction(functionFdAllocate, fdAllocate,
|
||||
functionFdAllocate, "fd", "offset", "len")
|
||||
builder.ExportFunction(functionFdClose, fdClose,
|
||||
functionFdClose, "fd")
|
||||
builder.ExportFunction(functionFdDatasync, fdDatasync,
|
||||
functionFdDatasync, "fd")
|
||||
builder.ExportFunction(functionFdFdstatGet, fdFdstatGet,
|
||||
functionFdFdstatGet, "fd", "result.stat")
|
||||
builder.ExportFunction(functionFdFdstatSetFlags, fdFdstatSetFlags,
|
||||
functionFdFdstatSetFlags, "fd", "flags")
|
||||
builder.ExportFunction(functionFdFdstatSetRights, fdFdstatSetRights,
|
||||
functionFdFdstatSetRights, "fd", "fs_rights_base", "fs_rights_inheriting")
|
||||
builder.ExportFunction(functionFdFilestatGet, fdFilestatGet,
|
||||
functionFdFilestatGet, "fd", "result.buf")
|
||||
builder.ExportFunction(functionFdFilestatSetSize, fdFilestatSetSize,
|
||||
functionFdFilestatSetSize, "fd", "size")
|
||||
builder.ExportFunction(functionFdFilestatSetTimes, fdFilestatSetTimes,
|
||||
functionFdFilestatSetTimes, "fd", "atim", "mtim", "fst_flags")
|
||||
builder.ExportFunction(functionFdPread, fdPread,
|
||||
functionFdPread, "fd", "iovs", "iovs_len", "offset", "result.nread")
|
||||
builder.ExportFunction(functionFdPrestatGet, fdPrestatGet,
|
||||
functionFdPrestatGet, "fd", "result.prestat")
|
||||
builder.ExportFunction(functionFdPrestatDirName, fdPrestatDirName,
|
||||
functionFdPrestatDirName, "fd", "path", "path_len")
|
||||
builder.ExportFunction(functionFdPwrite, fdPwrite,
|
||||
functionFdPwrite, "fd", "iovs", "iovs_len", "offset", "result.nwritten")
|
||||
builder.ExportFunction(functionFdRead, fdRead,
|
||||
functionFdRead, "fd", "iovs", "iovs_len", "result.size")
|
||||
builder.ExportFunction(functionFdReaddir, fdReaddir,
|
||||
functionFdReaddir, "fd", "buf", "buf_len", "cookie", "result.bufused")
|
||||
builder.ExportFunction(functionFdRenumber, fdRenumber,
|
||||
functionFdRenumber, "fd", "to")
|
||||
builder.ExportFunction(functionFdSeek, fdSeek,
|
||||
functionFdSeek, "fd", "offset", "whence", "result.newoffset")
|
||||
builder.ExportFunction(functionFdSync, fdSync,
|
||||
functionFdSync, "fd")
|
||||
builder.ExportFunction(functionFdTell, fdTell,
|
||||
functionFdTell, "fd", "result.offset")
|
||||
builder.ExportFunction(functionFdWrite, fdWrite,
|
||||
functionFdWrite, "fd", "iovs", "iovs_len", "result.size")
|
||||
builder.ExportFunction(functionPathCreateDirectory, pathCreateDirectory,
|
||||
functionPathCreateDirectory, "fd", "path", "path_len")
|
||||
builder.ExportFunction(functionPathFilestatGet, pathFilestatGet,
|
||||
functionPathFilestatGet, "fd", "flags", "path", "path_len", "result.buf")
|
||||
builder.ExportFunction(functionPathFilestatSetTimes, pathFilestatSetTimes,
|
||||
functionPathFilestatSetTimes, "fd", "flags", "path", "path_len", "atim", "mtim", "fst_flags")
|
||||
builder.ExportFunction(functionPathLink, pathLink,
|
||||
functionPathLink, "old_fd", "old_flags", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len")
|
||||
builder.ExportFunction(functionPathOpen, pathOpen,
|
||||
functionPathOpen, "fd", "dirflags", "path", "path_len", "oflags", "fs_rights_base", "fs_rights_inheriting", "fdflags", "result.opened_fd")
|
||||
builder.ExportFunction(functionPathReadlink, pathReadlink,
|
||||
functionPathReadlink, "fd", "path", "path_len", "buf", "buf_len", "result.bufused")
|
||||
builder.ExportFunction(functionPathRemoveDirectory, pathRemoveDirectory,
|
||||
functionPathRemoveDirectory, "fd", "path", "path_len")
|
||||
builder.ExportFunction(functionPathRename, pathRename,
|
||||
functionPathRename, "fd", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len")
|
||||
builder.ExportFunction(functionPathSymlink, pathSymlink,
|
||||
functionPathSymlink, "old_path", "old_path_len", "fd", "new_path", "new_path_len")
|
||||
builder.ExportFunction(functionPathUnlinkFile, pathUnlinkFile,
|
||||
functionPathUnlinkFile, "fd", "path", "path_len")
|
||||
builder.ExportFunction(functionPollOneoff, pollOneoff,
|
||||
functionPollOneoff, "in", "out", "nsubscriptions", "result.nevents")
|
||||
builder.ExportFunction(functionProcExit, procExit,
|
||||
functionProcExit, "rval")
|
||||
builder.ExportFunction(functionProcRaise, procRaise,
|
||||
functionProcRaise, "sig")
|
||||
builder.ExportFunction(functionSchedYield, schedYield,
|
||||
functionSchedYield)
|
||||
builder.ExportFunction(functionRandomGet, randomGet,
|
||||
functionRandomGet, "buf", "buf_len")
|
||||
builder.ExportFunction(functionSockRecv, sockRecv,
|
||||
functionSockRecv, "fd", "ri_data", "ri_data_count", "ri_flags", "result.ro_datalen", "result.ro_flags")
|
||||
builder.ExportFunction(functionSockSend, sockSend,
|
||||
functionSockSend, "fd", "si_data", "si_data_count", "si_flags", "result.so_datalen")
|
||||
builder.ExportFunction(functionSockShutdown, sockShutdown,
|
||||
functionSockShutdown, "fd", "how")
|
||||
builder.ExportFunction(argsGet.Name, argsGet)
|
||||
builder.ExportFunction(argsSizesGet.Name, argsSizesGet)
|
||||
builder.ExportFunction(environGet.Name, environGet)
|
||||
builder.ExportFunction(environSizesGet.Name, environSizesGet)
|
||||
builder.ExportFunction(clockResGet.Name, clockResGet)
|
||||
builder.ExportFunction(clockTimeGet.Name, clockTimeGet)
|
||||
builder.ExportFunction(fdAdvise.Name, fdAdvise)
|
||||
builder.ExportFunction(fdAllocate.Name, fdAllocate)
|
||||
builder.ExportFunction(fdClose.Name, fdClose)
|
||||
builder.ExportFunction(fdDatasync.Name, fdDatasync)
|
||||
builder.ExportFunction(fdFdstatGet.Name, fdFdstatGet)
|
||||
builder.ExportFunction(fdFdstatSetFlags.Name, fdFdstatSetFlags)
|
||||
builder.ExportFunction(fdFdstatSetRights.Name, fdFdstatSetRights)
|
||||
builder.ExportFunction(fdFilestatGet.Name, fdFilestatGet)
|
||||
builder.ExportFunction(fdFilestatSetSize.Name, fdFilestatSetSize)
|
||||
builder.ExportFunction(fdFilestatSetTimes.Name, fdFilestatSetTimes)
|
||||
builder.ExportFunction(fdPread.Name, fdPread)
|
||||
builder.ExportFunction(fdPrestatGet.Name, fdPrestatGet)
|
||||
builder.ExportFunction(fdPrestatDirName.Name, fdPrestatDirName)
|
||||
builder.ExportFunction(fdPwrite.Name, fdPwrite)
|
||||
builder.ExportFunction(fdRead.Name, fdRead)
|
||||
builder.ExportFunction(fdReaddir.Name, fdReaddir)
|
||||
builder.ExportFunction(fdRenumber.Name, fdRenumber)
|
||||
builder.ExportFunction(fdSeek.Name, fdSeek)
|
||||
builder.ExportFunction(fdSync.Name, fdSync)
|
||||
builder.ExportFunction(fdTell.Name, fdTell)
|
||||
builder.ExportFunction(fdWrite.Name, fdWrite)
|
||||
builder.ExportFunction(pathCreateDirectory.Name, pathCreateDirectory)
|
||||
builder.ExportFunction(pathFilestatGet.Name, pathFilestatGet)
|
||||
builder.ExportFunction(pathFilestatSetTimes.Name, pathFilestatSetTimes)
|
||||
builder.ExportFunction(pathLink.Name, pathLink)
|
||||
builder.ExportFunction(pathOpen.Name, pathOpen)
|
||||
builder.ExportFunction(pathReadlink.Name, pathReadlink)
|
||||
builder.ExportFunction(pathRemoveDirectory.Name, pathRemoveDirectory)
|
||||
builder.ExportFunction(pathRename.Name, pathRename)
|
||||
builder.ExportFunction(pathSymlink.Name, pathSymlink)
|
||||
builder.ExportFunction(pathUnlinkFile.Name, pathUnlinkFile)
|
||||
builder.ExportFunction(pollOneoff.Name, pollOneoff)
|
||||
builder.ExportFunction(procExit.Name, procExit)
|
||||
builder.ExportFunction(procRaise.Name, procRaise)
|
||||
builder.ExportFunction(schedYield.Name, schedYield)
|
||||
builder.ExportFunction(randomGet.Name, randomGet)
|
||||
builder.ExportFunction(sockRecv.Name, sockRecv)
|
||||
builder.ExportFunction(sockSend.Name, sockSend)
|
||||
builder.ExportFunction(sockShutdown.Name, sockShutdown)
|
||||
}
|
||||
|
||||
func writeOffsetsAndNullTerminatedValues(ctx context.Context, mem api.Memory, values []string, offsets, bytes uint32) Errno {
|
||||
@@ -225,15 +180,14 @@ func writeOffsetsAndNullTerminatedValues(ctx context.Context, mem api.Memory, va
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// stubFunction returns a function for the given params which returns ErrnoNosys.
|
||||
func stubFunction(params ...wasm.ValueType) *wasm.Func {
|
||||
// stubFunction stubs for GrainLang per #271.
|
||||
func stubFunction(name string, paramTypes []wasm.ValueType, paramNames []string) *wasm.Func {
|
||||
return &wasm.Func{
|
||||
Type: &wasm.FunctionType{
|
||||
Params: params,
|
||||
Results: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
ParamNumInUint64: len(params),
|
||||
ResultNumInUint64: 1,
|
||||
},
|
||||
Code: &wasm.Code{Body: []byte{wasm.OpcodeI32Const, byte(ErrnoNosys), wasm.OpcodeEnd}},
|
||||
Name: name,
|
||||
ExportNames: []string{name},
|
||||
ParamTypes: paramTypes,
|
||||
ParamNames: paramNames,
|
||||
ResultTypes: []wasm.ValueType{i32},
|
||||
Code: &wasm.Code{Body: []byte{wasm.OpcodeI32Const, byte(ErrnoNosys), wasm.OpcodeEnd}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,13 @@ func Benchmark_EnvironGet(b *testing.B) {
|
||||
|
||||
b.Run("environGet", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if environGet(testCtx, mod, 0, 4) != ErrnoSuccess {
|
||||
b.Fatal()
|
||||
results, err := mod.ExportedFunction(functionEnvironGet).Call(testCtx, uint64(0), uint64(4))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
errno := Errno(results[0])
|
||||
if errno != ErrnoSuccess {
|
||||
b.Fatal(ErrnoName(errno))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user