]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/api: speed up API check by 2x, caching parser.ParseFile calls
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 19 Nov 2012 21:50:20 +0000 (13:50 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 19 Nov 2012 21:50:20 +0000 (13:50 -0800)
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

src/cmd/api/clone.go [new file with mode: 0644]
src/cmd/api/goapi.go
src/cmd/api/testdata/src/pkg/p1/p1.go

diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go
new file mode 100644 (file)
index 0000000..180215f
--- /dev/null
@@ -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
+}
index 9b7e0902776a03bf8a496b0038ac4839aea49a6a..a61497c52dc6f642ddfaf2663a97f77be4294711 100644 (file)
@@ -353,6 +353,21 @@ func fileDeps(f *ast.File) (pkgs []string) {
        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 does nothing if the package has already been loaded.
 func (w *Walker) WalkPackage(name string) {
@@ -386,7 +401,7 @@ func (w *Walker) WalkPackage(name string) {
 
        files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
        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 {
                        log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
                }
index a98ca1e91151195aeabac260d386b84899c5755b..a18a6418fa468c5c1cc6a133666873a29fbac4af 100644 (file)
@@ -167,3 +167,25 @@ const (
        foo2  string = "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"]