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()
|
defer fin.Close()
|
||||||
|
|
||||||
p, err := NewProcess(fin)
|
p, err := NewProcess(fin, fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ln.FatalErr(ctx, err, ln.F{"msg": "can't create process", "fname": fname})
|
ln.FatalErr(ctx, err, ln.F{"msg": "can't create process", "fname": fname})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
gcontext "context"
|
gcontext "context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -23,10 +24,11 @@ type Process struct {
|
||||||
mod *wasm.Module
|
mod *wasm.Module
|
||||||
fs afero.Fs
|
fs afero.Fs
|
||||||
files []afero.File
|
files []afero.File
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProcess constructs a new webassembly process based on the input webassembly module as a reader.
|
// 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{}
|
p := &Process{}
|
||||||
|
|
||||||
mod, err := wasm.ReadModule(fin, p.importer)
|
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},
|
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Form: 0,
|
||||||
|
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||||
|
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
m.FunctionIndexSpace = []wasm.Function{
|
m.FunctionIndexSpace = []wasm.Function{
|
||||||
|
@ -84,6 +91,16 @@ func (p *Process) importer(name string) (*wasm.Module, error) {
|
||||||
Host: reflect.ValueOf(p.write),
|
Host: reflect.ValueOf(p.write),
|
||||||
Body: &wasm.FunctionBody{},
|
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{
|
m.Export = &wasm.SectionExports{
|
||||||
Entries: map[string]wasm.ExportEntry{
|
Entries: map[string]wasm.ExportEntry{
|
||||||
|
@ -102,6 +119,16 @@ func (p *Process) importer(name string) (*wasm.Module, error) {
|
||||||
Kind: wasm.ExternalFunction,
|
Kind: wasm.ExternalFunction,
|
||||||
Index: 2,
|
Index: 2,
|
||||||
},
|
},
|
||||||
|
"read": {
|
||||||
|
FieldStr: "read",
|
||||||
|
Kind: wasm.ExternalFunction,
|
||||||
|
Index: 3,
|
||||||
|
},
|
||||||
|
"close": {
|
||||||
|
FieldStr: "close",
|
||||||
|
Kind: wasm.ExternalFunction,
|
||||||
|
Index: 4,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
|
@ -179,8 +206,12 @@ func (p *Process) open(fnamesP int32, flags int32) int32 {
|
||||||
var fi afero.File
|
var fi afero.File
|
||||||
var err error
|
var err error
|
||||||
switch str {
|
switch str {
|
||||||
case "magic://stdout":
|
case "stdout":
|
||||||
fi, err = afero.OsFs{}.Create("/dev/stdout")
|
fi = stdFD{
|
||||||
|
ReadWriteCloser: loggerFile{
|
||||||
|
Logger: log.New(os.Stdout, p.name, log.LstdFlags),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fi, err = p.fs.OpenFile(string(str), int(flags), 0666)
|
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)
|
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) {
|
func (p *Process) Main() (uint32, error) {
|
||||||
foundMain := false
|
foundMain := false
|
||||||
mainID := uint32(0)
|
mainID := uint32(0)
|
||||||
|
|
|
@ -5,14 +5,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testWasmFile(t *testing.T, fname string) {
|
func testWasmFile(t *testing.T, fname string) *Process {
|
||||||
fin, err := os.Open(fname)
|
fin, err := os.Open(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer fin.Close()
|
defer fin.Close()
|
||||||
|
|
||||||
p, err := NewProcess(fin)
|
p, err := NewProcess(fin, fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ func testWasmFile(t *testing.T, fname string) {
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
t.Fatalf("expected return code to be 0, got: %d", ret)
|
t.Fatalf("expected return code to be 0, got: %d", ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHelloWorld(t *testing.T) {
|
func TestHelloWorld(t *testing.T) {
|
||||||
|
@ -34,7 +36,7 @@ func TestHelloWorld(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer fin.Close()
|
defer fin.Close()
|
||||||
|
|
||||||
p, err := NewProcess(fin)
|
p, err := NewProcess(fin, "hello")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -67,3 +69,11 @@ func TestHelloWorld(t *testing.T) {
|
||||||
func TestWriteFile(t *testing.T) {
|
func TestWriteFile(t *testing.T) {
|
||||||
testWasmFile(t, "./testdata/writefile.wasm")
|
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