diff --git a/interpreter.go b/interpreter.go index a0c7a53b..708ce86f 100644 --- a/interpreter.go +++ b/interpreter.go @@ -22,6 +22,7 @@ var ignoreInitCalls = map[string]struct{}{ "(syscall/js.Value).Get": struct{}{}, "(syscall/js.Value).New": struct{}{}, "(syscall/js.Value).Int": struct{}{}, + "os.init$1": struct{}{}, } // Interpret instructions as far as possible, and drop those instructions from @@ -91,14 +92,67 @@ func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter if callee == nil { return i, nil // don't understand dynamic dispatch } - if _, ok := ignoreInitCalls[callee.String()]; ok && callee.Signature.Results().Len() == 1 { + if _, ok := ignoreInitCalls[callee.String()]; ok { // These calls are not needed and can be ignored, for the time // being. - var err error - locals[instr], err = p.getZeroValue(callee.Signature.Results().At(0).Type()) + results := make([]Value, callee.Signature.Results().Len()) + for i := range results { + var err error + results[i], err = p.getZeroValue(callee.Signature.Results().At(i).Type()) + if err != nil { + return i, err + } + } + if len(results) == 1 { + locals[instr] = results[0] + } else if len(results) > 1 { + locals[instr] = &StructValue{Fields: results} + } + continue + } + if callee.String() == "os.NewFile" { + // Emulate the creation of os.Stdin, os.Stdout and os.Stderr. + resultPtrType := callee.Signature.Results().At(0).Type().(*types.Pointer) + resultStructOuterType := resultPtrType.Elem().Underlying().(*types.Struct) + if resultStructOuterType.NumFields() != 1 { + panic("expected 1 field in os.File struct") + } + fileInnerPtrType := resultStructOuterType.Field(0).Type().(*types.Pointer) + fileInnerType := fileInnerPtrType.Elem().(*types.Named) + fileInnerStructType := fileInnerType.Underlying().(*types.Struct) + fileInner, err := p.getZeroValue(fileInnerType) // os.file if err != nil { return i, err } + for fieldIndex := 0; fieldIndex < fileInnerStructType.NumFields(); fieldIndex++ { + field := fileInnerStructType.Field(fieldIndex) + if field.Name() == "name" { + // Set the 'name' field. + name, err := p.getValue(common.Args[1], locals) + if err != nil { + return i, err + } + fileInner.(*StructValue).Fields[fieldIndex] = name + } else if field.Type().String() == "internal/poll.FD" { + // Set the file descriptor field. + field := field.Type().Underlying().(*types.Struct) + for subfieldIndex := 0; subfieldIndex < field.NumFields(); subfieldIndex++ { + subfield := field.Field(subfieldIndex) + if subfield.Name() == "Sysfd" { + sysfd, err := p.getValue(common.Args[0], locals) + if err != nil { + return i, err + } + sysfd = &ConstValue{Expr: ssa.NewConst(sysfd.(*ConstValue).Expr.Value, subfield.Type())} + fileInner.(*StructValue).Fields[fieldIndex].(*StructValue).Fields[subfieldIndex] = sysfd + } + } + } + } + fileInnerPtr := &PointerValue{fileInnerPtrType, &fileInner} // *os.file + var fileOuter Value = &StructValue{Type: resultPtrType.Elem(), Fields: []Value{fileInnerPtr}} // os.File + result := &PointerValue{resultPtrType.Elem(), &fileOuter} // *os.File + locals[instr] = result continue } if canInterpret(callee) { @@ -165,6 +219,12 @@ func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter } case *ssa.DebugRef: // ignore + case *ssa.Extract: + tuple, err := p.getValue(instr.Tuple, locals) + if err != nil { + return i, err + } + locals[instr] = tuple.(*StructValue).Fields[instr.Index] case *ssa.FieldAddr: x, err := p.getValue(instr.X, locals) if err != nil { @@ -311,6 +371,7 @@ func canInterpret(callee *ssa.Function) bool { case *ssa.Alloc: case *ssa.Convert: case *ssa.DebugRef: + case *ssa.Extract: case *ssa.FieldAddr: case *ssa.IndexAddr: case *ssa.MakeInterface: diff --git a/src/runtime/atomic.go b/src/runtime/atomic.go new file mode 100644 index 00000000..f9ae3031 --- /dev/null +++ b/src/runtime/atomic.go @@ -0,0 +1,24 @@ +package runtime + +// This file contains implementations for the sync/atomic package. + +// All implementations assume there are no goroutines, threads or interrupts. + +//go:linkname loadUint64 sync/atomic.LoadUint64 +func loadUint64(addr *uint64) uint64 { + return *addr +} + +//go:linkname storeUint32 sync/atomic.StoreUint32 +func storeUint32(addr *uint32, val uint32) { + *addr = val +} + +//go:linkname compareAndSwapUint64 sync/atomic.CompareAndSwapUint64 +func compareAndSwapUint64(addr *uint64, old, new uint64) bool { + if *addr == old { + *addr = new + return true + } + return false +} diff --git a/src/runtime/poll.go b/src/runtime/poll.go new file mode 100644 index 00000000..c0bc71f2 --- /dev/null +++ b/src/runtime/poll.go @@ -0,0 +1,23 @@ +package runtime + +// This file implements stub functions for internal/poll. + +//go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit +func poll_runtime_pollServerInit(ctx *uint8) { + panic("todo: runtime_pollServerInit") +} + +//go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen +func poll_runtime_pollOpen(fd uintptr) (uintptr, int) { + panic("todo: runtime_pollOpen") +} + +//go:linkname poll_runtime_pollClose internal/poll.runtime_pollClose +func poll_runtime_pollClose(ctx uintptr) { + panic("todo: runtime_pollClose") +} + +//go:linkname poll_runtime_pollUnblock internal/poll.runtime_pollUnblock +func poll_runtime_pollUnblock(ctx uintptr) { + panic("todo: runtime_pollUnblock") +} diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 159e65fa..233c34da 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -49,6 +49,11 @@ func GOROOT() string { return "/usr/local/go" } +//go:linkname os_runtime_args os.runtime_args +func os_runtime_args() []string { + return nil +} + // Copy size bytes from src to dst. The memory areas must not overlap. func memcpy(dst, src unsafe.Pointer, size uintptr) { for i := uintptr(0); i < size; i++ { diff --git a/src/runtime/sync.go b/src/runtime/sync.go index 7ccdf5f6..a0851401 100644 --- a/src/runtime/sync.go +++ b/src/runtime/sync.go @@ -1 +1,13 @@ package runtime + +// This file contains stub implementations for internal/poll. + +//go:linkname semacquire internal/poll.runtime_Semacquire +func semacquire(sema *uint32) { + panic("todo: semacquire") +} + +//go:linkname semrelease internal/poll.runtime_Semrelease +func semrelease(sema *uint32) { + panic("todo: semrelease") +} diff --git a/src/runtime/syscall.go b/src/runtime/syscall.go new file mode 100644 index 00000000..6bc703b8 --- /dev/null +++ b/src/runtime/syscall.go @@ -0,0 +1,13 @@ +package runtime + +// This file implements syscall.Syscall and the like. + +//go:linkname syscall_Syscall syscall.Syscall +func syscall_Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err uintptr) { + panic("syscall") +} + +//go:linkname syscall_Syscall6 syscall.Syscall6 +func syscall_Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err uintptr) { + panic("syscall6") +}