cmd/land: read, write, close, open
This commit is contained in:
parent
4308cdd781
commit
7ec598dc43
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -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)))
|
Loading…
Reference in New Issue