diff --git a/_scripts/gen-suitablemethods.go b/_scripts/gen-suitablemethods.go new file mode 100644 index 0000000000..48e9bda80f --- /dev/null +++ b/_scripts/gen-suitablemethods.go @@ -0,0 +1,81 @@ +package main + +import ( + "bytes" + "fmt" + "go/format" + "go/token" + "go/types" + "io" + "log" + "os" + "strings" + + "golang.org/x/tools/go/packages" +) + +func must(err error, fmtstr string, args ...interface{}) { + if err != nil { + log.Fatalf(fmtstr, args...) + } +} + +func main() { + buf := bytes.NewBuffer([]byte{}) + fmt.Fprintf(buf, "// DO NOT EDIT: auto-generated using _scripts/gen-suitablemethods.go\n\n") + fmt.Fprintf(buf, "package rpccommon\n\n") + fmt.Fprintf(buf, "import ( \"reflect\"; \"github.com/go-delve/delve/service/rpc2\" )\n") + + dorpc(buf, "rpc2.RPCServer", "rpc2", "2") + dorpc(buf, "RPCServer", "rpccommon", "Common") + + src, err := format.Source(buf.Bytes()) + must(err, "error formatting source: %v", err) + + out := os.Stdout + if os.Args[1] != "-" { + if !strings.HasSuffix(os.Args[1], ".go") { + os.Args[1] += ".go" + } + out, err = os.Create(os.Args[1]) + must(err, "error creating %s: %v", os.Args[1], err) + } + _, err = out.Write(src) + must(err, "error writing output: %v", err) + if out != os.Stdout { + must(out.Close(), "error closing %s: %v", os.Args[1], err) + } +} + +func dorpc(buf io.Writer, typename, pkgname, outname string) { + fmt.Fprintf(buf, "func suitableMethods%s(s *%s, methods map[string]*methodType) {\n", outname, typename) + + fset := &token.FileSet{} + cfg := &packages.Config{ + Mode: packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedName | packages.NeedCompiledGoFiles | packages.NeedTypes, + Fset: fset, + } + pkgs, err := packages.Load(cfg, fmt.Sprintf("github.com/go-delve/delve/service/%s", pkgname)) + must(err, "error loading packages: %v", err) + packages.Visit(pkgs, func(pkg *packages.Package) bool { + if pkg.PkgPath != fmt.Sprintf("github.com/go-delve/delve/service/%s", pkgname) { + return true + } + mset := types.NewMethodSet(types.NewPointer(pkg.Types.Scope().Lookup("RPCServer").Type())) + for i := 0; i < mset.Len(); i++ { + fn := mset.At(i).Obj().(*types.Func) + name := fn.Name() + if name[0] >= 'a' && name[0] <= 'z' { + continue + } + sig := fn.Type().(*types.Signature) + if sig.Params().Len() != 2 { + continue + } + fmt.Fprintf(buf, "methods[\"RPCServer.%s\"] = &methodType{ method: reflect.ValueOf(s.%s) }\n", name, name) + + } + return true + }, nil) + fmt.Fprintf(buf, "}\n\n") +} diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go index 633b31ea80..06e8e0b7af 100644 --- a/cmd/dlv/dlv_test.go +++ b/cmd/dlv/dlv_test.go @@ -374,6 +374,7 @@ 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, "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/faq.md", "'go run _scripts/gen-faq-toc.go Documentation/faq.md Documentation/faq.md'", runScript("_scripts/gen-faq-toc.go", "Documentation/faq.md", "-")) + checkAutogenDoc(t, "service/rpccommon/suitablemethods.go", "'go generate' inside service/rpccommon", runScript("_scripts/gen-suitablemethods.go", "-")) if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) { 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") @@ -1346,7 +1347,7 @@ func TestVersion(t *testing.T) { want1 := []byte("mod\tgithub.com/go-delve/delve") want2 := []byte("dep\tgithub.com/google/go-dap") if !bytes.Contains(got, want1) || !bytes.Contains(got, want2) { - t.Errorf("got %s\nwant %v and %v in the output", got, want1, want2) + t.Errorf("got %s\nwant %s and %s in the output", got, want1, want2) } } @@ -1462,3 +1463,14 @@ func TestUnixDomainSocket(t *testing.T) { client.Detach(true) cmd.Wait() } + +func TestDeadcodeEliminated(t *testing.T) { + dlvbin := protest.GetDlvBinary(t) + buf, err := exec.Command("go", "tool", "nm", dlvbin).CombinedOutput() + if err != nil { + t.Fatalf("error executing go tool nm %s: %v", dlvbin, err) + } + if strings.Contains(string(buf), "MethodByName") { + t.Fatal("output of go tool nm contains MethodByName") + } +} diff --git a/go.mod b/go.mod index 572759119b..05f0bbd3ec 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,8 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.7.0 - github.com/spf13/pflag v1.0.5 + github.com/spf13/cobra v1.9.1 + github.com/spf13/pflag v1.0.6 go.starlark.net v0.0.0-20231101134539-556fd59b42f6 golang.org/x/arch v0.11.0 golang.org/x/sys v0.26.0 @@ -24,7 +24,7 @@ require ( ) require ( - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/rivo/uniseg v0.2.0 // indirect diff --git a/go.sum b/go.sum index 06e5fa1bd3..9f4ed5e79c 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.20 h1:VIPb/a2s17qNeQgDnkfZC35RScx+blkKF8GV68n80J4= github.com/creack/pty v1.1.20/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -45,10 +45,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/pkg/version/buildinfo.go b/pkg/version/buildinfo.go index 6c34a453d7..150bea52bc 100644 --- a/pkg/version/buildinfo.go +++ b/pkg/version/buildinfo.go @@ -2,19 +2,14 @@ package version import ( "bytes" + "fmt" "runtime/debug" - "text/template" ) func init() { buildInfo = moduleBuildInfo } -var buildInfoTmpl = ` mod {{.Main.Path}} {{.Main.Version}} {{.Main.Sum}} -{{range .Deps}} dep {{.Path}} {{.Version}} {{.Sum}}{{if .Replace}} - => {{.Replace.Path}} {{.Replace.Version}} {{.Replace.Sum}}{{end}} -{{end}}` - func moduleBuildInfo() string { info, ok := debug.ReadBuildInfo() if !ok { @@ -22,9 +17,13 @@ func moduleBuildInfo() string { } buf := new(bytes.Buffer) - err := template.Must(template.New("buildinfo").Parse(buildInfoTmpl)).Execute(buf, info) - if err != nil { - panic(err) + fmt.Fprintf(buf, " mod\t%s\t%s\t%s\n", info.Main.Path, info.Main.Version, info.Main.Sum) + for _, dep := range info.Deps { + fmt.Fprintf(buf, " dep\t%s\t%s\t%s", dep.Path, dep.Version, dep.Sum) + if dep.Replace != nil { + fmt.Fprintf(buf, "\t=> %s\t%s\t%s", dep.Replace.Path, dep.Replace.Version, dep.Replace.Sum) + } + fmt.Fprintf(buf, "\n") } return buf.String() } diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go index 3d202374a5..d0b2ad258e 100644 --- a/service/rpccommon/server.go +++ b/service/rpccommon/server.go @@ -14,8 +14,6 @@ import ( "reflect" "runtime" "sync" - "unicode" - "unicode/utf8" "github.com/go-delve/delve/pkg/logflags" "github.com/go-delve/delve/pkg/version" @@ -27,6 +25,8 @@ import ( "github.com/go-delve/delve/service/rpc2" ) +//go:generate go run ../../_scripts/gen-suitablemethods.go suitablemethods + // ServerImpl implements a JSON-RPC server that can switch between two // versions of the API. type ServerImpl struct { @@ -62,8 +62,7 @@ type RPCServer struct { } type methodType struct { - method reflect.Method - Rcvr reflect.Value + method reflect.Value ArgType reflect.Type ReplyType reflect.Type Synchronous bool @@ -128,8 +127,10 @@ func (s *ServerImpl) Run() error { s.methodMaps = make([]map[string]*methodType, 2) s.methodMaps[1] = map[string]*methodType{} - suitableMethods(s.s2, s.methodMaps[1], s.log) - suitableMethods(rpcServer, s.methodMaps[1], s.log) + + suitableMethods2(s.s2, s.methodMaps[1]) + suitableMethodsCommon(rpcServer, s.methodMaps[1]) + finishMethodsMapInit(s.methodMaps[1]) go func() { defer s.listener.Close() @@ -183,92 +184,15 @@ func (s *ServerImpl) serveConnectionDemux(c io.ReadWriteCloser) { } } -// Precompute the reflect type for error. Can't use error directly -// because Typeof takes an empty interface value. This is annoying. -var typeOfError = reflect.TypeOf((*error)(nil)).Elem() - -// Is this an exported - upper case - name? -func isExported(name string) bool { - ch, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(ch) -} - -// Is this type exported or a builtin? -func isExportedOrBuiltinType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - // PkgPath will be non-empty even for an exported type, - // so we need to check the type name as well. - return isExported(t.Name()) || t.PkgPath() == "" -} - -// Fills methods map with the methods of receiver that should be made -// available through the RPC interface. -// These are all the public methods of rcvr that have one of those -// two signatures: -// -// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error -// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback) -func suitableMethods(rcvr interface{}, methods map[string]*methodType, log logflags.Logger) { - typ := reflect.TypeOf(rcvr) - rcvrv := reflect.ValueOf(rcvr) - sname := reflect.Indirect(rcvrv).Type().Name() - if sname == "" { - log.Debugf("rpc.Register: no service name for type %s", typ) - return - } - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - mname := method.Name - mtype := method.Type - // method must be exported - if method.PkgPath != "" { - continue - } - // Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback) - if mtype.NumIn() != 3 { - log.Warn("method", mname, "has wrong number of ins:", mtype.NumIn()) - continue - } - // First arg need not be a pointer. - argType := mtype.In(1) - if !isExportedOrBuiltinType(argType) { - log.Warn(mname, "argument type not exported:", argType) - continue - } - - replyType := mtype.In(2) - synchronous := replyType.String() != "service.RPCCallback" - - if synchronous { - // Second arg must be a pointer. - if replyType.Kind() != reflect.Ptr { - log.Warn("method", mname, "reply type not a pointer:", replyType) - continue - } - // Reply type must be exported. - if !isExportedOrBuiltinType(replyType) { - log.Warn("method", mname, "reply type not exported:", replyType) - continue - } - - // Method needs one out. - if mtype.NumOut() != 1 { - log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut()) - continue - } - // The return type of the method must be error. - if returnType := mtype.Out(0); returnType != typeOfError { - log.Warn("method", mname, "returns", returnType.String(), "not error") - continue - } - } else if mtype.NumOut() != 0 { - // Method needs zero outs. - log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut()) - continue +func finishMethodsMapInit(methods map[string]*methodType) { + for name, method := range methods { + mtype := method.method.Type() + if mtype.NumIn() != 2 { + panic(fmt.Errorf("wrong number of inputs for method %s (%d)", name, mtype.NumIn())) } - methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv} + method.ArgType = mtype.In(0) + method.ReplyType = mtype.In(1) + method.Synchronous = method.ReplyType.String() != "service.RPCCallback" } } @@ -326,7 +250,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { s.log.Debugf("<- %s(%T%s)", req.ServiceMethod, argv.Interface(), argvbytes) } replyv = reflect.New(mtype.ReplyType.Elem()) - function := mtype.method.Func + function := mtype.method var returnValues []reflect.Value var errInter interface{} func() { @@ -335,7 +259,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { errInter = newInternalError(ierr, 2) } }() - returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv}) + returnValues = function.Call([]reflect.Value{argv, replyv}) errInter = returnValues[0].Interface() }() @@ -358,7 +282,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { argvbytes, _ := json.Marshal(argv.Interface()) s.log.Debugf("(async %d) <- %s(%T%s)", req.Seq, req.ServiceMethod, argv.Interface(), argvbytes) } - function := mtype.method.Func + function := mtype.method ctl := &RPCCallback{s, sending, codec, req, make(chan struct{}), clientDisconnectChan} go func() { defer func() { @@ -366,7 +290,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { ctl.Return(nil, newInternalError(ierr, 2)) } }() - function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)}) + function.Call([]reflect.Value{argv, reflect.ValueOf(ctl)}) }() <-ctl.setupDone } diff --git a/service/rpccommon/suitablemethods.go b/service/rpccommon/suitablemethods.go new file mode 100644 index 0000000000..0b4e80538f --- /dev/null +++ b/service/rpccommon/suitablemethods.go @@ -0,0 +1,68 @@ +// DO NOT EDIT: auto-generated using _scripts/gen-suitablemethods.go + +package rpccommon + +import ( + "github.com/go-delve/delve/service/rpc2" + "reflect" +) + +func suitableMethods2(s *rpc2.RPCServer, methods map[string]*methodType) { + methods["RPCServer.AmendBreakpoint"] = &methodType{method: reflect.ValueOf(s.AmendBreakpoint)} + methods["RPCServer.Ancestors"] = &methodType{method: reflect.ValueOf(s.Ancestors)} + methods["RPCServer.AttachedToExistingProcess"] = &methodType{method: reflect.ValueOf(s.AttachedToExistingProcess)} + methods["RPCServer.BuildID"] = &methodType{method: reflect.ValueOf(s.BuildID)} + methods["RPCServer.CancelNext"] = &methodType{method: reflect.ValueOf(s.CancelNext)} + methods["RPCServer.Checkpoint"] = &methodType{method: reflect.ValueOf(s.Checkpoint)} + methods["RPCServer.ClearBreakpoint"] = &methodType{method: reflect.ValueOf(s.ClearBreakpoint)} + methods["RPCServer.ClearCheckpoint"] = &methodType{method: reflect.ValueOf(s.ClearCheckpoint)} + methods["RPCServer.Command"] = &methodType{method: reflect.ValueOf(s.Command)} + methods["RPCServer.CreateBreakpoint"] = &methodType{method: reflect.ValueOf(s.CreateBreakpoint)} + methods["RPCServer.CreateEBPFTracepoint"] = &methodType{method: reflect.ValueOf(s.CreateEBPFTracepoint)} + methods["RPCServer.CreateWatchpoint"] = &methodType{method: reflect.ValueOf(s.CreateWatchpoint)} + methods["RPCServer.DebugInfoDirectories"] = &methodType{method: reflect.ValueOf(s.DebugInfoDirectories)} + methods["RPCServer.Detach"] = &methodType{method: reflect.ValueOf(s.Detach)} + methods["RPCServer.Disassemble"] = &methodType{method: reflect.ValueOf(s.Disassemble)} + methods["RPCServer.DumpCancel"] = &methodType{method: reflect.ValueOf(s.DumpCancel)} + methods["RPCServer.DumpStart"] = &methodType{method: reflect.ValueOf(s.DumpStart)} + methods["RPCServer.DumpWait"] = &methodType{method: reflect.ValueOf(s.DumpWait)} + methods["RPCServer.Eval"] = &methodType{method: reflect.ValueOf(s.Eval)} + methods["RPCServer.ExamineMemory"] = &methodType{method: reflect.ValueOf(s.ExamineMemory)} + methods["RPCServer.FindLocation"] = &methodType{method: reflect.ValueOf(s.FindLocation)} + methods["RPCServer.FollowExec"] = &methodType{method: reflect.ValueOf(s.FollowExec)} + methods["RPCServer.FollowExecEnabled"] = &methodType{method: reflect.ValueOf(s.FollowExecEnabled)} + methods["RPCServer.FunctionReturnLocations"] = &methodType{method: reflect.ValueOf(s.FunctionReturnLocations)} + methods["RPCServer.GetBreakpoint"] = &methodType{method: reflect.ValueOf(s.GetBreakpoint)} + methods["RPCServer.GetBufferedTracepoints"] = &methodType{method: reflect.ValueOf(s.GetBufferedTracepoints)} + methods["RPCServer.GetThread"] = &methodType{method: reflect.ValueOf(s.GetThread)} + methods["RPCServer.GuessSubstitutePath"] = &methodType{method: reflect.ValueOf(s.GuessSubstitutePath)} + methods["RPCServer.IsMulticlient"] = &methodType{method: reflect.ValueOf(s.IsMulticlient)} + methods["RPCServer.LastModified"] = &methodType{method: reflect.ValueOf(s.LastModified)} + methods["RPCServer.ListBreakpoints"] = &methodType{method: reflect.ValueOf(s.ListBreakpoints)} + methods["RPCServer.ListCheckpoints"] = &methodType{method: reflect.ValueOf(s.ListCheckpoints)} + methods["RPCServer.ListDynamicLibraries"] = &methodType{method: reflect.ValueOf(s.ListDynamicLibraries)} + methods["RPCServer.ListFunctionArgs"] = &methodType{method: reflect.ValueOf(s.ListFunctionArgs)} + methods["RPCServer.ListFunctions"] = &methodType{method: reflect.ValueOf(s.ListFunctions)} + methods["RPCServer.ListGoroutines"] = &methodType{method: reflect.ValueOf(s.ListGoroutines)} + methods["RPCServer.ListLocalVars"] = &methodType{method: reflect.ValueOf(s.ListLocalVars)} + methods["RPCServer.ListPackageVars"] = &methodType{method: reflect.ValueOf(s.ListPackageVars)} + methods["RPCServer.ListPackagesBuildInfo"] = &methodType{method: reflect.ValueOf(s.ListPackagesBuildInfo)} + methods["RPCServer.ListRegisters"] = &methodType{method: reflect.ValueOf(s.ListRegisters)} + methods["RPCServer.ListSources"] = &methodType{method: reflect.ValueOf(s.ListSources)} + methods["RPCServer.ListTargets"] = &methodType{method: reflect.ValueOf(s.ListTargets)} + methods["RPCServer.ListThreads"] = &methodType{method: reflect.ValueOf(s.ListThreads)} + methods["RPCServer.ListTypes"] = &methodType{method: reflect.ValueOf(s.ListTypes)} + methods["RPCServer.ProcessPid"] = &methodType{method: reflect.ValueOf(s.ProcessPid)} + methods["RPCServer.Recorded"] = &methodType{method: reflect.ValueOf(s.Recorded)} + methods["RPCServer.Restart"] = &methodType{method: reflect.ValueOf(s.Restart)} + methods["RPCServer.Set"] = &methodType{method: reflect.ValueOf(s.Set)} + methods["RPCServer.Stacktrace"] = &methodType{method: reflect.ValueOf(s.Stacktrace)} + methods["RPCServer.State"] = &methodType{method: reflect.ValueOf(s.State)} + methods["RPCServer.StopRecording"] = &methodType{method: reflect.ValueOf(s.StopRecording)} + methods["RPCServer.ToggleBreakpoint"] = &methodType{method: reflect.ValueOf(s.ToggleBreakpoint)} +} + +func suitableMethodsCommon(s *RPCServer, methods map[string]*methodType) { + methods["RPCServer.GetVersion"] = &methodType{method: reflect.ValueOf(s.GetVersion)} + methods["RPCServer.SetApiVersion"] = &methodType{method: reflect.ValueOf(s.SetApiVersion)} +} diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go new file mode 100644 index 0000000000..0ec4b12c75 --- /dev/null +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go @@ -0,0 +1,62 @@ +package md2man + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/russross/blackfriday/v2" +) + +func fmtListFlags(flags blackfriday.ListType) string { + knownFlags := []struct { + name string + flag blackfriday.ListType + }{ + {"ListTypeOrdered", blackfriday.ListTypeOrdered}, + {"ListTypeDefinition", blackfriday.ListTypeDefinition}, + {"ListTypeTerm", blackfriday.ListTypeTerm}, + {"ListItemContainsBlock", blackfriday.ListItemContainsBlock}, + {"ListItemBeginningOfList", blackfriday.ListItemBeginningOfList}, + {"ListItemEndOfList", blackfriday.ListItemEndOfList}, + } + + var f []string + for _, kf := range knownFlags { + if flags&kf.flag != 0 { + f = append(f, kf.name) + flags &^= kf.flag + } + } + if flags != 0 { + f = append(f, fmt.Sprintf("Unknown(%#x)", flags)) + } + return strings.Join(f, "|") +} + +type debugDecorator struct { + blackfriday.Renderer +} + +func depth(node *blackfriday.Node) int { + d := 0 + for n := node.Parent; n != nil; n = n.Parent { + d++ + } + return d +} + +func (d *debugDecorator) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + fmt.Fprintf(os.Stderr, "%s%s %v %v\n", + strings.Repeat(" ", depth(node)), + map[bool]string{true: "+", false: "-"}[entering], + node, + fmtListFlags(node.ListFlags)) + var b strings.Builder + status := d.Renderer.RenderNode(io.MultiWriter(&b, w), node, entering) + if b.Len() > 0 { + fmt.Fprintf(os.Stderr, ">> %q\n", b.String()) + } + return status +} diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go index b480056734..62d91b77d5 100644 --- a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go @@ -1,14 +1,23 @@ package md2man import ( + "os" + "strconv" + "github.com/russross/blackfriday/v2" ) // Render converts a markdown document into a roff formatted document. func Render(doc []byte) []byte { renderer := NewRoffRenderer() + var r blackfriday.Renderer = renderer + if v, _ := strconv.ParseBool(os.Getenv("MD2MAN_DEBUG")); v { + r = &debugDecorator{Renderer: r} + } return blackfriday.Run(doc, - []blackfriday.Option{blackfriday.WithRenderer(renderer), - blackfriday.WithExtensions(renderer.GetExtensions())}...) + []blackfriday.Option{ + blackfriday.WithRenderer(r), + blackfriday.WithExtensions(renderer.GetExtensions()), + }...) } diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go index be2b343606..96a80c99b8 100644 --- a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go @@ -1,6 +1,8 @@ package md2man import ( + "bufio" + "bytes" "fmt" "io" "os" @@ -12,68 +14,72 @@ import ( // roffRenderer implements the blackfriday.Renderer interface for creating // roff format (manpages) from markdown text type roffRenderer struct { - extensions blackfriday.Extensions listCounters []int firstHeader bool - firstDD bool listDepth int } const ( - titleHeader = ".TH " - topLevelHeader = "\n\n.SH " - secondLevelHdr = "\n.SH " - otherHeader = "\n.SS " - crTag = "\n" - emphTag = "\\fI" - emphCloseTag = "\\fP" - strongTag = "\\fB" - strongCloseTag = "\\fP" - breakTag = "\n.br\n" - paraTag = "\n.PP\n" - hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" - linkTag = "\n\\[la]" - linkCloseTag = "\\[ra]" - codespanTag = "\\fB\\fC" - codespanCloseTag = "\\fR" - codeTag = "\n.PP\n.RS\n\n.nf\n" - codeCloseTag = "\n.fi\n.RE\n" - quoteTag = "\n.PP\n.RS\n" - quoteCloseTag = "\n.RE\n" - listTag = "\n.RS\n" - listCloseTag = "\n.RE\n" - dtTag = "\n.TP\n" - dd2Tag = "\n" - tableStart = "\n.TS\nallbox;\n" - tableEnd = ".TE\n" - tableCellStart = "T{\n" - tableCellEnd = "\nT}\n" + titleHeader = ".TH " + topLevelHeader = "\n\n.SH " + secondLevelHdr = "\n.SH " + otherHeader = "\n.SS " + crTag = "\n" + emphTag = "\\fI" + emphCloseTag = "\\fP" + strongTag = "\\fB" + strongCloseTag = "\\fP" + breakTag = "\n.br\n" + paraTag = "\n.PP\n" + hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" + linkTag = "\n\\[la]" + linkCloseTag = "\\[ra]" + codespanTag = "\\fB" + codespanCloseTag = "\\fR" + codeTag = "\n.EX\n" + codeCloseTag = ".EE\n" // Do not prepend a newline character since code blocks, by definition, include a newline already (or at least as how blackfriday gives us on). + quoteTag = "\n.PP\n.RS\n" + quoteCloseTag = "\n.RE\n" + listTag = "\n.RS\n" + listCloseTag = ".RE\n" + dtTag = "\n.TP\n" + dd2Tag = "\n" + tableStart = "\n.TS\nallbox;\n" + tableEnd = ".TE\n" + tableCellStart = "T{\n" + tableCellEnd = "\nT}\n" + tablePreprocessor = `'\" t` ) // NewRoffRenderer creates a new blackfriday Renderer for generating roff documents // from markdown func NewRoffRenderer() *roffRenderer { // nolint: golint - var extensions blackfriday.Extensions - - extensions |= blackfriday.NoIntraEmphasis - extensions |= blackfriday.Tables - extensions |= blackfriday.FencedCode - extensions |= blackfriday.SpaceHeadings - extensions |= blackfriday.Footnotes - extensions |= blackfriday.Titleblock - extensions |= blackfriday.DefinitionLists - return &roffRenderer{ - extensions: extensions, - } + return &roffRenderer{} } // GetExtensions returns the list of extensions used by this renderer implementation -func (r *roffRenderer) GetExtensions() blackfriday.Extensions { - return r.extensions +func (*roffRenderer) GetExtensions() blackfriday.Extensions { + return blackfriday.NoIntraEmphasis | + blackfriday.Tables | + blackfriday.FencedCode | + blackfriday.SpaceHeadings | + blackfriday.Footnotes | + blackfriday.Titleblock | + blackfriday.DefinitionLists } // RenderHeader handles outputting the header at document start func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) { + // We need to walk the tree to check if there are any tables. + // If there are, we need to enable the roff table preprocessor. + ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + if node.Type == blackfriday.Table { + out(w, tablePreprocessor+"\n") + return blackfriday.Terminate + } + return blackfriday.GoToNext + }) + // disable hyphenation out(w, ".nh\n") } @@ -86,12 +92,27 @@ func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) { // RenderNode is called for each node in a markdown document; based on the node // type the equivalent roff output is sent to the writer func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { - - var walkAction = blackfriday.GoToNext + walkAction := blackfriday.GoToNext switch node.Type { case blackfriday.Text: - escapeSpecialChars(w, node.Literal) + // Special case: format the NAME section as required for proper whatis parsing. + // Refer to the lexgrog(1) and groff_man(7) manual pages for details. + if node.Parent != nil && + node.Parent.Type == blackfriday.Paragraph && + node.Parent.Prev != nil && + node.Parent.Prev.Type == blackfriday.Heading && + node.Parent.Prev.FirstChild != nil && + bytes.EqualFold(node.Parent.Prev.FirstChild.Literal, []byte("NAME")) { + before, after, found := bytesCut(node.Literal, []byte(" - ")) + escapeSpecialChars(w, before) + if found { + out(w, ` \- `) + escapeSpecialChars(w, after) + } + } else { + escapeSpecialChars(w, node.Literal) + } case blackfriday.Softbreak: out(w, crTag) case blackfriday.Hardbreak: @@ -109,9 +130,16 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering out(w, strongCloseTag) } case blackfriday.Link: - if !entering { - out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag) + // Don't render the link text for automatic links, because this + // will only duplicate the URL in the roff output. + // See https://daringfireball.net/projects/markdown/syntax#autolink + if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) { + out(w, string(node.FirstChild.Literal)) } + // Hyphens in a link must be escaped to avoid word-wrap in the rendered man page. + escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-") + out(w, linkTag+escapedLink+linkCloseTag) + walkAction = blackfriday.SkipChildren case blackfriday.Image: // ignore images walkAction = blackfriday.SkipChildren @@ -122,14 +150,25 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering case blackfriday.Document: break case blackfriday.Paragraph: - // roff .PP markers break lists - if r.listDepth > 0 { - return blackfriday.GoToNext - } if entering { - out(w, paraTag) + if r.listDepth > 0 { + // roff .PP markers break lists + if node.Prev != nil { // continued paragraph + if node.Prev.Type == blackfriday.List && node.Prev.ListFlags&blackfriday.ListTypeDefinition == 0 { + out(w, ".IP\n") + } else { + out(w, crTag) + } + } + } else if node.Prev != nil && node.Prev.Type == blackfriday.Heading { + out(w, crTag) + } else { + out(w, paraTag) + } } else { - out(w, crTag) + if node.Next == nil || node.Next.Type != blackfriday.List { + out(w, crTag) + } } case blackfriday.BlockQuote: if entering { @@ -160,6 +199,11 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering r.handleTableCell(w, node, entering) case blackfriday.HTMLSpan: // ignore other HTML tags + case blackfriday.HTMLBlock: + if bytes.HasPrefix(node.Literal, []byte("