mirror of
https://github.com/golang/go
synced 2024-11-23 00:00:07 -07:00
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 #4380 R=golang-dev, remyoudompheng, minux.ma, gri CC=golang-dev https://golang.org/cl/6845058
This commit is contained in:
parent
c00bda1352
commit
aeca7a7cd2
251
src/cmd/api/clone.go
Normal file
251
src/cmd/api/clone.go
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const debugClone = false
|
||||||
|
|
||||||
|
// TODO(bradfitz): delete this function (and whole file) once
|
||||||
|
// http://golang.org/issue/4380 is fixed.
|
||||||
|
func clone(i interface{}) (cloned interface{}) {
|
||||||
|
if debugClone {
|
||||||
|
defer func() {
|
||||||
|
if !reflect.DeepEqual(i, cloned) {
|
||||||
|
log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
switch v := i.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *ast.File:
|
||||||
|
o := &ast.File{
|
||||||
|
Doc: v.Doc, // shallow
|
||||||
|
Package: v.Package,
|
||||||
|
Comments: v.Comments, // shallow
|
||||||
|
Name: v.Name,
|
||||||
|
Scope: v.Scope,
|
||||||
|
}
|
||||||
|
for _, x := range v.Decls {
|
||||||
|
o.Decls = append(o.Decls, clone(x).(ast.Decl))
|
||||||
|
}
|
||||||
|
for _, x := range v.Imports {
|
||||||
|
o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec))
|
||||||
|
}
|
||||||
|
for _, x := range v.Unresolved {
|
||||||
|
o.Unresolved = append(o.Unresolved, x)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
case *ast.GenDecl:
|
||||||
|
o := new(ast.GenDecl)
|
||||||
|
*o = *v
|
||||||
|
o.Specs = nil
|
||||||
|
for _, x := range v.Specs {
|
||||||
|
o.Specs = append(o.Specs, clone(x).(ast.Spec))
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
o := new(ast.TypeSpec)
|
||||||
|
*o = *v
|
||||||
|
o.Type = cloneExpr(v.Type)
|
||||||
|
return o
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
o := new(ast.InterfaceType)
|
||||||
|
*o = *v
|
||||||
|
o.Methods = clone(v.Methods).(*ast.FieldList)
|
||||||
|
return o
|
||||||
|
case *ast.FieldList:
|
||||||
|
if v == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
o := new(ast.FieldList)
|
||||||
|
*o = *v
|
||||||
|
o.List = nil
|
||||||
|
for _, x := range v.List {
|
||||||
|
o.List = append(o.List, clone(x).(*ast.Field))
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
case *ast.Field:
|
||||||
|
o := &ast.Field{
|
||||||
|
Doc: v.Doc, // shallow
|
||||||
|
Type: cloneExpr(v.Type),
|
||||||
|
Tag: clone(v.Tag).(*ast.BasicLit),
|
||||||
|
Comment: v.Comment, // shallow
|
||||||
|
}
|
||||||
|
for _, x := range v.Names {
|
||||||
|
o.Names = append(o.Names, clone(x).(*ast.Ident))
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
case *ast.FuncType:
|
||||||
|
if v == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return &ast.FuncType{
|
||||||
|
Func: v.Func,
|
||||||
|
Params: clone(v.Params).(*ast.FieldList),
|
||||||
|
Results: clone(v.Results).(*ast.FieldList),
|
||||||
|
}
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
if v == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return &ast.FuncDecl{
|
||||||
|
Recv: clone(v.Recv).(*ast.FieldList),
|
||||||
|
Name: v.Name,
|
||||||
|
Type: clone(v.Type).(*ast.FuncType),
|
||||||
|
Body: v.Body, // shallow
|
||||||
|
}
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
if v == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
o := &ast.ValueSpec{
|
||||||
|
Type: cloneExpr(v.Type),
|
||||||
|
}
|
||||||
|
for _, x := range v.Names {
|
||||||
|
o.Names = append(o.Names, x)
|
||||||
|
}
|
||||||
|
for _, x := range v.Values {
|
||||||
|
o.Values = append(o.Values, cloneExpr(x))
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
case *ast.CallExpr:
|
||||||
|
if v == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
o := &ast.CallExpr{}
|
||||||
|
*o = *v
|
||||||
|
o.Args = cloneExprs(v.Args)
|
||||||
|
o.Fun = cloneExpr(v.Fun)
|
||||||
|
return o
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.SelectorExpr{
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
Sel: v.Sel,
|
||||||
|
}
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return &ast.ArrayType{
|
||||||
|
Lbrack: v.Lbrack,
|
||||||
|
Len: cloneExpr(v.Len),
|
||||||
|
Elt: cloneExpr(v.Elt),
|
||||||
|
}
|
||||||
|
case *ast.StructType:
|
||||||
|
return &ast.StructType{
|
||||||
|
Struct: v.Struct,
|
||||||
|
Fields: clone(v.Fields).(*ast.FieldList),
|
||||||
|
Incomplete: v.Incomplete,
|
||||||
|
}
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return &ast.StarExpr{
|
||||||
|
Star: v.Star,
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
}
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
return &ast.CompositeLit{
|
||||||
|
Type: cloneExpr(v.Type),
|
||||||
|
Lbrace: v.Lbrace,
|
||||||
|
Elts: cloneExprs(v.Elts),
|
||||||
|
Rbrace: v.Rbrace,
|
||||||
|
}
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
return &ast.UnaryExpr{
|
||||||
|
OpPos: v.OpPos,
|
||||||
|
Op: v.Op,
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
}
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
return &ast.BinaryExpr{
|
||||||
|
OpPos: v.OpPos,
|
||||||
|
Op: v.Op,
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
Y: cloneExpr(v.Y),
|
||||||
|
}
|
||||||
|
case *ast.Ellipsis:
|
||||||
|
return &ast.Ellipsis{
|
||||||
|
Ellipsis: v.Ellipsis,
|
||||||
|
Elt: cloneExpr(v.Elt),
|
||||||
|
}
|
||||||
|
case *ast.KeyValueExpr:
|
||||||
|
return &ast.KeyValueExpr{
|
||||||
|
Key: cloneExpr(v.Key),
|
||||||
|
Colon: v.Colon,
|
||||||
|
Value: cloneExpr(v.Value),
|
||||||
|
}
|
||||||
|
case *ast.FuncLit:
|
||||||
|
return &ast.FuncLit{
|
||||||
|
Type: clone(v.Type).(*ast.FuncType),
|
||||||
|
Body: v.Body, // shallow
|
||||||
|
}
|
||||||
|
case *ast.MapType:
|
||||||
|
return &ast.MapType{
|
||||||
|
Map: v.Map,
|
||||||
|
Key: cloneExpr(v.Key),
|
||||||
|
Value: cloneExpr(v.Value),
|
||||||
|
}
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
return &ast.ParenExpr{
|
||||||
|
Lparen: v.Lparen,
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
Rparen: v.Rparen,
|
||||||
|
}
|
||||||
|
case *ast.Ident, *ast.BasicLit:
|
||||||
|
return v
|
||||||
|
case *ast.ImportSpec:
|
||||||
|
return &ast.ImportSpec{
|
||||||
|
Doc: v.Doc, // shallow
|
||||||
|
Name: v.Name,
|
||||||
|
Path: clone(v.Path).(*ast.BasicLit),
|
||||||
|
Comment: v.Comment, // shallow
|
||||||
|
EndPos: v.EndPos,
|
||||||
|
}
|
||||||
|
case *ast.ChanType:
|
||||||
|
return &ast.ChanType{
|
||||||
|
Begin: v.Begin,
|
||||||
|
Arrow: v.Arrow,
|
||||||
|
Dir: v.Dir,
|
||||||
|
Value: cloneExpr(v.Value),
|
||||||
|
}
|
||||||
|
case *ast.TypeAssertExpr:
|
||||||
|
return &ast.TypeAssertExpr{
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
Type: cloneExpr(v.Type),
|
||||||
|
}
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
return &ast.IndexExpr{
|
||||||
|
X: cloneExpr(v.X),
|
||||||
|
Index: cloneExpr(v.Index),
|
||||||
|
Lbrack: v.Lbrack,
|
||||||
|
Rbrack: v.Rbrack,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("Uncloneable type %T", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneExpr(x ast.Expr) ast.Expr {
|
||||||
|
if x == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return clone(x).(ast.Expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneExprs(x []ast.Expr) []ast.Expr {
|
||||||
|
if x == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
o := make([]ast.Expr, len(x))
|
||||||
|
for i, x := range x {
|
||||||
|
o[i] = cloneExpr(x)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
@ -353,6 +353,21 @@ func fileDeps(f *ast.File) (pkgs []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parsedFileCache = make(map[string]*ast.File)
|
||||||
|
|
||||||
|
func parseFile(filename string) (*ast.File, error) {
|
||||||
|
f, ok := parsedFileCache[filename]
|
||||||
|
if !ok {
|
||||||
|
var err error
|
||||||
|
f, err = parser.ParseFile(fset, filename, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parsedFileCache[filename] = f
|
||||||
|
}
|
||||||
|
return clone(f).(*ast.File), nil
|
||||||
|
}
|
||||||
|
|
||||||
// WalkPackage walks all files in package `name'.
|
// WalkPackage walks all files in package `name'.
|
||||||
// WalkPackage does nothing if the package has already been loaded.
|
// WalkPackage does nothing if the package has already been loaded.
|
||||||
func (w *Walker) WalkPackage(name string) {
|
func (w *Walker) WalkPackage(name string) {
|
||||||
@ -386,7 +401,7 @@ func (w *Walker) WalkPackage(name string) {
|
|||||||
|
|
||||||
files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
|
files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
|
f, err := parseFile(filepath.Join(dir, file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
|
log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
|
||||||
}
|
}
|
||||||
|
22
src/cmd/api/testdata/src/pkg/p1/p1.go
vendored
22
src/cmd/api/testdata/src/pkg/p1/p1.go
vendored
@ -167,3 +167,25 @@ const (
|
|||||||
foo2 string = "foo2"
|
foo2 string = "foo2"
|
||||||
truth = foo == "foo" || foo2 == "foo2"
|
truth = foo == "foo" || foo2 == "foo2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ellipsis(...string) {}
|
||||||
|
|
||||||
|
var x = &S{
|
||||||
|
Public: nil,
|
||||||
|
private: nil,
|
||||||
|
publicTime: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
var parenExpr = (1 + 5)
|
||||||
|
|
||||||
|
var funcLit = func() {}
|
||||||
|
|
||||||
|
var m map[string]int
|
||||||
|
|
||||||
|
var chanVar chan int
|
||||||
|
|
||||||
|
var ifaceVar interface{} = 5
|
||||||
|
|
||||||
|
var assertVar = ifaceVar.(int)
|
||||||
|
|
||||||
|
var indexVar = m["foo"]
|
||||||
|
Loading…
Reference in New Issue
Block a user