// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ir import ( "bytes" "fmt" "go/constant" "io" "math" "os" "path/filepath" "reflect" "strings" "unicode/utf8" "cmd/compile/internal/base" "cmd/compile/internal/types" "cmd/internal/src" ) // Op var OpNames = []string{ OADDR: "&", OADD: "+", OADDSTR: "+", OALIGNOF: "unsafe.Alignof", OANDAND: "&&", OANDNOT: "&^", OAND: "&", OAPPEND: "append", OAS: "=", OAS2: "=", OBREAK: "break", OCALL: "function call", // not actual syntax OCAP: "cap", OCASE: "case", OCLOSE: "close", OCOMPLEX: "complex", OBITNOT: "^", OCONTINUE: "continue", OCOPY: "copy", ODELETE: "delete", ODEFER: "defer", ODIV: "/", OEQ: "==", OFALL: "fallthrough", OFOR: "for", OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892 OGE: ">=", OGOTO: "goto", OGT: ">", OIF: "if", OIMAG: "imag", OINLMARK: "inlmark", ODEREF: "*", OLEN: "len", OLE: "<=", OLSH: "<<", OLT: "<", OMAKE: "make", ONEG: "-", OMOD: "%", OMUL: "*", ONEW: "new", ONE: "!=", ONOT: "!", OOFFSETOF: "unsafe.Offsetof", OOROR: "||", OOR: "|", OPANIC: "panic", OPLUS: "+", OPRINTN: "println", OPRINT: "print", ORANGE: "range", OREAL: "real", ORECV: "<-", ORECOVER: "recover", ORETURN: "return", ORSH: ">>", OSELECT: "select", OSEND: "<-", OSIZEOF: "unsafe.Sizeof", OSUB: "-", OSWITCH: "switch", OUNSAFEADD: "unsafe.Add", OUNSAFESLICE: "unsafe.Slice", OXOR: "^", } // GoString returns the Go syntax for the Op, or else its name. func (o Op) GoString() string { if int(o) < len(OpNames) && OpNames[o] != "" { return OpNames[o] } return o.String() } // Format implements formatting for an Op. // The valid formats are: // // %v Go syntax ("+", "<-", "print") // %+v Debug syntax ("ADD", "RECV", "PRINT") // func (o Op) Format(s fmt.State, verb rune) { switch verb { default: fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o)) case 'v': if s.Flag('+') { // %+v is OMUL instead of "*" io.WriteString(s, o.String()) return } io.WriteString(s, o.GoString()) } } // Node // FmtNode implements formatting for a Node n. // Every Node implementation must define a Format method that calls FmtNode. // The valid formats are: // // %v Go syntax // %L Go syntax followed by " (type T)" if type is known. // %+v Debug syntax, as in Dump. // func fmtNode(n Node, s fmt.State, verb rune) { // %+v prints Dump. // Otherwise we print Go syntax. if s.Flag('+') && verb == 'v' { dumpNode(s, n, 1) return } if verb != 'v' && verb != 'S' && verb != 'L' { fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n) return } if n == nil { fmt.Fprint(s, "") return } t := n.Type() if verb == 'L' && t != nil { if t.Kind() == types.TNIL { fmt.Fprint(s, "nil") } else if n.Op() == ONAME && n.Name().AutoTemp() { fmt.Fprintf(s, "%v value", t) } else { fmt.Fprintf(s, "%v (type %v)", n, t) } return } // TODO inlining produces expressions with ninits. we can't print these yet. if OpPrec[n.Op()] < 0 { stmtFmt(n, s) return } exprFmt(n, s, 0) } var OpPrec = []int{ OALIGNOF: 8, OAPPEND: 8, OBYTES2STR: 8, OARRAYLIT: 8, OSLICELIT: 8, ORUNES2STR: 8, OCALLFUNC: 8, OCALLINTER: 8, OCALLMETH: 8, OCALL: 8, OCAP: 8, OCLOSE: 8, OCOMPLIT: 8, OCONVIFACE: 8, OCONVIDATA: 8, OCONVNOP: 8, OCONV: 8, OCOPY: 8, ODELETE: 8, OGETG: 8, OLEN: 8, OLITERAL: 8, OMAKESLICE: 8, OMAKESLICECOPY: 8, OMAKE: 8, OMAPLIT: 8, ONAME: 8, ONEW: 8, ONIL: 8, ONONAME: 8, OOFFSETOF: 8, OPACK: 8, OPANIC: 8, OPAREN: 8, OPRINTN: 8, OPRINT: 8, ORUNESTR: 8, OSIZEOF: 8, OSLICE2ARRPTR: 8, OSTR2BYTES: 8, OSTR2RUNES: 8, OSTRUCTLIT: 8, OTARRAY: 8, OTSLICE: 8, OTCHAN: 8, OTFUNC: 8, OTINTER: 8, OTMAP: 8, OTSTRUCT: 8, OTYPE: 8, OUNSAFEADD: 8, OUNSAFESLICE: 8, OINDEXMAP: 8, OINDEX: 8, OSLICE: 8, OSLICESTR: 8, OSLICEARR: 8, OSLICE3: 8, OSLICE3ARR: 8, OSLICEHEADER: 8, ODOTINTER: 8, ODOTMETH: 8, ODOTPTR: 8, ODOTTYPE2: 8, ODOTTYPE: 8, ODOT: 8, OXDOT: 8, OMETHVALUE: 8, OMETHEXPR: 8, OPLUS: 7, ONOT: 7, OBITNOT: 7, ONEG: 7, OADDR: 7, ODEREF: 7, ORECV: 7, OMUL: 6, ODIV: 6, OMOD: 6, OLSH: 6, ORSH: 6, OAND: 6, OANDNOT: 6, OADD: 5, OSUB: 5, OOR: 5, OXOR: 5, OEQ: 4, OLT: 4, OLE: 4, OGE: 4, OGT: 4, ONE: 4, OSEND: 3, OANDAND: 2, OOROR: 1, // Statements handled by stmtfmt OAS: -1, OAS2: -1, OAS2DOTTYPE: -1, OAS2FUNC: -1, OAS2MAPR: -1, OAS2RECV: -1, OASOP: -1, OBLOCK: -1, OBREAK: -1, OCASE: -1, OCONTINUE: -1, ODCL: -1, ODEFER: -1, OFALL: -1, OFOR: -1, OFORUNTIL: -1, OGOTO: -1, OIF: -1, OLABEL: -1, OGO: -1, ORANGE: -1, ORETURN: -1, OSELECT: -1, OSWITCH: -1, OEND: 0, } // StmtWithInit reports whether op is a statement with an explicit init list. func StmtWithInit(op Op) bool { switch op { case OIF, OFOR, OFORUNTIL, OSWITCH: return true } return false } func stmtFmt(n Node, s fmt.State) { // NOTE(rsc): This code used to support the text-based // which was more aggressive about printing full Go syntax // (for example, an actual loop instead of "for loop"). // The code is preserved for now in case we want to expand // any of those shortenings later. Or maybe we will delete // the code. But for now, keep it. const exportFormat = false // some statements allow for an init, but at most one, // but we may have an arbitrary number added, eg by typecheck // and inlining. If it doesn't fit the syntax, emit an enclosing // block starting with the init statements. // if we can just say "for" n->ninit; ... then do so simpleinit := len(n.Init()) == 1 && len(n.Init()[0].Init()) == 0 && StmtWithInit(n.Op()) // otherwise, print the inits as separate statements complexinit := len(n.Init()) != 0 && !simpleinit && exportFormat // but if it was for if/for/switch, put in an extra surrounding block to limit the scope extrablock := complexinit && StmtWithInit(n.Op()) if extrablock { fmt.Fprint(s, "{") } if complexinit { fmt.Fprintf(s, " %v; ", n.Init()) } switch n.Op() { case ODCL: n := n.(*Decl) fmt.Fprintf(s, "var %v %v", n.X.Sym(), n.X.Type()) // Don't export "v = " initializing statements, hope they're always // preceded by the DCL which will be re-parsed and typechecked to reproduce // the "v = " again. case OAS: n := n.(*AssignStmt) if n.Def && !complexinit { fmt.Fprintf(s, "%v := %v", n.X, n.Y) } else { fmt.Fprintf(s, "%v = %v", n.X, n.Y) } case OASOP: n := n.(*AssignOpStmt) if n.IncDec { if n.AsOp == OADD { fmt.Fprintf(s, "%v++", n.X) } else { fmt.Fprintf(s, "%v--", n.X) } break } fmt.Fprintf(s, "%v %v= %v", n.X, n.AsOp, n.Y) case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: n := n.(*AssignListStmt) if n.Def && !complexinit { fmt.Fprintf(s, "%.v := %.v", n.Lhs, n.Rhs) } else { fmt.Fprintf(s, "%.v = %.v", n.Lhs, n.Rhs) } case OBLOCK: n := n.(*BlockStmt) if len(n.List) != 0 { fmt.Fprintf(s, "%v", n.List) } case ORETURN: n := n.(*ReturnStmt) fmt.Fprintf(s, "return %.v", n.Results) case OTAILCALL: n := n.(*TailCallStmt) fmt.Fprintf(s, "tailcall %v", n.Call) case OINLMARK: n := n.(*InlineMarkStmt) fmt.Fprintf(s, "inlmark %d", n.Index) case OGO: n := n.(*GoDeferStmt) fmt.Fprintf(s, "go %v", n.Call) case ODEFER: n := n.(*GoDeferStmt) fmt.Fprintf(s, "defer %v", n.Call) case OIF: n := n.(*IfStmt) if simpleinit { fmt.Fprintf(s, "if %v; %v { %v }", n.Init()[0], n.Cond, n.Body) } else { fmt.Fprintf(s, "if %v { %v }", n.Cond, n.Body) } if len(n.Else) != 0 { fmt.Fprintf(s, " else { %v }", n.Else) } case OFOR, OFORUNTIL: n := n.(*ForStmt) opname := "for" if n.Op() == OFORUNTIL { opname = "foruntil" } if !exportFormat { // TODO maybe only if FmtShort, same below fmt.Fprintf(s, "%s loop", opname) break } fmt.Fprint(s, opname) if simpleinit { fmt.Fprintf(s, " %v;", n.Init()[0]) } else if n.Post != nil { fmt.Fprint(s, " ;") } if n.Cond != nil { fmt.Fprintf(s, " %v", n.Cond) } if n.Post != nil { fmt.Fprintf(s, "; %v", n.Post) } else if simpleinit { fmt.Fprint(s, ";") } if n.Op() == OFORUNTIL && len(n.Late) != 0 { fmt.Fprintf(s, "; %v", n.Late) } fmt.Fprintf(s, " { %v }", n.Body) case ORANGE: n := n.(*RangeStmt) if !exportFormat { fmt.Fprint(s, "for loop") break } fmt.Fprint(s, "for") if n.Key != nil { fmt.Fprintf(s, " %v", n.Key) if n.Value != nil { fmt.Fprintf(s, ", %v", n.Value) } fmt.Fprint(s, " =") } fmt.Fprintf(s, " range %v { %v }", n.X, n.Body) case OSELECT: n := n.(*SelectStmt) if !exportFormat { fmt.Fprintf(s, "%v statement", n.Op()) break } fmt.Fprintf(s, "select { %v }", n.Cases) case OSWITCH: n := n.(*SwitchStmt) if !exportFormat { fmt.Fprintf(s, "%v statement", n.Op()) break } fmt.Fprintf(s, "switch") if simpleinit { fmt.Fprintf(s, " %v;", n.Init()[0]) } if n.Tag != nil { fmt.Fprintf(s, " %v ", n.Tag) } fmt.Fprintf(s, " { %v }", n.Cases) case OCASE: n := n.(*CaseClause) if len(n.List) != 0 { fmt.Fprintf(s, "case %.v", n.List) } else { fmt.Fprint(s, "default") } fmt.Fprintf(s, ": %v", n.Body) case OBREAK, OCONTINUE, OGOTO, OFALL: n := n.(*BranchStmt) if n.Label != nil { fmt.Fprintf(s, "%v %v", n.Op(), n.Label) } else { fmt.Fprintf(s, "%v", n.Op()) } case OLABEL: n := n.(*LabelStmt) fmt.Fprintf(s, "%v: ", n.Label) } if extrablock { fmt.Fprint(s, "}") } } func exprFmt(n Node, s fmt.State, prec int) { // NOTE(rsc): This code used to support the text-based // which was more aggressive about printing full Go syntax // (for example, an actual loop instead of "for loop"). // The code is preserved for now in case we want to expand // any of those shortenings later. Or maybe we will delete // the code. But for now, keep it. const exportFormat = false for { if n == nil { fmt.Fprint(s, "") return } // We always want the original, if any. if o := Orig(n); o != n { n = o continue } // Skip implicit operations introduced during typechecking. switch nn := n; nn.Op() { case OADDR: nn := nn.(*AddrExpr) if nn.Implicit() { n = nn.X continue } case ODEREF: nn := nn.(*StarExpr) if nn.Implicit() { n = nn.X continue } case OCONV, OCONVNOP, OCONVIFACE, OCONVIDATA: nn := nn.(*ConvExpr) if nn.Implicit() { n = nn.X continue } } break } nprec := OpPrec[n.Op()] if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() { nprec = OpPrec[ODEREF] } if prec > nprec { fmt.Fprintf(s, "(%v)", n) return } if n, ok := n.(*RawOrigExpr); ok { fmt.Fprint(s, n.Raw) return } switch n.Op() { case OPAREN: n := n.(*ParenExpr) fmt.Fprintf(s, "(%v)", n.X) case ONIL: fmt.Fprint(s, "nil") case OLITERAL: // this is a bit of a mess if !exportFormat && n.Sym() != nil { fmt.Fprint(s, n.Sym()) return } needUnparen := false if n.Type() != nil && !n.Type().IsUntyped() { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if n.Type().IsPtr() || (n.Type().IsChan() && n.Type().ChanDir() == types.Crecv) { fmt.Fprintf(s, "(%v)(", n.Type()) } else { fmt.Fprintf(s, "%v(", n.Type()) } needUnparen = true } if n.Type() == types.UntypedRune { switch x, ok := constant.Uint64Val(n.Val()); { case !ok: fallthrough default: fmt.Fprintf(s, "('\\x00' + %v)", n.Val()) case x < utf8.RuneSelf: fmt.Fprintf(s, "%q", x) case x < 1<<16: fmt.Fprintf(s, "'\\u%04x'", x) case x <= utf8.MaxRune: fmt.Fprintf(s, "'\\U%08x'", x) } } else { fmt.Fprint(s, types.FmtConst(n.Val(), s.Flag('#'))) } if needUnparen { fmt.Fprintf(s, ")") } case ODCLFUNC: n := n.(*Func) if sym := n.Sym(); sym != nil { fmt.Fprint(s, sym) return } fmt.Fprintf(s, "") case ONAME: n := n.(*Name) // Special case: name used as local variable in export. // _ becomes ~b%d internally; print as _ for export if !exportFormat && n.Sym() != nil && n.Sym().Name[0] == '~' && n.Sym().Name[1] == 'b' { fmt.Fprint(s, "_") return } fallthrough case OPACK, ONONAME: fmt.Fprint(s, n.Sym()) case OLINKSYMOFFSET: n := n.(*LinksymOffsetExpr) fmt.Fprintf(s, "(%v)(%s@%d)", n.Type(), n.Linksym.Name, n.Offset_) case OTYPE: if n.Type() == nil && n.Sym() != nil { fmt.Fprint(s, n.Sym()) return } fmt.Fprintf(s, "%v", n.Type()) case OTSLICE: n := n.(*SliceType) if n.DDD { fmt.Fprintf(s, "...%v", n.Elem) } else { fmt.Fprintf(s, "[]%v", n.Elem) // happens before typecheck } case OTARRAY: n := n.(*ArrayType) if n.Len == nil { fmt.Fprintf(s, "[...]%v", n.Elem) } else { fmt.Fprintf(s, "[%v]%v", n.Len, n.Elem) } case OTMAP: n := n.(*MapType) fmt.Fprintf(s, "map[%v]%v", n.Key, n.Elem) case OTCHAN: n := n.(*ChanType) switch n.Dir { case types.Crecv: fmt.Fprintf(s, "<-chan %v", n.Elem) case types.Csend: fmt.Fprintf(s, "chan<- %v", n.Elem) default: if n.Elem != nil && n.Elem.Op() == OTCHAN && n.Elem.(*ChanType).Dir == types.Crecv { fmt.Fprintf(s, "chan (%v)", n.Elem) } else { fmt.Fprintf(s, "chan %v", n.Elem) } } case OTSTRUCT: fmt.Fprint(s, "") case OTINTER: fmt.Fprint(s, "") case OTFUNC: fmt.Fprint(s, "") case OCLOSURE: n := n.(*ClosureExpr) if !exportFormat { fmt.Fprint(s, "func literal") return } fmt.Fprintf(s, "%v { %v }", n.Type(), n.Func.Body) case OCOMPLIT: n := n.(*CompLitExpr) if !exportFormat { if n.Implicit() { fmt.Fprintf(s, "... argument") return } if typ := n.Type(); typ != nil { fmt.Fprintf(s, "%v{%s}", typ, ellipsisIf(len(n.List) != 0)) return } if n.Ntype != nil { fmt.Fprintf(s, "%v{%s}", n.Ntype, ellipsisIf(len(n.List) != 0)) return } fmt.Fprint(s, "composite literal") return } fmt.Fprintf(s, "(%v{ %.v })", n.Ntype, n.List) case OPTRLIT: n := n.(*AddrExpr) fmt.Fprintf(s, "&%v", n.X) case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT: n := n.(*CompLitExpr) if !exportFormat { fmt.Fprintf(s, "%v{%s}", n.Type(), ellipsisIf(len(n.List) != 0)) return } fmt.Fprintf(s, "(%v{ %.v })", n.Type(), n.List) case OKEY: n := n.(*KeyExpr) if n.Key != nil && n.Value != nil { fmt.Fprintf(s, "%v:%v", n.Key, n.Value) return } if n.Key == nil && n.Value != nil { fmt.Fprintf(s, ":%v", n.Value) return } if n.Key != nil && n.Value == nil { fmt.Fprintf(s, "%v:", n.Key) return } fmt.Fprint(s, ":") case OSTRUCTKEY: n := n.(*StructKeyExpr) fmt.Fprintf(s, "%v:%v", n.Field, n.Value) case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OMETHVALUE, OMETHEXPR: n := n.(*SelectorExpr) exprFmt(n.X, s, nprec) if n.Sel == nil { fmt.Fprint(s, ".") return } fmt.Fprintf(s, ".%s", n.Sel.Name) case ODOTTYPE, ODOTTYPE2: n := n.(*TypeAssertExpr) exprFmt(n.X, s, nprec) if n.Ntype != nil { fmt.Fprintf(s, ".(%v)", n.Ntype) return } fmt.Fprintf(s, ".(%v)", n.Type()) case OINDEX, OINDEXMAP: n := n.(*IndexExpr) exprFmt(n.X, s, nprec) fmt.Fprintf(s, "[%v]", n.Index) case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: n := n.(*SliceExpr) exprFmt(n.X, s, nprec) fmt.Fprint(s, "[") if n.Low != nil { fmt.Fprint(s, n.Low) } fmt.Fprint(s, ":") if n.High != nil { fmt.Fprint(s, n.High) } if n.Op().IsSlice3() { fmt.Fprint(s, ":") if n.Max != nil { fmt.Fprint(s, n.Max) } } fmt.Fprint(s, "]") case OSLICEHEADER: n := n.(*SliceHeaderExpr) fmt.Fprintf(s, "sliceheader{%v,%v,%v}", n.Ptr, n.Len, n.Cap) case OCOMPLEX, OCOPY, OUNSAFEADD, OUNSAFESLICE: n := n.(*BinaryExpr) fmt.Fprintf(s, "%v(%v, %v)", n.Op(), n.X, n.Y) case OCONV, OCONVIFACE, OCONVIDATA, OCONVNOP, OBYTES2STR, ORUNES2STR, OSTR2BYTES, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR: n := n.(*ConvExpr) if n.Type() == nil || n.Type().Sym() == nil { fmt.Fprintf(s, "(%v)", n.Type()) } else { fmt.Fprintf(s, "%v", n.Type()) } fmt.Fprintf(s, "(%v)", n.X) case OREAL, OIMAG, OCAP, OCLOSE, OLEN, ONEW, OPANIC, OALIGNOF, OOFFSETOF, OSIZEOF: n := n.(*UnaryExpr) fmt.Fprintf(s, "%v(%v)", n.Op(), n.X) case OAPPEND, ODELETE, OMAKE, ORECOVER, OPRINT, OPRINTN: n := n.(*CallExpr) if n.IsDDD { fmt.Fprintf(s, "%v(%.v...)", n.Op(), n.Args) return } fmt.Fprintf(s, "%v(%.v)", n.Op(), n.Args) case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG: n := n.(*CallExpr) exprFmt(n.X, s, nprec) if n.IsDDD { fmt.Fprintf(s, "(%.v...)", n.Args) return } fmt.Fprintf(s, "(%.v)", n.Args) case OINLCALL: n := n.(*InlinedCallExpr) // TODO(mdempsky): Print Init and/or Body? if len(n.ReturnVars) == 1 { fmt.Fprintf(s, "%v", n.ReturnVars[0]) return } fmt.Fprintf(s, "(.%v)", n.ReturnVars) case OMAKEMAP, OMAKECHAN, OMAKESLICE: n := n.(*MakeExpr) if n.Cap != nil { fmt.Fprintf(s, "make(%v, %v, %v)", n.Type(), n.Len, n.Cap) return } if n.Len != nil && (n.Op() == OMAKESLICE || !n.Len.Type().IsUntyped()) { fmt.Fprintf(s, "make(%v, %v)", n.Type(), n.Len) return } fmt.Fprintf(s, "make(%v)", n.Type()) case OMAKESLICECOPY: n := n.(*MakeExpr) fmt.Fprintf(s, "makeslicecopy(%v, %v, %v)", n.Type(), n.Len, n.Cap) case OPLUS, ONEG, OBITNOT, ONOT, ORECV: // Unary n := n.(*UnaryExpr) fmt.Fprintf(s, "%v", n.Op()) if n.X != nil && n.X.Op() == n.Op() { fmt.Fprint(s, " ") } exprFmt(n.X, s, nprec+1) case OADDR: n := n.(*AddrExpr) fmt.Fprintf(s, "%v", n.Op()) if n.X != nil && n.X.Op() == n.Op() { fmt.Fprint(s, " ") } exprFmt(n.X, s, nprec+1) case ODEREF: n := n.(*StarExpr) fmt.Fprintf(s, "%v", n.Op()) exprFmt(n.X, s, nprec+1) // Binary case OADD, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT, OLSH, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR: n := n.(*BinaryExpr) exprFmt(n.X, s, nprec) fmt.Fprintf(s, " %v ", n.Op()) exprFmt(n.Y, s, nprec+1) case OANDAND, OOROR: n := n.(*LogicalExpr) exprFmt(n.X, s, nprec) fmt.Fprintf(s, " %v ", n.Op()) exprFmt(n.Y, s, nprec+1) case OSEND: n := n.(*SendStmt) exprFmt(n.Chan, s, nprec) fmt.Fprintf(s, " <- ") exprFmt(n.Value, s, nprec+1) case OADDSTR: n := n.(*AddStringExpr) for i, n1 := range n.List { if i != 0 { fmt.Fprint(s, " + ") } exprFmt(n1, s, nprec) } default: fmt.Fprintf(s, "", n.Op()) } } func ellipsisIf(b bool) string { if b { return "..." } return "" } // Nodes // Format implements formatting for a Nodes. // The valid formats are: // // %v Go syntax, semicolon-separated // %.v Go syntax, comma-separated // %+v Debug syntax, as in DumpList. // func (l Nodes) Format(s fmt.State, verb rune) { if s.Flag('+') && verb == 'v' { // %+v is DumpList output dumpNodes(s, l, 1) return } if verb != 'v' { fmt.Fprintf(s, "%%!%c(Nodes)", verb) return } sep := "; " if _, ok := s.Precision(); ok { // %.v is expr list sep = ", " } for i, n := range l { fmt.Fprint(s, n) if i+1 < len(l) { fmt.Fprint(s, sep) } } } // Dump // Dump prints the message s followed by a debug dump of n. func Dump(s string, n Node) { fmt.Printf("%s%+v\n", s, n) } // DumpList prints the message s followed by a debug dump of each node in the list. func DumpList(s string, list Nodes) { var buf bytes.Buffer FDumpList(&buf, s, list) os.Stdout.Write(buf.Bytes()) } // FDumpList prints to w the message s followed by a debug dump of each node in the list. func FDumpList(w io.Writer, s string, list Nodes) { io.WriteString(w, s) dumpNodes(w, list, 1) io.WriteString(w, "\n") } // indent prints indentation to w. func indent(w io.Writer, depth int) { fmt.Fprint(w, "\n") for i := 0; i < depth; i++ { fmt.Fprint(w, ". ") } } // EscFmt is set by the escape analysis code to add escape analysis details to the node print. var EscFmt func(n Node) string // dumpNodeHeader prints the debug-format node header line to w. func dumpNodeHeader(w io.Writer, n Node) { // Useful to see which nodes in an AST printout are actually identical if base.Debug.DumpPtrs != 0 { fmt.Fprintf(w, " p(%p)", n) } if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Defn != nil { // Useful to see where Defn is set and what node it points to fmt.Fprintf(w, " defn(%p)", n.Name().Defn) } if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Curfn != nil { // Useful to see where Defn is set and what node it points to fmt.Fprintf(w, " curfn(%p)", n.Name().Curfn) } if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Outer != nil { // Useful to see where Defn is set and what node it points to fmt.Fprintf(w, " outer(%p)", n.Name().Outer) } if EscFmt != nil { if esc := EscFmt(n); esc != "" { fmt.Fprintf(w, " %s", esc) } } if n.Sym() != nil && n.Op() != ONAME && n.Op() != ONONAME && n.Op() != OTYPE { fmt.Fprintf(w, " %+v", n.Sym()) } // Print Node-specific fields of basic type in header line. v := reflect.ValueOf(n).Elem() t := v.Type() nf := t.NumField() for i := 0; i < nf; i++ { tf := t.Field(i) if tf.PkgPath != "" { // skip unexported field - Interface will fail continue } k := tf.Type.Kind() if reflect.Bool <= k && k <= reflect.Complex128 { name := strings.TrimSuffix(tf.Name, "_") vf := v.Field(i) vfi := vf.Interface() if name == "Offset" && vfi == types.BADWIDTH || name != "Offset" && isZero(vf) { continue } if vfi == true { fmt.Fprintf(w, " %s", name) } else { fmt.Fprintf(w, " %s:%+v", name, vf.Interface()) } } } // Print Node-specific booleans by looking for methods. // Different v, t from above - want *Struct not Struct, for methods. v = reflect.ValueOf(n) t = v.Type() nm := t.NumMethod() for i := 0; i < nm; i++ { tm := t.Method(i) if tm.PkgPath != "" { // skip unexported method - call will fail continue } m := v.Method(i) mt := m.Type() if mt.NumIn() == 0 && mt.NumOut() == 1 && mt.Out(0).Kind() == reflect.Bool { // TODO(rsc): Remove the func/defer/recover wrapping, // which is guarding against panics in miniExpr, // once we get down to the simpler state in which // nodes have no getter methods that aren't allowed to be called. func() { defer func() { recover() }() if m.Call(nil)[0].Bool() { name := strings.TrimSuffix(tm.Name, "_") fmt.Fprintf(w, " %s", name) } }() } } if n.Op() == OCLOSURE { n := n.(*ClosureExpr) if fn := n.Func; fn != nil && fn.Nname.Sym() != nil { fmt.Fprintf(w, " fnName(%+v)", fn.Nname.Sym()) } } if n.Type() != nil { if n.Op() == OTYPE { fmt.Fprintf(w, " type") } fmt.Fprintf(w, " %+v", n.Type()) } if n.Typecheck() != 0 { fmt.Fprintf(w, " tc(%d)", n.Typecheck()) } if n.Pos().IsKnown() { fmt.Fprint(w, " # ") switch n.Pos().IsStmt() { case src.PosNotStmt: fmt.Fprint(w, "_") // "-" would be confusing case src.PosIsStmt: fmt.Fprint(w, "+") } for i, pos := range base.Ctxt.AllPos(n.Pos(), nil) { if i > 0 { fmt.Fprint(w, ",") } // TODO(mdempsky): Print line pragma details too. file := filepath.Base(pos.Filename()) // Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync. fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col()) } } } func dumpNode(w io.Writer, n Node, depth int) { indent(w, depth) if depth > 40 { fmt.Fprint(w, "...") return } if n == nil { fmt.Fprint(w, "NilIrNode") return } if len(n.Init()) != 0 { fmt.Fprintf(w, "%+v-init", n.Op()) dumpNodes(w, n.Init(), depth+1) indent(w, depth) } switch n.Op() { default: fmt.Fprintf(w, "%+v", n.Op()) dumpNodeHeader(w, n) case OLITERAL: fmt.Fprintf(w, "%+v-%v", n.Op(), n.Val()) dumpNodeHeader(w, n) return case ONAME, ONONAME: if n.Sym() != nil { fmt.Fprintf(w, "%+v-%+v", n.Op(), n.Sym()) } else { fmt.Fprintf(w, "%+v", n.Op()) } dumpNodeHeader(w, n) if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil { indent(w, depth) fmt.Fprintf(w, "%+v-ntype", n.Op()) dumpNode(w, n.Name().Ntype, depth+1) } return case OASOP: n := n.(*AssignOpStmt) fmt.Fprintf(w, "%+v-%+v", n.Op(), n.AsOp) dumpNodeHeader(w, n) case OTYPE: fmt.Fprintf(w, "%+v %+v", n.Op(), n.Sym()) dumpNodeHeader(w, n) if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil { indent(w, depth) fmt.Fprintf(w, "%+v-ntype", n.Op()) dumpNode(w, n.Name().Ntype, depth+1) } return case OCLOSURE: fmt.Fprintf(w, "%+v", n.Op()) dumpNodeHeader(w, n) case ODCLFUNC: // Func has many fields we don't want to print. // Bypass reflection and just print what we want. n := n.(*Func) fmt.Fprintf(w, "%+v", n.Op()) dumpNodeHeader(w, n) fn := n if len(fn.Dcl) > 0 { indent(w, depth) fmt.Fprintf(w, "%+v-Dcl", n.Op()) for _, dcl := range n.Dcl { dumpNode(w, dcl, depth+1) } } if len(fn.ClosureVars) > 0 { indent(w, depth) fmt.Fprintf(w, "%+v-ClosureVars", n.Op()) for _, cv := range fn.ClosureVars { dumpNode(w, cv, depth+1) } } if len(fn.Enter) > 0 { indent(w, depth) fmt.Fprintf(w, "%+v-Enter", n.Op()) dumpNodes(w, fn.Enter, depth+1) } if len(fn.Body) > 0 { indent(w, depth) fmt.Fprintf(w, "%+v-body", n.Op()) dumpNodes(w, fn.Body, depth+1) } return } v := reflect.ValueOf(n).Elem() t := reflect.TypeOf(n).Elem() nf := t.NumField() for i := 0; i < nf; i++ { tf := t.Field(i) vf := v.Field(i) if tf.PkgPath != "" { // skip unexported field - Interface will fail continue } switch tf.Type.Kind() { case reflect.Interface, reflect.Ptr, reflect.Slice: if vf.IsNil() { continue } } name := strings.TrimSuffix(tf.Name, "_") // Do not bother with field name header lines for the // most common positional arguments: unary, binary expr, // index expr, send stmt, go and defer call expression. switch name { case "X", "Y", "Index", "Chan", "Value", "Call": name = "" } switch val := vf.Interface().(type) { case Node: if name != "" { indent(w, depth) fmt.Fprintf(w, "%+v-%s", n.Op(), name) } dumpNode(w, val, depth+1) case Nodes: if len(val) == 0 { continue } if name != "" { indent(w, depth) fmt.Fprintf(w, "%+v-%s", n.Op(), name) } dumpNodes(w, val, depth+1) default: if vf.Kind() == reflect.Slice && vf.Type().Elem().Implements(nodeType) { if vf.Len() == 0 { continue } if name != "" { indent(w, depth) fmt.Fprintf(w, "%+v-%s", n.Op(), name) } for i, n := 0, vf.Len(); i < n; i++ { dumpNode(w, vf.Index(i).Interface().(Node), depth+1) } } } } } var nodeType = reflect.TypeOf((*Node)(nil)).Elem() func dumpNodes(w io.Writer, list Nodes, depth int) { if len(list) == 0 { fmt.Fprintf(w, " ") return } for _, n := range list { dumpNode(w, n, depth) } } // reflect.IsZero is not available in Go 1.4 (added in Go 1.13), so we use this copy instead. func isZero(v reflect.Value) bool { switch v.Kind() { case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return math.Float64bits(v.Float()) == 0 case reflect.Complex64, reflect.Complex128: c := v.Complex() return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 case reflect.Array: for i := 0; i < v.Len(); i++ { if !isZero(v.Index(i)) { return false } } return true case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: return v.IsNil() case reflect.String: return v.Len() == 0 case reflect.Struct: for i := 0; i < v.NumField(); i++ { if !isZero(v.Field(i)) { return false } } return true default: return false } }