// 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"
"fmt"
"html"
"io"
"os"
)
type HTMLWriter struct {
Logger
*os.File
}
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(0, "%v", err)
}
html := HTMLWriter{File: out, Logger: logger}
html.start(funcname)
return &html
}
func (w *HTMLWriter) start(name string) {
if w == nil {
return
}
w.WriteString("")
w.WriteString(`
`)
// TODO: Add javascript click handlers for blocks
// to outline that block across all phases
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, which may vary across passes.
(TODO: Fix this.)
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
}
w.WriteString("
")
w.WriteString("
")
w.WriteString("")
w.WriteString("")
w.File.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.File, msg, v...); err != nil {
w.Fatalf(0, "%v", err)
}
}
func (w *HTMLWriter) WriteString(s string) {
if _, err := w.File.WriteString(s); err != nil {
w.Fatalf(0, "%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.
return fmt.Sprintf("%[1]s", v.String())
}
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())
}
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.
return fmt.Sprintf("%[1]s", html.EscapeString(b.String()))
}
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, "", b, dead)
fmt.Fprintf(p.w, "- %s:", b.HTML())
if len(b.Preds) > 0 {
io.WriteString(p.w, " ←") // left arrow
for _, e := range b.Preds {
pred := e.b
fmt.Fprintf(p.w, " %s", pred.HTML())
}
}
io.WriteString(p.w, "
")
if len(b.Values) > 0 { // start list of values
io.WriteString(p.w, "- ")
io.WriteString(p.w, "
")
}
}
func (p htmlFuncPrinter) endBlock(b *Block) {
if len(b.Values) > 0 { // end list of values
io.WriteString(p.w, "
")
io.WriteString(p.w, " ")
}
io.WriteString(p.w, "- ")
fmt.Fprint(p.w, b.LongHTML())
io.WriteString(p.w, "
")
io.WriteString(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) {
// TODO
}