go.tools/ssa: new Function.Syntax() returns the declaring AST (debug mode) or just the Pos/End of the function's extent (otherwise).
R=gri
CC=golang-dev
https://golang.org/cl/16980043
diff --git a/ssa/builder.go b/ssa/builder.go
index 8675fb6..6474be6 100644
--- a/ssa/builder.go
+++ b/ssa/builder.go
@@ -514,10 +514,7 @@
Enclosing: fn,
Pkg: fn.Pkg,
Prog: fn.Prog,
- syntax: &funcSyntax{
- functype: e.Type,
- body: e.Body,
- },
+ syntax: e,
}
fn.AnonFuncs = append(fn.AnonFuncs, fn2)
b.buildFunction(fn2)
@@ -2214,10 +2211,25 @@
if fn.Blocks != nil {
return // building already started
}
- if fn.syntax == nil {
+
+ var recvField *ast.FieldList
+ var body *ast.BlockStmt
+ var functype *ast.FuncType
+ switch n := fn.syntax.(type) {
+ case nil:
return // not a Go source function. (Synthetic, or from object file.)
+ case *ast.FuncDecl:
+ functype = n.Type
+ recvField = n.Recv
+ body = n.Body
+ case *ast.FuncLit:
+ functype = n.Type
+ body = n.Body
+ default:
+ panic(n)
}
- if fn.syntax.body == nil {
+
+ if body == nil {
// External function.
if fn.Params == nil {
// This condition ensures we add a non-empty
@@ -2241,8 +2253,8 @@
defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))()
}
fn.startBody()
- fn.createSyntacticParams()
- b.stmt(fn, fn.syntax.body)
+ fn.createSyntacticParams(recvField, functype)
+ b.stmt(fn, body)
if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) {
// Run function calls deferred in this function when
// falling off the end of the body block.
@@ -2269,11 +2281,7 @@
pos: decl.Name.NamePos,
Pkg: pkg,
Prog: pkg.Prog,
- syntax: &funcSyntax{
- functype: decl.Type,
- recvField: decl.Recv,
- body: decl.Body,
- },
+ syntax: decl,
}
var v Call
diff --git a/ssa/create.go b/ssa/create.go
index 67e730e..5af1fd9 100644
--- a/ssa/create.go
+++ b/ssa/create.go
@@ -95,26 +95,19 @@
pkg.Members[name] = g
case *types.Func:
- var fs *funcSyntax
- synthetic := "loaded from gc object file"
- if decl, ok := syntax.(*ast.FuncDecl); ok {
- synthetic = ""
- fs = &funcSyntax{
- functype: decl.Type,
- recvField: decl.Recv,
- body: decl.Body,
- }
- }
fn := &Function{
name: name,
object: obj,
Signature: obj.Type().(*types.Signature),
- Synthetic: synthetic,
+ syntax: syntax,
pos: obj.Pos(), // (iff syntax)
Pkg: pkg,
Prog: pkg.Prog,
- syntax: fs,
}
+ if syntax == nil {
+ fn.Synthetic = "loaded from gc object file"
+ }
+
pkg.values[obj] = fn
if fn.Signature.Recv() == nil {
pkg.Members[name] = fn // package-level function
diff --git a/ssa/func.go b/ssa/func.go
index cf380fa..5294ebf 100644
--- a/ssa/func.go
+++ b/ssa/func.go
@@ -147,13 +147,6 @@
_continue *BasicBlock
}
-// funcSyntax holds the syntax tree for the function declaration and body.
-type funcSyntax struct {
- recvField *ast.FieldList
- body *ast.BlockStmt
- functype *ast.FuncType
-}
-
// labelledBlock returns the branch target associated with the
// specified label, creating it if needed.
//
@@ -221,15 +214,14 @@
// syntax. In addition it populates the f.objects mapping.
//
// Preconditions:
-// f.syntax != nil, i.e. this is a Go source function.
// f.startBody() was called.
// Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
//
-func (f *Function) createSyntacticParams() {
+func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
// Receiver (at most one inner iteration).
- if f.syntax.recvField != nil {
- for _, field := range f.syntax.recvField.List {
+ if recv != nil {
+ for _, field := range recv.List {
for _, n := range field.Names {
f.addSpilledParam(f.Pkg.objectOf(n))
}
@@ -241,9 +233,9 @@
}
// Parameters.
- if f.syntax.functype.Params != nil {
+ if functype.Params != nil {
n := len(f.Params) // 1 if has recv, 0 otherwise
- for _, field := range f.syntax.functype.Params.List {
+ for _, field := range functype.Params.List {
for _, n := range field.Names {
f.addSpilledParam(f.Pkg.objectOf(n))
}
@@ -255,8 +247,8 @@
}
// Named results.
- if f.syntax.functype.Results != nil {
- for _, field := range f.syntax.functype.Results.List {
+ if functype.Results != nil {
+ for _, field := range functype.Results.List {
// Implicit "var" decl of locals for named results.
for _, n := range field.Names {
f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
@@ -308,7 +300,11 @@
f.objects = nil
f.currentBlock = nil
f.lblocks = nil
- f.syntax = nil
+
+ // Don't pin the AST in memory (except in debug mode).
+ if n := f.syntax; n != nil && !f.debugInfo() {
+ f.syntax = extentNode{n.Pos(), n.End()}
+ }
// Remove any f.Locals that are now heap-allocated.
j := 0
@@ -380,15 +376,13 @@
// debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool {
- return f.Pkg.debug
+ return f.Pkg != nil && f.Pkg.debug
}
// addNamedLocal creates a local variable, adds it to function f and
// returns it. Its name and type are taken from obj. Subsequent
// calls to f.lookup(obj) will return the same local.
//
-// Precondition: f.syntax != nil (i.e. a Go source function).
-//
func (f *Function) addNamedLocal(obj types.Object) *Alloc {
l := f.addLocal(obj.Type(), obj.Pos())
l.Comment = obj.Name()
@@ -660,3 +654,19 @@
func NewFunction(name string, sig *types.Signature, provenance string) *Function {
return &Function{name: name, Signature: sig, Synthetic: provenance}
}
+
+type extentNode [2]token.Pos
+
+func (n extentNode) Pos() token.Pos { return n[0] }
+func (n extentNode) End() token.Pos { return n[1] }
+
+// Syntax returns an ast.Node whose Pos/End methods provide the
+// lexical extent of the function if it was defined by Go source code
+// (f.Synthetic==""), or nil otherwise.
+//
+// If f was built with debug information (see Package.SetDebugRef),
+// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
+// function. Otherwise, it is an opaque Node providing only position
+// information; this avoids pinning the AST in memory.
+//
+func (f *Function) Syntax() ast.Node { return f.syntax }
diff --git a/ssa/sanity.go b/ssa/sanity.go
index c364d4e..81f3351 100644
--- a/ssa/sanity.go
+++ b/ssa/sanity.go
@@ -339,6 +339,9 @@
s.errorf("nil Pkg")
}
}
+ if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
+ s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
+ }
for i, l := range fn.Locals {
if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
diff --git a/ssa/source.go b/ssa/source.go
index 726d80e..051d901 100644
--- a/ssa/source.go
+++ b/ssa/source.go
@@ -26,8 +26,8 @@
// Returns nil if not found; reasons might include:
// - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and
-// its SSA function has not been created yet (pkg.BuildPackage()
-// has not yet been called).
+// its SSA function has not been created yet
+// (pkg.Build() has not yet been called).
//
func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function...
diff --git a/ssa/ssa.go b/ssa/ssa.go
index e3624d4..d958e3a 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -268,6 +268,7 @@
pos token.Pos
Synthetic string // provenance of synthetic function; "" for true source functions
+ syntax ast.Node // *ast.Func{Decl,Lit}; replaced with simple ast.Node after build, unless debug mode
Enclosing *Function // enclosing function if anon; nil if global
Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error)
Prog *Program // enclosing program
@@ -283,7 +284,6 @@
currentBlock *BasicBlock // where to emit code
objects map[types.Object]Value // addresses of local variables
namedResults []*Alloc // tuple of named results
- syntax *funcSyntax // abstract syntax trees for Go source functions
targets *targets // linked stack of branch targets
lblocks map[*ast.Object]*lblock // labelled blocks
}
@@ -1168,6 +1168,9 @@
// consistency is maintained during transformation passes by the
// ordinary SSA renaming machinery.)
//
+// DebugRefs are generated only for functions built with debugging
+// enabled; see Package.SetDebugMode().
+//
type DebugRef struct {
anInstruction
X Value // the value whose position we're declaring