diff --git a/cmd/oracle/main.go b/cmd/oracle/main.go index 05f33d05..5dff0547 100644 --- a/cmd/oracle/main.go +++ b/cmd/oracle/main.go @@ -14,8 +14,8 @@ package main import ( "bufio" - "bytes" "encoding/json" + "encoding/xml" "flag" "fmt" "go/build" @@ -39,7 +39,7 @@ var modeFlag = flag.String("mode", "", var ptalogFlag = flag.String("ptalog", "", "Location of the points-to analysis log file, or empty to disable logging.") -var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.") +var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.") // TODO(adonovan): eliminate or flip this flag after PTA presolver is implemented. var reflectFlag = flag.Bool("reflect", true, "Analyze reflection soundly (slow).") @@ -53,6 +53,7 @@ The -format flag controls the output format: plain an editor-friendly format in which every line of output is of the form "pos: text", where pos is "-" if unknown. json structured data in JSON syntax. + xml structured data in XML syntax. The -pos flag is required in all modes except 'callgraph'. @@ -140,7 +141,10 @@ func main() { } // -format flag - if *formatFlag != "json" && *formatFlag != "plain" { + switch *formatFlag { + case "json", "plain", "xml": + // ok + default: fmt.Fprintf(os.Stderr, "Error: illegal -format value: %q\n"+useHelp, *formatFlag) os.Exit(2) } @@ -161,17 +165,20 @@ func main() { // Print the result. switch *formatFlag { case "json": - b, err := json.Marshal(res) + b, err := json.MarshalIndent(res.Serial(), "", "\t") if err != nil { fmt.Fprintf(os.Stderr, "JSON error: %s\n", err) os.Exit(1) } - var buf bytes.Buffer - if err := json.Indent(&buf, b, "", "\t"); err != nil { - fmt.Fprintf(os.Stderr, "json.Indent failed: %s", err) + os.Stdout.Write(b) + + case "xml": + b, err := xml.MarshalIndent(res.Serial(), "", "\t") + if err != nil { + fmt.Fprintf(os.Stderr, "XML error: %s\n", err) os.Exit(1) } - os.Stdout.Write(buf.Bytes()) + os.Stdout.Write(b) case "plain": res.WriteTo(os.Stdout) diff --git a/oracle/callees.go b/oracle/callees.go index a9ee1890..66a10c37 100644 --- a/oracle/callees.go +++ b/oracle/callees.go @@ -10,7 +10,7 @@ import ( "sort" "code.google.com/p/go.tools/go/types" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) @@ -113,13 +113,13 @@ func (r *calleesResult) display(printf printfFunc) { } } -func (r *calleesResult) toJSON(res *json.Result, fset *token.FileSet) { - j := &json.Callees{ +func (r *calleesResult) toSerial(res *serial.Result, fset *token.FileSet) { + j := &serial.Callees{ Pos: fset.Position(r.site.Pos()).String(), Desc: r.site.Description(), } for _, callee := range r.funcs { - j.Callees = append(j.Callees, &json.CalleesItem{ + j.Callees = append(j.Callees, &serial.CalleesItem{ Name: callee.String(), Pos: fset.Position(callee.Pos()).String(), }) diff --git a/oracle/callers.go b/oracle/callers.go index 01f827be..1233bccd 100644 --- a/oracle/callers.go +++ b/oracle/callers.go @@ -7,7 +7,7 @@ package oracle import ( "go/token" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) @@ -73,10 +73,10 @@ func (r *callersResult) display(printf printfFunc) { } } -func (r *callersResult) toJSON(res *json.Result, fset *token.FileSet) { - var callers []json.Caller +func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) { + var callers []serial.Caller for _, site := range r.calls { - var c json.Caller + var c serial.Caller c.Caller = site.Caller().Func().String() if site.Caller() == r.root { c.Desc = "synthetic call" diff --git a/oracle/callgraph.go b/oracle/callgraph.go index c8cdd680..3598fd84 100644 --- a/oracle/callgraph.go +++ b/oracle/callgraph.go @@ -8,7 +8,7 @@ import ( "go/token" "strings" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" ) @@ -90,8 +90,8 @@ Some nodes may appear multiple times due to context-sensitive print(r.root, 0) } -func (r *callgraphResult) toJSON(res *json.Result, fset *token.FileSet) { - cg := make([]json.CallGraph, len(r.numbering)) +func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) { + cg := make([]serial.CallGraph, len(r.numbering)) for n, i := range r.numbering { j := &cg[i] fn := n.Func() diff --git a/oracle/callstack.go b/oracle/callstack.go index 6a5f4323..afec7554 100644 --- a/oracle/callstack.go +++ b/oracle/callstack.go @@ -7,7 +7,7 @@ package oracle import ( "go/token" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) @@ -98,16 +98,16 @@ func (r *callstackResult) display(printf printfFunc) { } } -func (r *callstackResult) toJSON(res *json.Result, fset *token.FileSet) { - var callers []json.Caller +func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) { + var callers []serial.Caller for _, site := range r.callstack { - callers = append(callers, json.Caller{ + callers = append(callers, serial.Caller{ Pos: fset.Position(site.Pos()).String(), Caller: site.Caller().Func().String(), Desc: site.Description(), }) } - res.Callstack = &json.CallStack{ + res.Callstack = &serial.CallStack{ Pos: fset.Position(r.target.Pos()).String(), Target: r.target.String(), Callers: callers, diff --git a/oracle/describe.go b/oracle/describe.go index 2c30823c..688beeb4 100644 --- a/oracle/describe.go +++ b/oracle/describe.go @@ -17,7 +17,7 @@ import ( "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) @@ -70,8 +70,8 @@ func (r *describeUnknownResult) display(printf printfFunc) { printf(r.node, "%s", importer.NodeDescription(r.node)) } -func (r *describeUnknownResult) toJSON(res *json.Result, fset *token.FileSet) { - res.Describe = &json.Describe{ +func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) { + res.Describe = &serial.Describe{ Desc: importer.NodeDescription(r.node), Pos: fset.Position(r.node.Pos()).String(), } @@ -553,7 +553,7 @@ func (r *describeValueResult) display(printf printfFunc) { } } -func (r *describeValueResult) toJSON(res *json.Result, fset *token.FileSet) { +func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { var value, objpos, ptaerr string if r.constVal != nil { value = r.constVal.String() @@ -565,31 +565,31 @@ func (r *describeValueResult) toJSON(res *json.Result, fset *token.FileSet) { ptaerr = r.ptaErr.Error() } - var pts []*json.DescribePointer + var pts []*serial.DescribePointer for _, ptr := range r.ptrs { var namePos string if nt, ok := deref(ptr.typ).(*types.Named); ok { namePos = fset.Position(nt.Obj().Pos()).String() } - var labels []json.DescribePTALabel + var labels []serial.DescribePTALabel for _, l := range ptr.labels { - labels = append(labels, json.DescribePTALabel{ + labels = append(labels, serial.DescribePTALabel{ Pos: fset.Position(l.Pos()).String(), Desc: l.String(), }) } - pts = append(pts, &json.DescribePointer{ + pts = append(pts, &serial.DescribePointer{ Type: ptr.typ.String(), NamePos: namePos, Labels: labels, }) } - res.Describe = &json.Describe{ + res.Describe = &serial.Describe{ Desc: importer.NodeDescription(r.expr), Pos: fset.Position(r.expr.Pos()).String(), Detail: "value", - Value: &json.DescribeValue{ + Value: &serial.DescribeValue{ Type: r.typ.String(), Value: value, ObjPos: objpos, @@ -689,21 +689,21 @@ func (r *describeTypeResult) display(printf printfFunc) { } } -func (r *describeTypeResult) toJSON(res *json.Result, fset *token.FileSet) { +func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) { var namePos, nameDef string if nt, ok := r.typ.(*types.Named); ok { namePos = fset.Position(nt.Obj().Pos()).String() nameDef = nt.Underlying().String() } - res.Describe = &json.Describe{ + res.Describe = &serial.Describe{ Desc: r.description, Pos: fset.Position(r.node.Pos()).String(), Detail: "type", - Type: &json.DescribeType{ + Type: &serial.DescribeType{ Type: r.typ.String(), NamePos: namePos, NameDef: nameDef, - Methods: methodsToJSON(r.methods, fset), + Methods: methodsToSerial(r.methods, fset), }, } } @@ -831,8 +831,8 @@ func formatMember(obj types.Object, maxname int) string { return buf.String() } -func (r *describePackageResult) toJSON(res *json.Result, fset *token.FileSet) { - var members []*json.DescribeMember +func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) { + var members []*serial.DescribeMember for _, mem := range r.members { typ := mem.obj.Type() var val string @@ -842,20 +842,20 @@ func (r *describePackageResult) toJSON(res *json.Result, fset *token.FileSet) { case *types.TypeName: typ = typ.Underlying() } - members = append(members, &json.DescribeMember{ + members = append(members, &serial.DescribeMember{ Name: mem.obj.Name(), Type: typ.String(), Value: val, Pos: fset.Position(mem.obj.Pos()).String(), Kind: tokenOf(mem.obj), - Methods: methodsToJSON(mem.methods, fset), + Methods: methodsToSerial(mem.methods, fset), }) } - res.Describe = &json.Describe{ + res.Describe = &serial.Describe{ Desc: r.description, Pos: fset.Position(r.node.Pos()).String(), Detail: "package", - Package: &json.DescribePackage{ + Package: &serial.DescribePackage{ Path: r.pkg.Path(), Members: members, }, @@ -907,8 +907,8 @@ func (r *describeStmtResult) display(printf printfFunc) { printf(r.node, "%s", r.description) } -func (r *describeStmtResult) toJSON(res *json.Result, fset *token.FileSet) { - res.Describe = &json.Describe{ +func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) { + res.Describe = &serial.Describe{ Desc: r.description, Pos: fset.Position(r.node.Pos()).String(), Detail: "unknown", @@ -946,10 +946,10 @@ func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { return ast.IsExported(obj.Name()) || obj.Pkg() == pkg } -func methodsToJSON(methods []*types.Selection, fset *token.FileSet) []json.DescribeMethod { - var jmethods []json.DescribeMethod +func methodsToSerial(methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod { + var jmethods []serial.DescribeMethod for _, meth := range methods { - jmethods = append(jmethods, json.DescribeMethod{ + jmethods = append(jmethods, serial.DescribeMethod{ Name: meth.String(), Pos: fset.Position(meth.Obj().Pos()).String(), }) diff --git a/oracle/freevars.go b/oracle/freevars.go index 5e5d7548..d3e1af95 100644 --- a/oracle/freevars.go +++ b/oracle/freevars.go @@ -10,7 +10,7 @@ import ( "sort" "code.google.com/p/go.tools/go/types" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" ) // freevars displays the lexical (not package-level) free variables of @@ -169,11 +169,11 @@ func (r *freevarsResult) display(printf printfFunc) { } } -func (r *freevarsResult) toJSON(res *json.Result, fset *token.FileSet) { - var refs []*json.FreeVar +func (r *freevarsResult) toSerial(res *serial.Result, fset *token.FileSet) { + var refs []*serial.FreeVar for _, ref := range r.refs { refs = append(refs, - &json.FreeVar{ + &serial.FreeVar{ Pos: fset.Position(ref.obj.Pos()).String(), Kind: ref.kind, Ref: ref.ref, diff --git a/oracle/implements.go b/oracle/implements.go index d26cd1ec..76b2e1fd 100644 --- a/oracle/implements.go +++ b/oracle/implements.go @@ -8,7 +8,7 @@ import ( "go/token" "code.google.com/p/go.tools/go/types" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" ) // Implements displays the 'implements" relation among all @@ -89,10 +89,10 @@ func (r *implementsResult) display(printf printfFunc) { } } -func (r *implementsResult) toJSON(res *json.Result, fset *token.FileSet) { - var facts []*json.Implements +func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) { + var facts []*serial.Implements for _, fact := range r.facts { - facts = append(facts, &json.Implements{ + facts = append(facts, &serial.Implements{ I: fact.iface.String(), IPos: fset.Position(fact.iface.Obj().Pos()).String(), C: fact.conc.String(), diff --git a/oracle/oracle.go b/oracle/oracle.go index 73239b92..2c9a33cd 100644 --- a/oracle/oracle.go +++ b/oracle/oracle.go @@ -18,7 +18,6 @@ package oracle import ( "bytes" - encjson "encoding/json" "errors" "fmt" "go/ast" @@ -34,7 +33,7 @@ import ( "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/importer" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) @@ -97,7 +96,7 @@ type printfFunc func(pos interface{}, format string, args ...interface{}) // queryResult is the interface of each query-specific result type. type queryResult interface { - toJSON(res *json.Result, fset *token.FileSet) + toSerial(res *serial.Result, fset *token.FileSet) display(printf printfFunc) } @@ -119,9 +118,6 @@ type QueryPos struct { } // A Result encapsulates the result of an oracle.Query. -// -// Result instances implement the json.Marshaler interface, i.e. they -// can be JSON-serialized. type Result struct { fset *token.FileSet // fprintf is a closure over the oracle's fileset and start/end position. @@ -131,16 +127,20 @@ type Result struct { warnings []warning // pointer analysis warnings } -func (res *Result) MarshalJSON() ([]byte, error) { - resj := &json.Result{Mode: res.mode} - res.q.toJSON(resj, res.fset) +// Serial returns an instance of serial.Result, which implements the +// {xml,json}.Marshaler interfaces so that query results can be +// serialized as JSON or XML. +// +func (res *Result) Serial() *serial.Result { + resj := &serial.Result{Mode: res.mode} + res.q.toSerial(resj, res.fset) for _, w := range res.warnings { - resj.Warnings = append(resj.Warnings, json.PTAWarning{ + resj.Warnings = append(resj.Warnings, serial.PTAWarning{ Pos: res.fset.Position(w.pos).String(), Message: fmt.Sprintf(w.format, w.args...), }) } - return encjson.Marshal(resj) + return resj } // Query runs a single oracle query. diff --git a/oracle/oracle_test.go b/oracle/oracle_test.go index 337c85c6..e7934c70 100644 --- a/oracle/oracle_test.go +++ b/oracle/oracle_test.go @@ -175,17 +175,12 @@ func doQuery(out io.Writer, q *query, useJson bool) { if useJson { // JSON output - b, err := json.Marshal(res) + b, err := json.MarshalIndent(res.Serial(), "", "\t") if err != nil { fmt.Fprintf(out, "JSON error: %s\n", err.Error()) return } - var buf bytes.Buffer - if err := json.Indent(&buf, b, "", "\t"); err != nil { - fmt.Fprintf(out, "json.Indent failed: %s", err) - return - } - out.Write(buf.Bytes()) + out.Write(b) } else { // "plain" (compiler diagnostic format) output capture := new(bytes.Buffer) // capture standard output diff --git a/oracle/peers.go b/oracle/peers.go index 09dba85d..1039331e 100644 --- a/oracle/peers.go +++ b/oracle/peers.go @@ -10,7 +10,7 @@ import ( "sort" "code.google.com/p/go.tools/go/types" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) @@ -174,8 +174,8 @@ func (r *peersResult) display(printf printfFunc) { } } -func (r *peersResult) toJSON(res *json.Result, fset *token.FileSet) { - peers := &json.Peers{ +func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) { + peers := &serial.Peers{ Pos: fset.Position(r.queryPos).String(), Type: r.queryType.String(), } diff --git a/oracle/referrers.go b/oracle/referrers.go index 4bf16bd8..8ba9ffb9 100644 --- a/oracle/referrers.go +++ b/oracle/referrers.go @@ -10,7 +10,7 @@ import ( "sort" "code.google.com/p/go.tools/go/types" - "code.google.com/p/go.tools/oracle/json" + "code.google.com/p/go.tools/oracle/serial" ) // Referrers reports all identifiers that resolve to the same object @@ -84,8 +84,8 @@ func (r *referrersResult) display(printf printfFunc) { } } -func (r *referrersResult) toJSON(res *json.Result, fset *token.FileSet) { - referrers := &json.Referrers{ +func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) { + referrers := &serial.Referrers{ Pos: fset.Position(r.query).String(), Desc: r.obj.String(), } diff --git a/oracle/json/json.go b/oracle/serial/serial.go similarity index 98% rename from oracle/json/json.go rename to oracle/serial/serial.go index be0d9963..ac1ee6d6 100644 --- a/oracle/json/json.go +++ b/oracle/serial/serial.go @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package json defines the oracle's JSON schema. -package json +// Package serial defines the oracle's schema for structured data +// serialization using JSON, XML, etc. +package serial // All 'pos' strings are of the form "file:line:col". // TODO(adonovan): improve performance by sharing filename strings.