2014-02-20 09:35:09 -07:00
|
|
|
// Copyright 2014 The Go Authors. All rights reserved.
|
2013-08-27 16:49:13 -06:00
|
|
|
// 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.
|
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
// TODO(adonovan): new queries
|
|
|
|
// - show all statements that may update the selected lvalue
|
|
|
|
// (local, global, field, etc).
|
|
|
|
// - show all places where an object of type T is created
|
|
|
|
// (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
|
|
|
|
|
|
|
|
// ORACLE CONTROL FLOW
|
|
|
|
//
|
|
|
|
// The Oracle is somewhat convoluted due to the need to support two
|
|
|
|
// very different use-cases, "one-shot" and "long running", and to do
|
|
|
|
// so quickly.
|
|
|
|
//
|
|
|
|
// The cmd/oracle tool issues "one-shot" queries via the exported
|
|
|
|
// Query function, which creates an Oracle to answer a single query.
|
|
|
|
// newOracle consults the 'needs' flags of the query mode and the
|
|
|
|
// package containing the query to avoid doing more work than it needs
|
|
|
|
// (loading, parsing, type checking, SSA construction).
|
|
|
|
//
|
2014-02-20 09:35:09 -07:00
|
|
|
// The Pythia tool (github.com/fzipp/pythia) is an example of a "long
|
2013-12-13 08:04:55 -07:00
|
|
|
// running" tool. It calls New() and then loops, calling
|
|
|
|
// ParseQueryPos and (*Oracle).Query to handle each incoming HTTP
|
|
|
|
// query. Since New cannot see which queries will follow, it must
|
|
|
|
// load, parse, type-check and SSA-build the entire transitive closure
|
|
|
|
// of the analysis scope, retaining full debug information and all
|
|
|
|
// typed ASTs.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): experiment with inverting the control flow by
|
|
|
|
// making each mode consist of two functions: a "one-shot setup"
|
|
|
|
// function and the existing "impl" function. The one-shot setup
|
|
|
|
// function would do all of the work of Query and newOracle,
|
|
|
|
// specialized to each mode, calling library utilities for the common
|
|
|
|
// things. This would give it more control over "scope reduction".
|
|
|
|
// Long running tools would not call the one-shot setup function but
|
|
|
|
// would have their own setup function equivalent to the existing
|
|
|
|
// 'needsAll' flow path.
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/build"
|
|
|
|
"go/token"
|
|
|
|
"io"
|
|
|
|
|
2013-12-09 07:36:29 -07:00
|
|
|
"code.google.com/p/go.tools/astutil"
|
2014-01-16 07:33:58 -07:00
|
|
|
"code.google.com/p/go.tools/go/loader"
|
|
|
|
"code.google.com/p/go.tools/go/pointer"
|
|
|
|
"code.google.com/p/go.tools/go/ssa"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/go/types"
|
2013-09-24 13:08:14 -06:00
|
|
|
"code.google.com/p/go.tools/oracle/serial"
|
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
|
|
|
// An Oracle holds the program state required for one or more queries.
|
|
|
|
type Oracle struct {
|
2014-01-16 07:33:58 -07:00
|
|
|
fset *token.FileSet // file set [all queries]
|
|
|
|
prog *ssa.Program // the SSA program [needSSA]
|
|
|
|
ptaConfig pointer.Config // pointer analysis configuration [needPTA]
|
|
|
|
typeInfo map[*types.Package]*loader.PackageInfo // type info for all ASTs in the program [needRetainTypeInfo]
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2013-12-13 08:04:55 -07:00
|
|
|
// needRetainTypeInfo is set.
|
2013-08-27 15:58:26 -06:00
|
|
|
const (
|
2013-12-13 08:04:55 -07:00
|
|
|
needPos = 1 << iota // needs a position
|
|
|
|
needExactPos // needs an exact AST selection; implies needPos
|
|
|
|
needRetainTypeInfo // 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{
|
2013-12-13 16:00:55 -07:00
|
|
|
// Pointer analyses, whole 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
|
|
|
{"callees", needPTA | needExactPos, callees},
|
|
|
|
{"callers", needPTA | needPos, callers},
|
2014-01-16 12:04:19 -07:00
|
|
|
{"callgraph", needPTA, doCallgraph},
|
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
|
|
|
{"callstack", needPTA | needPos, callstack},
|
2013-12-13 08:04:55 -07:00
|
|
|
{"peers", needPTA | needSSADebug | needPos, peers},
|
|
|
|
{"pointsto", needPTA | needSSADebug | needExactPos, pointsto},
|
|
|
|
|
2013-12-13 16:00:55 -07:00
|
|
|
// Type-based, modular analyses:
|
2013-12-13 08:04:55 -07:00
|
|
|
{"definition", needPos, definition},
|
|
|
|
{"describe", needExactPos, describe},
|
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
|
|
|
{"freevars", needPos, freevars},
|
2013-12-13 16:00:55 -07:00
|
|
|
|
|
|
|
// Type-based, whole-program analyses:
|
|
|
|
{"implements", needRetainTypeInfo | needPos, implements},
|
|
|
|
{"referrers", needRetainTypeInfo | 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 {
|
2013-12-13 08:04:55 -07:00
|
|
|
fset *token.FileSet
|
2014-01-16 07:33:58 -07:00
|
|
|
start, end token.Pos // source extent of query
|
|
|
|
path []ast.Node // AST path from query node to root of ast.File
|
|
|
|
exact bool // 2nd result of PathEnclosingInterval
|
|
|
|
info *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
|
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-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 {
|
2013-12-13 08:04:55 -07:00
|
|
|
fset *token.FileSet
|
2013-09-30 10:39:54 -06:00
|
|
|
q queryResult // the query-specific result
|
|
|
|
mode string // query mode
|
2014-03-11 16:24:39 -06:00
|
|
|
warnings []pointer.Warning // pointer analysis warnings (TODO(adonovan): fix: never populated!)
|
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.
|
|
|
|
//
|
2014-01-16 07:33:58 -07:00
|
|
|
// args specify the main package in (*loader.Config).FromArgs 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:
|
|
|
|
//
|
2014-01-16 07:33:58 -07:00
|
|
|
// conf := loader.Config{Build: buildContext, SourceImports: true}
|
2014-01-15 19:37:55 -07:00
|
|
|
// ... populate config, e.g. conf.FromArgs(args) ...
|
|
|
|
// iprog, err := conf.Load()
|
|
|
|
// if err != nil { ... }
|
|
|
|
// o, err := oracle.New(iprog, nil, false)
|
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 { ... }
|
|
|
|
// 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) {
|
2013-12-13 08:04:55 -07:00
|
|
|
if mode == "what" {
|
|
|
|
// Bypass package loading, type checking, SSA construction.
|
|
|
|
return what(pos, buildContext)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-01-16 07:33:58 -07:00
|
|
|
conf := loader.Config{Build: buildContext, SourceImports: true}
|
2014-01-15 19:37:55 -07:00
|
|
|
|
|
|
|
// Determine initial packages.
|
2014-02-11 14:52:16 -07:00
|
|
|
args, err := conf.FromArgs(args, true)
|
2014-01-15 19:37:55 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(args) > 0 {
|
|
|
|
return nil, fmt.Errorf("surplus arguments: %q", args)
|
|
|
|
}
|
2013-12-13 08:04:55 -07:00
|
|
|
|
|
|
|
// For queries needing only a single typed package,
|
|
|
|
// reduce the analysis scope to that package.
|
|
|
|
if minfo.needs&(needSSA|needRetainTypeInfo) == 0 {
|
2014-01-15 19:37:55 -07:00
|
|
|
reduceScope(pos, &conf)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
// TODO(adonovan): report type errors to the user via Serial
|
|
|
|
// types, not stderr?
|
2014-01-15 19:37:55 -07:00
|
|
|
// conf.TypeChecker.Error = func(err error) {
|
2013-12-13 08:04:55 -07:00
|
|
|
// E := err.(types.Error)
|
|
|
|
// fmt.Fprintf(os.Stderr, "%s: %s\n", E.Fset.Position(E.Pos), E.Msg)
|
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
|
|
|
// }
|
2014-01-15 19:37:55 -07:00
|
|
|
|
|
|
|
// Load/parse/type-check the program.
|
|
|
|
iprog, err := conf.Load()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
o, err := newOracle(iprog, ptalog, minfo.needs, reflection)
|
2013-12-13 08:04:55 -07:00
|
|
|
if err != nil {
|
|
|
|
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
|
|
|
|
2014-02-21 08:46:02 -07:00
|
|
|
qpos, err := ParseQueryPos(iprog, pos, minfo.needs&needExactPos != 0)
|
|
|
|
if err != nil && minfo.needs&(needPos|needExactPos) != 0 {
|
|
|
|
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.
|
2014-01-15 19:37:55 -07:00
|
|
|
iprog = nil
|
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.query(minfo, qpos)
|
|
|
|
}
|
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
// reduceScope is called for one-shot queries that need only a single
|
|
|
|
// typed package. It attempts to guess the query package from pos and
|
|
|
|
// reduce the analysis scope (set of loaded packages) to just that one
|
|
|
|
// plus (the exported parts of) its dependencies. It leaves its
|
|
|
|
// arguments unchanged on failure.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): this is a real mess... but it's fast.
|
|
|
|
//
|
2014-01-16 07:33:58 -07:00
|
|
|
func reduceScope(pos string, conf *loader.Config) {
|
2013-12-13 08:04:55 -07:00
|
|
|
fqpos, err := fastQueryPos(pos)
|
|
|
|
if err != nil {
|
|
|
|
return // bad query
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(adonovan): fix: this gives the wrong results for files
|
|
|
|
// in non-importable packages such as tests and ad-hoc packages
|
|
|
|
// specified as a list of files (incl. the oracle's tests).
|
2014-01-15 19:37:55 -07:00
|
|
|
_, importPath, err := guessImportPath(fqpos.fset.File(fqpos.start).Name(), conf.Build)
|
2013-12-13 08:04:55 -07:00
|
|
|
if err != nil {
|
|
|
|
return // can't find GOPATH dir
|
|
|
|
}
|
|
|
|
if importPath == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that it's possible to load the queried package.
|
|
|
|
// (e.g. oracle tests contain different 'package' decls in same dir.)
|
2014-01-16 07:33:58 -07:00
|
|
|
// Keep consistent with logic in loader/util.go!
|
2014-01-15 19:37:55 -07:00
|
|
|
cfg2 := *conf.Build
|
|
|
|
cfg2.CgoEnabled = false
|
|
|
|
bp, err := cfg2.Import(importPath, "", 0)
|
2013-12-13 08:04:55 -07:00
|
|
|
if err != nil {
|
|
|
|
return // no files for package
|
|
|
|
}
|
|
|
|
|
2014-03-19 12:00:35 -06:00
|
|
|
// Check that the queried file appears in the package:
|
|
|
|
// it might be a '// +build ignore' from an ad-hoc main
|
2014-09-08 11:24:38 -06:00
|
|
|
// package, e.g. $GOROOT/src/net/http/triv.go.
|
2014-03-19 12:00:35 -06:00
|
|
|
if !pkgContainsFile(bp, fqpos.fset.File(fqpos.start).Name()) {
|
|
|
|
return // not found
|
|
|
|
}
|
2013-12-13 08:04:55 -07:00
|
|
|
|
2014-01-15 19:37:55 -07:00
|
|
|
conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
|
|
|
|
|
|
|
|
// Ignore packages specified on command line.
|
|
|
|
conf.CreatePkgs = nil
|
|
|
|
conf.ImportPkgs = nil
|
|
|
|
|
|
|
|
// Instead load just the one containing the query position
|
|
|
|
// (and possibly its corresponding tests/production code).
|
|
|
|
// TODO(adonovan): set 'augment' based on which file list
|
|
|
|
// contains
|
|
|
|
_ = conf.ImportWithTests(importPath) // ignore error
|
2013-12-13 08:04:55 -07:00
|
|
|
}
|
|
|
|
|
2014-03-19 12:00:35 -06:00
|
|
|
func pkgContainsFile(bp *build.Package, filename string) bool {
|
|
|
|
for _, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
|
|
|
|
for _, file := range files {
|
|
|
|
if sameFile(file, filename) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// New constructs a new Oracle that can be used for a sequence of queries.
|
|
|
|
//
|
2014-01-15 19:37:55 -07:00
|
|
|
// iprog specifies the program to analyze.
|
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
|
|
|
//
|
2014-01-16 07:33:58 -07:00
|
|
|
func New(iprog *loader.Program, ptalog io.Writer, reflection bool) (*Oracle, error) {
|
2014-01-15 19:37:55 -07:00
|
|
|
return newOracle(iprog, 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
|
|
|
}
|
|
|
|
|
2014-01-16 07:33:58 -07:00
|
|
|
func newOracle(iprog *loader.Program, ptalog io.Writer, needs int, reflection bool) (*Oracle, error) {
|
2014-01-15 19:37:55 -07:00
|
|
|
o := &Oracle{fset: iprog.Fset}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:11:42 -06:00
|
|
|
// Retain type info for all ASTs in the program.
|
2013-12-13 08:04:55 -07:00
|
|
|
if needs&needRetainTypeInfo != 0 {
|
2014-01-15 19:37:55 -07:00
|
|
|
o.typeInfo = iprog.AllPackages
|
2013-09-10 12:11:42 -06:00
|
|
|
}
|
|
|
|
|
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 {
|
2014-01-15 19:37:55 -07:00
|
|
|
var mode ssa.BuilderMode
|
|
|
|
if needs&needSSADebug != 0 {
|
|
|
|
mode |= ssa.GlobalDebug
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2014-01-15 19:37:55 -07:00
|
|
|
prog := ssa.Create(iprog, mode)
|
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.
|
2013-12-13 08:04:55 -07:00
|
|
|
var testPkgs, mains []*ssa.Package
|
2014-01-15 19:37:55 -07:00
|
|
|
for _, info := range iprog.InitialPackages() {
|
2013-12-13 08:04:55 -07:00
|
|
|
initialPkg := prog.Package(info.Pkg)
|
2013-09-06 16:13:57 -06:00
|
|
|
|
|
|
|
// Add package to the pointer analysis scope.
|
2013-10-23 16:07:53 -06:00
|
|
|
if initialPkg.Func("main") != nil {
|
2013-12-13 08:04:55 -07:00
|
|
|
mains = append(mains, initialPkg)
|
2013-10-23 16:07:53 -06:00
|
|
|
} else {
|
|
|
|
testPkgs = append(testPkgs, initialPkg)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-10-23 16:07:53 -06:00
|
|
|
}
|
|
|
|
if testPkgs != nil {
|
2013-12-13 08:04:55 -07:00
|
|
|
if p := prog.CreateTestMainPackage(testPkgs...); p != nil {
|
|
|
|
mains = append(mains, p)
|
2013-10-23 16:07:53 -06:00
|
|
|
}
|
|
|
|
}
|
2013-12-13 08:04:55 -07:00
|
|
|
if mains == nil {
|
2013-10-23 16:07:53 -06:00
|
|
|
return nil, fmt.Errorf("analysis scope has no main and no tests")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-12-13 08:04:55 -07:00
|
|
|
o.ptaConfig.Log = ptalog
|
|
|
|
o.ptaConfig.Reflection = reflection
|
|
|
|
o.ptaConfig.Mains = mains
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
o.prog = prog
|
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
|
|
|
return o, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query runs the query of the specified mode and selection.
|
2013-12-13 08:04:55 -07:00
|
|
|
//
|
|
|
|
// TODO(adonovan): fix: this function does not currently support the
|
|
|
|
// "what" query, which needs to access the go/build.Context.
|
|
|
|
//
|
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) 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) {
|
2013-12-13 08:04:55 -07:00
|
|
|
// Clear out residue of previous query (for long-running clients).
|
|
|
|
o.ptaConfig.Queries = nil
|
|
|
|
o.ptaConfig.IndirectQueries = nil
|
|
|
|
|
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
|
|
|
res := &Result{
|
2013-12-13 08:04:55 -07:00
|
|
|
mode: minfo.name,
|
|
|
|
fset: o.fset,
|
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
|
|
|
}
|
|
|
|
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.
|
2013-12-13 08:04:55 -07:00
|
|
|
// If needExact, it must identify a single AST subtree;
|
|
|
|
// this is appropriate for queries that allow fairly arbitrary syntax,
|
|
|
|
// e.g. "describe".
|
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
|
|
|
//
|
2014-01-16 07:33:58 -07:00
|
|
|
func ParseQueryPos(iprog *loader.Program, posFlag string, needExact bool) (*QueryPos, error) {
|
2013-12-13 08:04:55 -07:00
|
|
|
filename, startOffset, endOffset, err := parsePosFlag(posFlag)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-01-15 19:37:55 -07:00
|
|
|
start, end, err := findQueryPos(iprog.Fset, filename, startOffset, endOffset)
|
2013-08-27 15:58:26 -06:00
|
|
|
if err != nil {
|
2013-09-03 13:29:02 -06:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-01-15 19:37:55 -07:00
|
|
|
info, path, exact := iprog.PathEnclosingInterval(start, end)
|
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 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
|
|
|
}
|
2014-01-15 19:37:55 -07:00
|
|
|
return &QueryPos{iprog.Fset, start, end, path, exact, info}, 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{}) {
|
2013-12-13 08:04:55 -07:00
|
|
|
fprintf(out, res.fset, 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
|
|
|
o.prog.BuildAll()
|
|
|
|
}
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
// ptrAnalysis runs the pointer analysis and returns its result.
|
|
|
|
func ptrAnalysis(o *Oracle) *pointer.Result {
|
2014-02-27 12:13:52 -07:00
|
|
|
result, err := pointer.Analyze(&o.ptaConfig)
|
|
|
|
if err != nil {
|
|
|
|
panic(err) // pointer analysis internal error
|
|
|
|
}
|
|
|
|
return result
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
//
|
2013-12-13 08:04:55 -07:00
|
|
|
func fprintf(w io.Writer, fset *token.FileSet, 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))
|
|
|
|
}
|
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
if sp := fset.Position(start); start == end {
|
2013-08-27 15:58:26 -06:00
|
|
|
// (prints "-: " for token.NoPos)
|
|
|
|
fmt.Fprintf(w, "%s: ", sp)
|
|
|
|
} else {
|
2013-12-13 08:04:55 -07:00
|
|
|
ep := fset.Position(end)
|
2013-08-27 15:58:26 -06:00
|
|
|
// 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")
|
|
|
|
}
|