2013-08-27 16:49:13 -06:00
|
|
|
// Copyright 2013 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.
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// Package oracle contains the implementation of the oracle tool whose
|
|
|
|
// command-line is provided by code.google.com/p/go.tools/cmd/oracle.
|
|
|
|
//
|
|
|
|
// http://golang.org/s/oracle-design
|
|
|
|
// http://golang.org/s/oracle-user-manual
|
|
|
|
//
|
2013-08-27 15:58:26 -06:00
|
|
|
package oracle
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
// This file defines oracle.Query, the entry point for the oracle tool.
|
2013-08-27 15:58:26 -06:00
|
|
|
// The actual executable is defined in cmd/oracle.
|
|
|
|
|
|
|
|
// TODO(adonovan): new query: show all statements that may update the
|
|
|
|
// selected lvalue (local, global, field, etc).
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/build"
|
|
|
|
"go/printer"
|
|
|
|
"go/token"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2013-09-03 08:58:58 -06:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2013-08-27 15:58:26 -06:00
|
|
|
"time"
|
|
|
|
|
2013-12-09 07:36:29 -07:00
|
|
|
"code.google.com/p/go.tools/astutil"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
"code.google.com/p/go.tools/importer"
|
2013-09-24 13:08:14 -06:00
|
|
|
"code.google.com/p/go.tools/oracle/serial"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/pointer"
|
|
|
|
"code.google.com/p/go.tools/ssa"
|
|
|
|
)
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// An Oracle holds the program state required for one or more queries.
|
|
|
|
type Oracle struct {
|
2013-08-27 15:58:26 -06:00
|
|
|
out io.Writer // standard output
|
2013-09-10 12:11:42 -06:00
|
|
|
prog *ssa.Program // the SSA program [only populated if need&SSA]
|
2013-09-23 14:13:01 -06:00
|
|
|
config pointer.Config // pointer analysis configuration [TODO rename ptaConfig]
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:11:42 -06:00
|
|
|
// need&AllTypeInfo
|
|
|
|
typeInfo map[*types.Package]*importer.PackageInfo // type info for all ASTs in the program
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
timers map[string]time.Duration // phase timing information
|
|
|
|
}
|
|
|
|
|
|
|
|
// A set of bits indicating the analytical requirements of each mode.
|
2013-09-12 08:55:24 -06:00
|
|
|
//
|
|
|
|
// Typed ASTs for the whole program are always constructed
|
|
|
|
// transiently; they are retained only for the queried package unless
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// needAllTypeInfo is set.
|
2013-08-27 15:58:26 -06:00
|
|
|
const (
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
needPos = 1 << iota // needs a position
|
|
|
|
needExactPos // needs an exact AST selection; implies needPos
|
|
|
|
needAllTypeInfo // needs to retain type info for all ASTs in the program
|
|
|
|
needSSA // needs ssa.Packages for whole program
|
|
|
|
needSSADebug // needs debug info for ssa.Packages
|
|
|
|
needPTA = needSSA // needs pointer analysis
|
|
|
|
needAll = -1 // needs everything (e.g. a sequence of queries)
|
2013-08-27 15:58:26 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type modeInfo struct {
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
name string
|
2013-08-27 15:58:26 -06:00
|
|
|
needs int
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
impl func(*Oracle, *QueryPos) (queryResult, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
var modes = []*modeInfo{
|
|
|
|
{"callees", needPTA | needExactPos, callees},
|
|
|
|
{"callers", needPTA | needPos, callers},
|
|
|
|
{"callgraph", needPTA, callgraph},
|
|
|
|
{"callstack", needPTA | needPos, callstack},
|
|
|
|
{"describe", needPTA | needSSADebug | needExactPos, describe},
|
|
|
|
{"freevars", needPos, freevars},
|
|
|
|
{"implements", needPos, implements},
|
|
|
|
{"peers", needPTA | needSSADebug | needPos, peers},
|
|
|
|
{"referrers", needAllTypeInfo | needPos, referrers},
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func findMode(mode string) *modeInfo {
|
|
|
|
for _, m := range modes {
|
|
|
|
if m.name == mode {
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
type printfFunc func(pos interface{}, format string, args ...interface{})
|
|
|
|
|
|
|
|
// queryResult is the interface of each query-specific result type.
|
2013-08-27 15:58:26 -06:00
|
|
|
type queryResult interface {
|
2013-09-24 13:08:14 -06:00
|
|
|
toSerial(res *serial.Result, fset *token.FileSet)
|
2013-09-03 13:29:02 -06:00
|
|
|
display(printf printfFunc)
|
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// A QueryPos represents the position provided as input to a query:
|
|
|
|
// a textual extent in the program's source code, the AST node it
|
|
|
|
// corresponds to, and the package to which it belongs.
|
|
|
|
// Instances are created by ParseQueryPos.
|
|
|
|
//
|
|
|
|
type QueryPos struct {
|
|
|
|
start, end token.Pos // source extent of query
|
|
|
|
info *importer.PackageInfo // type info for the queried package
|
|
|
|
path []ast.Node // AST path from query node to root of ast.File
|
|
|
|
}
|
|
|
|
|
2013-11-15 07:22:16 -07:00
|
|
|
// TypeString prints type T relative to the query position.
|
|
|
|
func (qpos *QueryPos) TypeString(T types.Type) string {
|
|
|
|
return types.TypeString(qpos.info.Pkg, T)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObjectString prints object obj relative to the query position.
|
|
|
|
func (qpos *QueryPos) ObjectString(obj types.Object) string {
|
|
|
|
return types.ObjectString(qpos.info.Pkg, obj)
|
|
|
|
}
|
|
|
|
|
2013-11-15 10:35:11 -07:00
|
|
|
// SelectionString prints selection sel relative to the query position.
|
|
|
|
func (qpos *QueryPos) SelectionString(sel *types.Selection) string {
|
|
|
|
return types.SelectionString(qpos.info.Pkg, sel)
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
// A Result encapsulates the result of an oracle.Query.
|
|
|
|
type Result struct {
|
|
|
|
fset *token.FileSet
|
|
|
|
// fprintf is a closure over the oracle's fileset and start/end position.
|
|
|
|
fprintf func(w io.Writer, pos interface{}, format string, args ...interface{})
|
2013-09-30 10:39:54 -06:00
|
|
|
q queryResult // the query-specific result
|
|
|
|
mode string // query mode
|
|
|
|
warnings []pointer.Warning // pointer analysis warnings
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
// 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)
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, w := range res.warnings {
|
2013-09-24 13:08:14 -06:00
|
|
|
resj.Warnings = append(resj.Warnings, serial.PTAWarning{
|
2013-09-30 10:39:54 -06:00
|
|
|
Pos: res.fset.Position(w.Pos).String(),
|
|
|
|
Message: w.Message,
|
2013-09-03 13:29:02 -06:00
|
|
|
})
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
return resj
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// Query runs a single oracle query.
|
|
|
|
//
|
2013-11-07 14:47:35 -07:00
|
|
|
// args specify the main package in importer.LoadInitialPackages syntax.
|
2013-08-27 15:58:26 -06:00
|
|
|
// mode is the query mode ("callers", etc).
|
|
|
|
// ptalog is the (optional) pointer-analysis log file.
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// buildContext is the go/build configuration for locating packages.
|
2013-09-23 14:13:01 -06:00
|
|
|
// reflection determines whether to model reflection soundly (currently slow).
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
//
|
|
|
|
// Clients that intend to perform multiple queries against the same
|
|
|
|
// analysis scope should use this pattern instead:
|
|
|
|
//
|
|
|
|
// imp := importer.New(&importer.Config{Build: buildContext})
|
|
|
|
// o, err := oracle.New(imp, args, nil)
|
|
|
|
// if err != nil { ... }
|
|
|
|
// for ... {
|
|
|
|
// qpos, err := oracle.ParseQueryPos(imp, pos, needExact)
|
|
|
|
// if err != nil { ... }
|
|
|
|
//
|
|
|
|
// res, err := o.Query(mode, qpos)
|
|
|
|
// if err != nil { ... }
|
|
|
|
//
|
|
|
|
// // use res
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos
|
|
|
|
// depends on the query mode; how should we expose this?
|
2013-08-27 15:58:26 -06:00
|
|
|
//
|
2013-09-23 14:13:01 -06:00
|
|
|
func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context, reflection bool) (*Result, error) {
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
minfo := findMode(mode)
|
|
|
|
if minfo == nil {
|
2013-09-03 13:29:02 -06:00
|
|
|
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-04 11:15:49 -06:00
|
|
|
imp := importer.New(&importer.Config{Build: buildContext})
|
2013-09-23 14:13:01 -06:00
|
|
|
o, err := New(imp, args, ptalog, reflection)
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// Phase timing diagnostics.
|
|
|
|
// TODO(adonovan): needs more work.
|
|
|
|
// if false {
|
|
|
|
// defer func() {
|
|
|
|
// fmt.Println()
|
|
|
|
// for name, duration := range o.timers {
|
|
|
|
// fmt.Printf("# %-30s %s\n", name, duration)
|
|
|
|
// }
|
|
|
|
// }()
|
|
|
|
// }
|
|
|
|
|
|
|
|
var qpos *QueryPos
|
|
|
|
if minfo.needs&(needPos|needExactPos) != 0 {
|
|
|
|
var err error
|
|
|
|
qpos, err = ParseQueryPos(imp, pos, minfo.needs&needExactPos != 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// SSA is built and we have the QueryPos.
|
|
|
|
// Release the other ASTs and type info to the GC.
|
|
|
|
imp = nil
|
|
|
|
|
|
|
|
return o.query(minfo, qpos)
|
|
|
|
}
|
|
|
|
|
|
|
|
// New constructs a new Oracle that can be used for a sequence of queries.
|
|
|
|
//
|
|
|
|
// imp will be used to load source code for imported packages.
|
|
|
|
// It must not yet have loaded any packages.
|
|
|
|
//
|
2013-11-07 14:47:35 -07:00
|
|
|
// args specify the main package in importer.LoadInitialPackages syntax.
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
//
|
|
|
|
// ptalog is the (optional) pointer-analysis log file.
|
2013-09-23 14:13:01 -06:00
|
|
|
// reflection determines whether to model reflection soundly (currently slow).
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
//
|
2013-09-23 14:13:01 -06:00
|
|
|
func New(imp *importer.Importer, args []string, ptalog io.Writer, reflection bool) (*Oracle, error) {
|
|
|
|
return newOracle(imp, args, ptalog, needAll, reflection)
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
}
|
|
|
|
|
2013-09-23 14:13:01 -06:00
|
|
|
func newOracle(imp *importer.Importer, args []string, ptalog io.Writer, needs int, reflection bool) (*Oracle, error) {
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
o := &Oracle{
|
|
|
|
prog: ssa.NewProgram(imp.Fset, 0),
|
|
|
|
timers: make(map[string]time.Duration),
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
o.config.Log = ptalog
|
2013-09-23 14:13:01 -06:00
|
|
|
o.config.Reflection = reflection
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
// Load/parse/type-check program from args.
|
|
|
|
start := time.Now()
|
2013-09-06 16:13:57 -06:00
|
|
|
initialPkgInfos, args, err := imp.LoadInitialPackages(args)
|
2013-08-27 15:58:26 -06:00
|
|
|
if err != nil {
|
2013-09-06 16:13:57 -06:00
|
|
|
return nil, err // I/O or parser error
|
|
|
|
}
|
|
|
|
if len(args) > 0 {
|
|
|
|
return nil, fmt.Errorf("surplus arguments: %q", args)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
o.timers["load/parse/type"] = time.Since(start)
|
|
|
|
|
2013-09-10 12:11:42 -06:00
|
|
|
// Retain type info for all ASTs in the program.
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
if needs&needAllTypeInfo != 0 {
|
2013-09-10 12:11:42 -06:00
|
|
|
m := make(map[*types.Package]*importer.PackageInfo)
|
|
|
|
for _, p := range imp.AllPackages() {
|
|
|
|
m[p.Pkg] = p
|
|
|
|
}
|
|
|
|
o.typeInfo = m
|
|
|
|
}
|
|
|
|
|
2013-11-15 07:22:16 -07:00
|
|
|
// Create SSA package for the initial packages and their dependencies.
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
if needs&needSSA != 0 {
|
2013-08-27 15:58:26 -06:00
|
|
|
start = time.Now()
|
|
|
|
|
2013-09-06 16:13:57 -06:00
|
|
|
// Create SSA packages.
|
|
|
|
if err := o.prog.CreatePackages(imp); err != nil {
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, err
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-10-23 16:07:53 -06:00
|
|
|
// For each initial package (specified on the command line),
|
|
|
|
// if it has a main function, analyze that,
|
|
|
|
// otherwise analyze its tests, if any.
|
|
|
|
var testPkgs []*ssa.Package
|
2013-09-06 16:13:57 -06:00
|
|
|
for _, info := range initialPkgInfos {
|
|
|
|
initialPkg := o.prog.Package(info.Pkg)
|
|
|
|
|
|
|
|
// Add package to the pointer analysis scope.
|
2013-10-23 16:07:53 -06:00
|
|
|
if initialPkg.Func("main") != nil {
|
|
|
|
o.config.Mains = append(o.config.Mains, initialPkg)
|
|
|
|
} else {
|
|
|
|
testPkgs = append(testPkgs, initialPkg)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-10-23 16:07:53 -06:00
|
|
|
}
|
|
|
|
if testPkgs != nil {
|
|
|
|
if p := o.prog.CreateTestMainPackage(testPkgs...); p != nil {
|
|
|
|
o.config.Mains = append(o.config.Mains, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if o.config.Mains == nil {
|
|
|
|
return nil, fmt.Errorf("analysis scope has no main and no tests")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
if needs&needSSADebug != 0 {
|
|
|
|
for _, pkg := range o.prog.AllPackages() {
|
|
|
|
pkg.SetDebugMode(true)
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
o.timers["SSA-create"] = time.Since(start)
|
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
return o, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query runs the query of the specified mode and selection.
|
|
|
|
func (o *Oracle) Query(mode string, qpos *QueryPos) (*Result, error) {
|
|
|
|
minfo := findMode(mode)
|
|
|
|
if minfo == nil {
|
|
|
|
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
|
|
|
}
|
|
|
|
return o.query(minfo, qpos)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Oracle) query(minfo *modeInfo, qpos *QueryPos) (*Result, error) {
|
|
|
|
res := &Result{
|
|
|
|
mode: minfo.name,
|
|
|
|
fset: o.prog.Fset,
|
|
|
|
fprintf: o.fprintf, // captures o.prog, o.{start,end}Pos for later printing
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
res.q, err = minfo.impl(o, qpos)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// ParseQueryPos parses the source query position pos.
|
|
|
|
// If needExact, it must identify a single AST subtree.
|
|
|
|
//
|
|
|
|
func ParseQueryPos(imp *importer.Importer, pos string, needExact bool) (*QueryPos, error) {
|
|
|
|
start, end, err := parseQueryPos(imp.Fset, pos)
|
2013-08-27 15:58:26 -06:00
|
|
|
if err != nil {
|
2013-09-03 13:29:02 -06:00
|
|
|
return nil, err
|
|
|
|
}
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
info, path, exact := imp.PathEnclosingInterval(start, end)
|
|
|
|
if path == nil {
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("no syntax here")
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
}
|
|
|
|
if needExact && !exact {
|
2013-12-09 07:36:29 -07:00
|
|
|
return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
}
|
|
|
|
return &QueryPos{start, end, info, path}, nil
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// WriteTo writes the oracle query result res to out in a compiler diagnostic format.
|
|
|
|
func (res *Result) WriteTo(out io.Writer) {
|
|
|
|
printf := func(pos interface{}, format string, args ...interface{}) {
|
|
|
|
res.fprintf(out, pos, format, args...)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
res.q.display(printf)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
// Print warnings after the main output.
|
2013-09-03 13:29:02 -06:00
|
|
|
if res.warnings != nil {
|
|
|
|
fmt.Fprintln(out, "\nPointer analysis warnings:")
|
|
|
|
for _, w := range res.warnings {
|
2013-09-30 10:39:54 -06:00
|
|
|
printf(w.Pos, "warning: "+w.Message)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------- Utilities ----------
|
|
|
|
|
|
|
|
// buildSSA constructs the SSA representation of Go-source function bodies.
|
|
|
|
// Not needed in simpler modes, e.g. freevars.
|
|
|
|
//
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func buildSSA(o *Oracle) {
|
2013-08-27 15:58:26 -06:00
|
|
|
start := time.Now()
|
|
|
|
o.prog.BuildAll()
|
|
|
|
o.timers["SSA-build"] = time.Since(start)
|
|
|
|
}
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
// ptrAnalysis runs the pointer analysis and returns its result.
|
|
|
|
func ptrAnalysis(o *Oracle) *pointer.Result {
|
2013-08-27 15:58:26 -06:00
|
|
|
start := time.Now()
|
2013-09-25 15:17:42 -06:00
|
|
|
result := pointer.Analyze(&o.config)
|
2013-08-27 15:58:26 -06:00
|
|
|
o.timers["pointer analysis"] = time.Since(start)
|
2013-09-25 15:17:42 -06:00
|
|
|
return result
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-04 12:35:24 -06:00
|
|
|
// parseOctothorpDecimal returns the numeric value if s matches "#%d",
|
|
|
|
// otherwise -1.
|
|
|
|
func parseOctothorpDecimal(s string) int {
|
|
|
|
if s != "" && s[0] == '#' {
|
|
|
|
if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil {
|
|
|
|
return int(s)
|
|
|
|
}
|
2013-09-03 08:58:58 -06:00
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseQueryPos parses a string of the form "file:pos" or
|
2013-09-08 20:10:11 -06:00
|
|
|
// file:start,end" where pos, start, end match #%d and represent byte
|
2013-09-04 12:35:24 -06:00
|
|
|
// offsets, and returns the extent to which it refers.
|
|
|
|
//
|
|
|
|
// (Numbers without a '#' prefix are reserved for future use,
|
|
|
|
// e.g. to indicate line/column positions.)
|
2013-08-27 15:58:26 -06:00
|
|
|
//
|
|
|
|
func parseQueryPos(fset *token.FileSet, queryPos string) (start, end token.Pos, err error) {
|
|
|
|
if queryPos == "" {
|
|
|
|
err = fmt.Errorf("no source position specified (-pos flag)")
|
|
|
|
return
|
|
|
|
}
|
2013-09-03 08:58:58 -06:00
|
|
|
|
|
|
|
colon := strings.LastIndex(queryPos, ":")
|
|
|
|
if colon < 0 {
|
|
|
|
err = fmt.Errorf("invalid source position -pos=%q", queryPos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
filename, offset := queryPos[:colon], queryPos[colon+1:]
|
|
|
|
startOffset := -1
|
|
|
|
endOffset := -1
|
2013-09-08 20:10:11 -06:00
|
|
|
if hyphen := strings.Index(offset, ","); hyphen < 0 {
|
2013-09-04 12:35:24 -06:00
|
|
|
// e.g. "foo.go:#123"
|
|
|
|
startOffset = parseOctothorpDecimal(offset)
|
2013-08-27 15:58:26 -06:00
|
|
|
endOffset = startOffset
|
2013-09-03 08:58:58 -06:00
|
|
|
} else {
|
2013-09-08 20:10:11 -06:00
|
|
|
// e.g. "foo.go:#123,#456"
|
2013-09-04 12:35:24 -06:00
|
|
|
startOffset = parseOctothorpDecimal(offset[:hyphen])
|
|
|
|
endOffset = parseOctothorpDecimal(offset[hyphen+1:])
|
2013-09-03 08:58:58 -06:00
|
|
|
}
|
|
|
|
if startOffset < 0 || endOffset < 0 {
|
|
|
|
err = fmt.Errorf("invalid -pos offset %q", offset)
|
|
|
|
return
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
var file *token.File
|
|
|
|
fset.Iterate(func(f *token.File) bool {
|
|
|
|
if sameFile(filename, f.Name()) {
|
|
|
|
// (f.Name() is absolute)
|
|
|
|
file = f
|
|
|
|
return false // done
|
|
|
|
}
|
|
|
|
return true // continue
|
|
|
|
})
|
|
|
|
if file == nil {
|
|
|
|
err = fmt.Errorf("couldn't find file containing position -pos=%q", queryPos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Range check [start..end], inclusive of both end-points.
|
|
|
|
|
|
|
|
if 0 <= startOffset && startOffset <= file.Size() {
|
|
|
|
start = file.Pos(int(startOffset))
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("start position is beyond end of file -pos=%q", queryPos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if 0 <= endOffset && endOffset <= file.Size() {
|
|
|
|
end = file.Pos(int(endOffset))
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("end position is beyond end of file -pos=%q", queryPos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// sameFile returns true if x and y have the same basename and denote
|
|
|
|
// the same file.
|
|
|
|
//
|
|
|
|
func sameFile(x, y string) bool {
|
|
|
|
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
|
|
|
|
if xi, err := os.Stat(x); err == nil {
|
|
|
|
if yi, err := os.Stat(y); err == nil {
|
|
|
|
return os.SameFile(xi, yi)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// unparen returns e with any enclosing parentheses stripped.
|
|
|
|
func unparen(e ast.Expr) ast.Expr {
|
|
|
|
for {
|
|
|
|
p, ok := e.(*ast.ParenExpr)
|
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
e = p.X
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
// deref returns a pointer's element type; otherwise it returns typ.
|
|
|
|
func deref(typ types.Type) types.Type {
|
|
|
|
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
|
|
|
return p.Elem()
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
|
|
|
// fprintf prints to w a message of the form "location: message\n"
|
|
|
|
// where location is derived from pos.
|
|
|
|
//
|
|
|
|
// pos must be one of:
|
|
|
|
// - a token.Pos, denoting a position
|
|
|
|
// - an ast.Node, denoting an interval
|
|
|
|
// - anything with a Pos() method:
|
|
|
|
// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
// - a QueryPos, denoting the extent of the user's query.
|
2013-08-27 15:58:26 -06:00
|
|
|
// - nil, meaning no position at all.
|
|
|
|
//
|
|
|
|
// The output format is is compatible with the 'gnu'
|
|
|
|
// compilation-error-regexp in Emacs' compilation mode.
|
|
|
|
// TODO(adonovan): support other editors.
|
|
|
|
//
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func (o *Oracle) fprintf(w io.Writer, pos interface{}, format string, args ...interface{}) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var start, end token.Pos
|
|
|
|
switch pos := pos.(type) {
|
|
|
|
case ast.Node:
|
|
|
|
start = pos.Pos()
|
|
|
|
end = pos.End()
|
|
|
|
case token.Pos:
|
|
|
|
start = pos
|
|
|
|
end = start
|
|
|
|
case interface {
|
|
|
|
Pos() token.Pos
|
|
|
|
}:
|
|
|
|
start = pos.Pos()
|
|
|
|
end = start
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
case *QueryPos:
|
|
|
|
start = pos.start
|
|
|
|
end = pos.end
|
2013-08-27 15:58:26 -06:00
|
|
|
case nil:
|
|
|
|
// no-op
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("invalid pos: %T", pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
if sp := o.prog.Fset.Position(start); start == end {
|
|
|
|
// (prints "-: " for token.NoPos)
|
|
|
|
fmt.Fprintf(w, "%s: ", sp)
|
|
|
|
} else {
|
|
|
|
ep := o.prog.Fset.Position(end)
|
|
|
|
// The -1 below is a concession to Emacs's broken use of
|
|
|
|
// inclusive (not half-open) intervals.
|
|
|
|
// Other editors may not want it.
|
|
|
|
// TODO(adonovan): add an -editor=vim|emacs|acme|auto
|
|
|
|
// flag; auto uses EMACS=t / VIM=... / etc env vars.
|
|
|
|
fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
|
|
|
|
sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, format, args...)
|
|
|
|
io.WriteString(w, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
// printNode returns the pretty-printed syntax of n.
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func (o *Oracle) printNode(n ast.Node) string {
|
2013-08-27 15:58:26 -06:00
|
|
|
var buf bytes.Buffer
|
|
|
|
printer.Fprint(&buf, o.prog.Fset, n)
|
|
|
|
return buf.String()
|
|
|
|
}
|