_scripts: auto check that pkg/proc and runtime are synchronized (#2557)
Adds a script that check automatically that the the assumptions that pkg/proc makes about runtime are met by the actual runtime, using a combination of parsing and magic comments. Also emits a file describing all the struct fields, constants and variables of the runtime that we use in pkg/proc.
This commit is contained in:
parent
3d6bbbe92a
commit
c379296cc8
78
_scripts/rtype-out.txt
Normal file
78
_scripts/rtype-out.txt
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
var firstmoduledata moduledata
|
||||||
|
|
||||||
|
var debug anytype
|
||||||
|
|
||||||
|
type _defer struct {
|
||||||
|
fn anytype
|
||||||
|
pc uintptr
|
||||||
|
sp uintptr
|
||||||
|
siz int32 (optional)
|
||||||
|
link *_defer
|
||||||
|
}
|
||||||
|
|
||||||
|
type bmap struct {
|
||||||
|
tophash [8]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type eface struct {
|
||||||
|
_type *_type
|
||||||
|
data unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
type g struct {
|
||||||
|
sched gobuf
|
||||||
|
goid int64
|
||||||
|
gopc uintptr
|
||||||
|
startpc uintptr
|
||||||
|
waitsince int64
|
||||||
|
waitreason waitReason (optional)
|
||||||
|
stack stack
|
||||||
|
atomicstatus uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type gobuf struct {
|
||||||
|
pc uintptr
|
||||||
|
sp uintptr
|
||||||
|
bp uintptr (optional)
|
||||||
|
lr uintptr (optional)
|
||||||
|
}
|
||||||
|
|
||||||
|
type hmap struct {
|
||||||
|
count int
|
||||||
|
B uint8
|
||||||
|
buckets unsafe.Pointer
|
||||||
|
oldbuckets unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
type iface struct {
|
||||||
|
tab *itab
|
||||||
|
data unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
type itab struct {
|
||||||
|
_type *_type
|
||||||
|
}
|
||||||
|
|
||||||
|
type moduledata struct {
|
||||||
|
text uintptr
|
||||||
|
types uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type stack struct {
|
||||||
|
hi uintptr
|
||||||
|
lo uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyOne = 1
|
||||||
|
|
||||||
|
const emptyRest = 0
|
||||||
|
|
||||||
|
const kindDirectIface = 32
|
||||||
|
|
||||||
|
const kindGCProg = 64
|
||||||
|
|
||||||
|
const kindMask = 31
|
||||||
|
|
||||||
|
const minTopHash = 4
|
||||||
|
or const minTopHash = 5
|
||||||
|
|
666
_scripts/rtype.go
Normal file
666
_scripts/rtype.go
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
// This script checks that the Go runtime hasn't changed in ways that Delve
|
||||||
|
// doesn't understand. It accomplishes this task by parsing the pkg/proc
|
||||||
|
// package and extracting rules from all the comments starting with the
|
||||||
|
// magic string '+rtype'.
|
||||||
|
//
|
||||||
|
// COMMAND LINE
|
||||||
|
//
|
||||||
|
// go run _scripts/rtype.go (report [output-file]|check)
|
||||||
|
//
|
||||||
|
// Invoked with the command 'report' it will extract rules from pkg/proc and
|
||||||
|
// print them to stdout.
|
||||||
|
// Invoked with the command 'check' it will actually check that the runtime
|
||||||
|
// conforms to the rules in pkg/proc.
|
||||||
|
//
|
||||||
|
// RTYPE RULES
|
||||||
|
//
|
||||||
|
// // +rtype -var V T
|
||||||
|
//
|
||||||
|
// checks that variable runtime.V exists and has type T
|
||||||
|
//
|
||||||
|
// // +rtype -field S.F T
|
||||||
|
//
|
||||||
|
// checks that struct runtime.S has a field called F of type T
|
||||||
|
//
|
||||||
|
// const C1 = V // +rtype C2
|
||||||
|
//
|
||||||
|
// checks that constant runtime.C2 exists and has value V
|
||||||
|
//
|
||||||
|
// case "F": // +rtype -fieldof S T
|
||||||
|
//
|
||||||
|
// checks that struct runtime.S has a field called F of type T
|
||||||
|
//
|
||||||
|
// v := ... // +rtype T
|
||||||
|
//
|
||||||
|
// if v is declared as *proc.Variable it will assume that it has type
|
||||||
|
// runtime.T and it will then parse the enclosing function, searching for
|
||||||
|
// all calls to:
|
||||||
|
// v.loadFieldNamed
|
||||||
|
// v.fieldVariable
|
||||||
|
// v.structMember
|
||||||
|
// and check that type T has the specified fields.
|
||||||
|
//
|
||||||
|
// v.loadFieldNamed("F") // +rtype T
|
||||||
|
// v.loadFieldNamed("F") // +rtype -opt T
|
||||||
|
//
|
||||||
|
// checks that field F of the struct type declared for v has type T. Can
|
||||||
|
// also be used for fieldVariable, structMember and, inside parseG,
|
||||||
|
// loadInt64Maybe.
|
||||||
|
// The -opt flag specifies that the field can be missing (but if it exists
|
||||||
|
// it must have type T).
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Anywhere a type is required anytype can be used to specify that we don't
|
||||||
|
// care about its type.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/constant"
|
||||||
|
"go/printer"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/packages"
|
||||||
|
)
|
||||||
|
|
||||||
|
const magicCommentPrefix = "+rtype"
|
||||||
|
|
||||||
|
var fset = &token.FileSet{}
|
||||||
|
var checkVarTypeRules = []*checkVarType{}
|
||||||
|
var checkFieldTypeRules = map[string][]*checkFieldType{}
|
||||||
|
var checkConstValRules = map[string][]*checkConstVal{}
|
||||||
|
var showRuleOrigin = false
|
||||||
|
|
||||||
|
// rtypeCmnt represents a +rtype comment
|
||||||
|
type rtypeCmnt struct {
|
||||||
|
slash token.Pos
|
||||||
|
txt string
|
||||||
|
node ast.Node // associated node
|
||||||
|
toplevel ast.Decl // toplevel declaration that contains the Slash of the comment
|
||||||
|
stmt ast.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
type checkVarType struct {
|
||||||
|
V, T string // V must have type T
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *checkVarType) String() string {
|
||||||
|
if showRuleOrigin {
|
||||||
|
pos := fset.Position(c.pos)
|
||||||
|
return fmt.Sprintf("var %s %s // %s:%d", c.V, c.T, relative(pos.Filename), pos.Line)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("var %s %s", c.V, c.T)
|
||||||
|
}
|
||||||
|
|
||||||
|
type checkFieldType struct {
|
||||||
|
S, F, T string // S.F must have type T
|
||||||
|
opt bool
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *checkFieldType) String() string {
|
||||||
|
pos := fset.Position(c.pos)
|
||||||
|
return fmt.Sprintf("field %s.%s %s // %s:%d", c.S, c.F, c.T, relative(pos.Filename), pos.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
type checkConstVal struct {
|
||||||
|
C string // const C = V
|
||||||
|
V constant.Value
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *checkConstVal) String() string {
|
||||||
|
if showRuleOrigin {
|
||||||
|
pos := fset.Position(c.pos)
|
||||||
|
return fmt.Sprintf("const %s = %s // %s:%d", c.C, c.V, relative(pos.Filename), pos.Line)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("const %s = %s", c.C, c.V)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Wrong number of arguments.\n\trtype (report [output-file]|check)\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := os.Args[1]
|
||||||
|
|
||||||
|
setup()
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "report":
|
||||||
|
if len(os.Args) > 2 {
|
||||||
|
fh, err := os.Create(os.Args[2])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating output file: %v", err)
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
os.Stdout = fh
|
||||||
|
}
|
||||||
|
report()
|
||||||
|
case "check":
|
||||||
|
check()
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "Wrong argument %s\n", command)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup parses the proc package, extracting all +rtype comments and
|
||||||
|
// converting them into rules.
|
||||||
|
func setup() {
|
||||||
|
pkgs, err := packages.Load(&packages.Config{Mode: packages.LoadSyntax, Fset: fset}, "github.com/go-delve/delve/pkg/proc")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("could not load proc package: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range pkgs[0].Syntax {
|
||||||
|
cmntmap := ast.NewCommentMap(fset, file, file.Comments)
|
||||||
|
rtypeCmnts := getRtypeCmnts(file, cmntmap)
|
||||||
|
for _, rtcmnt := range rtypeCmnts {
|
||||||
|
if rtcmnt == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
process(pkgs[0], rtcmnt, cmntmap, rtypeCmnts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRtypeCmnts returns all +rtype comments inside 'file'. It also
|
||||||
|
// decorates them with the toplevel declaration that contains them as well
|
||||||
|
// as the statement they are associated with (where applicable).
|
||||||
|
func getRtypeCmnts(file *ast.File, cmntmap ast.CommentMap) []*rtypeCmnt {
|
||||||
|
r := []*rtypeCmnt{}
|
||||||
|
|
||||||
|
for n, cmntgrps := range cmntmap {
|
||||||
|
for _, cmntgrp := range cmntgrps {
|
||||||
|
if len(cmntgrp.List) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmnt := range cmntgrp.List {
|
||||||
|
txt := cleanupCommentText(cmnt.Text)
|
||||||
|
if !strings.HasPrefix(txt, magicCommentPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r = append(r, &rtypeCmnt{slash: cmnt.Slash, txt: txt, node: n})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(r, func(i, j int) bool { return r[i].slash < r[j].slash })
|
||||||
|
|
||||||
|
// assign each comment to the toplevel declaration that contains it
|
||||||
|
for i, j := 0, 0; i < len(r) && j < len(file.Decls); {
|
||||||
|
decl := file.Decls[j]
|
||||||
|
if decl.Pos() <= r[i].slash && r[i].slash < decl.End() {
|
||||||
|
r[i].toplevel = decl
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for comments declared inside a function also find the statement that contains them.
|
||||||
|
for i := range r {
|
||||||
|
fndecl, ok := r[i].toplevel.(*ast.FuncDecl)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastStmt ast.Stmt
|
||||||
|
ast.Inspect(fndecl, func(n ast.Node) bool {
|
||||||
|
if stmt, _ := n.(ast.Stmt); stmt != nil {
|
||||||
|
lastStmt = stmt
|
||||||
|
}
|
||||||
|
if n == r[i].node {
|
||||||
|
r[i].stmt = lastStmt
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupCommentText(txt string) string {
|
||||||
|
if strings.HasPrefix(txt, "/*") || strings.HasPrefix(txt, "//") {
|
||||||
|
txt = txt[2:]
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(strings.TrimSuffix(txt, "*/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// process processes a single +rtype comment, turning it into a rule.
|
||||||
|
// If the +rtype comment is associated with a *proc.Variable declaration
|
||||||
|
// then it also checks the containing function for all uses of that
|
||||||
|
// variable.
|
||||||
|
func process(pkg *packages.Package, rtcmnt *rtypeCmnt, cmntmap ast.CommentMap, rtcmnts []*rtypeCmnt) {
|
||||||
|
tinfo := pkg.TypesInfo
|
||||||
|
fields := strings.Split(rtcmnt.txt, " ")
|
||||||
|
|
||||||
|
switch fields[1] {
|
||||||
|
case "-var":
|
||||||
|
// -var V T
|
||||||
|
// requests that variable V is of type T
|
||||||
|
addCheckVarType(fields[2], fields[3], rtcmnt.slash)
|
||||||
|
case "-field":
|
||||||
|
// -field S.F T
|
||||||
|
// requests that field F of type S is of type T
|
||||||
|
v := strings.Split(fields[2], ".")
|
||||||
|
addCheckFieldType(v[0], v[1], fields[3], false, rtcmnt.slash)
|
||||||
|
default:
|
||||||
|
ok := false
|
||||||
|
if ident := isProcVariableDecl(rtcmnt.stmt, tinfo); ident != nil {
|
||||||
|
if len(fields) == 2 {
|
||||||
|
processProcVariableUses(rtcmnt.toplevel, tinfo, ident, cmntmap, rtcmnts, fields[1])
|
||||||
|
ok = true
|
||||||
|
} else if len(fields) == 3 && fields[1] == "-opt" {
|
||||||
|
processProcVariableUses(rtcmnt.toplevel, tinfo, ident, cmntmap, rtcmnts, fields[2])
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
} else if ident := isConstDecl(rtcmnt.toplevel, rtcmnt.node); len(fields) == 2 && ident != nil {
|
||||||
|
addCheckConstVal(fields[1], constValue(tinfo.Defs[ident]), rtcmnt.slash)
|
||||||
|
ok = true
|
||||||
|
} else if F := isStringCaseClause(rtcmnt.stmt); F != "" && len(fields) == 4 && fields[1] == "-fieldof" {
|
||||||
|
addCheckFieldType(fields[2], F, fields[3], false, rtcmnt.slash)
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
pos := fset.Position(rtcmnt.slash)
|
||||||
|
log.Fatalf("%s:%d: unrecognized +rtype comment\n", pos.Filename, pos.Line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isProcVariableDecl returns true if stmt is a declaration of a
|
||||||
|
// *proc.Variable variable.
|
||||||
|
func isProcVariableDecl(stmt ast.Stmt, tinfo *types.Info) *ast.Ident {
|
||||||
|
ass, _ := stmt.(*ast.AssignStmt)
|
||||||
|
if ass == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(ass.Lhs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ident, _ := ass.Lhs[0].(*ast.Ident)
|
||||||
|
if ident == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var typ types.Type
|
||||||
|
if def := tinfo.Defs[ident]; def != nil {
|
||||||
|
typ = def.Type()
|
||||||
|
}
|
||||||
|
if tv, ok := tinfo.Types[ident]; ok {
|
||||||
|
typ = tv.Type
|
||||||
|
}
|
||||||
|
if typ == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if typ == nil || typ.String() != "*github.com/go-delve/delve/pkg/proc.Variable" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ident
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConstDecl(toplevel ast.Decl, node ast.Node) *ast.Ident {
|
||||||
|
gendecl, _ := toplevel.(*ast.GenDecl)
|
||||||
|
if gendecl == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if gendecl.Tok != token.CONST {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
valspec, _ := node.(*ast.ValueSpec)
|
||||||
|
if valspec == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(valspec.Names) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return valspec.Names[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStringCaseClause(stmt ast.Stmt) string {
|
||||||
|
c, _ := stmt.(*ast.CaseClause)
|
||||||
|
if c == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(c.List) != 1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
lit := c.List[0].(*ast.BasicLit)
|
||||||
|
if lit == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if lit.Kind != token.STRING {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
r, _ := strconv.Unquote(lit.Value)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// processProcVariableUses scans the body of the function declaration 'decl'
|
||||||
|
// looking for uses of 'procVarIdent' which is assumed to be an identifier
|
||||||
|
// for a *proc.Variable variable.
|
||||||
|
func processProcVariableUses(decl ast.Node, tinfo *types.Info, procVarIdent *ast.Ident, cmntmap ast.CommentMap, rtcmnts []*rtypeCmnt, S string) {
|
||||||
|
if len(S) > 0 && S[0] == '*' {
|
||||||
|
S = S[1:]
|
||||||
|
}
|
||||||
|
isParseG := false
|
||||||
|
if fndecl, _ := decl.(*ast.FuncDecl); fndecl != nil {
|
||||||
|
if fndecl.Name.Name == "parseG" {
|
||||||
|
if procVarIdent.Name == "v" {
|
||||||
|
isParseG = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var lastStmt ast.Stmt
|
||||||
|
ast.Inspect(decl, func(n ast.Node) bool {
|
||||||
|
if stmt, _ := n.(ast.Stmt); stmt != nil {
|
||||||
|
lastStmt = stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
fncall, _ := n.(*ast.CallExpr)
|
||||||
|
if fncall == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
var methodName string
|
||||||
|
if isParseG {
|
||||||
|
if xident, _ := fncall.Fun.(*ast.Ident); xident != nil && xident.Name == "loadInt64Maybe" {
|
||||||
|
methodName = "loadInt64Maybe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if methodName == "" {
|
||||||
|
sel, _ := fncall.Fun.(*ast.SelectorExpr)
|
||||||
|
if sel == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
methodName = sel.Sel.Name
|
||||||
|
xident, _ := sel.X.(*ast.Ident)
|
||||||
|
if xident == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if xident.Obj != procVarIdent.Obj {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(fncall.Args) < 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
arg0, _ := fncall.Args[0].(*ast.BasicLit)
|
||||||
|
if arg0 == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if arg0.Kind != token.STRING {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch methodName {
|
||||||
|
case "loadFieldNamed", "fieldVariable", "loadInt64Maybe", "structMember":
|
||||||
|
rtcmntIdx := -1
|
||||||
|
if cmntgrps := cmntmap[lastStmt]; len(cmntgrps) > 0 && len(cmntgrps[0].List) > 0 {
|
||||||
|
rtcmntIdx = findComment(cmntgrps[0].List[0].Slash, rtcmnts)
|
||||||
|
}
|
||||||
|
typ := "anytype"
|
||||||
|
opt := false
|
||||||
|
|
||||||
|
if rtcmntIdx >= 0 {
|
||||||
|
fields := strings.Split(rtcmnts[rtcmntIdx].txt, " ")
|
||||||
|
if len(fields) == 2 {
|
||||||
|
typ = fields[1]
|
||||||
|
} else if len(fields) == 3 && fields[1] == "-opt" {
|
||||||
|
opt = true
|
||||||
|
typ = fields[2]
|
||||||
|
}
|
||||||
|
if isProcVariableDecl(lastStmt, tinfo) == nil {
|
||||||
|
// remove it because we have already processed it
|
||||||
|
rtcmnts[rtcmntIdx] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
F, _ := strconv.Unquote(arg0.Value)
|
||||||
|
addCheckFieldType(S, F, typ, opt, fncall.Pos())
|
||||||
|
//printNode(fset, fncall)
|
||||||
|
default:
|
||||||
|
pos := fset.Position(n.Pos())
|
||||||
|
log.Fatalf("unknown node at %s:%d", pos.Filename, pos.Line)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func findComment(slash token.Pos, rtcmnts []*rtypeCmnt) int {
|
||||||
|
for i := range rtcmnts {
|
||||||
|
if rtcmnts[i] != nil && rtcmnts[i].slash == slash {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCheckVarType(V, T string, pos token.Pos) {
|
||||||
|
checkVarTypeRules = append(checkVarTypeRules, &checkVarType{V, T, pos})
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCheckFieldType(S, F, T string, opt bool, pos token.Pos) {
|
||||||
|
checkFieldTypeRules[S] = append(checkFieldTypeRules[S], &checkFieldType{S, F, T, opt, pos})
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCheckConstVal(C string, V constant.Value, pos token.Pos) {
|
||||||
|
checkConstValRules[C] = append(checkConstValRules[C], &checkConstVal{C, V, pos})
|
||||||
|
}
|
||||||
|
|
||||||
|
// report writes a report of all rules derived from the proc package to stdout.
|
||||||
|
func report() {
|
||||||
|
for _, rule := range checkVarTypeRules {
|
||||||
|
fmt.Printf("%s\n\n", rule.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var Ss []string
|
||||||
|
for S := range checkFieldTypeRules {
|
||||||
|
Ss = append(Ss, S)
|
||||||
|
}
|
||||||
|
sort.Strings(Ss)
|
||||||
|
for _, S := range Ss {
|
||||||
|
rules := checkFieldTypeRules[S]
|
||||||
|
fmt.Printf("type %s struct {\n", S)
|
||||||
|
for _, rule := range rules {
|
||||||
|
fmt.Printf("\t%s %s", rule.F, rule.T)
|
||||||
|
if rule.opt {
|
||||||
|
fmt.Printf(" (optional)")
|
||||||
|
}
|
||||||
|
pos := fset.Position(rule.pos)
|
||||||
|
if showRuleOrigin {
|
||||||
|
fmt.Printf("\t// %s:%d", relative(pos.Filename), pos.Line)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
fmt.Printf("}\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var Cs []string
|
||||||
|
for C := range checkConstValRules {
|
||||||
|
Cs = append(Cs, C)
|
||||||
|
}
|
||||||
|
sort.Strings(Cs)
|
||||||
|
for _, C := range Cs {
|
||||||
|
rules := checkConstValRules[C]
|
||||||
|
for i, rule := range rules {
|
||||||
|
if i == 0 {
|
||||||
|
fmt.Printf("%s\n", rule.String())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("or %s\n", rule.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check parses the runtime package and checks that all the rules retrieved
|
||||||
|
// from the 'proc' package pass.
|
||||||
|
func check() {
|
||||||
|
pkgs2, err := packages.Load(&packages.Config{Mode: packages.LoadSyntax, Fset: fset}, "runtime")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("could not load runtime package: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allok := true
|
||||||
|
|
||||||
|
for _, rule := range checkVarTypeRules {
|
||||||
|
//TODO: implement
|
||||||
|
pos := fset.Position(rule.pos)
|
||||||
|
def := pkgs2[0].Types.Scope().Lookup(rule.V)
|
||||||
|
if def == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: could not find variable %s\n", pos.Filename, pos.Line, rule.V)
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !matchType(def.Type(), rule.T) {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: wrong type for variable %s, expected %s got %s\n", pos.Filename, pos.Line, rule.V, rule.T, typeStr(def.Type()))
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Ss []string
|
||||||
|
for S := range checkFieldTypeRules {
|
||||||
|
Ss = append(Ss, S)
|
||||||
|
}
|
||||||
|
sort.Strings(Ss)
|
||||||
|
for _, S := range Ss {
|
||||||
|
rules := checkFieldTypeRules[S]
|
||||||
|
pos := fset.Position(rules[0].pos)
|
||||||
|
|
||||||
|
def := pkgs2[0].Types.Scope().Lookup(S)
|
||||||
|
if def == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: could not find struct %s\n", pos.Filename, pos.Line, S)
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := def.Type()
|
||||||
|
if typ == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: could not find struct %s\n", pos.Filename, pos.Line, S)
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
styp, _ := typ.Underlying().(*types.Struct)
|
||||||
|
if styp == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: could not find struct %s\n", pos.Filename, pos.Line, S)
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range rules {
|
||||||
|
pos := fset.Position(rule.pos)
|
||||||
|
fieldType := fieldTypeByName(styp, rule.F)
|
||||||
|
if fieldType == nil {
|
||||||
|
if rule.opt {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: could not find field %s.%s\n", pos.Filename, pos.Line, rule.S, rule.F)
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !matchType(fieldType, rule.T) {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: wrong type for field %s.%s, expected %s got %s\n", pos.Filename, pos.Line, rule.S, rule.F, rule.T, typeStr(fieldType))
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Cs []string
|
||||||
|
for C := range checkConstValRules {
|
||||||
|
Cs = append(Cs, C)
|
||||||
|
}
|
||||||
|
sort.Strings(Cs)
|
||||||
|
for _, C := range Cs {
|
||||||
|
rules := checkConstValRules[C]
|
||||||
|
pos := fset.Position(rules[0].pos)
|
||||||
|
def := pkgs2[0].Types.Scope().Lookup(C)
|
||||||
|
if def == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: could not find constant %s\n", pos.Filename, pos.Line, C)
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val := constValue(def)
|
||||||
|
found := false
|
||||||
|
for _, rule := range rules {
|
||||||
|
if val == rule.V {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: wrong value for constant %s (%s)\n", pos.Filename, pos.Line, C, val.String())
|
||||||
|
allok = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allok {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldTypeByName(typ *types.Struct, name string) types.Type {
|
||||||
|
for i := 0; i < typ.NumFields(); i++ {
|
||||||
|
field := typ.Field(i)
|
||||||
|
if field.Name() == name {
|
||||||
|
return field.Type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchType(typ types.Type, T string) bool {
|
||||||
|
if T == "anytype" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return typeStr(typ) == T
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeStr(typ types.Type) string {
|
||||||
|
return types.TypeString(typ, func(pkg *types.Package) string {
|
||||||
|
if pkg.Path() == "runtime" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return pkg.Path()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func constValue(obj types.Object) constant.Value {
|
||||||
|
return obj.(*types.Const).Val()
|
||||||
|
}
|
||||||
|
|
||||||
|
func printNode(fset *token.FileSet, n ast.Node) {
|
||||||
|
ast.Fprint(os.Stderr, fset, n, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exprToString(t ast.Expr) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
printer.Fprint(&buf, token.NewFileSet(), t)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func relative(s string) string {
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
r, err := filepath.Rel(wd, s)
|
||||||
|
if err != nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
@ -401,6 +401,9 @@ func TestGeneratedDoc(t *testing.T) {
|
|||||||
checkAutogenDoc(t, "pkg/terminal/starbind/starlark_mapping.go", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "go", "-"))
|
checkAutogenDoc(t, "pkg/terminal/starbind/starlark_mapping.go", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "go", "-"))
|
||||||
checkAutogenDoc(t, "Documentation/cli/starlark.md", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "doc/dummy", "Documentation/cli/starlark.md"))
|
checkAutogenDoc(t, "Documentation/cli/starlark.md", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "doc/dummy", "Documentation/cli/starlark.md"))
|
||||||
checkAutogenDoc(t, "Documentation/backend_test_health.md", "go run _scripts/gen-backend_test_health.go", runScript("_scripts/gen-backend_test_health.go", "-"))
|
checkAutogenDoc(t, "Documentation/backend_test_health.md", "go run _scripts/gen-backend_test_health.go", runScript("_scripts/gen-backend_test_health.go", "-"))
|
||||||
|
checkAutogenDoc(t, "_scripts/rtype-out.txt", "go run _scripts/rtype.go report _scripts/rtype-out.txt", runScript("_scripts/rtype.go", "report"))
|
||||||
|
|
||||||
|
runScript("_scripts/rtype.go", "check")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExitInInit(t *testing.T) {
|
func TestExitInInit(t *testing.T) {
|
||||||
|
@ -13,6 +13,10 @@ type moduleData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) ([]moduleData, error) {
|
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) ([]moduleData, error) {
|
||||||
|
// +rtype -var firstmoduledata moduledata
|
||||||
|
// +rtype -field moduledata.text uintptr
|
||||||
|
// +rtype -field moduledata.types uintptr
|
||||||
|
|
||||||
scope := globalScope(nil, bi, bi.Images[0], mem)
|
scope := globalScope(nil, bi, bi.Images[0], mem)
|
||||||
var md *Variable
|
var md *Variable
|
||||||
md, err := scope.findGlobal("runtime", "firstmoduledata")
|
md, err := scope.findGlobal("runtime", "firstmoduledata")
|
||||||
|
@ -575,13 +575,14 @@ func (g *G) readDefers(frames []Stackframe) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Defer) load() {
|
func (d *Defer) load() {
|
||||||
d.variable.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
|
v := d.variable // +rtype _defer
|
||||||
if d.variable.Unreadable != nil {
|
v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
|
||||||
d.Unreadable = d.variable.Unreadable
|
if v.Unreadable != nil {
|
||||||
|
d.Unreadable = v.Unreadable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fnvar := d.variable.fieldVariable("fn")
|
fnvar := v.fieldVariable("fn")
|
||||||
if fnvar.Kind == reflect.Func {
|
if fnvar.Kind == reflect.Func {
|
||||||
// In Go 1.18, fn is a func().
|
// In Go 1.18, fn is a func().
|
||||||
d.DwrapPC = fnvar.Base
|
d.DwrapPC = fnvar.Base
|
||||||
@ -593,9 +594,9 @@ func (d *Defer) load() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
|
d.DeferPC, _ = constant.Uint64Val(v.fieldVariable("pc").Value) // +rtype uintptr
|
||||||
d.SP, _ = constant.Uint64Val(d.variable.fieldVariable("sp").Value)
|
d.SP, _ = constant.Uint64Val(v.fieldVariable("sp").Value) // +rtype uintptr
|
||||||
sizVar := d.variable.fieldVariable("siz")
|
sizVar := v.fieldVariable("siz") // +rtype -opt int32
|
||||||
if sizVar != nil {
|
if sizVar != nil {
|
||||||
// In Go <1.18, siz stores the number of bytes of
|
// In Go <1.18, siz stores the number of bytes of
|
||||||
// defer arguments following the defer record. In Go
|
// defer arguments following the defer record. In Go
|
||||||
@ -604,7 +605,7 @@ func (d *Defer) load() {
|
|||||||
d.argSz, _ = constant.Int64Val(sizVar.Value)
|
d.argSz, _ = constant.Int64Val(sizVar.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
linkvar := d.variable.fieldVariable("link").maybeDereference()
|
linkvar := v.fieldVariable("link").maybeDereference() // +rtype *_defer
|
||||||
if linkvar.Addr != 0 {
|
if linkvar.Addr != 0 {
|
||||||
d.link = &Defer{variable: linkvar}
|
d.link = &Defer{variable: linkvar}
|
||||||
}
|
}
|
||||||
|
@ -345,12 +345,13 @@ func setAsyncPreemptOff(p *Target, v int64) {
|
|||||||
}
|
}
|
||||||
logger := p.BinInfo().logger
|
logger := p.BinInfo().logger
|
||||||
scope := globalScope(p, p.BinInfo(), p.BinInfo().Images[0], p.Memory())
|
scope := globalScope(p, p.BinInfo(), p.BinInfo().Images[0], p.Memory())
|
||||||
|
// +rtype -var debug anytype
|
||||||
debugv, err := scope.findGlobal("runtime", "debug")
|
debugv, err := scope.findGlobal("runtime", "debug")
|
||||||
if err != nil || debugv.Unreadable != nil {
|
if err != nil || debugv.Unreadable != nil {
|
||||||
logger.Warnf("could not find runtime/debug variable (or unreadable): %v %v", err, debugv.Unreadable)
|
logger.Warnf("could not find runtime/debug variable (or unreadable): %v %v", err, debugv.Unreadable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
asyncpreemptoffv, err := debugv.structMember("asyncpreemptoff")
|
asyncpreemptoffv, err := debugv.structMember("asyncpreemptoff") // +rtype int32
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warnf("could not find asyncpreemptoff field: %v", err)
|
logger.Warnf("could not find asyncpreemptoff field: %v", err)
|
||||||
return
|
return
|
||||||
|
@ -18,10 +18,10 @@ import (
|
|||||||
// some extra flags defined here.
|
// some extra flags defined here.
|
||||||
// See equivalent declaration in $GOROOT/src/reflect/type.go
|
// See equivalent declaration in $GOROOT/src/reflect/type.go
|
||||||
const (
|
const (
|
||||||
kindDirectIface = 1 << 5
|
kindDirectIface = 1 << 5 // +rtype kindDirectIface
|
||||||
kindGCProg = 1 << 6 // Type.gc points to GC program
|
kindGCProg = 1 << 6 // +rtype kindGCProg
|
||||||
kindNoPointers = 1 << 7
|
kindNoPointers = 1 << 7
|
||||||
kindMask = (1 << 5) - 1
|
kindMask = (1 << 5) - 1 // +rtype kindMask
|
||||||
)
|
)
|
||||||
|
|
||||||
// Value of tflag field in runtime._type.
|
// Value of tflag field in runtime._type.
|
||||||
|
@ -25,10 +25,14 @@ const (
|
|||||||
|
|
||||||
maxArrayStridePrefetch = 1024 // Maximum size of array stride for which we will prefetch the array contents
|
maxArrayStridePrefetch = 1024 // Maximum size of array stride for which we will prefetch the array contents
|
||||||
|
|
||||||
hashTophashEmptyZero = 0 // used by map reading code, indicates an empty cell
|
// hashTophashEmptyZero is used by map reading code, indicates an empty cell
|
||||||
hashTophashEmptyOne = 1 // used by map reading code, indicates an empty cell in Go 1.12 and later
|
hashTophashEmptyZero = 0 // +rtype emptyRest
|
||||||
hashMinTopHashGo111 = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated, in Go1.11
|
// hashTophashEmptyOne is used by map reading code, indicates an empty cell in Go 1.12 and later
|
||||||
hashMinTopHashGo112 = 5 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated, in Go1.12
|
hashTophashEmptyOne = 1 // +rtype emptyOne
|
||||||
|
// hashMinTopHashGo111 used by map reading code, indicates minimum value of tophash that isn't empty or evacuated, in Go1.11
|
||||||
|
hashMinTopHashGo111 = 4 // +rtype minTopHash
|
||||||
|
// hashMinTopHashGo112 is used by map reading code, indicates minimum value of tophash that isn't empty or evacuated, in Go1.12
|
||||||
|
hashMinTopHashGo112 = 5 // +rtype minTopHash
|
||||||
|
|
||||||
maxFramePrefetchSize = 1 * 1024 * 1024 // Maximum prefetch size for a stack frame
|
maxFramePrefetchSize = 1 * 1024 * 1024 // Maximum prefetch size for a stack frame
|
||||||
|
|
||||||
@ -843,22 +847,22 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
if _, isptr := v.RealType.(*godwarf.PtrType); !isptr {
|
if _, isptr := v.RealType.(*godwarf.PtrType); !isptr {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v = v.maybeDereference()
|
v = v.maybeDereference() // +rtype g
|
||||||
}
|
}
|
||||||
|
|
||||||
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
||||||
|
|
||||||
schedVar := v.loadFieldNamed("sched")
|
schedVar := v.loadFieldNamed("sched") // +rtype gobuf
|
||||||
if schedVar == nil {
|
if schedVar == nil {
|
||||||
return nil, ErrUnreadableG
|
return nil, ErrUnreadableG
|
||||||
}
|
}
|
||||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value) // +rtype uintptr
|
||||||
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value) // +rtype uintptr
|
||||||
var bp, lr int64
|
var bp, lr int64
|
||||||
if bpvar := schedVar.fieldVariable("bp"); bpvar != nil && bpvar.Value != nil {
|
if bpvar := schedVar.fieldVariable("bp"); /* +rtype -opt uintptr */ bpvar != nil && bpvar.Value != nil {
|
||||||
bp, _ = constant.Int64Val(bpvar.Value)
|
bp, _ = constant.Int64Val(bpvar.Value)
|
||||||
}
|
}
|
||||||
if bpvar := schedVar.fieldVariable("lr"); bpvar != nil && bpvar.Value != nil {
|
if bpvar := schedVar.fieldVariable("lr"); /* +rtype -opt uintptr */ bpvar != nil && bpvar.Value != nil {
|
||||||
lr, _ = constant.Int64Val(bpvar.Value)
|
lr, _ = constant.Int64Val(bpvar.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -874,25 +878,25 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
id := loadInt64Maybe("goid")
|
id := loadInt64Maybe("goid") // +rtype int64
|
||||||
gopc := loadInt64Maybe("gopc")
|
gopc := loadInt64Maybe("gopc") // +rtype uintptr
|
||||||
startpc := loadInt64Maybe("startpc")
|
startpc := loadInt64Maybe("startpc") // +rtype uintptr
|
||||||
waitSince := loadInt64Maybe("waitsince")
|
waitSince := loadInt64Maybe("waitsince") // +rtype int64
|
||||||
waitReason := int64(0)
|
waitReason := int64(0)
|
||||||
if producer := v.bi.Producer(); producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) {
|
if producer := v.bi.Producer(); producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) {
|
||||||
waitReason = loadInt64Maybe("waitreason")
|
waitReason = loadInt64Maybe("waitreason") // +rtype -opt waitReason
|
||||||
}
|
}
|
||||||
var stackhi, stacklo uint64
|
var stackhi, stacklo uint64
|
||||||
if stackVar := v.loadFieldNamed("stack"); stackVar != nil {
|
if stackVar := v.loadFieldNamed("stack"); /* +rtype stack */ stackVar != nil {
|
||||||
if stackhiVar := stackVar.fieldVariable("hi"); stackhiVar != nil {
|
if stackhiVar := stackVar.fieldVariable("hi"); /* +rtype uintptr */ stackhiVar != nil {
|
||||||
stackhi, _ = constant.Uint64Val(stackhiVar.Value)
|
stackhi, _ = constant.Uint64Val(stackhiVar.Value)
|
||||||
}
|
}
|
||||||
if stackloVar := stackVar.fieldVariable("lo"); stackloVar != nil {
|
if stackloVar := stackVar.fieldVariable("lo"); /* +rtype uintptr */ stackloVar != nil {
|
||||||
stacklo, _ = constant.Uint64Val(stackloVar.Value)
|
stacklo, _ = constant.Uint64Val(stackloVar.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status := loadInt64Maybe("atomicstatus")
|
status := loadInt64Maybe("atomicstatus") // +rtype uint32
|
||||||
|
|
||||||
if unreadable {
|
if unreadable {
|
||||||
return nil, ErrUnreadableG
|
return nil, ErrUnreadableG
|
||||||
@ -1923,16 +1927,16 @@ func (v *Variable) mapIterator() *mapIterator {
|
|||||||
var err error
|
var err error
|
||||||
field, _ := sv.toField(f)
|
field, _ := sv.toField(f)
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "count":
|
case "count": // +rtype -fieldof hmap int
|
||||||
v.Len, err = field.asInt()
|
v.Len, err = field.asInt()
|
||||||
case "B":
|
case "B": // +rtype -fieldof hmap uint8
|
||||||
var b uint64
|
var b uint64
|
||||||
b, err = field.asUint()
|
b, err = field.asUint()
|
||||||
it.numbuckets = 1 << b
|
it.numbuckets = 1 << b
|
||||||
it.oldmask = (1 << (b - 1)) - 1
|
it.oldmask = (1 << (b - 1)) - 1
|
||||||
case "buckets":
|
case "buckets": // +rtype -fieldof hmap unsafe.Pointer
|
||||||
it.buckets = field.maybeDereference()
|
it.buckets = field.maybeDereference()
|
||||||
case "oldbuckets":
|
case "oldbuckets": // +rtype -fieldof hmap unsafe.Pointer
|
||||||
it.oldbuckets = field.maybeDereference()
|
it.oldbuckets = field.maybeDereference()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2034,7 +2038,7 @@ func (it *mapIterator) nextBucket() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "tophash":
|
case "tophash": // +rtype -fieldof bmap [8]uint8
|
||||||
it.tophashes = field
|
it.tophashes = field
|
||||||
case "keys":
|
case "keys":
|
||||||
it.keys = field
|
it.keys = field
|
||||||
@ -2150,15 +2154,20 @@ func (v *Variable) readInterface() (_type, data *Variable, isnil bool) {
|
|||||||
|
|
||||||
ityp := resolveTypedef(&v.RealType.(*godwarf.InterfaceType).TypedefType).(*godwarf.StructType)
|
ityp := resolveTypedef(&v.RealType.(*godwarf.InterfaceType).TypedefType).(*godwarf.StructType)
|
||||||
|
|
||||||
|
// +rtype -field iface.tab *itab
|
||||||
|
// +rtype -field iface.data unsafe.Pointer
|
||||||
|
// +rtype -field eface._type *_type
|
||||||
|
// +rtype -field eface.data unsafe.Pointer
|
||||||
|
|
||||||
for _, f := range ityp.Field {
|
for _, f := range ityp.Field {
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "tab": // for runtime.iface
|
case "tab": // for runtime.iface
|
||||||
tab, _ := v.toField(f)
|
tab, _ := v.toField(f) // +rtype *itab
|
||||||
tab = tab.maybeDereference()
|
tab = tab.maybeDereference()
|
||||||
isnil = tab.Addr == 0
|
isnil = tab.Addr == 0
|
||||||
if !isnil {
|
if !isnil {
|
||||||
var err error
|
var err error
|
||||||
_type, err = tab.structMember("_type")
|
_type, err = tab.structMember("_type") // +rtype *_type
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user