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/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
}