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"
|
|
|
|
"errors"
|
|
|
|
"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"
|
|
|
|
|
|
|
|
"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)
|
|
|
|
}
|
|
|
|
|
|
|
|
type warning struct {
|
|
|
|
pos token.Pos
|
|
|
|
format string
|
|
|
|
args []interface{}
|
|
|
|
}
|
|
|
|
|
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-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{})
|
|
|
|
q queryResult // the query-specific result
|
|
|
|
mode string // query mode
|
|
|
|
warnings []warning // pointer analysis warnings
|
|
|
|
}
|
|
|
|
|
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-03 13:29:02 -06:00
|
|
|
Pos: res.fset.Position(w.pos).String(),
|
|
|
|
Message: fmt.Sprintf(w.format, w.args...),
|
|
|
|
})
|
|
|
|
}
|
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-08-27 15:58:26 -06:00
|
|
|
// args specify the main package in importer.CreatePackageFromArgs syntax.
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// args specify the main package in importer.CreatePackageFromArgs syntax.
|
|
|
|
//
|
|
|
|
// 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-08-27 15:58:26 -06:00
|
|
|
// Create SSA package for the initial package and its 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 {
|
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 nil, o.errorf(nil, "%s", err)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-06 16:13:57 -06:00
|
|
|
// Initial packages (specified on command line)
|
|
|
|
for _, info := range initialPkgInfos {
|
|
|
|
initialPkg := o.prog.Package(info.Pkg)
|
|
|
|
|
|
|
|
// Add package to the pointer analysis scope.
|
|
|
|
if initialPkg.Func("main") == nil {
|
|
|
|
// TODO(adonovan): to simulate 'go test' more faithfully, we
|
|
|
|
// should build a single synthetic testmain package,
|
|
|
|
// not synthetic main functions to many packages.
|
|
|
|
if initialPkg.CreateTestMainFunction() == 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 nil, o.errorf(nil, "analysis scope has no main() entry points")
|
2013-09-06 16:13:57 -06:00
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-06 16:13:57 -06:00
|
|
|
o.config.Mains = append(o.config.Mains, initialPkg)
|
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
|
|
|
|
}
|
|
|
|
o.config.Warn = func(pos token.Pos, format string, args ...interface{}) {
|
|
|
|
res.warnings = append(res.warnings, warning{pos, format, args})
|
|
|
|
}
|
|
|
|
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 {
|
|
|
|
return nil, errors.New("no syntax here")
|
|
|
|
}
|
|
|
|
if needExact && !exact {
|
|
|
|
return nil, fmt.Errorf("ambiguous selection within %s", importer.NodeDescription(path[0]))
|
|
|
|
}
|
|
|
|
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 {
|
|
|
|
printf(w.pos, "warning: "+w.format, w.args...)
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ptrAnalysis runs the pointer analysis and returns the synthetic
|
|
|
|
// root of the callgraph.
|
|
|
|
//
|
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 ptrAnalysis(o *Oracle) pointer.CallGraphNode {
|
2013-08-27 15:58:26 -06:00
|
|
|
start := time.Now()
|
|
|
|
root := pointer.Analyze(&o.config)
|
|
|
|
o.timers["pointer analysis"] = time.Since(start)
|
|
|
|
return root
|
|
|
|
}
|
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
|
|
|
// errorf is like fprintf, but returns a formatted error string.
|
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) errorf(pos interface{}, format string, args ...interface{}) error {
|
2013-08-27 15:58:26 -06:00
|
|
|
var buf bytes.Buffer
|
|
|
|
o.fprintf(&buf, pos, format, args...)
|
|
|
|
return errors.New(buf.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
}
|