This commit is contained in:
2024-05-14 12:10:58 +02:00
parent a9bb79b01c
commit 59911aebb9
645 changed files with 263320 additions and 0 deletions

21
vendor/github.com/shurcooL/go-goon/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Dave Collins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

92
vendor/github.com/shurcooL/go-goon/README.md generated vendored Normal file
View File

@@ -0,0 +1,92 @@
goon
====
[![Go Reference](https://pkg.go.dev/badge/github.com/shurcooL/go-goon.svg)](https://pkg.go.dev/github.com/shurcooL/go-goon)
Package goon is a deep pretty printer with Go-like notation. It implements the [goon](https://github.com/shurcooL/goon) specification.
**Deprecated:** This package is old, incomplete, low code quality, and now unmaintained.
See [github.com/hexops/valast](https://github.com/hexops/valast) for a newer package that is the closest known direct replacement.
See the [Alternatives](#alternatives) section for other known entries in this problem space.
Installation
------------
```bash
go get github.com/shurcooL/go-goon
```
Examples
--------
```Go
x := Lang{
Name: "Go",
Year: 2009,
URL: "http",
Inner: &Inner{
Field1: "Secret!",
},
}
goon.Dump(x)
// Output:
// (Lang)(Lang{
// Name: (string)("Go"),
// Year: (int)(2009),
// URL: (string)("http"),
// Inner: (*Inner)(&Inner{
// Field1: (string)("Secret!"),
// Field2: (int)(0),
// }),
// })
```
```Go
items := []int{1, 2, 3}
goon.DumpExpr(len(items))
// Output:
// len(items) = (int)(3)
```
```Go
adderFunc := func(a int, b int) int {
c := a + b
return c
}
goon.DumpExpr(adderFunc)
// Output:
// adderFunc = (func(int, int) int)(func(a int, b int) int {
// c := a + b
// return c
// })
```
Directories
-----------
| Path | Synopsis |
|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| [bypass](https://pkg.go.dev/github.com/shurcooL/go-goon/bypass) | Package bypass allows bypassing reflect restrictions on accessing unexported struct fields. |
Alternatives
------------
- [`go-spew`](https://github.com/davecgh/go-spew) - A deep pretty printer for Go data structures to aid in debugging.
- [`valast`](https://github.com/hexops/valast) - Convert Go values to their AST.
- [`repr`](https://github.com/alecthomas/repr) - Python's repr() for Go.
Attribution
-----------
go-goon source was based on the existing source of [go-spew](https://github.com/davecgh/go-spew) by [Dave Collins](https://github.com/davecgh).
License
-------
- [MIT License](LICENSE)

394
vendor/github.com/shurcooL/go-goon/dump.go generated vendored Normal file
View File

@@ -0,0 +1,394 @@
package goon
import (
"bytes"
"fmt"
"go/format"
"io"
"reflect"
"strconv"
"strings"
"time"
"github.com/shurcooL/go/reflectsource"
)
var config = struct {
indent string
}{
indent: "\t",
}
// dumpState contains information about the state of a dump operation.
type dumpState struct {
w io.Writer
depth int
pointers map[uintptr]int
ignoreNextType bool
ignoreNextIndent bool
}
// indent performs indentation according to the depth level and cs.Indent
// option.
func (d *dumpState) indent() {
if d.ignoreNextIndent {
d.ignoreNextIndent = false
return
}
d.w.Write(bytes.Repeat([]byte(config.indent), d.depth))
}
// unpackValue returns values inside of non-nil interfaces when possible.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
return v
}
// dumpPtr handles formatting of pointers by indirecting them as necessary.
func (d *dumpState) dumpPtr(v reflect.Value) {
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range d.pointers {
if depth >= d.depth {
delete(d.pointers, k)
}
}
// Figure out how many levels of indirection there are by dereferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
cycleFound = true
indirects--
break
}
d.pointers[addr] = d.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type information.
d.w.Write(bytes.Repeat(ampersandBytes, indirects))
// Display dereferenced value.
switch {
case nilFound:
d.w.Write(nilBytes)
case cycleFound:
d.w.Write(circularBytes)
default:
d.ignoreNextType = true
d.dump(ve)
}
}
// dump is the main workhorse for dumping a value. It uses the passed reflect
// value to figure out what kind of object we are dealing with and formats it
// appropriately. It is a recursive function, however circular data structures
// are detected and handled properly.
func (d *dumpState) dump(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
d.w.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
d.indent()
d.w.Write(openParenBytes)
d.w.Write([]byte(typeStringWithoutPackagePrefix(v)))
d.w.Write(closeParenBytes)
d.w.Write(openParenBytes)
d.dumpPtr(v)
d.w.Write(closeParenBytes)
return
}
// Print type information unless already handled elsewhere.
var shouldPrintClosingBr = false
if !d.ignoreNextType {
d.indent()
d.w.Write(openParenBytes)
d.w.Write([]byte(typeStringWithoutPackagePrefix(v)))
d.w.Write(closeParenBytes)
d.w.Write(openParenBytes)
shouldPrintClosingBr = true
}
d.ignoreNextType = false
if v.Type() == timeType {
t := v.Interface().(time.Time)
switch t.IsZero() {
case false:
var location string
switch t.Location() {
case time.UTC:
location = "time.UTC"
case time.Local:
location = "time.Local"
default:
location = fmt.Sprintf("must(time.LoadLocation(%q))", t.Location().String())
}
fmt.Fprintf(d.w, "time.Date(%d, %d, %d, %d, %d, %d, %d, %s)", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), location)
case true:
d.w.Write([]byte("time.Time{}"))
}
goto AfterKindSwitch
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(d.w, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(d.w, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(d.w, v.Uint(), 10)
case reflect.Float32:
printFloat(d.w, v.Float(), 32)
case reflect.Float64:
printFloat(d.w, v.Float(), 64)
case reflect.Complex64:
printComplex(d.w, v.Complex(), 32)
case reflect.Complex128:
printComplex(d.w, v.Complex(), 64)
case reflect.Array:
d.w.Write([]byte(typeStringWithoutPackagePrefix(v)))
d.w.Write(openBraceNewlineBytes)
d.depth++
for i := 0; i < v.Len(); i++ {
d.dump(d.unpackValue(v.Index(i)))
d.w.Write(commaNewlineBytes)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Slice:
if v.IsNil() {
d.w.Write(nilBytes)
} else {
d.w.Write([]byte(typeStringWithoutPackagePrefix(v)))
d.w.Write(openBraceNewlineBytes)
d.depth++
for i := 0; i < v.Len(); i++ {
d.dump(d.unpackValue(v.Index(i)))
d.w.Write(commaNewlineBytes)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
}
case reflect.String:
d.w.Write([]byte(strconv.Quote(v.String())))
case reflect.Interface:
// If we got here, it's because interface is nil
// See https://github.com/davecgh/go-spew/issues/12
d.w.Write(nilBytes)
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
if v.IsNil() {
d.w.Write(nilBytes)
} else {
d.w.Write([]byte(typeStringWithoutPackagePrefix(v)))
d.w.Write(openBraceNewlineBytes)
d.depth++
keys := v.MapKeys()
for _, key := range keys {
d.dump(d.unpackValue(key))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.MapIndex(key)))
d.w.Write(commaNewlineBytes)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
}
case reflect.Struct:
d.w.Write([]byte(typeStringWithoutPackagePrefix(v)))
d.w.Write(openBraceBytes)
d.depth++
{
vt := v.Type()
numFields := v.NumField()
if numFields > 0 {
d.w.Write(newlineBytes)
}
for i := 0; i < numFields; i++ {
d.indent()
vtf := vt.Field(i)
d.w.Write([]byte(vtf.Name))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.Field(i)))
d.w.Write(commaBytes)
d.w.Write(newlineBytes)
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(d.w, uintptr(v.Uint()))
case reflect.Func:
d.w.Write([]byte(reflectsource.GetFuncValueSourceAsString(v)))
case reflect.UnsafePointer, reflect.Chan:
printHexPtr(d.w, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it in case any new
// types are added.
default:
if v.CanInterface() {
fmt.Fprintf(d.w, "%v", v.Interface())
} else {
fmt.Fprintf(d.w, "%v", v.String())
}
}
AfterKindSwitch:
if shouldPrintClosingBr {
d.w.Write(closeParenBytes)
}
}
var timeType = reflect.TypeOf(time.Time{})
func typeStringWithoutPackagePrefix(v reflect.Value) string {
//return v.Type().String()[len(v.Type().PkgPath())+1:] // TODO: Error checking?
//return v.Type().PkgPath()
//return v.Type().String()
//return v.Type().Name()
/*x := v.Type().String()
if strings.HasPrefix(x, "main.") {
x = x[len("main."):]
}
return x*/
px := v.Type().String()
prefix := px[0 : len(px)-len(strings.TrimLeft(px, "*"))] // Split "**main.Lang" -> "**" and "main.Lang"
x := px[len(prefix):]
x = strings.TrimPrefix(x, "main.")
x = strings.TrimPrefix(x, "goon_test.")
return prefix + x
/*x = string(debug.Stack())//GetLine(string(debug.Stack()), 0)
//x = x[1:strings.Index(x, ":")]
//spew.Printf(">%s<\n", x)
//panic(nil)
//st := string(debug.Stack())
//debug.PrintStack()
return x*/
}
// fdump is a helper function to consolidate the logic from the various public
// methods which take varying writers and config states.
func fdump(w io.Writer, a ...interface{}) {
for _, arg := range a {
d := dumpState{w: w}
if arg == nil {
d.w.Write(interfaceBytes)
d.w.Write(nilParenBytes)
} else {
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
}
d.w.Write(newlineBytes)
}
}
// bdump dumps to []byte.
func bdump(a ...interface{}) []byte {
var buf bytes.Buffer
fdump(&buf, a...)
return gofmt(buf.Bytes())
}
func fdumpNamed(w io.Writer, names []string, a ...interface{}) {
for argIndex, arg := range a {
d := dumpState{w: w}
if argIndex < len(names) {
d.w.Write([]byte(names[argIndex]))
d.w.Write([]byte(" = "))
}
if arg == nil {
d.w.Write(interfaceBytes)
d.w.Write(nilParenBytes)
} else {
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
}
if len(names) >= len(a) {
d.w.Write(newlineBytes)
} else {
if argIndex < len(a)-1 {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
}
func bdumpNamed(names []string, a ...interface{}) []byte {
var buf bytes.Buffer
fdumpNamed(&buf, names, a...)
return gofmt(buf.Bytes())
}
func gofmt(src []byte) []byte {
formattedSrc, err := format.Source(src)
if nil != err {
return []byte("gofmt error (" + err.Error() + ")!\n" + string(src))
}
return formattedSrc
}

56
vendor/github.com/shurcooL/go-goon/goon.go generated vendored Normal file
View File

@@ -0,0 +1,56 @@
// Package goon is a deep pretty printer with Go-like notation. It implements the goon specification.
//
// Deprecated: This package is old, incomplete, low code quality, and now unmaintained.
// See github.com/hexops/valast for a newer package that is the closest known direct replacement.
// See the Alternatives section in README.md for other known entries in this problem space.
package goon
import (
"io"
"os"
"github.com/shurcooL/go/reflectsource"
)
// Dump dumps goons to stdout.
func Dump(a ...interface{}) (n int, err error) {
return os.Stdout.Write(bdump(a...))
}
// Sdump dumps goons to a string.
func Sdump(a ...interface{}) string {
return string(bdump(a...))
}
// Fdump dumps goons to a writer.
func Fdump(w io.Writer, a ...interface{}) (n int, err error) {
return w.Write(bdump(a...))
}
// DumpExpr dumps goon expressions to stdout.
//
// E.g., this:
//
// somethingImportant := 5
// DumpExpr(somethingImportant)
//
// Will print:
//
// somethingImportant = (int)(5)
func DumpExpr(a ...interface{}) (n int, err error) {
return os.Stdout.Write(bdumpNamed(reflectsource.GetParentArgExprAllAsString(), a...))
}
// SdumpExpr dumps goon expressions to a string.
func SdumpExpr(a ...interface{}) string {
return string(bdumpNamed(reflectsource.GetParentArgExprAllAsString(), a...))
}
// FdumpExpr dumps goon expressions to a writer.
func FdumpExpr(w io.Writer, a ...interface{}) (n int, err error) {
names := reflectsource.GetParentArgExprAllAsString()
if len(names) >= 1 {
names = names[1:] // First argument is the writer, skip it.
}
return w.Write(bdumpNamed(names, a...))
}

107
vendor/github.com/shurcooL/go-goon/print_types.go generated vendored Normal file
View File

@@ -0,0 +1,107 @@
package goon
import (
"io"
"strconv"
)
// Some constants in the form of bytes to avoid string overhead. This mirrors
// the technique used in the fmt package.
var (
plusBytes = []byte("+")
iBytes = []byte("i")
trueBytes = []byte("true")
falseBytes = []byte("false")
interfaceBytes = []byte("(interface{})")
commaBytes = []byte(",")
commaNewlineBytes = []byte(",\n")
newlineBytes = []byte("\n")
openBraceBytes = []byte("{")
openBraceNewlineBytes = []byte("{\n")
closeBraceBytes = []byte("}")
ampersandBytes = []byte("&")
colonSpaceBytes = []byte(": ")
openParenBytes = []byte("(")
closeParenBytes = []byte(")")
nilBytes = []byte("nil")
nilParenBytes = []byte("(nil)")
circularBytes = []byte("already_shown")
invalidAngleBytes = []byte("<invalid>")
)
// hexDigits is used to map a decimal value to a hex digit.
var hexDigits = "0123456789abcdef"
// printBool outputs a boolean value as true or false to Writer w.
func printBool(w io.Writer, val bool) {
if val {
w.Write(trueBytes)
} else {
w.Write(falseBytes)
}
}
// printInt outputs a signed integer value to Writer w.
func printInt(w io.Writer, val int64, base int) {
w.Write([]byte(strconv.FormatInt(val, base)))
}
// printUint outputs an unsigned integer value to Writer w.
func printUint(w io.Writer, val uint64, base int) {
w.Write([]byte(strconv.FormatUint(val, base)))
}
// printFloat outputs a floating point value using the specified precision,
// which is expected to be 32 or 64bit, to Writer w.
func printFloat(w io.Writer, val float64, precision int) {
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
}
// printComplex outputs a complex value using the specified float precision
// for the real and imaginary parts to Writer w.
func printComplex(w io.Writer, c complex128, floatPrecision int) {
r := real(c)
w.Write(openParenBytes)
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
i := imag(c)
if i >= 0 {
w.Write(plusBytes)
}
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
w.Write(iBytes)
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.
num := uint64(p)
if num == 0 {
w.Write(nilBytes)
return
}
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
buf := make([]byte, 18)
// It's simpler to construct the hex string right to left.
base := uint64(16)
i := len(buf) - 1
for num >= base {
buf[i] = hexDigits[num%base]
num /= base
i--
}
buf[i] = hexDigits[num]
// Add '0x' prefix.
i--
buf[i] = 'x'
i--
buf[i] = '0'
// Strip unused leading bytes.
buf = buf[i:]
w.Write(buf)
}

21
vendor/github.com/shurcooL/go/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Dmitri Shuralyov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

34
vendor/github.com/shurcooL/go/parserutil/parserutil.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
// Package parserutil offers convenience functions for parsing Go code to AST.
package parserutil
import (
"errors"
"go/ast"
"go/parser"
"go/token"
)
// ParseStmt is a convenience function for obtaining the AST of a statement x.
// The position information recorded in the AST is undefined. The filename used
// in error messages is the empty string.
func ParseStmt(x string) (ast.Stmt, error) {
file, err := parser.ParseFile(token.NewFileSet(), "", "package p;func _(){\n//line :1\n"+x+"\n;}", 0)
if err != nil {
return nil, err
}
return file.Decls[0].(*ast.FuncDecl).Body.List[0], nil
}
// ParseDecl is a convenience function for obtaining the AST of a declaration x.
// The position information recorded in the AST is undefined. The filename used
// in error messages is the empty string.
func ParseDecl(x string) (ast.Decl, error) {
file, err := parser.ParseFile(token.NewFileSet(), "", "package p\n//line :1\n"+x+"\n", 0)
if err != nil {
return nil, err
}
if len(file.Decls) == 0 {
return nil, errors.New("no declaration")
}
return file.Decls[0], nil
}

View File

@@ -0,0 +1,36 @@
// Package printerutil provides formatted printing of AST nodes.
package printerutil
import (
"bytes"
"fmt"
"go/printer"
"go/token"
)
// Consistent with the default gofmt behavior.
var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
// SprintAst prints node, using fset, and returns it as string.
func SprintAst(fset *token.FileSet, node interface{}) string {
var buf bytes.Buffer
config.Fprint(&buf, fset, node)
return buf.String()
}
// SprintAstBare prints node and returns it as string.
func SprintAstBare(node interface{}) string {
fset := token.NewFileSet()
return SprintAst(fset, node)
}
// PrintlnAst prints node, using fset, to stdout.
func PrintlnAst(fset *token.FileSet, node interface{}) {
fmt.Println(SprintAst(fset, node))
}
// PrintlnAstBare prints node to stdout.
func PrintlnAstBare(node interface{}) {
fset := token.NewFileSet()
PrintlnAst(fset, node)
}

View File

@@ -0,0 +1,112 @@
// Package reflectfind offers funcs to perform deep-search via reflect to find instances that satisfy given query.
package reflectfind
import "reflect"
// First finds the first instances of i that satisfies query within d.
func First(d interface{}, query func(i interface{}) bool) interface{} {
s := state{Visited: make(map[uintptr]struct{})}
return s.findFirst(reflect.ValueOf(d), query)
}
type state struct {
Visited map[uintptr]struct{}
}
func (s *state) findFirst(v reflect.Value, query func(i interface{}) bool) interface{} {
// TODO: Should I check v.CanInterface()? It seems like I might be able to get away without it...
if query(v.Interface()) {
return v.Interface()
}
switch v.Kind() {
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if q := s.findFirst(v.Field(i), query); q != nil {
return q
}
}
case reflect.Map:
for _, key := range v.MapKeys() {
if q := s.findFirst(v.MapIndex(key), query); q != nil {
return q
}
}
case reflect.Array, reflect.Slice:
for i := 0; i < v.Len(); i++ {
if q := s.findFirst(v.Index(i), query); q != nil {
return q
}
}
case reflect.Ptr:
if !v.IsNil() {
if _, visited := s.Visited[v.Pointer()]; !visited {
s.Visited[v.Pointer()] = struct{}{}
if q := s.findFirst(v.Elem(), query); q != nil {
return q
}
}
}
case reflect.Interface:
if !v.IsNil() {
if q := s.findFirst(v.Elem(), query); q != nil {
return q
}
}
}
return nil
}
// All finds all instances of i that satisfy query within d.
func All(d interface{}, query func(i interface{}) bool) map[interface{}]struct{} {
s := stateAll{state: state{Visited: make(map[uintptr]struct{})}, Found: make(map[interface{}]struct{})}
s.findAll(reflect.ValueOf(d), query)
return s.Found
}
type stateAll struct {
state
Found map[interface{}]struct{}
}
func (s *stateAll) findAll(v reflect.Value, query func(i interface{}) bool) {
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
// TODO: Instead of skipping nil values, maybe pass the info as a bool parameter to query?
if v.IsNil() {
return
}
}
// TODO: Should I check v.CanInterface()? It seems like I might be able to get away without it...
if query(v.Interface()) {
s.Found[v.Interface()] = struct{}{}
}
switch v.Kind() {
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
s.findAll(v.Field(i), query)
}
case reflect.Map:
for _, key := range v.MapKeys() {
s.findAll(v.MapIndex(key), query)
}
case reflect.Array, reflect.Slice:
for i := 0; i < v.Len(); i++ {
s.findAll(v.Index(i), query)
}
case reflect.Ptr:
if !v.IsNil() {
if _, visited := s.Visited[v.Pointer()]; !visited {
s.Visited[v.Pointer()] = struct{}{}
s.findAll(v.Elem(), query)
}
}
case reflect.Interface:
if !v.IsNil() {
s.findAll(v.Elem(), query)
}
}
}

View File

@@ -0,0 +1,219 @@
package reflectsource
import (
"bytes"
"fmt"
"go/ast"
"io/ioutil"
"runtime"
"strings"
"github.com/shurcooL/go/parserutil"
"github.com/shurcooL/go/printerutil"
"github.com/shurcooL/go/reflectfind"
)
// GetParentFuncAsString gets the parent func as a string.
func GetParentFuncAsString() string {
// TODO: Replace use of debug.Stack() with direct use of runtime package...
// TODO: Use runtime.FuncForPC(runtime.Caller()).Name() to get func name if source code not found.
stack := string(stack())
funcName := getLine(stack, 3)
funcName = funcName[1:strings.Index(funcName, ": ")]
if dotPos := strings.LastIndex(funcName, "."); dotPos != -1 { // Trim package prefix.
funcName = funcName[dotPos+1:]
}
funcArgs := getLine(stack, 5)
funcArgs = funcArgs[strings.Index(funcArgs, ": ")+len(": "):]
funcArgs = funcArgs[strings.Index(funcArgs, "(") : strings.LastIndex(funcArgs, ")")+len(")")] // TODO: This may fail if there are 2+ func calls on one line.
return funcName + funcArgs
}
// GetParentFuncArgsAsString gets the parent func with its args as a string.
func GetParentFuncArgsAsString(args ...interface{}) string {
// TODO: Replace use of debug.Stack() with direct use of runtime package...
// TODO: Use runtime.FuncForPC(runtime.Caller()).Name() to get func name if source code not found.
stack := string(stack())
funcName := getLine(stack, 3)
funcName = funcName[1:strings.Index(funcName, ": ")]
if dotPos := strings.LastIndex(funcName, "."); dotPos != -1 { // Trim package prefix.
funcName = funcName[dotPos+1:]
}
funcArgs := "("
for i, arg := range args {
// TODO: Add arg names. Maybe not?
if i != 0 {
funcArgs += ", "
}
funcArgs += fmt.Sprintf("%#v", arg) // TODO: Maybe use goon instead. Need to move elsewhere to avoid import cycle.
}
funcArgs += ")"
return funcName + funcArgs
}
// GetExprAsString gets the expression as a string.
func GetExprAsString(_ interface{}) string {
return GetParentArgExprAsString(0)
}
func getParent2ArgExprAllAsAst() []ast.Expr {
// TODO: Replace use of debug.Stack() with direct use of runtime package...
stack := string(stack())
// TODO: Bounds error checking, get rid of GetLine gists, etc.
parentName := getLine(stack, 5)
if !strings.Contains(parentName, ": ") {
// TODO: This happens when source file isn't present in same location as when built. See if can do anything better
// via direct use of runtime package (instead of debug.Stack(), which will exclude any func names)...
return nil
}
parentName = parentName[1:strings.Index(parentName, ": ")]
if dotPos := strings.LastIndex(parentName, "."); dotPos != -1 { // Trim package prefix.
parentName = parentName[dotPos+1:]
}
str := getLine(stack, 7)
str = str[strings.Index(str, ": ")+len(": "):]
p, err := parserutil.ParseStmt(str)
if err != nil {
return nil
}
innerQuery := func(i interface{}) bool {
if ident, ok := i.(*ast.Ident); ok && ident.Name == parentName {
return true
}
return false
}
query := func(i interface{}) bool {
if c, ok := i.(*ast.CallExpr); ok && nil != reflectfind.First(c.Fun, innerQuery) {
return true
}
return false
}
callExpr, _ := reflectfind.First(p, query).(*ast.CallExpr)
if callExpr == nil {
return nil
}
return callExpr.Args
}
// GetParentArgExprAsString gets the argIndex argument expression of parent func call as a string.
func GetParentArgExprAsString(argIndex uint32) string {
args := getParent2ArgExprAllAsAst()
if args == nil {
return "<expr not found>"
}
if argIndex >= uint32(len(args)) {
return "<out of range>"
}
return printerutil.SprintAstBare(args[argIndex])
}
// GetParentArgExprAllAsString gets all argument expressions of parent func call as a string.
func GetParentArgExprAllAsString() []string {
args := getParent2ArgExprAllAsAst()
if args == nil {
return nil
}
out := make([]string, len(args))
for i := range args {
out[i] = printerutil.SprintAstBare(args[i])
}
return out
}
func getMySecondArgExprAsString(int, int) string {
return GetParentArgExprAsString(1)
}
func getLine(s string, lineIndex int) string {
return strings.Split(s, "\n")[lineIndex]
}
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
slash = []byte("/")
)
// stack returns a formatted stack trace of the goroutine that calls it.
// For each routine, it includes the source line information and PC value,
// then attempts to discover, for Go functions, the calling function or
// method and the text of the line containing the invocation.
//
// It was deprecated in Go 1.5, suggested to use package runtime's Stack instead,
// and replaced by another implementation in Go 1.6.
//
// stack implements the Go 1.5 version of debug.Stack(), skipping 1 frame,
// instead of 2, since it's being called directly (rather than via debug.Stack()).
func stack() []byte {
buf := new(bytes.Buffer) // the returned data
// As we loop, we open files and read them. These variables record the currently
// loaded file.
var lines [][]byte
var lastFile string
for i := 1; ; i++ { // Caller we care about is the user, 1 frame up
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
if file != lastFile {
data, err := ioutil.ReadFile(file)
if err != nil {
continue
}
lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
}
return buf.Bytes()
}
// source returns a space-trimmed slice of the n'th line.
func source(lines [][]byte, n int) []byte {
if n < 0 || n >= len(lines) {
return dunno
}
return bytes.Trim(lines[n], " \t")
}
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
// Since the package path might contains dots (e.g. code.google.com/...),
// we first remove the path prefix if there is one.
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
}
name = bytes.Replace(name, centerDot, dot, -1)
return name
}

9
vendor/github.com/shurcooL/go/reflectsource/doc.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// Package sourcereflect implements run-time source reflection, allowing a program to
// look up string representation of objects from the underlying .go source files.
//
// Specifically, it implements ability to get name of caller funcs and their parameters.
// It also implements functionality to get a string containing source code of provided func.
//
// In order to succeed, it expects the program's source code to be available in normal location.
// It's intended to be used for development purposes, or for experimental programs.
package reflectsource

View File

@@ -0,0 +1,82 @@
package reflectsource
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"reflect"
"runtime"
"github.com/shurcooL/go/printerutil"
"github.com/shurcooL/go/reflectfind"
)
// GetSourceAsString returns the source of the func f.
func GetSourceAsString(f interface{}) string {
// No need to check for f being nil, since that's handled below.
fv := reflect.ValueOf(f)
return GetFuncValueSourceAsString(fv)
}
// GetFuncValueSourceAsString returns the source of the func value fv.
func GetFuncValueSourceAsString(fv reflect.Value) string {
// Checking the kind catches cases where f was nil, resulting in fv being a zero Value (i.e. invalid kind),
// as well as when fv is non-func.
if fv.Kind() != reflect.Func {
return "kind not func"
}
pc := fv.Pointer()
if pc == 0 {
return "nil"
}
function := runtime.FuncForPC(pc)
if function == nil {
return "nil"
}
file, line := function.FileLine(pc)
var startIndex, endIndex int
{
b, err := ioutil.ReadFile(file)
if err != nil {
return "<file not found>"
}
startIndex, endIndex = getLineStartEndIndicies(b, line-1)
}
fs := token.NewFileSet()
fileAst, err := parser.ParseFile(fs, file, nil, 0*parser.ParseComments)
if err != nil {
return "<ParseFile failed>"
}
// TODO: Consider using ast.Walk() instead of custom FindFirst()
query := func(i interface{}) bool {
// TODO: Factor-out the unusual overlap check
if f, ok := i.(*ast.FuncLit); ok && ((startIndex <= int(f.Pos())-1 && int(f.Pos())-1 <= endIndex) || (int(f.Pos())-1 <= startIndex && startIndex <= int(f.End())-1)) {
return true
}
return false
}
funcAst := reflectfind.First(fileAst, query)
// If func literal wasn't found, try again looking for func declaration
if funcAst == nil {
query := func(i interface{}) bool {
// TODO: Factor-out the unusual overlap check
if f, ok := i.(*ast.FuncDecl); ok && ((startIndex <= int(f.Pos())-1 && int(f.Pos())-1 <= endIndex) || (int(f.Pos())-1 <= startIndex && startIndex <= int(f.End())-1)) {
return true
}
return false
}
funcAst = reflectfind.First(fileAst, query)
}
if funcAst == nil {
return fmt.Sprintf("<func src not found at %v:%v>", file, line)
}
return printerutil.SprintAst(fs, funcAst)
}

View File

@@ -0,0 +1,29 @@
package reflectsource
import (
"bytes"
)
// getLineStartEndIndicies gets the starting and ending caret indicies of line with specified lineIndex.
// Does not include newline character.
// First line has index 0.
// Returns (-1, -1) if line is not found.
func getLineStartEndIndicies(b []byte, lineIndex int) (startIndex, endIndex int) {
index := 0
for line := 0; ; line++ {
lineLength := bytes.IndexByte(b[index:], '\n')
if line == lineIndex {
if lineLength == -1 {
return index, len(b)
} else {
return index, index + lineLength
}
}
if lineLength == -1 {
break
}
index += lineLength + 1
}
return -1, -1
}