Skip to content

Commit aeca7a7

Browse files
committed
cmd/api: speed up API check by 2x, caching parser.ParseFile calls
Saves 5 seconds on my machine. If Issue 4380 is fixed this clone can be removed. Update golang#4380 R=golang-dev, remyoudompheng, minux.ma, gri CC=golang-dev https://golang.org/cl/6845058
1 parent c00bda1 commit aeca7a7

File tree

3 files changed

+289
-1
lines changed

3 files changed

+289
-1
lines changed

src/cmd/api/clone.go

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// Copyright 2012 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"go/ast"
10+
"log"
11+
"reflect"
12+
)
13+
14+
const debugClone = false
15+
16+
// TODO(bradfitz): delete this function (and whole file) once
17+
// http://golang.org/issue/4380 is fixed.
18+
func clone(i interface{}) (cloned interface{}) {
19+
if debugClone {
20+
defer func() {
21+
if !reflect.DeepEqual(i, cloned) {
22+
log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned)
23+
}
24+
}()
25+
}
26+
switch v := i.(type) {
27+
case nil:
28+
return nil
29+
case *ast.File:
30+
o := &ast.File{
31+
Doc: v.Doc, // shallow
32+
Package: v.Package,
33+
Comments: v.Comments, // shallow
34+
Name: v.Name,
35+
Scope: v.Scope,
36+
}
37+
for _, x := range v.Decls {
38+
o.Decls = append(o.Decls, clone(x).(ast.Decl))
39+
}
40+
for _, x := range v.Imports {
41+
o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec))
42+
}
43+
for _, x := range v.Unresolved {
44+
o.Unresolved = append(o.Unresolved, x)
45+
}
46+
return o
47+
case *ast.GenDecl:
48+
o := new(ast.GenDecl)
49+
*o = *v
50+
o.Specs = nil
51+
for _, x := range v.Specs {
52+
o.Specs = append(o.Specs, clone(x).(ast.Spec))
53+
}
54+
return o
55+
case *ast.TypeSpec:
56+
o := new(ast.TypeSpec)
57+
*o = *v
58+
o.Type = cloneExpr(v.Type)
59+
return o
60+
case *ast.InterfaceType:
61+
o := new(ast.InterfaceType)
62+
*o = *v
63+
o.Methods = clone(v.Methods).(*ast.FieldList)
64+
return o
65+
case *ast.FieldList:
66+
if v == nil {
67+
return v
68+
}
69+
o := new(ast.FieldList)
70+
*o = *v
71+
o.List = nil
72+
for _, x := range v.List {
73+
o.List = append(o.List, clone(x).(*ast.Field))
74+
}
75+
return o
76+
case *ast.Field:
77+
o := &ast.Field{
78+
Doc: v.Doc, // shallow
79+
Type: cloneExpr(v.Type),
80+
Tag: clone(v.Tag).(*ast.BasicLit),
81+
Comment: v.Comment, // shallow
82+
}
83+
for _, x := range v.Names {
84+
o.Names = append(o.Names, clone(x).(*ast.Ident))
85+
}
86+
return o
87+
case *ast.FuncType:
88+
if v == nil {
89+
return v
90+
}
91+
return &ast.FuncType{
92+
Func: v.Func,
93+
Params: clone(v.Params).(*ast.FieldList),
94+
Results: clone(v.Results).(*ast.FieldList),
95+
}
96+
case *ast.FuncDecl:
97+
if v == nil {
98+
return v
99+
}
100+
return &ast.FuncDecl{
101+
Recv: clone(v.Recv).(*ast.FieldList),
102+
Name: v.Name,
103+
Type: clone(v.Type).(*ast.FuncType),
104+
Body: v.Body, // shallow
105+
}
106+
case *ast.ValueSpec:
107+
if v == nil {
108+
return v
109+
}
110+
o := &ast.ValueSpec{
111+
Type: cloneExpr(v.Type),
112+
}
113+
for _, x := range v.Names {
114+
o.Names = append(o.Names, x)
115+
}
116+
for _, x := range v.Values {
117+
o.Values = append(o.Values, cloneExpr(x))
118+
}
119+
return o
120+
case *ast.CallExpr:
121+
if v == nil {
122+
return v
123+
}
124+
o := &ast.CallExpr{}
125+
*o = *v
126+
o.Args = cloneExprs(v.Args)
127+
o.Fun = cloneExpr(v.Fun)
128+
return o
129+
case *ast.SelectorExpr:
130+
if v == nil {
131+
return nil
132+
}
133+
return &ast.SelectorExpr{
134+
X: cloneExpr(v.X),
135+
Sel: v.Sel,
136+
}
137+
case *ast.ArrayType:
138+
return &ast.ArrayType{
139+
Lbrack: v.Lbrack,
140+
Len: cloneExpr(v.Len),
141+
Elt: cloneExpr(v.Elt),
142+
}
143+
case *ast.StructType:
144+
return &ast.StructType{
145+
Struct: v.Struct,
146+
Fields: clone(v.Fields).(*ast.FieldList),
147+
Incomplete: v.Incomplete,
148+
}
149+
case *ast.StarExpr:
150+
return &ast.StarExpr{
151+
Star: v.Star,
152+
X: cloneExpr(v.X),
153+
}
154+
case *ast.CompositeLit:
155+
return &ast.CompositeLit{
156+
Type: cloneExpr(v.Type),
157+
Lbrace: v.Lbrace,
158+
Elts: cloneExprs(v.Elts),
159+
Rbrace: v.Rbrace,
160+
}
161+
case *ast.UnaryExpr:
162+
return &ast.UnaryExpr{
163+
OpPos: v.OpPos,
164+
Op: v.Op,
165+
X: cloneExpr(v.X),
166+
}
167+
case *ast.BinaryExpr:
168+
return &ast.BinaryExpr{
169+
OpPos: v.OpPos,
170+
Op: v.Op,
171+
X: cloneExpr(v.X),
172+
Y: cloneExpr(v.Y),
173+
}
174+
case *ast.Ellipsis:
175+
return &ast.Ellipsis{
176+
Ellipsis: v.Ellipsis,
177+
Elt: cloneExpr(v.Elt),
178+
}
179+
case *ast.KeyValueExpr:
180+
return &ast.KeyValueExpr{
181+
Key: cloneExpr(v.Key),
182+
Colon: v.Colon,
183+
Value: cloneExpr(v.Value),
184+
}
185+
case *ast.FuncLit:
186+
return &ast.FuncLit{
187+
Type: clone(v.Type).(*ast.FuncType),
188+
Body: v.Body, // shallow
189+
}
190+
case *ast.MapType:
191+
return &ast.MapType{
192+
Map: v.Map,
193+
Key: cloneExpr(v.Key),
194+
Value: cloneExpr(v.Value),
195+
}
196+
case *ast.ParenExpr:
197+
return &ast.ParenExpr{
198+
Lparen: v.Lparen,
199+
X: cloneExpr(v.X),
200+
Rparen: v.Rparen,
201+
}
202+
case *ast.Ident, *ast.BasicLit:
203+
return v
204+
case *ast.ImportSpec:
205+
return &ast.ImportSpec{
206+
Doc: v.Doc, // shallow
207+
Name: v.Name,
208+
Path: clone(v.Path).(*ast.BasicLit),
209+
Comment: v.Comment, // shallow
210+
EndPos: v.EndPos,
211+
}
212+
case *ast.ChanType:
213+
return &ast.ChanType{
214+
Begin: v.Begin,
215+
Arrow: v.Arrow,
216+
Dir: v.Dir,
217+
Value: cloneExpr(v.Value),
218+
}
219+
case *ast.TypeAssertExpr:
220+
return &ast.TypeAssertExpr{
221+
X: cloneExpr(v.X),
222+
Type: cloneExpr(v.Type),
223+
}
224+
case *ast.IndexExpr:
225+
return &ast.IndexExpr{
226+
X: cloneExpr(v.X),
227+
Index: cloneExpr(v.Index),
228+
Lbrack: v.Lbrack,
229+
Rbrack: v.Rbrack,
230+
}
231+
}
232+
panic(fmt.Sprintf("Uncloneable type %T", i))
233+
}
234+
235+
func cloneExpr(x ast.Expr) ast.Expr {
236+
if x == nil {
237+
return nil
238+
}
239+
return clone(x).(ast.Expr)
240+
}
241+
242+
func cloneExprs(x []ast.Expr) []ast.Expr {
243+
if x == nil {
244+
return nil
245+
}
246+
o := make([]ast.Expr, len(x))
247+
for i, x := range x {
248+
o[i] = cloneExpr(x)
249+
}
250+
return o
251+
}

src/cmd/api/goapi.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,21 @@ func fileDeps(f *ast.File) (pkgs []string) {
353353
return
354354
}
355355

356+
var parsedFileCache = make(map[string]*ast.File)
357+
358+
func parseFile(filename string) (*ast.File, error) {
359+
f, ok := parsedFileCache[filename]
360+
if !ok {
361+
var err error
362+
f, err = parser.ParseFile(fset, filename, nil, 0)
363+
if err != nil {
364+
return nil, err
365+
}
366+
parsedFileCache[filename] = f
367+
}
368+
return clone(f).(*ast.File), nil
369+
}
370+
356371
// WalkPackage walks all files in package `name'.
357372
// WalkPackage does nothing if the package has already been loaded.
358373
func (w *Walker) WalkPackage(name string) {
@@ -386,7 +401,7 @@ func (w *Walker) WalkPackage(name string) {
386401

387402
files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
388403
for _, file := range files {
389-
f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
404+
f, err := parseFile(filepath.Join(dir, file))
390405
if err != nil {
391406
log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
392407
}

src/cmd/api/testdata/src/pkg/p1/p1.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,25 @@ const (
167167
foo2 string = "foo2"
168168
truth = foo == "foo" || foo2 == "foo2"
169169
)
170+
171+
func ellipsis(...string) {}
172+
173+
var x = &S{
174+
Public: nil,
175+
private: nil,
176+
publicTime: time.Now(),
177+
}
178+
179+
var parenExpr = (1 + 5)
180+
181+
var funcLit = func() {}
182+
183+
var m map[string]int
184+
185+
var chanVar chan int
186+
187+
var ifaceVar interface{} = 5
188+
189+
var assertVar = ifaceVar.(int)
190+
191+
var indexVar = m["foo"]

0 commit comments

Comments
 (0)