// Copyright 2015 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 ssa import ( "bytes" "cmd/internal/src" "fmt" "html" "io" "os" "strings" ) type HTMLWriter struct { Logger w io.WriteCloser } func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter { out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { logger.Fatalf(src.NoXPos, "%v", err) } html := HTMLWriter{w: out, Logger: logger} html.start(funcname) return &html } func (w *HTMLWriter) start(name string) { if w == nil { return } w.WriteString("") w.WriteString(` `) w.WriteString("") w.WriteString("

") w.WriteString(html.EscapeString(name)) w.WriteString("

") w.WriteString(` help

Click on a value or block to toggle highlighting of that value/block and its uses. (Values and blocks are highlighted by ID, and IDs of dead items may be reused, so not all highlights necessarily correspond to the clicked item.)

Faded out values and blocks are dead code that has not been eliminated.

Values printed in italics have a dependency cycle.

`) w.WriteString("") w.WriteString("") } func (w *HTMLWriter) Close() { if w == nil { return } io.WriteString(w.w, "") io.WriteString(w.w, "
") io.WriteString(w.w, "") io.WriteString(w.w, "") w.w.Close() } // WriteFunc writes f in a column headed by title. func (w *HTMLWriter) WriteFunc(title string, f *Func) { if w == nil { return // avoid generating HTML just to discard it } w.WriteColumn(title, f.HTML()) // TODO: Add visual representation of f's CFG. } // WriteColumn writes raw HTML in a column headed by title. // It is intended for pre- and post-compilation log output. func (w *HTMLWriter) WriteColumn(title string, html string) { if w == nil { return } w.WriteString("") w.WriteString("

" + title + "

") w.WriteString(html) w.WriteString("") } func (w *HTMLWriter) Printf(msg string, v ...interface{}) { if _, err := fmt.Fprintf(w.w, msg, v...); err != nil { w.Fatalf(src.NoXPos, "%v", err) } } func (w *HTMLWriter) WriteString(s string) { if _, err := io.WriteString(w.w, s); err != nil { w.Fatalf(src.NoXPos, "%v", err) } } func (v *Value) HTML() string { // TODO: Using the value ID as the class ignores the fact // that value IDs get recycled and that some values // are transmuted into other values. s := v.String() return fmt.Sprintf("%s", s, s) } func (v *Value) LongHTML() string { // TODO: Any intra-value formatting? // I'm wary of adding too much visual noise, // but a little bit might be valuable. // We already have visual noise in the form of punctuation // maybe we could replace some of that with formatting. s := fmt.Sprintf("", v.String()) s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String()) s += " <" + html.EscapeString(v.Type.String()) + ">" s += html.EscapeString(v.auxString()) for _, a := range v.Args { s += fmt.Sprintf(" %s", a.HTML()) } r := v.Block.Func.RegAlloc if int(v.ID) < len(r) && r[v.ID] != nil { s += " : " + html.EscapeString(r[v.ID].Name()) } var names []string for name, values := range v.Block.Func.NamedValues { for _, value := range values { if value == v { names = append(names, name.Name()) break // drop duplicates. } } } if len(names) != 0 { s += " (" + strings.Join(names, ", ") + ")" } s += "" return s } func (b *Block) HTML() string { // TODO: Using the value ID as the class ignores the fact // that value IDs get recycled and that some values // are transmuted into other values. s := html.EscapeString(b.String()) return fmt.Sprintf("%s", s, s) } func (b *Block) LongHTML() string { // TODO: improve this for HTML? s := fmt.Sprintf("%s", html.EscapeString(b.String()), html.EscapeString(b.Kind.String())) if b.Aux != nil { s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux)) } if b.Control != nil { s += fmt.Sprintf(" %s", b.Control.HTML()) } if len(b.Succs) > 0 { s += " →" // right arrow for _, e := range b.Succs { c := e.b s += " " + c.HTML() } } switch b.Likely { case BranchUnlikely: s += " (unlikely)" case BranchLikely: s += " (likely)" } return s } func (f *Func) HTML() string { var buf bytes.Buffer fmt.Fprint(&buf, "") p := htmlFuncPrinter{w: &buf} fprintFunc(p, f) // fprintFunc(&buf, f) // TODO: HTML, not text,
for line breaks, etc. fmt.Fprint(&buf, "
") return buf.String() } type htmlFuncPrinter struct { w io.Writer } func (p htmlFuncPrinter) header(f *Func) {} func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { // TODO: Make blocks collapsable? var dead string if !reachable { dead = "dead-block" } fmt.Fprintf(p.w, "") // io.WriteString(p.w, "") } func (p htmlFuncPrinter) value(v *Value, live bool) { var dead string if !live { dead = "dead-value" } fmt.Fprintf(p.w, "
  • ", dead) fmt.Fprint(p.w, v.LongHTML()) io.WriteString(p.w, "
  • ") } func (p htmlFuncPrinter) startDepCycle() { fmt.Fprintln(p.w, "") } func (p htmlFuncPrinter) endDepCycle() { fmt.Fprintln(p.w, "") } func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) { fmt.Fprintf(p.w, "
  • name %s: ", n.Name()) for _, val := range vals { fmt.Fprintf(p.w, "%s ", val.HTML()) } fmt.Fprintf(p.w, "
  • ") }