| // 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, "<nil>") |
| 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, |
| OPANIC: 8, |
| OPAREN: 8, |
| OPRINTN: 8, |
| OPRINT: 8, |
| ORUNESTR: 8, |
| OSIZEOF: 8, |
| OSLICE2ARRPTR: 8, |
| OSTR2BYTES: 8, |
| OSTR2RUNES: 8, |
| OSTRUCTLIT: 8, |
| OTFUNC: 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 = <N>" initializing statements, hope they're always |
| // preceded by the DCL which will be re-parsed and typechecked to reproduce |
| // the "v = <N>" 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, "<nil>") |
| 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, "<unnamed Func>") |
| |
| 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 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 OTFUNC: |
| fmt.Fprint(s, "<func>") |
| |
| 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, ".<nil>") |
| 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, "<node %v>", 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, " <nil>") |
| 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 |
| } |
| } |