From 7ec598dc438ad42e1d8309b73c39913175a0bbcf Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Mon, 18 Jun 2018 01:48:10 +0000 Subject: [PATCH] cmd/land: read, write, close, open --- build.sh | 13 ++++++ cmd/land/logfile.go | 16 ++++++++ cmd/land/main.go | 2 +- cmd/land/process.go | 72 +++++++++++++++++++++++++++++++-- cmd/land/process_test.go | 16 ++++++-- cmd/land/testdata/fileops.wasm | Bin 0 -> 184 bytes userland/src/lib/fileops.wast | 51 +++++++++++++++++++++++ 7 files changed, 163 insertions(+), 7 deletions(-) create mode 100755 build.sh create mode 100644 cmd/land/logfile.go create mode 100644 cmd/land/testdata/fileops.wasm create mode 100644 userland/src/lib/fileops.wast diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..2c3828d --- /dev/null +++ b/build.sh @@ -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 diff --git a/cmd/land/logfile.go b/cmd/land/logfile.go new file mode 100644 index 0000000..c56d54a --- /dev/null +++ b/cmd/land/logfile.go @@ -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 +} diff --git a/cmd/land/main.go b/cmd/land/main.go index 1aa046b..2dff9b6 100644 --- a/cmd/land/main.go +++ b/cmd/land/main.go @@ -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}) } diff --git a/cmd/land/process.go b/cmd/land/process.go index 30d0072..1d5e9fd 100644 --- a/cmd/land/process.go +++ b/cmd/land/process.go @@ -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) diff --git a/cmd/land/process_test.go b/cmd/land/process_test.go index 72f32a5..bbc939a 100644 --- a/cmd/land/process_test.go +++ b/cmd/land/process_test.go @@ -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)) + } +} diff --git a/cmd/land/testdata/fileops.wasm b/cmd/land/testdata/fileops.wasm new file mode 100644 index 0000000000000000000000000000000000000000..71d662eccff013d944cdf2176311c10e5980321f GIT binary patch literal 184 zcmYkyu?oU400hvxBt>bUbQ8MRt|IC$Wb-o`Yz851q%C&L@A)PEhMG<;cgN#kr=9?~ zQ!OK7D`H9+*8-zBE9r)3cJpC!K-wxi+yJh;jQ80=tUNl~{idMIB*D#=s<-z6D!=eV tM)RUMlx$GLylIM3R5eQc^Cz*awBC#4T6K1|>8*>a^FAEwC5*mn@-K|JCx8F| literal 0 HcmV?d00001 diff --git a/userland/src/lib/fileops.wast b/userland/src/lib/fileops.wast new file mode 100644 index 0000000..2086629 --- /dev/null +++ b/userland/src/lib/fileops.wast @@ -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)))