From: Alan Donovan Date: Wed, 27 Feb 2013 21:43:16 +0000 (-0500) Subject: exp/ssa: a number of bug fixes. X-Git-Tag: go1.1rc2~805 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1c5e079600a820bdb9a61d1813ab2a342cc70ce9;p=gostls13.git exp/ssa: a number of bug fixes. ssadump: - permit naming a package (not just *.go files) on command line. - set BuildSerially flag when setting Log* flags (Q. should instead the logging functions take a lock?) Builder: - fixed bug when calling variadic function with zero '...'-params. Added regression test. interp: - more external functions: the 'error' interface bytes.{Equal,IndexByte} reflect.(Value).{Bool,NumOut,Out} syscall.{Close,Fstat,Read,Open,Stat,Lstat,Fstat, Getdents,ParseDirents,Getwd} - permit comparisons between *Function and *closure. With this CL, ssadump can now interpret ssadump itself (!), loading, parsing, typing, SSA-building, and running println("Hello, World!"). While a fmt-based equivalent still lacks some external routines, e.g. math/big, I think there are diminishing returns in expanding the interpreter (and debugging it is starting to feel like "Inception"). I'm pretty confident this package is now good enough for wider use. R=gri CC=golang-dev https://golang.org/cl/7392053 --- diff --git a/src/pkg/exp/ssa/builder.go b/src/pkg/exp/ssa/builder.go index 8b727b30ed..0e62104e2e 100644 --- a/src/pkg/exp/ssa/builder.go +++ b/src/pkg/exp/ssa/builder.go @@ -1077,7 +1077,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { case *types.Signature: np := len(typ.Params) if !c.HasEllipsis { - if typ.IsVariadic && len(args) > np-1 { + if typ.IsVariadic { // case 2: ordinary call of variadic function. vt = typ.Params[np-1].Type args, varargs = args[:np-1], args[np-1:] @@ -1216,7 +1216,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { } // Common code for varargs. - if len(varargs) > 0 { // case 2 + if vt != nil { // case 2 at := &types.Array{ Elt: vt, Len: int64(len(varargs)), diff --git a/src/pkg/exp/ssa/interp/external.go b/src/pkg/exp/ssa/interp/external.go index a099ca8b75..25b012eed3 100644 --- a/src/pkg/exp/ssa/interp/external.go +++ b/src/pkg/exp/ssa/interp/external.go @@ -21,6 +21,7 @@ type externalFn func(fn *ssa.Function, args []value) value // Key strings are from Function.FullName(). // That little dot Û° is an Arabic zero numeral (U+06F0), categories [Nd]. var externals = map[string]externalFn{ + "(reflect.Value).Bool": extÛ°reflectÛ°ValueÛ°Bool, "(reflect.Value).CanAddr": extÛ°reflectÛ°ValueÛ°CanAddr, "(reflect.Value).CanInterface": extÛ°reflectÛ°ValueÛ°CanInterface, "(reflect.Value).Elem": extÛ°reflectÛ°ValueÛ°Elem, @@ -36,10 +37,15 @@ var externals = map[string]externalFn{ "(reflect.Value).Pointer": extÛ°reflectÛ°ValueÛ°Pointer, "(reflect.Value).String": extÛ°reflectÛ°ValueÛ°String, "(reflect.Value).Type": extÛ°reflectÛ°ValueÛ°Type, + "(reflect.error).Error": extÛ°reflectÛ°errorÛ°Error, "(reflect.rtype).Bits": extÛ°reflectÛ°rtypeÛ°Bits, "(reflect.rtype).Elem": extÛ°reflectÛ°rtypeÛ°Elem, "(reflect.rtype).Kind": extÛ°reflectÛ°rtypeÛ°Kind, + "(reflect.rtype).NumOut": extÛ°reflectÛ°rtypeÛ°NumOut, + "(reflect.rtype).Out": extÛ°reflectÛ°rtypeÛ°Out, "(reflect.rtype).String": extÛ°reflectÛ°rtypeÛ°String, + "bytes.Equal": extÛ°bytesÛ°Equal, + "bytes.IndexByte": extÛ°bytesÛ°IndexByte, "math.Float32bits": extÛ°mathÛ°Float32bits, "math.Float32frombits": extÛ°mathÛ°Float32frombits, "math.Float64bits": extÛ°mathÛ°Float64bits, @@ -61,14 +67,58 @@ var externals = map[string]externalFn{ "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.Getdents": extÛ°syscallÛ°Getdents, "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.Read": extÛ°syscallÛ°Read, + "syscall.Stat": extÛ°syscallÛ°Stat, "syscall.Write": extÛ°syscallÛ°Write, "time.Sleep": extÛ°timeÛ°Sleep, "time.now": extÛ°timeÛ°now, } +// wrapError returns an interpreted 'error' interface value for err. +func wrapError(err error) value { + if err == nil { + return iface{} + } + return iface{t: errorType, v: err.Error()} +} + +func extÛ°bytesÛ°Equal(fn *ssa.Function, args []value) value { + // func Equal(a, b []byte) bool + a := args[0].([]value) + b := args[1].([]value) + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} + +func extÛ°bytesÛ°IndexByte(fn *ssa.Function, args []value) value { + // func IndexByte(s []byte, c byte) int + s := args[0].([]value) + c := args[1].(byte) + for i, b := range s { + if b.(byte) == c { + return i + } + } + return -1 +} + func extÛ°mathÛ°Float64frombits(fn *ssa.Function, args []value) value { return math.Float64frombits(args[0].(uint64)) } @@ -171,15 +221,17 @@ func extÛ°syscallÛ°Exit(fn *ssa.Function, args []value) value { panic(exitPanic(args[0].(int))) } +func extÛ°syscallÛ°Getwd(fn *ssa.Function, args []value) value { + s, err := syscall.Getwd() + return tuple{s, wrapError(err)} +} + func extÛ°syscallÛ°Getpid(fn *ssa.Function, args []value) value { - // We could emulate syscall.Syscall but it's more effort. return syscall.Getpid() } // The set of remaining native functions we need to implement (as needed): -// bytes/bytes.go:42:func Equal(a, b []byte) bool -// bytes/bytes_decl.go:8:func IndexByte(s []byte, c byte) int // asm_$GOARCH.s // crypto/aes/cipher_asm.go:10:func hasAsm() bool // crypto/aes/cipher_asm.go:11:func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) // crypto/aes/cipher_asm.go:12:func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) @@ -283,10 +335,6 @@ func extÛ°syscallÛ°Getpid(fn *ssa.Function, args []value) value { // syscall/syscall_linux_amd64.go:60:func Gettimeofday(tv *Timeval) (err error) // syscall/syscall_linux_amd64.go:61:func Time(t *Time_t) (tt Time_t, err error) // syscall/syscall_linux_arm.go:28:func Seek(fd int, offset int64, whence int) (newoffset int64, err error) -// syscall/syscall_unix.go:23:func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) -// syscall/syscall_unix.go:24:func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) -// syscall/syscall_unix.go:25:func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) -// syscall/syscall_unix.go:26:func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) // time/sleep.go:25:func startTimer(*runtimeTimer) // time/sleep.go:26:func stopTimer(*runtimeTimer) bool // time/time.go:758:func now() (sec int64, nsec int32) diff --git a/src/pkg/exp/ssa/interp/external_plan9.go b/src/pkg/exp/ssa/interp/external_plan9.go index 5f17cacda6..ce7fd529b1 100644 --- a/src/pkg/exp/ssa/interp/external_plan9.go +++ b/src/pkg/exp/ssa/interp/external_plan9.go @@ -9,18 +9,40 @@ import ( "syscall" ) +func extÛ°syscallÛ°Close(fn *ssa.Function, args []value) value { + panic("syscall.Close not yet implemented") +} +func extÛ°syscallÛ°Fstat(fn *ssa.Function, args []value) value { + panic("syscall.Fstat not yet implemented") +} +func extÛ°syscallÛ°Getdents(fn *ssa.Function, args []value) value { + panic("syscall.Getdents not yet implemented") +} func extÛ°syscallÛ°Kill(fn *ssa.Function, args []value) value { panic("syscall.Kill not yet implemented") } +func extÛ°syscallÛ°Lstat(fn *ssa.Function, args []value) value { + panic("syscall.Lstat not yet implemented") +} +func extÛ°syscallÛ°Open(fn *ssa.Function, args []value) value { + panic("syscall.Open not yet implemented") +} +func extÛ°syscallÛ°ParseDirent(fn *ssa.Function, args []value) value { + panic("syscall.ParseDirent not yet implemented") +} +func extÛ°syscallÛ°Read(fn *ssa.Function, args []value) value { + panic("syscall.Read not yet implemented") +} +func extÛ°syscallÛ°Stat(fn *ssa.Function, args []value) value { + panic("syscall.Stat not yet implemented") +} func extÛ°syscallÛ°Write(fn *ssa.Function, args []value) value { - // We could emulate syscall.Syscall but it's more effort. p := args[1].([]value) b := make([]byte, 0, len(p)) for i := range p { b = append(b, p[i].(byte)) } - n, _ := syscall.Write(args[0].(int), b) - err := iface{} // TODO(adonovan): fix: adapt concrete err to interpreted iface. - return tuple{n, err} + n, err := syscall.Write(args[0].(int), b) + return tuple{n, wrapError(err)} } diff --git a/src/pkg/exp/ssa/interp/external_unix.go b/src/pkg/exp/ssa/interp/external_unix.go index afc874535f..c81454ae26 100644 --- a/src/pkg/exp/ssa/interp/external_unix.go +++ b/src/pkg/exp/ssa/interp/external_unix.go @@ -11,21 +11,126 @@ import ( "syscall" ) +func valueToBytes(v value) []byte { + in := v.([]value) + b := make([]byte, len(in)) + for i := range in { + b[i] = in[i].(byte) + } + return b +} + +func fillStat(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 + stat[10] = st.Blocks + // TODO(adonovan): fix: copy Timespecs. + // stat[11] = st.Atim + // stat[12] = st.Mtim + // stat[13] = st.Ctim +} + +func extÛ°syscallÛ°Close(fn *ssa.Function, args []value) value { + // func Close(fd int) (err error) + return wrapError(syscall.Close(args[0].(int))) +} + +func extÛ°syscallÛ°Fstat(fn *ssa.Function, args []value) value { + // func Fstat(fd int, stat *Stat_t) (err error) + fd := args[0].(int) + stat := (*args[1].(*value)).(structure) + + var st syscall.Stat_t + err := syscall.Fstat(fd, &st) + fillStat(&st, stat) + return wrapError(err) +} + +func extÛ°syscallÛ°Getdents(fn *ssa.Function, args []value) value { + // func GetDents(fd int, buf []byte) (n int, err error) + fd := args[0].(int) + p := args[1].([]value) + b := make([]byte, len(p)) + n, err := syscall.Getdents(fd, b) + for i := 0; i < n; i++ { + p[i] = b[i] + } + return tuple{n, wrapError(err)} +} + func extÛ°syscallÛ°Kill(fn *ssa.Function, args []value) value { - // We could emulate syscall.Syscall but it's more effort. - err := syscall.Kill(args[0].(int), syscall.Signal(args[1].(int))) - err = err // TODO(adonovan): fix: adapt concrete err to interpreted iface (e.g. call interpreted errors.New) - return iface{} + // func Kill(pid int, sig Signal) (err error) + return wrapError(syscall.Kill(args[0].(int), syscall.Signal(args[1].(int)))) } -func extÛ°syscallÛ°Write(fn *ssa.Function, args []value) value { - // We could emulate syscall.Syscall but it's more effort. +func extÛ°syscallÛ°Lstat(fn *ssa.Function, args []value) value { + // func Lstat(name string, stat *Stat_t) (err error) + name := args[0].(string) + stat := (*args[1].(*value)).(structure) + + var st syscall.Stat_t + err := syscall.Lstat(name, &st) + fillStat(&st, stat) + return wrapError(err) +} + +func extÛ°syscallÛ°Open(fn *ssa.Function, args []value) value { + // func Open(path string, mode int, perm uint32) (fd int, err error) { + path := args[0].(string) + mode := args[1].(int) + perm := args[2].(uint32) + fd, err := syscall.Open(path, mode, perm) + return tuple{fd, wrapError(err)} +} + +func extÛ°syscallÛ°ParseDirent(fn *ssa.Function, args []value) value { + // func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) + max := args[1].(int) + var names []string + for _, iname := range args[2].([]value) { + names = append(names, iname.(string)) + } + consumed, count, newnames := syscall.ParseDirent(valueToBytes(args[0]), max, names) + var inewnames []value + for _, newname := range newnames { + inewnames = append(inewnames, newname) + } + return tuple{consumed, count, inewnames} +} + +func extÛ°syscallÛ°Read(fn *ssa.Function, args []value) value { + // func Read(fd int, p []byte) (n int, err error) + fd := args[0].(int) p := args[1].([]value) - b := make([]byte, 0, len(p)) - for i := range p { - b = append(b, p[i].(byte)) + b := make([]byte, len(p)) + n, err := syscall.Read(fd, b) + for i := 0; i < n; i++ { + p[i] = b[i] } - n, _ := syscall.Write(args[0].(int), b) - err := iface{} // TODO(adonovan): fix: adapt concrete err to interpreted iface. - return tuple{n, err} + return tuple{n, wrapError(err)} +} + +func extÛ°syscallÛ°Stat(fn *ssa.Function, args []value) value { + // func Stat(name string, stat *Stat_t) (err error) + name := args[0].(string) + stat := (*args[1].(*value)).(structure) + + var st syscall.Stat_t + err := syscall.Stat(name, &st) + fillStat(&st, stat) + return wrapError(err) +} + +func extÛ°syscallÛ°Write(fn *ssa.Function, args []value) value { + // func Write(fd int, p []byte) (n int, err error) + n, err := syscall.Write(args[0].(int), valueToBytes(args[1])) + return tuple{n, wrapError(err)} } diff --git a/src/pkg/exp/ssa/interp/external_windows.go b/src/pkg/exp/ssa/interp/external_windows.go index 5bdc1b9edf..9b782ea1a6 100644 --- a/src/pkg/exp/ssa/interp/external_windows.go +++ b/src/pkg/exp/ssa/interp/external_windows.go @@ -10,10 +10,33 @@ import ( "exp/ssa" ) +func extÛ°syscallÛ°Close(fn *ssa.Function, args []value) value { + panic("syscall.Close not yet implemented") +} +func extÛ°syscallÛ°Fstat(fn *ssa.Function, args []value) value { + panic("syscall.Fstat not yet implemented") +} +func extÛ°syscallÛ°Getdents(fn *ssa.Function, args []value) value { + panic("syscall.Getdents not yet implemented") +} func extÛ°syscallÛ°Kill(fn *ssa.Function, args []value) value { panic("syscall.Kill not yet implemented") } - +func extÛ°syscallÛ°Lstat(fn *ssa.Function, args []value) value { + panic("syscall.Lstat not yet implemented") +} +func extÛ°syscallÛ°Open(fn *ssa.Function, args []value) value { + panic("syscall.Open not yet implemented") +} +func extÛ°syscallÛ°ParseDirent(fn *ssa.Function, args []value) value { + panic("syscall.ParseDirent not yet implemented") +} +func extÛ°syscallÛ°Read(fn *ssa.Function, args []value) value { + panic("syscall.Read not yet implemented") +} +func extÛ°syscallÛ°Stat(fn *ssa.Function, args []value) value { + panic("syscall.Stat not yet implemented") +} func extÛ°syscallÛ°Write(fn *ssa.Function, args []value) value { panic("syscall.Write not yet implemented") } diff --git a/src/pkg/exp/ssa/interp/interp.go b/src/pkg/exp/ssa/interp/interp.go index 1df63bd663..9787252413 100644 --- a/src/pkg/exp/ssa/interp/interp.go +++ b/src/pkg/exp/ssa/interp/interp.go @@ -84,6 +84,7 @@ type interpreter struct { globals map[ssa.Value]*value // addresses of global variables (immutable) mode Mode // interpreter options reflectPackage *ssa.Package // the fake reflect package + errorMethods ssa.MethodSet // the method set of reflect.error, which implements the error interface. rtypeMethods ssa.MethodSet // the method set of rtype, which implements the reflect.Type interface. } @@ -132,8 +133,11 @@ func (fr *frame) rundefers() { // findMethodSet returns the method set for type typ, which may be one // of the interpreter's fake types. func findMethodSet(i *interpreter, typ types.Type) ssa.MethodSet { - if typ == rtypeType { + switch typ { + case rtypeType: return i.rtypeMethods + case errorType: + return i.errorMethods } return i.prog.MethodSet(typ) } @@ -211,8 +215,9 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { return kJump case *ssa.Defer: + pos := instr.Pos // TODO(gri): workaround for bug in typeswitch+funclit. fn, args := prepareCall(fr, &instr.CallCommon) - fr.defers = append(fr.defers, func() { call(fr.i, fr, instr.Pos, fn, args) }) + fr.defers = append(fr.defers, func() { call(fr.i, fr, pos, fn, args) }) case *ssa.Go: fn, args := prepareCall(fr, &instr.CallCommon) diff --git a/src/pkg/exp/ssa/interp/reflect.go b/src/pkg/exp/ssa/interp/reflect.go index 26a8338126..770792c05d 100644 --- a/src/pkg/exp/ssa/interp/reflect.go +++ b/src/pkg/exp/ssa/interp/reflect.go @@ -28,6 +28,14 @@ var reflectTypesPackage = &types.Package{ // type rtype var rtypeType = makeNamedType("rtype", &types.Basic{Name: "rtype"}) +// error is an (interpreted) named type whose underlying type is string. +// The interpreter uses it for all implementations of the built-in error +// interface that it creates. +// We put it in the "reflect" package for expedience. +// +// type error string +var errorType = makeNamedType("error", &types.Basic{Name: "error"}) + func makeNamedType(name string, underlying types.Type) *types.NamedType { nt := &types.NamedType{Underlying: underlying} nt.Obj = &types.TypeName{ @@ -123,6 +131,17 @@ func extÛ°reflectÛ°rtypeÛ°Kind(fn *ssa.Function, args []value) value { return uint(reflectKind(args[0].(rtype).t)) } +func extÛ°reflectÛ°rtypeÛ°NumOut(fn *ssa.Function, args []value) value { + // Signature: func (t reflect.rtype) int + return len(args[0].(rtype).t.(*types.Signature).Results) +} + +func extÛ°reflectÛ°rtypeÛ°Out(fn *ssa.Function, args []value) value { + // Signature: func (t reflect.rtype, i int) int + i := args[1].(int) + return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results[i].Type}) +} + func extÛ°reflectÛ°rtypeÛ°String(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) string return args[0].(rtype).t.String() @@ -279,6 +298,11 @@ func extÛ°reflectÛ°ValueÛ°Index(fn *ssa.Function, args []value) value { return nil // unreachable } +func extÛ°reflectÛ°ValueÛ°Bool(fn *ssa.Function, args []value) value { + // Signature: func (reflect.Value) bool + return rV2V(args[0]).(bool) +} + func extÛ°reflectÛ°ValueÛ°CanAddr(fn *ssa.Function, args []value) value { // Signature: func (v reflect.Value) bool // Always false for our representation. @@ -373,6 +397,10 @@ func extÛ°reflectÛ°valueInterface(fn *ssa.Function, args []value) value { return iface{rV2T(v).t, rV2V(v)} } +func extÛ°reflectÛ°errorÛ°Error(fn *ssa.Function, args []value) value { + return args[0] +} + // newMethod creates a new method of the specified name, package and receiver type. func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function { fn := &ssa.Function{ @@ -402,6 +430,11 @@ func initReflect(i *interpreter) { ssa.Id{nil, "Bits"}: newMethod(i.reflectPackage, rtypeType, "Bits"), ssa.Id{nil, "Elem"}: newMethod(i.reflectPackage, rtypeType, "Elem"), ssa.Id{nil, "Kind"}: newMethod(i.reflectPackage, rtypeType, "Kind"), + ssa.Id{nil, "NumOut"}: newMethod(i.reflectPackage, rtypeType, "NumOut"), + ssa.Id{nil, "Out"}: newMethod(i.reflectPackage, rtypeType, "Out"), ssa.Id{nil, "String"}: newMethod(i.reflectPackage, rtypeType, "String"), } + i.errorMethods = ssa.MethodSet{ + ssa.Id{nil, "Error"}: newMethod(i.reflectPackage, errorType, "Error"), + } } diff --git a/src/pkg/exp/ssa/interp/testdata/coverage.go b/src/pkg/exp/ssa/interp/testdata/coverage.go index 5cfbdbdd84..03e14275aa 100644 --- a/src/pkg/exp/ssa/interp/testdata/coverage.go +++ b/src/pkg/exp/ssa/interp/testdata/coverage.go @@ -23,6 +23,13 @@ func init() { } } +func init() { + // Call of variadic function with (implicit) empty slice. + if x := fmt.Sprint(); x != "" { + panic(x) + } +} + type empty interface{} type I interface { diff --git a/src/pkg/exp/ssa/interp/value.go b/src/pkg/exp/ssa/interp/value.go index f24d751145..3218fb441f 100644 --- a/src/pkg/exp/ssa/interp/value.go +++ b/src/pkg/exp/ssa/interp/value.go @@ -226,10 +226,8 @@ func equals(x, y value) bool { return x == y.(*hashmap) case map[value]value: return (x != nil) == (y.(map[value]value) != nil) - case *ssa.Function: - return x == y.(*ssa.Function) - case *closure: - return x == y.(*closure) + case *ssa.Function, *closure: + return x == y case []value: return (x != nil) == (y.([]value) != nil) } diff --git a/src/pkg/exp/ssa/ssadump.go b/src/pkg/exp/ssa/ssadump.go index 8a7f6b6f82..f74ff08263 100644 --- a/src/pkg/exp/ssa/ssadump.go +++ b/src/pkg/exp/ssa/ssadump.go @@ -9,6 +9,7 @@ import ( "exp/ssa/interp" "flag" "fmt" + "go/ast" "log" "os" "runtime/pprof" @@ -36,7 +37,8 @@ T [T]race execution of the program. Best for single-threaded programs! `) const usage = `SSA builder and interpreter. -Usage: ssadump [ ...] ... +Usage: ssadump [ ...] [ ...] [ ...] + ssadump [ ...] [ ...] Use -help flag to display options. Examples: @@ -56,11 +58,11 @@ func main() { for _, c := range *buildFlag { switch c { case 'P': - mode |= ssa.LogPackages + mode |= ssa.LogPackages | ssa.BuildSerially case 'F': - mode |= ssa.LogFunctions + mode |= ssa.LogFunctions | ssa.BuildSerially case 'S': - mode |= ssa.LogSource + mode |= ssa.LogSource | ssa.BuildSerially case 'C': mode |= ssa.SanityCheckFunctions case 'N': @@ -91,19 +93,6 @@ func main() { os.Exit(1) } - // Treat all leading consecutive "*.go" arguments as a single package. - // - // TODO(gri): make it a typechecker error for there to be - // duplicate (e.g.) main functions in the same package. - var gofiles []string - for len(args) > 0 && strings.HasSuffix(args[0], ".go") { - gofiles = append(gofiles, args[0]) - args = args[1:] - } - if gofiles == nil { - log.Fatal("No *.go source files specified.") - } - // Profiling support. if *cpuprofile != "" { f, err := os.Create(*cpuprofile) @@ -114,20 +103,46 @@ func main() { defer pprof.StopCPUProfile() } - // TODO(adonovan): permit naming a package directly instead of - // a list of .go files. - // TODO(adonovan/gri): the cascade of errors is confusing due // to reentrant control flow. Disable for now and re-think. var errh func(error) // errh = func(err error) { fmt.Println(err.Error()) } - b := ssa.NewBuilder(mode, ssa.GorootLoader, errh) - files, err := ssa.ParseFiles(b.Prog.Files, ".", gofiles...) + loader := ssa.GorootLoader + b := ssa.NewBuilder(mode, loader, errh) + + var pkgname string + var files []*ast.File + var err error + + switch { + case len(args) == 0: + log.Fatal("No *.go source files nor package name was specified.") + + case strings.HasSuffix(args[0], ".go"): + // % ssadump a.go b.go ... + // Leading consecutive *.go arguments constitute main package. + i := 1 + for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ { + } + files, err = ssa.ParseFiles(b.Prog.Files, ".", args[:i]...) + pkgname = "main" + args = args[i:] + + default: + // % ssadump my/package ... + // First argument is import path of main package. + pkgname = args[0] + args = args[1:] + files, err = loader(b.Prog.Files, pkgname) + } if err != nil { log.Fatalf(err.Error()) } - mainpkg, err := b.CreatePackage("main", files) + + // TODO(gri): make it a typechecker error for there to be + // duplicate (e.g.) main functions in the same package. + mainpkg, err := b.CreatePackage(pkgname, files) if err != nil { log.Fatalf(err.Error()) } @@ -135,6 +150,6 @@ func main() { b = nil // discard Builder if *runFlag { - interp.Interpret(mainpkg, interpMode, gofiles[0], args) + interp.Interpret(mainpkg, interpMode, pkgname, args) } }