go/ssa/interp: add several intrinsics for Darwin

Also: drop all pretense of support for platforms other than darwin and linux.
This package is just a test of go/ssa, not a portable interpreter, and these
are the only platforms to which I have easy access.

Builds on: freebsd darwin plan9 linux windows

Change-Id: I965abc67b1280d33e933b83607a4372d65e070cf
Reviewed-on: https://go-review.googlesource.com/33163
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go
index 8b33f25..d31d2b3 100644
--- a/go/ssa/interp/external.go
+++ b/go/ssa/interp/external.go
@@ -16,7 +16,6 @@
 	"runtime"
 	"strings"
 	"sync/atomic"
-	"syscall"
 	"time"
 	"unsafe"
 
@@ -30,11 +29,11 @@
 // We have not captured that correctly here.
 
 // Key strings are from Function.String().
-var externals map[string]externalFn
+var externals = make(map[string]externalFn)
 
 func init() {
 	// That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd].
-	externals = map[string]externalFn{
+	for k, v := range map[string]externalFn{
 		"(*sync.Pool).Get":                 ext۰sync۰Pool۰Get,
 		"(*sync.Pool).Put":                 ext۰nop,
 		"(reflect.Value).Bool":             ext۰reflect۰Value۰Bool,
@@ -86,9 +85,9 @@
 		"math.Log":                         ext۰math۰Log,
 		"math.Min":                         ext۰math۰Min,
 		"math.hasSSE4":                     ext۰math۰hasSSE4,
-		"os.Pipe":                          ext۰os۰Pipe,
 		"os.runtime_args":                  ext۰os۰runtime_args,
 		"os.runtime_beforeExit":            ext۰nop,
+		"os/signal.init":                   ext۰nop,
 		"reflect.New":                      ext۰reflect۰New,
 		"reflect.SliceOf":                  ext۰reflect۰SliceOf,
 		"reflect.TypeOf":                   ext۰reflect۰TypeOf,
@@ -130,25 +129,11 @@
 		"sync/atomic.LoadUint32":           ext۰atomic۰LoadUint32,
 		"sync/atomic.StoreInt32":           ext۰atomic۰StoreInt32,
 		"sync/atomic.StoreUint32":          ext۰atomic۰StoreUint32,
-		"syscall.Close":                    ext۰syscall۰Close,
-		"syscall.Exit":                     ext۰syscall۰Exit,
-		"syscall.Fstat":                    ext۰syscall۰Fstat,
-		"syscall.Getpid":                   ext۰syscall۰Getpid,
-		"syscall.Getwd":                    ext۰syscall۰Getwd,
-		"syscall.Kill":                     ext۰syscall۰Kill,
-		"syscall.Lstat":                    ext۰syscall۰Lstat,
-		"syscall.Open":                     ext۰syscall۰Open,
-		"syscall.ParseDirent":              ext۰syscall۰ParseDirent,
-		"syscall.RawSyscall":               ext۰syscall۰RawSyscall,
-		"syscall.Read":                     ext۰syscall۰Read,
-		"syscall.ReadDirent":               ext۰syscall۰ReadDirent,
-		"syscall.Readlink":                 ext۰syscall۰Readlink,
-		"syscall.Stat":                     ext۰syscall۰Stat,
-		"syscall.Write":                    ext۰syscall۰Write,
-		"syscall.runtime_envs":             ext۰runtime۰environ,
 		"testing.runExample":               ext۰testing۰runExample,
 		"time.Sleep":                       ext۰time۰Sleep,
 		"time.now":                         ext۰time۰now,
+	} {
+		externals[k] = v
 	}
 }
 
@@ -473,26 +458,6 @@
 	return nil
 }
 
-func ext۰syscall۰Exit(fr *frame, args []value) value {
-	panic(exitPanic(args[0].(int)))
-}
-
-func ext۰syscall۰Getwd(fr *frame, args []value) value {
-	s, err := syscall.Getwd()
-	return tuple{s, wrapError(err)}
-}
-
-func ext۰syscall۰Getpid(fr *frame, args []value) value {
-	return syscall.Getpid()
-}
-
-func ext۰syscall۰Readlink(fr *frame, args []value) value {
-	path := args[0].(string)
-	buf := valueToBytes(args[1])
-	n, err := syscall.Readlink(path, buf)
-	return tuple{n, wrapError(err)}
-}
-
 func valueToBytes(v value) []byte {
 	in := v.([]value)
 	b := make([]byte, len(in))
diff --git a/go/ssa/interp/external_darwin.go b/go/ssa/interp/external_darwin.go
index 4974ad6..4713009 100644
--- a/go/ssa/interp/external_darwin.go
+++ b/go/ssa/interp/external_darwin.go
@@ -10,6 +10,23 @@
 
 func init() {
 	externals["syscall.Sysctl"] = ext۰syscall۰Sysctl
+
+	fillStat = func(st *syscall.Stat_t, stat structure) {
+		stat[0] = st.Dev
+		stat[1] = st.Mode
+		stat[2] = st.Nlink
+		stat[3] = st.Ino
+		stat[4] = st.Uid
+		stat[5] = st.Gid
+		stat[6] = st.Rdev
+		// TODO(adonovan): fix: copy Timespecs.
+		// stat[8] = st.Atim
+		// stat[9] = st.Mtim
+		// stat[10] = st.Ctim
+		stat[12] = st.Size
+		stat[13] = st.Blocks
+		stat[14] = st.Blksize
+	}
 }
 
 func ext۰syscall۰Sysctl(fr *frame, args []value) value {
diff --git a/go/ssa/interp/external_freebsd.go b/go/ssa/interp/external_freebsd.go
deleted file mode 100644
index 5203303..0000000
--- a/go/ssa/interp/external_freebsd.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2014 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build freebsd
-
-package interp
-
-import "syscall"
-
-func init() {
-	externals["syscall.Sysctl"] = ext۰syscall۰Sysctl
-	externals["syscall.SysctlUint32"] = ext۰syscall۰SysctlUint32
-}
-
-func ext۰syscall۰Sysctl(fr *frame, args []value) value {
-	r, err := syscall.Sysctl(args[0].(string))
-	return tuple{r, wrapError(err)}
-}
-
-func ext۰syscall۰SysctlUint32(fr *frame, args []value) value {
-	r, err := syscall.SysctlUint32(args[0].(string))
-	return tuple{r, wrapError(err)}
-}
diff --git a/go/ssa/interp/external_plan9.go b/go/ssa/interp/external_plan9.go
deleted file mode 100644
index 81bedcf..0000000
--- a/go/ssa/interp/external_plan9.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2013 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package interp
-
-import "syscall"
-
-func ext۰os۰Pipe(fr *frame, args []value) value {
-	panic("os.Pipe not yet implemented")
-}
-func ext۰syscall۰Close(fr *frame, args []value) value {
-	panic("syscall.Close not yet implemented")
-}
-func ext۰syscall۰Fstat(fr *frame, args []value) value {
-	panic("syscall.Fstat not yet implemented")
-}
-func ext۰syscall۰Kill(fr *frame, args []value) value {
-	panic("syscall.Kill not yet implemented")
-}
-func ext۰syscall۰Lstat(fr *frame, args []value) value {
-	panic("syscall.Lstat not yet implemented")
-}
-func ext۰syscall۰Open(fr *frame, args []value) value {
-	panic("syscall.Open not yet implemented")
-}
-func ext۰syscall۰ParseDirent(fr *frame, args []value) value {
-	panic("syscall.ParseDirent not yet implemented")
-}
-func ext۰syscall۰Read(fr *frame, args []value) value {
-	panic("syscall.Read not yet implemented")
-}
-func ext۰syscall۰ReadDirent(fr *frame, args []value) value {
-	panic("syscall.ReadDirent not yet implemented")
-}
-func ext۰syscall۰Stat(fr *frame, args []value) value {
-	panic("syscall.Stat not yet implemented")
-}
-func ext۰syscall۰Write(fr *frame, args []value) value {
-	// func Write(fd int, p []byte) (n int, err error)
-	n, err := write(args[0].(int), valueToBytes(args[1]))
-	return tuple{n, wrapError(err)}
-}
-func ext۰syscall۰RawSyscall(fr *frame, args []value) value {
-	return tuple{^uintptr(0), uintptr(0), uintptr(0)}
-}
-
-func syswrite(fd int, b []byte) (int, error) {
-	return syscall.Write(fd, b)
-}
diff --git a/go/ssa/interp/external_unix.go b/go/ssa/interp/external_unix.go
index be12586..bfa39f6 100644
--- a/go/ssa/interp/external_unix.go
+++ b/go/ssa/interp/external_unix.go
@@ -2,12 +2,49 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !windows,!plan9
+// +build darwin linux
 
 package interp
 
 import "syscall"
 
+func init() {
+	for k, v := range map[string]externalFn{
+		"os.Pipe":              ext۰os۰Pipe,
+		"syscall.Close":        ext۰syscall۰Close,
+		"syscall.Exit":         ext۰syscall۰Exit,
+		"syscall.Fchown":       ext۰syscall۰Fchown,
+		"syscall.Fstat":        ext۰syscall۰Fstat,
+		"syscall.Ftruncate":    ext۰syscall۰Ftruncate,
+		"syscall.Getpid":       ext۰syscall۰Getpid,
+		"syscall.Getwd":        ext۰syscall۰Getwd,
+		"syscall.Kill":         ext۰syscall۰Kill,
+		"syscall.Link":         ext۰syscall۰Link,
+		"syscall.Lstat":        ext۰syscall۰Lstat,
+		"syscall.Mkdir":        ext۰syscall۰Mkdir,
+		"syscall.Open":         ext۰syscall۰Open,
+		"syscall.ParseDirent":  ext۰syscall۰ParseDirent,
+		"syscall.RawSyscall":   ext۰syscall۰RawSyscall,
+		"syscall.Read":         ext۰syscall۰Read,
+		"syscall.ReadDirent":   ext۰syscall۰ReadDirent,
+		"syscall.Readlink":     ext۰syscall۰Readlink,
+		"syscall.Rmdir":        ext۰syscall۰Rmdir,
+		"syscall.Seek":         ext۰syscall۰Seek,
+		"syscall.Stat":         ext۰syscall۰Stat,
+		"syscall.Symlink":      ext۰syscall۰Symlink,
+		"syscall.Write":        ext۰syscall۰Write,
+		"syscall.Unlink":       ext۰syscall۰Unlink,
+		"syscall۰UtimesNano":   ext۰syscall۰UtimesNano,
+		"syscall.setenv_c":     ext۰nop,
+		"syscall.unsetenv_c":   ext۰nop,
+		"syscall.runtime_envs": ext۰runtime۰environ,
+	} {
+		externals[k] = v
+	}
+
+	syswrite = syscall.Write
+}
+
 func ext۰os۰Pipe(fr *frame, args []value) value {
 	// func os.Pipe() (r *File, w *File, err error)
 
@@ -24,14 +61,14 @@
 	return tuple{r, w, wrapError(nil)}
 }
 
-func fillStat(st *syscall.Stat_t, stat structure) {
+// overridden on darwin
+var fillStat = func(st *syscall.Stat_t, stat structure) {
 	stat[0] = st.Dev
 	stat[1] = st.Ino
 	stat[2] = st.Nlink
 	stat[3] = st.Mode
 	stat[4] = st.Uid
 	stat[5] = st.Gid
-
 	stat[7] = st.Rdev
 	stat[8] = st.Size
 	stat[9] = st.Blksize
@@ -47,6 +84,17 @@
 	return wrapError(syscall.Close(args[0].(int)))
 }
 
+func ext۰syscall۰Exit(fr *frame, args []value) value {
+	panic(exitPanic(args[0].(int)))
+}
+
+func ext۰syscall۰Fchown(fr *frame, args []value) value {
+	fd := args[0].(int)
+	uid := args[1].(int)
+	gid := args[2].(int)
+	return wrapError(syscall.Fchown(fd, uid, gid))
+}
+
 func ext۰syscall۰Fstat(fr *frame, args []value) value {
 	// func Fstat(fd int, stat *Stat_t) (err error)
 	fd := args[0].(int)
@@ -58,16 +106,19 @@
 	return wrapError(err)
 }
 
-func ext۰syscall۰ReadDirent(fr *frame, args []value) value {
-	// func ReadDirent(fd int, buf []byte) (n int, err error)
+func ext۰syscall۰Ftruncate(fr *frame, args []value) value {
 	fd := args[0].(int)
-	p := args[1].([]value)
-	b := make([]byte, len(p))
-	n, err := syscall.ReadDirent(fd, b)
-	for i := 0; i < n; i++ {
-		p[i] = b[i]
-	}
-	return tuple{n, wrapError(err)}
+	length := args[1].(int64)
+	return wrapError(syscall.Ftruncate(fd, length))
+}
+
+func ext۰syscall۰Getpid(fr *frame, args []value) value {
+	return syscall.Getpid()
+}
+
+func ext۰syscall۰Getwd(fr *frame, args []value) value {
+	s, err := syscall.Getwd()
+	return tuple{s, wrapError(err)}
 }
 
 func ext۰syscall۰Kill(fr *frame, args []value) value {
@@ -75,6 +126,12 @@
 	return wrapError(syscall.Kill(args[0].(int), syscall.Signal(args[1].(int))))
 }
 
+func ext۰syscall۰Link(fr *frame, args []value) value {
+	path := args[0].(string)
+	link := args[1].(string)
+	return wrapError(syscall.Link(path, link))
+}
+
 func ext۰syscall۰Lstat(fr *frame, args []value) value {
 	// func Lstat(name string, stat *Stat_t) (err error)
 	name := args[0].(string)
@@ -86,6 +143,12 @@
 	return wrapError(err)
 }
 
+func ext۰syscall۰Mkdir(fr *frame, args []value) value {
+	path := args[0].(string)
+	mode := args[1].(uint32)
+	return wrapError(syscall.Mkdir(path, mode))
+}
+
 func ext۰syscall۰Open(fr *frame, args []value) value {
 	// func Open(path string, mode int, perm uint32) (fd int, err error) {
 	path := args[0].(string)
@@ -110,6 +173,10 @@
 	return tuple{consumed, count, inewnames}
 }
 
+func ext۰syscall۰RawSyscall(fr *frame, args []value) value {
+	return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)}
+}
+
 func ext۰syscall۰Read(fr *frame, args []value) value {
 	// func Read(fd int, p []byte) (n int, err error)
 	fd := args[0].(int)
@@ -122,6 +189,37 @@
 	return tuple{n, wrapError(err)}
 }
 
+func ext۰syscall۰ReadDirent(fr *frame, args []value) value {
+	// func ReadDirent(fd int, buf []byte) (n int, err error)
+	fd := args[0].(int)
+	p := args[1].([]value)
+	b := make([]byte, len(p))
+	n, err := syscall.ReadDirent(fd, b)
+	for i := 0; i < n; i++ {
+		p[i] = b[i]
+	}
+	return tuple{n, wrapError(err)}
+}
+
+func ext۰syscall۰Readlink(fr *frame, args []value) value {
+	path := args[0].(string)
+	buf := valueToBytes(args[1])
+	n, err := syscall.Readlink(path, buf)
+	return tuple{n, wrapError(err)}
+}
+
+func ext۰syscall۰Rmdir(fr *frame, args []value) value {
+	return wrapError(syscall.Rmdir(args[0].(string)))
+}
+
+func ext۰syscall۰Seek(fr *frame, args []value) value {
+	fd := args[0].(int)
+	offset := args[1].(int64)
+	whence := args[2].(int)
+	new, err := syscall.Seek(fd, offset, whence)
+	return tuple{new, wrapError(err)}
+}
+
 func ext۰syscall۰Stat(fr *frame, args []value) value {
 	// func Stat(name string, stat *Stat_t) (err error)
 	name := args[0].(string)
@@ -133,16 +231,26 @@
 	return wrapError(err)
 }
 
+func ext۰syscall۰Symlink(fr *frame, args []value) value {
+	path := args[0].(string)
+	link := args[1].(string)
+	return wrapError(syscall.Symlink(path, link))
+}
+
+func ext۰syscall۰Unlink(fr *frame, args []value) value {
+	return wrapError(syscall.Unlink(args[0].(string)))
+}
+
+func ext۰syscall۰UtimesNano(fr *frame, args []value) value {
+	path := args[0].(string)
+	var ts [2]syscall.Timespec
+	err := syscall.UtimesNano(path, ts[:])
+	// TODO(adonovan): copy the Timespecs into args[1]
+	return wrapError(err)
+}
+
 func ext۰syscall۰Write(fr *frame, args []value) value {
 	// func Write(fd int, p []byte) (n int, err error)
 	n, err := write(args[0].(int), valueToBytes(args[1]))
 	return tuple{n, wrapError(err)}
 }
-
-func ext۰syscall۰RawSyscall(fr *frame, args []value) value {
-	return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)}
-}
-
-func syswrite(fd int, b []byte) (int, error) {
-	return syscall.Write(fd, b)
-}
diff --git a/go/ssa/interp/external_windows.go b/go/ssa/interp/external_windows.go
deleted file mode 100644
index 24d1a87..0000000
--- a/go/ssa/interp/external_windows.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2013 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package interp
-
-import "syscall"
-
-func ext۰os۰Pipe(fr *frame, args []value) value {
-	panic("os.Pipe not yet implemented")
-}
-func ext۰syscall۰Close(fr *frame, args []value) value {
-	panic("syscall.Close not yet implemented")
-}
-func ext۰syscall۰Fstat(fr *frame, args []value) value {
-	panic("syscall.Fstat not yet implemented")
-}
-func ext۰syscall۰Kill(fr *frame, args []value) value {
-	panic("syscall.Kill not yet implemented")
-}
-func ext۰syscall۰Lstat(fr *frame, args []value) value {
-	panic("syscall.Lstat not yet implemented")
-}
-func ext۰syscall۰Open(fr *frame, args []value) value {
-	panic("syscall.Open not yet implemented")
-}
-func ext۰syscall۰ParseDirent(fr *frame, args []value) value {
-	panic("syscall.ParseDirent not yet implemented")
-}
-func ext۰syscall۰Read(fr *frame, args []value) value {
-	panic("syscall.Read not yet implemented")
-}
-func ext۰syscall۰ReadDirent(fr *frame, args []value) value {
-	panic("syscall.ReadDirent not yet implemented")
-}
-func ext۰syscall۰Stat(fr *frame, args []value) value {
-	panic("syscall.Stat not yet implemented")
-}
-func ext۰syscall۰Write(fr *frame, args []value) value {
-	panic("syscall.Write not yet implemented")
-}
-func ext۰syscall۰RawSyscall(fr *frame, args []value) value {
-	return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)}
-}
-func syswrite(fd int, b []byte) (int, error) {
-	panic("syswrite not yet implemented")
-}
diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go
index 730d0f5..bc1bd1b 100644
--- a/go/ssa/interp/interp.go
+++ b/go/ssa/interp/interp.go
@@ -604,6 +604,8 @@
 		caller.caller.panicking = false
 		p := caller.caller.panic
 		caller.caller.panic = nil
+
+		// TODO(adonovan): support runtime.Goexit.
 		switch p := p.(type) {
 		case targetPanic:
 			// The target program explicitly called panic().
@@ -665,6 +667,11 @@
 // The SSA program must include the "runtime" package.
 //
 func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) {
+	if syswrite == nil {
+		fmt.Fprintln(os.Stderr, "Interpret: unsupported platform.")
+		return 1
+	}
+
 	i := &interpreter{
 		prog:       mainpkg.Prog,
 		globals:    make(map[ssa.Value]*value),
diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go
index d51c2fd..f622666 100644
--- a/go/ssa/interp/interp_test.go
+++ b/go/ssa/interp/interp_test.go
@@ -4,7 +4,7 @@
 
 // +build go1.5
 
-// +build !android,!windows,!plan9
+// +build linux darwin
 
 package interp_test
 
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index 2de2fa8..e37ed5f 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -933,6 +933,8 @@
 	return syswrite(fd, b)
 }
 
+var syswrite func(int, []byte) (int, error) // set on darwin/linux only
+
 // callBuiltin interprets a call to builtin fn with arguments args,
 // returning its result.
 func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value {