How to discover all package types at runtime?
In Go 1.5, you can use the new package types and importer to inspect binary and source packages. For example:
package main
import (
"fmt"
"go/importer"
)
func main() {
pkg, err := importer.Default().Import("time")
if err != nil {
fmt.Printf("error: %s\n", err.Error())
return
}
for _, declName := range pkg.Scope().Names() {
fmt.Println(declName)
}
}
You can use the package go/build to extract all the packages installed. Or you can configure the Lookup
importer to inspect binaries outside the environment.
Before 1.5, the only no-hacky way is to use the package ast to compile the source code.
Unfortunately, I don't think this is possible. Packages are not "actionable" in Go, you can't "call a function" on it. You can't call a function on a type either, but you can call reflect.TypeOf
on an instance of the type and get reflect.Type
which is a runtime abstraction of a type. There just isn't such mechanism for packages, there isn't a reflect.Package
.
With that said, you could file an issue about the absence of (and practicality of adding) reflect.PackageOf
etc.
(see bottom for 2019 update)
Warning: untested and hacky. Can break whenever a new version of Go is released.
It is possible to get all types the runtime knows of by hacking around Go's runtime a little. Include a small assembly file in your own package, containing:
TEXT yourpackage·typelinks(SB), NOSPLIT, $0-0
JMP reflect·typelinks(SB)
In yourpackage
, declare the function prototype (without body):
func typelinks() []*typeDefDummy
Alongside a type definition:
type typeDefDummy struct {
_ uintptr // padding
_ uint64 // padding
_ [3]uintptr // padding
StrPtr *string
}
Then just call typelinks, iterate over the slice and read each StrPtr for the name. Seek those starting with yourpackage
. Note that if there are two packages called yourpackage
in different paths, this method won't work unambiguously.
can I somehow hook into the reflect package to instantiate new instances of those names?
Yeah, assuming d
is a value of type *typeDefDummy
(note the asterisk, very important):
t := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&d)))
Now t
is a reflect.Type
value which you can use to instantiate reflect.Value
s.
Edit: I tested and executed this code successfully and have uploaded it as a gist.
Adjust package names and include paths as necessary.
Update 2019
A lot has changed since I originally posted this answer. Here's a short description of how the same can be done with Go 1.11 in 2019.
$GOPATH/src/tl/tl.go
package tl
import (
"unsafe"
)
func Typelinks() (sections []unsafe.Pointer, offset [][]int32) {
return typelinks()
}
func typelinks() (sections []unsafe.Pointer, offset [][]int32)
func Add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
return add(p, x, whySafe)
}
func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer
$GOPATH/src/tl/tl.s
TEXT tl·typelinks(SB), $0-0
JMP reflect·typelinks(SB)
TEXT tl·add(SB), $0-0
JMP reflect·add(SB)
main.go
package main
import (
"fmt"
"reflect"
"tl"
"unsafe"
)
func main() {
sections, offsets := tl.Typelinks()
for i, base := range sections {
for _, offset := range offsets[i] {
typeAddr := tl.Add(base, uintptr(offset), "")
typ := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&typeAddr)))
fmt.Println(typ)
}
}
}
Happy hacking!