cmd/land: read, write, close, open

This commit is contained in:
Cadey Ratio 2018-06-18 01:48:10 +00:00
parent 4308cdd781
commit 7ec598dc43
7 changed files with 163 additions and 7 deletions

13
build.sh Executable file
View File

@ -0,0 +1,13 @@
#! /bin/sh -e
cd "cmd/land"
vgo build -o ../../bin/land
cd "../../userland/src/lib"
node ../../compile.js counter.walt counter.wasm
node ../../compile.js memory.walt memory.wasm
wat2wasm -o add.wasm add.wast
wat2wasm -o env.wasm env.wast
wat2wasm -o fileops.wasm fileops.wast
wat2wasm -o hello.wasm hello.wast
wat2wasm -o writefile.wasm writefile.wast
cd "../../.."
go test -v ./cmd/land | tee bin/test.log

16
cmd/land/logfile.go Normal file
View File

@ -0,0 +1,16 @@
package main
import "log"
type loggerFile struct {
*log.Logger
}
func (l loggerFile) Close() error { return nil }
func (l loggerFile) Read([]byte) (int, error) { return 0, ErrNotSupported }
func (l loggerFile) Write(data []byte) (int, error) {
l.Printf("%s", string(data))
return len(data), nil
}

View File

@ -25,7 +25,7 @@ func main() {
}
defer fin.Close()
p, err := NewProcess(fin)
p, err := NewProcess(fin, fname)
if err != nil {
ln.FatalErr(ctx, err, ln.F{"msg": "can't create process", "fname": fname})
}

View File

@ -4,6 +4,7 @@ import (
gcontext "context"
"errors"
"io"
"log"
"os"
"reflect"
"strings"
@ -23,10 +24,11 @@ type Process struct {
mod *wasm.Module
fs afero.Fs
files []afero.File
name string
}
// NewProcess constructs a new webassembly process based on the input webassembly module as a reader.
func NewProcess(fin io.Reader) (*Process, error) {
func NewProcess(fin io.Reader, name string) (*Process, error) {
p := &Process{}
mod, err := wasm.ReadModule(fin, p.importer)
@ -66,6 +68,11 @@ func (p *Process) importer(name string) (*wasm.Module, error) {
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
},
{
Form: 0,
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
},
},
}
m.FunctionIndexSpace = []wasm.Function{
@ -84,6 +91,16 @@ func (p *Process) importer(name string) (*wasm.Module, error) {
Host: reflect.ValueOf(p.write),
Body: &wasm.FunctionBody{},
},
{
Sig: &m.Types.Entries[1],
Host: reflect.ValueOf(p.read),
Body: &wasm.FunctionBody{},
},
{
Sig: &m.Types.Entries[2],
Host: reflect.ValueOf(p.close),
Body: &wasm.FunctionBody{},
},
}
m.Export = &wasm.SectionExports{
Entries: map[string]wasm.ExportEntry{
@ -102,6 +119,16 @@ func (p *Process) importer(name string) (*wasm.Module, error) {
Kind: wasm.ExternalFunction,
Index: 2,
},
"read": {
FieldStr: "read",
Kind: wasm.ExternalFunction,
Index: 3,
},
"close": {
FieldStr: "close",
Kind: wasm.ExternalFunction,
Index: 4,
},
},
}
return m, nil
@ -179,8 +206,12 @@ func (p *Process) open(fnamesP int32, flags int32) int32 {
var fi afero.File
var err error
switch str {
case "magic://stdout":
fi, err = afero.OsFs{}.Create("/dev/stdout")
case "stdout":
fi = stdFD{
ReadWriteCloser: loggerFile{
Logger: log.New(os.Stdout, p.name, log.LstdFlags),
},
}
default:
fi, err = p.fs.OpenFile(string(str), int(flags), 0666)
@ -211,6 +242,41 @@ func (p *Process) write(fd int32, ptr int32, len int32) int32 {
return int32(n)
}
func (p *Process) read(fd int32, ptr int32, len int32) int32 {
data := make([]byte, len)
na, err := p.files[fd].Read(data)
if err != nil {
panic(err)
}
nb, err := p.writeMem(ptr, data)
if err != nil {
panic(err)
}
if na != nb {
panic("did not copy the same number of bytes???")
}
return int32(na)
}
func (p *Process) close(fd int32) int32 {
f := p.files[fd]
err := f.Close()
if err != nil {
panic(err)
}
if len(p.files) == 1 {
p.files = []afero.File{}
} else {
p.files = append(p.files[:fd], p.files[fd+1])
}
return 0
}
func (p *Process) Main() (uint32, error) {
foundMain := false
mainID := uint32(0)

View File

@ -5,14 +5,14 @@ import (
"testing"
)
func testWasmFile(t *testing.T, fname string) {
func testWasmFile(t *testing.T, fname string) *Process {
fin, err := os.Open(fname)
if err != nil {
t.Fatal(err)
}
defer fin.Close()
p, err := NewProcess(fin)
p, err := NewProcess(fin, fname)
if err != nil {
t.Fatal(err)
}
@ -25,6 +25,8 @@ func testWasmFile(t *testing.T, fname string) {
if ret != 0 {
t.Fatalf("expected return code to be 0, got: %d", ret)
}
return p
}
func TestHelloWorld(t *testing.T) {
@ -34,7 +36,7 @@ func TestHelloWorld(t *testing.T) {
}
defer fin.Close()
p, err := NewProcess(fin)
p, err := NewProcess(fin, "hello")
if err != nil {
t.Fatal(err)
}
@ -67,3 +69,11 @@ func TestHelloWorld(t *testing.T) {
func TestWriteFile(t *testing.T) {
testWasmFile(t, "./testdata/writefile.wasm")
}
func TestFileOps(t *testing.T) {
p := testWasmFile(t, "./testdata/fileops.wasm")
data := p.readMem(255)
if string(data) != "Hello, world!\n" {
t.Fatalf("wanted \"Hello, world\", got: %q", string(data))
}
}

BIN
cmd/land/testdata/fileops.wasm vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,51 @@
(module
;; import functions from env
(func $close (import "env" "close") (param i32) (result i32))
(func $open (import "env" "open") (param i32 i32) (result i32))
(func $write (import "env" "write") (param i32 i32 i32) (result i32))
(func $read (import "env" "read") (param i32 i32 i32) (result i32))
(memory $mem 1)
(data (i32.const 200) "data")
(data (i32.const 230) "Hello, world!\n")
(func $main (result i32)
(local $fd i32)
;; open stdout
(i32.const 200)
(i32.const 42)
(call $open)
;; set $fd to the file descriptor
(set_local $fd)
;; write hello world
(get_local $fd)
(i32.const 230)
(i32.const 14)
(call $write)
(drop)
;; close file
(get_local $fd)
(call $close)
;; open new file
(i32.const 200)
(i32.const 42)
(call $open)
;; set $fd to the new file descriptor
(set_local $fd)
;; read hello world
(get_local $fd)
(i32.const 255)
(i32.const 14)
(call $read)
(drop)
;; return 0
)
(export "main" (func $main)))