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.
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
package oracle
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
2013-09-03 13:29:02 -06:00
|
|
|
"os"
|
2013-08-27 15:58:26 -06:00
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
"code.google.com/p/go.tools/go/exact"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
"code.google.com/p/go.tools/importer"
|
2013-09-24 13:08:14 -06:00
|
|
|
"code.google.com/p/go.tools/oracle/serial"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/pointer"
|
|
|
|
"code.google.com/p/go.tools/ssa"
|
|
|
|
)
|
|
|
|
|
|
|
|
// describe describes the syntax node denoted by the query position,
|
|
|
|
// including:
|
|
|
|
// - its syntactic category
|
|
|
|
// - the location of the definition of its referent (for identifiers)
|
|
|
|
// - its type and method set (for an expression or type expression)
|
|
|
|
// - its points-to set (for a pointer-like expression)
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// - its dynamic types (for an interface, reflect.Value, or
|
|
|
|
// reflect.Type expression) and their points-to sets.
|
2013-08-27 15:58:26 -06:00
|
|
|
//
|
2013-09-03 13:29:02 -06:00
|
|
|
// All printed sets are sorted to ensure determinism.
|
|
|
|
//
|
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 describe(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
if false { // debugging
|
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.fprintf(os.Stderr, qpos.path[0], "you selected: %s %s",
|
|
|
|
importer.NodeDescription(qpos.path[0]), pathToString2(qpos.path))
|
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
|
|
|
path, action := findInterestingNode(qpos.info, qpos.path)
|
2013-08-27 15:58:26 -06:00
|
|
|
switch action {
|
|
|
|
case actionExpr:
|
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 describeValue(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionType:
|
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 describeType(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionPackage:
|
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 describePackage(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionStmt:
|
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 describeStmt(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionUnknown:
|
|
|
|
return &describeUnknownResult{path[0]}, nil
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic(action) // unreachable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type describeUnknownResult struct {
|
|
|
|
node ast.Node
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeUnknownResult) display(printf printfFunc) {
|
2013-08-27 15:58:26 -06:00
|
|
|
// Nothing much to say about misc syntax.
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.node, "%s", importer.NodeDescription(r.node))
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: importer.NodeDescription(r.node),
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type action int
|
|
|
|
|
|
|
|
const (
|
|
|
|
actionUnknown action = iota // None of the below
|
|
|
|
actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var})
|
|
|
|
actionType // type Expr or Ident(types.TypeName).
|
|
|
|
actionStmt // Stmt or Ident(types.Label)
|
|
|
|
actionPackage // Ident(types.Package) or ImportSpec
|
|
|
|
)
|
|
|
|
|
|
|
|
// findInterestingNode classifies the syntax node denoted by path as one of:
|
|
|
|
// - an expression, part of an expression or a reference to a constant
|
|
|
|
// or variable;
|
|
|
|
// - a type, part of a type, or a reference to a named type;
|
|
|
|
// - a statement, part of a statement, or a label referring to a statement;
|
|
|
|
// - part of a package declaration or import spec.
|
|
|
|
// - none of the above.
|
|
|
|
// and returns the most "interesting" associated node, which may be
|
|
|
|
// the same node, an ancestor or a descendent.
|
|
|
|
//
|
|
|
|
func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast.Node, action) {
|
|
|
|
// TODO(adonovan): integrate with go/types/stdlib_test.go and
|
|
|
|
// apply this to every AST node we can find to make sure it
|
|
|
|
// doesn't crash.
|
|
|
|
|
|
|
|
// TODO(adonovan): audit for ParenExpr safety, esp. since we
|
|
|
|
// traverse up and down.
|
|
|
|
|
|
|
|
// TODO(adonovan): if the users selects the "." in
|
|
|
|
// "fmt.Fprintf()", they'll get an ambiguous selection error;
|
|
|
|
// we won't even reach here. Can we do better?
|
|
|
|
|
|
|
|
// TODO(adonovan): describing a field within 'type T struct {...}'
|
2013-09-03 13:29:02 -06:00
|
|
|
// describes the (anonymous) struct type and concludes "no methods".
|
|
|
|
// We should ascend to the enclosing type decl, if any.
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
for len(path) > 0 {
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.GenDecl:
|
|
|
|
if len(n.Specs) == 1 {
|
|
|
|
// Descend to sole {Import,Type,Value}Spec child.
|
|
|
|
path = append([]ast.Node{n.Specs[0]}, path...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return path, actionUnknown // uninteresting
|
|
|
|
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
// Descend to function name.
|
|
|
|
path = append([]ast.Node{n.Name}, path...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case *ast.ImportSpec:
|
|
|
|
return path, actionPackage
|
|
|
|
|
|
|
|
case *ast.ValueSpec:
|
|
|
|
if len(n.Names) == 1 {
|
|
|
|
// Descend to sole Ident child.
|
|
|
|
path = append([]ast.Node{n.Names[0]}, path...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return path, actionUnknown // uninteresting
|
|
|
|
|
|
|
|
case *ast.TypeSpec:
|
|
|
|
// Descend to type name.
|
|
|
|
path = append([]ast.Node{n.Name}, path...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case ast.Stmt:
|
|
|
|
return path, actionStmt
|
|
|
|
|
|
|
|
case *ast.ArrayType,
|
|
|
|
*ast.StructType,
|
|
|
|
*ast.FuncType,
|
|
|
|
*ast.InterfaceType,
|
|
|
|
*ast.MapType,
|
|
|
|
*ast.ChanType:
|
|
|
|
return path, actionType
|
|
|
|
|
|
|
|
case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
|
|
|
|
return path, actionUnknown // uninteresting
|
|
|
|
|
|
|
|
case *ast.Ellipsis:
|
|
|
|
// Continue to enclosing node.
|
|
|
|
// e.g. [...]T in ArrayType
|
|
|
|
// f(x...) in CallExpr
|
|
|
|
// f(x...T) in FuncType
|
|
|
|
|
|
|
|
case *ast.Field:
|
|
|
|
// TODO(adonovan): this needs more thought,
|
|
|
|
// since fields can be so many things.
|
|
|
|
if len(n.Names) == 1 {
|
|
|
|
// Descend to sole Ident child.
|
|
|
|
path = append([]ast.Node{n.Names[0]}, path...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Zero names (e.g. anon field in struct)
|
|
|
|
// or multiple field or param names:
|
|
|
|
// continue to enclosing field list.
|
|
|
|
|
|
|
|
case *ast.FieldList:
|
|
|
|
// Continue to enclosing node:
|
|
|
|
// {Struct,Func,Interface}Type or FuncDecl.
|
|
|
|
|
|
|
|
case *ast.BasicLit:
|
|
|
|
if _, ok := path[1].(*ast.ImportSpec); ok {
|
|
|
|
return path[1:], actionPackage
|
|
|
|
}
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
if pkginfo.ObjectOf(n.Sel) == nil {
|
|
|
|
// Is this reachable?
|
|
|
|
return path, actionUnknown
|
|
|
|
}
|
|
|
|
// Descend to .Sel child.
|
|
|
|
path = append([]ast.Node{n.Sel}, path...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case *ast.Ident:
|
|
|
|
switch obj := pkginfo.ObjectOf(n).(type) {
|
2013-09-13 10:52:57 -06:00
|
|
|
case *types.PkgName:
|
2013-08-27 15:58:26 -06:00
|
|
|
return path, actionPackage
|
|
|
|
|
|
|
|
case *types.Const:
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case *types.Label:
|
|
|
|
return path, actionStmt
|
|
|
|
|
|
|
|
case *types.TypeName:
|
|
|
|
return path, actionType
|
|
|
|
|
|
|
|
case *types.Var:
|
|
|
|
// For x in 'struct {x T}', return struct type, for now.
|
|
|
|
if _, ok := path[1].(*ast.Field); ok {
|
|
|
|
_ = path[2].(*ast.FieldList) // assertion
|
|
|
|
if _, ok := path[3].(*ast.StructType); ok {
|
|
|
|
return path[3:], actionType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case *types.Func:
|
|
|
|
// For f in 'interface {f()}', return the interface type, for now.
|
|
|
|
if _, ok := path[1].(*ast.Field); ok {
|
|
|
|
_ = path[2].(*ast.FieldList) // assertion
|
|
|
|
if _, ok := path[3].(*ast.InterfaceType); ok {
|
|
|
|
return path[3:], actionType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// For reference to built-in function, return enclosing call.
|
|
|
|
if _, ok := obj.Type().(*types.Builtin); ok {
|
|
|
|
// Ascend to enclosing function call.
|
|
|
|
path = path[1:]
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return path, actionExpr
|
|
|
|
}
|
|
|
|
|
|
|
|
// No object.
|
|
|
|
switch path[1].(type) {
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
// Return enclosing selector expression.
|
|
|
|
return path[1:], actionExpr
|
|
|
|
|
|
|
|
case *ast.Field:
|
|
|
|
// TODO(adonovan): test this.
|
|
|
|
// e.g. all f in:
|
|
|
|
// struct { f, g int }
|
|
|
|
// interface { f() }
|
|
|
|
// func (f T) method(f, g int) (f, g bool)
|
|
|
|
//
|
|
|
|
// switch path[3].(type) {
|
|
|
|
// case *ast.FuncDecl:
|
|
|
|
// case *ast.StructType:
|
|
|
|
// case *ast.InterfaceType:
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return path[1:], actionExpr
|
|
|
|
//
|
|
|
|
// Unclear what to do with these.
|
|
|
|
// Struct.Fields -- field
|
|
|
|
// Interface.Methods -- field
|
|
|
|
// FuncType.{Params.Results} -- actionExpr
|
|
|
|
// FuncDecl.Recv -- actionExpr
|
|
|
|
|
2013-09-13 10:52:57 -06:00
|
|
|
case *ast.File:
|
|
|
|
// 'package foo'
|
|
|
|
return path, actionPackage
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
case *ast.ImportSpec:
|
|
|
|
// TODO(adonovan): fix: why no package object? go/types bug?
|
|
|
|
return path[1:], actionPackage
|
|
|
|
|
|
|
|
default:
|
|
|
|
// e.g. blank identifier (go/types bug?)
|
|
|
|
// or y in "switch y := x.(type)" (go/types bug?)
|
|
|
|
fmt.Printf("unknown reference %s in %T\n", n, path[1])
|
|
|
|
return path, actionUnknown
|
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.StarExpr:
|
|
|
|
if pkginfo.IsType(n) {
|
|
|
|
return path, actionType
|
|
|
|
}
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case ast.Expr:
|
|
|
|
// All Expr but {BasicLit,Ident,StarExpr} are
|
|
|
|
// "true" expressions that evaluate to a value.
|
|
|
|
return path, actionExpr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ascend to parent.
|
|
|
|
path = path[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, actionUnknown // unreachable
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---- VALUE ------------------------------------------------------------
|
|
|
|
|
|
|
|
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
// to the root of the AST is path. isAddr reports whether the
|
|
|
|
// ssa.Value is the address denoted by the ast.Ident, not its value.
|
|
|
|
// ssaValueForIdent may return a nil Value without an error to
|
|
|
|
// indicate the pointer analysis is not appropriate.
|
2013-08-27 15:58:26 -06:00
|
|
|
//
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
func ssaValueForIdent(prog *ssa.Program, qinfo *importer.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
if obj, ok := obj.(*types.Var); ok {
|
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
|
|
|
pkg := prog.Package(qinfo.Pkg)
|
2013-08-27 15:58:26 -06:00
|
|
|
pkg.Build()
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
if v, addr := prog.VarValue(obj, pkg, path); v != nil {
|
2013-08-27 15:58:26 -06:00
|
|
|
// Don't run pointer analysis on a ref to a const expression.
|
|
|
|
if _, ok := v.(*ssa.Const); ok {
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
return
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
return v, addr, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name())
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Don't run pointer analysis on const/func objects.
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
return
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
|
|
|
|
// expression whose path to the root of the AST is path. It may
|
|
|
|
// return a nil Value without an error to indicate the pointer
|
|
|
|
// analysis is not appropriate.
|
|
|
|
//
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
func ssaValueForExpr(prog *ssa.Program, qinfo *importer.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err 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
|
|
|
pkg := prog.Package(qinfo.Pkg)
|
2013-08-27 15:58:26 -06:00
|
|
|
pkg.SetDebugMode(true)
|
|
|
|
pkg.Build()
|
|
|
|
|
|
|
|
fn := ssa.EnclosingFunction(pkg, path)
|
|
|
|
if fn == nil {
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
|
|
|
|
return v, addr, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn)
|
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 describeValue(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeValueResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var expr ast.Expr
|
2013-09-09 19:06:25 -06:00
|
|
|
var obj types.Object
|
2013-08-27 15:58:26 -06:00
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.ValueSpec:
|
|
|
|
// ambiguous ValueSpec containing multiple names
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("multiple value specification")
|
2013-09-09 19:06:25 -06:00
|
|
|
case *ast.Ident:
|
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
|
|
|
obj = qpos.info.ObjectOf(n)
|
2013-09-09 19:06:25 -06:00
|
|
|
expr = n
|
2013-08-27 15:58:26 -06:00
|
|
|
case ast.Expr:
|
|
|
|
expr = n
|
|
|
|
default:
|
|
|
|
// Is this reachable?
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("unexpected AST for expr: %T", n)
|
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
|
|
|
typ := qpos.info.TypeOf(expr)
|
|
|
|
constVal := qpos.info.ValueOf(expr)
|
2013-09-09 19:06:25 -06:00
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// From this point on, we cannot fail with an error.
|
|
|
|
// Failure to run the pointer analysis will be reported later.
|
2013-09-03 13:29:02 -06:00
|
|
|
//
|
|
|
|
// Our disposition to pointer analysis may be one of the following:
|
|
|
|
// - ok: ssa.Value was const or func.
|
|
|
|
// - error: no ssa.Value for expr (e.g. trivially dead code)
|
|
|
|
// - ok: ssa.Value is non-pointerlike
|
|
|
|
// - error: no Pointer for ssa.Value (e.g. analytically unreachable)
|
|
|
|
// - ok: Pointer has empty points-to set
|
|
|
|
// - ok: Pointer has non-empty points-to set
|
|
|
|
// ptaErr is non-nil only in the "error:" cases.
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
var ptaErr error
|
2013-09-03 13:29:02 -06:00
|
|
|
var ptrs []pointerResult
|
|
|
|
|
2013-09-09 19:06:25 -06:00
|
|
|
// Only run pointer analysis on pointerlike expression types.
|
|
|
|
if pointer.CanPoint(typ) {
|
|
|
|
// Determine the ssa.Value for the expression.
|
|
|
|
var value ssa.Value
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
var isAddr bool
|
2013-09-09 19:06:25 -06:00
|
|
|
if obj != nil {
|
|
|
|
// def/ref of func/var/const object
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
value, isAddr, ptaErr = ssaValueForIdent(o.prog, qpos.info, obj, path)
|
2013-09-03 13:29:02 -06:00
|
|
|
} else {
|
2013-09-09 19:06:25 -06:00
|
|
|
// any other expression
|
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 qpos.info.ValueOf(path[0].(ast.Expr)) == nil { // non-constant?
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
value, isAddr, ptaErr = ssaValueForExpr(o.prog, qpos.info, path)
|
2013-09-09 19:06:25 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
2013-09-09 19:06:25 -06:00
|
|
|
if value != nil {
|
go.tools/ssa: record lvalue/rvalue distinction precisely in DebugRef.
A DebugRef associates a source expression E with an ssa.Value
V, but until now did not record whether V was the value or the
address of E. So, we would guess from the "pointerness" of
the Value, leading to confusion in some cases, e.g.
type N *N
var n N
n = &n // lvalue and rvalue are both pointers
Now we explicitly record 'IsAddress bool' in DebugRef, and
plumb this everywhere: through (*Function).ValueForExpr and
(*Program).VarValue, all the way to forming the pointer
analysis query.
Also:
- VarValue now treats each reference to a global distinctly,
just like it does for other vars. So:
var g int
func f() {
g = 1 // VarValue(g) == Const(1:int), !isAddress
print(g) // VarValue(g) == Global(g), isAddress
}
- DebugRefs are not emitted for references to predeclared
identifiers (nil, built-in).
- DebugRefs no longer prevent lifting of an Alloc var into a
register; now we update or discard the debug info.
- TestValueForExpr: improve coverage of ssa.EnclosingFunction
by putting expectations in methods and init funcs, not just
normal funcs.
- oracle: fix golden file broken by recent
(*types.Var).IsField change.
R=gri
CC=golang-dev
https://golang.org/cl/16610045
2013-10-24 16:31:50 -06:00
|
|
|
ptrs, ptaErr = describePointer(o, value, isAddr)
|
2013-09-09 19:06:25 -06:00
|
|
|
}
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
return &describeValueResult{
|
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
|
|
|
qpos: qpos,
|
2013-09-03 13:29:02 -06:00
|
|
|
expr: expr,
|
|
|
|
typ: typ,
|
|
|
|
constVal: constVal,
|
|
|
|
obj: obj,
|
|
|
|
ptaErr: ptaErr,
|
|
|
|
ptrs: ptrs,
|
2013-08-27 15:58:26 -06:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2013-09-09 19:06:25 -06:00
|
|
|
// describePointer runs the pointer analysis of the selected SSA value.
|
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 describePointer(o *Oracle, v ssa.Value, indirect bool) (ptrs []pointerResult, err error) {
|
2013-09-09 19:06:25 -06:00
|
|
|
buildSSA(o)
|
|
|
|
|
|
|
|
// TODO(adonovan): don't run indirect pointer analysis on non-ptr-ptrlike types.
|
2013-09-25 15:17:42 -06:00
|
|
|
o.config.Queries = map[ssa.Value]pointer.Indirect{v: pointer.Indirect(indirect)}
|
|
|
|
ptares := ptrAnalysis(o)
|
2013-09-09 19:06:25 -06:00
|
|
|
|
|
|
|
// Combine the PT sets from all contexts.
|
2013-09-25 15:17:42 -06:00
|
|
|
pointers := ptares.Queries[v]
|
2013-09-09 19:06:25 -06:00
|
|
|
if pointers == nil {
|
|
|
|
return nil, fmt.Errorf("PTA did not encounter this expression (dead code?)")
|
|
|
|
}
|
|
|
|
pts := pointer.PointsToCombined(pointers)
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
if pointer.CanHaveDynamicTypes(v.Type()) {
|
|
|
|
// Show concrete types for interface/reflect.Value expression.
|
|
|
|
if concs := pts.DynamicTypes(); concs.Len() > 0 {
|
2013-09-09 19:06:25 -06:00
|
|
|
concs.Iterate(func(conc types.Type, pta interface{}) {
|
|
|
|
combined := pointer.PointsToCombined(pta.([]pointer.Pointer))
|
|
|
|
labels := combined.Labels()
|
|
|
|
sort.Sort(byPosAndString(labels)) // to ensure determinism
|
|
|
|
ptrs = append(ptrs, pointerResult{conc, labels})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Show labels for other expressions.
|
|
|
|
labels := pts.Labels()
|
|
|
|
sort.Sort(byPosAndString(labels)) // to ensure determinism
|
|
|
|
ptrs = append(ptrs, pointerResult{v.Type(), labels})
|
|
|
|
}
|
|
|
|
sort.Sort(byTypeString(ptrs)) // to ensure determinism
|
|
|
|
return ptrs, nil
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
type pointerResult struct {
|
|
|
|
typ types.Type // type of the pointer (always concrete)
|
|
|
|
labels []*pointer.Label
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
type describeValueResult 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
|
|
|
qpos *QueryPos
|
2013-09-03 13:29:02 -06:00
|
|
|
expr ast.Expr // query node
|
|
|
|
typ types.Type // type of expression
|
|
|
|
constVal exact.Value // value of expression, if constant
|
|
|
|
obj types.Object // var/func/const object, if expr was Ident
|
|
|
|
ptaErr error // reason why pointer analysis couldn't be run, or failed
|
|
|
|
ptrs []pointerResult // pointer info (typ is concrete => len==1)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeValueResult) display(printf printfFunc) {
|
2013-09-04 14:15:41 -06:00
|
|
|
var prefix, suffix string
|
2013-09-03 13:29:02 -06:00
|
|
|
if r.constVal != nil {
|
|
|
|
suffix = fmt.Sprintf(" of constant value %s", r.constVal)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-04 14:15:41 -06:00
|
|
|
switch obj := r.obj.(type) {
|
|
|
|
case *types.Func:
|
|
|
|
if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
|
|
|
|
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
|
|
|
|
prefix = "interface method "
|
|
|
|
} else {
|
|
|
|
prefix = "method "
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
// Describe the expression.
|
|
|
|
if r.obj != nil {
|
|
|
|
if r.obj.Pos() == r.expr.Pos() {
|
|
|
|
// defining ident
|
2013-09-04 14:15:41 -06:00
|
|
|
printf(r.expr, "definition of %s%s%s", prefix, r.obj, suffix)
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
// referring ident
|
2013-09-04 14:15:41 -06:00
|
|
|
printf(r.expr, "reference to %s%s%s", prefix, r.obj, suffix)
|
2013-08-27 15:58:26 -06:00
|
|
|
if def := r.obj.Pos(); def != token.NoPos {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(def, "defined here")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
desc := importer.NodeDescription(r.expr)
|
|
|
|
if suffix != "" {
|
|
|
|
// constant expression
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.expr, "%s%s", desc, suffix)
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
// non-constant expression
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.expr, "%s of type %s", desc, r.typ)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
// pointer analysis could not be run
|
|
|
|
if r.ptaErr != nil {
|
|
|
|
printf(r.expr, "no points-to information: %s", r.ptaErr)
|
2013-08-27 15:58:26 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.ptrs == nil {
|
2013-09-03 13:29:02 -06:00
|
|
|
return // PTA was not invoked (not an error)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Display the results of pointer analysis.
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
if pointer.CanHaveDynamicTypes(r.typ) {
|
|
|
|
// Show concrete types for interface, reflect.Type or
|
|
|
|
// reflect.Value expression.
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
if len(r.ptrs) > 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
|
|
|
printf(r.qpos, "this %s may contain these dynamic types:", r.typ)
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, ptr := range r.ptrs {
|
2013-08-27 15:58:26 -06:00
|
|
|
var obj types.Object
|
2013-09-03 13:29:02 -06:00
|
|
|
if nt, ok := deref(ptr.typ).(*types.Named); ok {
|
2013-08-27 15:58:26 -06:00
|
|
|
obj = nt.Obj()
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
if len(ptr.labels) > 0 {
|
|
|
|
printf(obj, "\t%s, may point to:", ptr.typ)
|
|
|
|
printLabels(printf, ptr.labels, "\t\t")
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(obj, "\t%s", ptr.typ)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
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
|
|
|
printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Show labels for other expressions.
|
2013-09-03 13:29:02 -06:00
|
|
|
if ptr := r.ptrs[0]; len(ptr.labels) > 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
|
|
|
printf(r.qpos, "value may point to these labels:")
|
2013-09-03 13:29:02 -06:00
|
|
|
printLabels(printf, ptr.labels, "\t")
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
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
|
|
|
printf(r.qpos, "value cannot point to anything.")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
2013-09-03 13:29:02 -06:00
|
|
|
var value, objpos, ptaerr string
|
|
|
|
if r.constVal != nil {
|
|
|
|
value = r.constVal.String()
|
|
|
|
}
|
|
|
|
if r.obj != nil {
|
|
|
|
objpos = fset.Position(r.obj.Pos()).String()
|
|
|
|
}
|
|
|
|
if r.ptaErr != nil {
|
|
|
|
ptaerr = r.ptaErr.Error()
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
var pts []*serial.DescribePointer
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, ptr := range r.ptrs {
|
|
|
|
var namePos string
|
|
|
|
if nt, ok := deref(ptr.typ).(*types.Named); ok {
|
|
|
|
namePos = fset.Position(nt.Obj().Pos()).String()
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
var labels []serial.DescribePTALabel
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, l := range ptr.labels {
|
2013-09-24 13:08:14 -06:00
|
|
|
labels = append(labels, serial.DescribePTALabel{
|
2013-09-03 13:29:02 -06:00
|
|
|
Pos: fset.Position(l.Pos()).String(),
|
|
|
|
Desc: l.String(),
|
|
|
|
})
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
pts = append(pts, &serial.DescribePointer{
|
2013-09-03 13:29:02 -06:00
|
|
|
Type: ptr.typ.String(),
|
|
|
|
NamePos: namePos,
|
|
|
|
Labels: labels,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: importer.NodeDescription(r.expr),
|
|
|
|
Pos: fset.Position(r.expr.Pos()).String(),
|
|
|
|
Detail: "value",
|
2013-09-24 13:08:14 -06:00
|
|
|
Value: &serial.DescribeValue{
|
2013-09-03 13:29:02 -06:00
|
|
|
Type: r.typ.String(),
|
|
|
|
Value: value,
|
|
|
|
ObjPos: objpos,
|
|
|
|
PTAErr: ptaerr,
|
|
|
|
PTS: pts,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type byTypeString []pointerResult
|
|
|
|
|
|
|
|
func (a byTypeString) Len() int { return len(a) }
|
|
|
|
func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() }
|
|
|
|
func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
type byPosAndString []*pointer.Label
|
|
|
|
|
|
|
|
func (a byPosAndString) Len() int { return len(a) }
|
|
|
|
func (a byPosAndString) Less(i, j int) bool {
|
|
|
|
cmp := a[i].Pos() - a[j].Pos()
|
|
|
|
return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
|
|
|
|
}
|
|
|
|
func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) {
|
2013-08-27 15:58:26 -06:00
|
|
|
// TODO(adonovan): due to context-sensitivity, many of these
|
|
|
|
// labels may differ only by context, which isn't apparent.
|
|
|
|
for _, label := range labels {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(label, "%s%s", prefix, label)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---- TYPE ------------------------------------------------------------
|
|
|
|
|
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 describeType(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeTypeResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var description string
|
|
|
|
var t types.Type
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.Ident:
|
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
|
|
|
t = qpos.info.TypeOf(n)
|
2013-08-27 15:58:26 -06:00
|
|
|
switch t := t.(type) {
|
|
|
|
case *types.Basic:
|
|
|
|
description = "reference to built-in type " + t.String()
|
|
|
|
|
|
|
|
case *types.Named:
|
|
|
|
isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above
|
|
|
|
if isDef {
|
|
|
|
description = "definition of type " + t.String()
|
|
|
|
} else {
|
|
|
|
description = "reference to type " + t.String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case ast.Expr:
|
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
|
|
|
t = qpos.info.TypeOf(n)
|
2013-08-27 15:58:26 -06:00
|
|
|
description = "type " + t.String()
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Unreachable?
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("unexpected AST for type: %T", n)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
return &describeTypeResult{
|
|
|
|
node: path[0],
|
|
|
|
description: description,
|
|
|
|
typ: t,
|
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
|
|
|
methods: accessibleMethods(t, qpos.info.Pkg),
|
2013-09-03 13:29:02 -06:00
|
|
|
}, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type describeTypeResult struct {
|
|
|
|
node ast.Node
|
|
|
|
description string
|
|
|
|
typ types.Type
|
2013-09-03 13:29:02 -06:00
|
|
|
methods []*types.Selection
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeTypeResult) display(printf printfFunc) {
|
|
|
|
printf(r.node, "%s", r.description)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
// Show the underlying type for a reference to a named type.
|
|
|
|
if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(nt.Obj(), "defined as %s", nt.Underlying())
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print the method set, if the type kind is capable of bearing methods.
|
|
|
|
switch r.typ.(type) {
|
|
|
|
case *types.Interface, *types.Struct, *types.Named:
|
2013-09-03 13:29:02 -06:00
|
|
|
if len(r.methods) > 0 {
|
|
|
|
printf(r.node, "Method set:")
|
|
|
|
for _, meth := range r.methods {
|
|
|
|
printf(meth.Obj(), "\t%s", meth)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
} else {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.node, "No methods.")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
2013-09-03 13:29:02 -06:00
|
|
|
var namePos, nameDef string
|
|
|
|
if nt, ok := r.typ.(*types.Named); ok {
|
|
|
|
namePos = fset.Position(nt.Obj().Pos()).String()
|
|
|
|
nameDef = nt.Underlying().String()
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: r.description,
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
Detail: "type",
|
2013-09-24 13:08:14 -06:00
|
|
|
Type: &serial.DescribeType{
|
2013-09-03 13:29:02 -06:00
|
|
|
Type: r.typ.String(),
|
|
|
|
NamePos: namePos,
|
|
|
|
NameDef: nameDef,
|
2013-09-24 13:08:14 -06:00
|
|
|
Methods: methodsToSerial(r.methods, fset),
|
2013-09-03 13:29:02 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// ---- PACKAGE ------------------------------------------------------------
|
|
|
|
|
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 describePackage(o *Oracle, qpos *QueryPos, path []ast.Node) (*describePackageResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var description string
|
2013-09-10 12:19:11 -06:00
|
|
|
var pkg *types.Package
|
2013-08-27 15:58:26 -06:00
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.ImportSpec:
|
2013-09-06 16:13:57 -06:00
|
|
|
// Most ImportSpecs have no .Name Ident so we can't
|
|
|
|
// use ObjectOf.
|
|
|
|
// We could use the types.Info.Implicits mechanism,
|
|
|
|
// but it's easier just to look it up by name.
|
2013-08-27 15:58:26 -06:00
|
|
|
description = "import of package " + n.Path.Value
|
2013-09-10 12:19:11 -06:00
|
|
|
importPath, _ := strconv.Unquote(n.Path.Value)
|
|
|
|
pkg = o.prog.ImportedPackage(importPath).Object
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case *ast.Ident:
|
|
|
|
if _, isDef := path[1].(*ast.File); isDef {
|
2013-09-06 16:13:57 -06:00
|
|
|
// e.g. package id
|
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
|
|
|
pkg = qpos.info.Pkg
|
2013-09-10 12:19:11 -06:00
|
|
|
description = fmt.Sprintf("definition of package %q", pkg.Path())
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
2013-09-06 16:13:57 -06:00
|
|
|
// e.g. import id
|
|
|
|
// or id.F()
|
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
|
|
|
pkg = qpos.info.ObjectOf(n).Pkg()
|
2013-09-10 12:19:11 -06:00
|
|
|
description = fmt.Sprintf("reference to package %q", pkg.Path())
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Unreachable?
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("unexpected AST for package: %T", n)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
var members []*describeMember
|
2013-09-10 12:19:11 -06:00
|
|
|
// NB: "unsafe" has no types.Package
|
2013-09-06 16:13:57 -06:00
|
|
|
if pkg != nil {
|
2013-09-10 12:19:11 -06:00
|
|
|
// Enumerate the accessible package members
|
|
|
|
// in lexicographic order.
|
|
|
|
for _, name := range pkg.Scope().Names() {
|
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 pkg == qpos.info.Pkg || ast.IsExported(name) {
|
2013-09-10 12:19:11 -06:00
|
|
|
mem := pkg.Scope().Lookup(name)
|
|
|
|
var methods []*types.Selection
|
|
|
|
if mem, ok := mem.(*types.TypeName); ok {
|
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
|
|
|
methods = accessibleMethods(mem.Type(), qpos.info.Pkg)
|
2013-09-10 12:19:11 -06:00
|
|
|
}
|
|
|
|
members = append(members, &describeMember{
|
|
|
|
mem,
|
|
|
|
methods,
|
|
|
|
})
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
return &describePackageResult{o.prog.Fset, path[0], description, pkg, members}, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type describePackageResult struct {
|
2013-09-03 13:29:02 -06:00
|
|
|
fset *token.FileSet
|
2013-08-27 15:58:26 -06:00
|
|
|
node ast.Node
|
|
|
|
description string
|
2013-09-10 12:19:11 -06:00
|
|
|
pkg *types.Package
|
2013-09-03 13:29:02 -06:00
|
|
|
members []*describeMember // in lexicographic name order
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
type describeMember struct {
|
2013-09-10 12:19:11 -06:00
|
|
|
obj types.Object
|
2013-09-03 13:29:02 -06:00
|
|
|
methods []*types.Selection // in types.MethodSet order
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describePackageResult) display(printf printfFunc) {
|
|
|
|
printf(r.node, "%s", r.description)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
// Compute max width of name "column".
|
|
|
|
maxname := 0
|
|
|
|
for _, mem := range r.members {
|
2013-09-10 12:19:11 -06:00
|
|
|
if l := len(mem.obj.Name()); l > maxname {
|
2013-09-03 13:29:02 -06:00
|
|
|
maxname = l
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, mem := range r.members {
|
2013-09-10 12:19:11 -06:00
|
|
|
printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, meth := range mem.methods {
|
|
|
|
printf(meth.Obj(), "\t\t%s", meth)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
func formatMember(obj types.Object, maxname int) string {
|
2013-08-27 15:58:26 -06:00
|
|
|
var buf bytes.Buffer
|
2013-09-10 12:19:11 -06:00
|
|
|
fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
|
|
|
|
switch obj := obj.(type) {
|
|
|
|
case *types.Const:
|
|
|
|
fmt.Fprintf(&buf, " %s = %s", obj.Type(), obj.Val().String())
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
case *types.Func:
|
|
|
|
fmt.Fprintf(&buf, " %s", obj.Type())
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
case *types.TypeName:
|
2013-08-27 15:58:26 -06:00
|
|
|
// Abbreviate long aggregate type names.
|
|
|
|
var abbrev string
|
2013-09-10 12:19:11 -06:00
|
|
|
switch t := obj.Type().Underlying().(type) {
|
2013-08-27 15:58:26 -06:00
|
|
|
case *types.Interface:
|
|
|
|
if t.NumMethods() > 1 {
|
|
|
|
abbrev = "interface{...}"
|
|
|
|
}
|
|
|
|
case *types.Struct:
|
|
|
|
if t.NumFields() > 1 {
|
|
|
|
abbrev = "struct{...}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if abbrev == "" {
|
2013-09-10 12:19:11 -06:00
|
|
|
fmt.Fprintf(&buf, " %s", obj.Type().Underlying())
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(&buf, " %s", abbrev)
|
|
|
|
}
|
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
case *types.Var:
|
|
|
|
fmt.Fprintf(&buf, " %s", obj.Type())
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
|
|
|
var members []*serial.DescribeMember
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, mem := range r.members {
|
2013-09-10 12:19:11 -06:00
|
|
|
typ := mem.obj.Type()
|
2013-09-03 13:29:02 -06:00
|
|
|
var val string
|
2013-09-10 12:19:11 -06:00
|
|
|
switch mem := mem.obj.(type) {
|
|
|
|
case *types.Const:
|
|
|
|
val = mem.Val().String()
|
|
|
|
case *types.TypeName:
|
2013-09-03 13:29:02 -06:00
|
|
|
typ = typ.Underlying()
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
members = append(members, &serial.DescribeMember{
|
2013-09-10 12:19:11 -06:00
|
|
|
Name: mem.obj.Name(),
|
2013-09-03 13:29:02 -06:00
|
|
|
Type: typ.String(),
|
|
|
|
Value: val,
|
2013-09-10 12:19:11 -06:00
|
|
|
Pos: fset.Position(mem.obj.Pos()).String(),
|
|
|
|
Kind: tokenOf(mem.obj),
|
2013-09-24 13:08:14 -06:00
|
|
|
Methods: methodsToSerial(mem.methods, fset),
|
2013-09-03 13:29:02 -06:00
|
|
|
})
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: r.description,
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
Detail: "package",
|
2013-09-24 13:08:14 -06:00
|
|
|
Package: &serial.DescribePackage{
|
2013-09-10 12:19:11 -06:00
|
|
|
Path: r.pkg.Path(),
|
2013-09-03 13:29:02 -06:00
|
|
|
Members: members,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
func tokenOf(o types.Object) string {
|
|
|
|
switch o.(type) {
|
|
|
|
case *types.Func:
|
|
|
|
return "func"
|
|
|
|
case *types.Var:
|
|
|
|
return "var"
|
|
|
|
case *types.TypeName:
|
|
|
|
return "type"
|
|
|
|
case *types.Const:
|
|
|
|
return "const"
|
2013-09-13 10:52:57 -06:00
|
|
|
case *types.PkgName:
|
2013-09-10 12:19:11 -06:00
|
|
|
return "package"
|
|
|
|
}
|
|
|
|
panic(o)
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// ---- STATEMENT ------------------------------------------------------------
|
|
|
|
|
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 describeStmt(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeStmtResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var description string
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.Ident:
|
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 qpos.info.ObjectOf(n).Pos() == n.Pos() {
|
2013-08-27 15:58:26 -06:00
|
|
|
description = "labelled statement"
|
|
|
|
} else {
|
|
|
|
description = "reference to labelled statement"
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Nothing much to say about statements.
|
|
|
|
description = importer.NodeDescription(n)
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
return &describeStmtResult{o.prog.Fset, path[0], description}, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type describeStmtResult struct {
|
2013-09-03 13:29:02 -06:00
|
|
|
fset *token.FileSet
|
2013-08-27 15:58:26 -06:00
|
|
|
node ast.Node
|
|
|
|
description string
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeStmtResult) display(printf printfFunc) {
|
|
|
|
printf(r.node, "%s", r.description)
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: r.description,
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
Detail: "unknown",
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------- Utilities -------------------
|
|
|
|
|
|
|
|
// pathToString returns a string containing the concrete types of the
|
|
|
|
// nodes in path.
|
|
|
|
func pathToString2(path []ast.Node) string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprint(&buf, "[")
|
|
|
|
for i, n := range path {
|
|
|
|
if i > 0 {
|
|
|
|
fmt.Fprint(&buf, " ")
|
|
|
|
}
|
|
|
|
fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
|
|
|
|
}
|
|
|
|
fmt.Fprint(&buf, "]")
|
|
|
|
return buf.String()
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
|
|
|
|
func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
|
|
|
|
var methods []*types.Selection
|
|
|
|
for _, meth := range ssa.IntuitiveMethodSet(t) {
|
|
|
|
if isAccessibleFrom(meth.Obj(), from) {
|
|
|
|
methods = append(methods, meth)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return methods
|
|
|
|
}
|
|
|
|
|
|
|
|
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
|
|
|
|
return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func methodsToSerial(methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
|
|
|
|
var jmethods []serial.DescribeMethod
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, meth := range methods {
|
2013-09-24 13:08:14 -06:00
|
|
|
jmethods = append(jmethods, serial.DescribeMethod{
|
2013-09-03 13:29:02 -06:00
|
|
|
Name: meth.String(),
|
|
|
|
Pos: fset.Position(meth.Obj().Pos()).String(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return jmethods
|
|
|
|
}
|