github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/internal/loader/util.go (about) 1 // Copyright 2013 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 loader 6 7 import ( 8 "go/ast" 9 "go/build" 10 "go/parser" 11 "go/token" 12 "io" 13 "os" 14 "path/filepath" 15 "strconv" 16 "sync" 17 ) 18 19 // We use a counting semaphore to limit 20 // the number of parallel I/O calls per process. 21 var sema = make(chan bool, 10) 22 23 // parseFiles parses the Go source files within directory dir and 24 // returns the ASTs of the ones that could be at least partially parsed, 25 // along with a list of I/O and parse errors encountered. 26 // 27 // I/O is done via ctxt, which may specify a virtual file system. 28 // displayPath is used to transform the filenames attached to the ASTs. 29 // 30 func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) { 31 isAbsPath := ctxt.IsAbsPath 32 if isAbsPath == nil { 33 isAbsPath = filepath.IsAbs 34 } 35 joinPath := ctxt.JoinPath 36 if joinPath == nil { 37 joinPath = filepath.Join 38 } 39 40 if displayPath == nil { 41 displayPath = func(path string) string { return path } 42 } 43 var wg sync.WaitGroup 44 n := len(files) 45 parsed := make([]*ast.File, n) 46 errors := make([]error, n) 47 for i, file := range files { 48 if !isAbsPath(file) { 49 file = joinPath(dir, file) 50 } 51 wg.Add(1) 52 go func(i int, file string) { 53 sema <- true // wait 54 defer func() { 55 wg.Done() 56 <-sema // signal 57 }() 58 var rd io.ReadCloser 59 var err error 60 if ctxt.OpenFile != nil { 61 rd, err = ctxt.OpenFile(file) 62 } else { 63 rd, err = os.Open(file) 64 } 65 if err != nil { 66 errors[i] = err // open failed 67 return 68 } 69 70 // ParseFile may return both an AST and an error. 71 parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode) 72 rd.Close() 73 }(i, file) 74 } 75 wg.Wait() 76 77 // Eliminate nils, preserving order. 78 var o int 79 for _, f := range parsed { 80 if f != nil { 81 parsed[o] = f 82 o++ 83 } 84 } 85 parsed = parsed[:o] 86 87 o = 0 88 for _, err := range errors { 89 if err != nil { 90 errors[o] = err 91 o++ 92 } 93 } 94 errors = errors[:o] 95 96 return parsed, errors 97 } 98 99 // scanImports returns the set of all package import paths from all 100 // import specs in the specified files. 101 func scanImports(files []*ast.File) map[string]bool { 102 imports := make(map[string]bool) 103 for _, f := range files { 104 for _, decl := range f.Decls { 105 if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT { 106 for _, spec := range decl.Specs { 107 spec := spec.(*ast.ImportSpec) 108 109 // NB: do not assume the program is well-formed! 110 path, err := strconv.Unquote(spec.Path.Value) 111 if err != nil { 112 continue // quietly ignore the error 113 } 114 if path == "C" || path == "unsafe" { 115 continue // skip pseudo packages 116 } 117 imports[path] = true 118 } 119 } 120 } 121 } 122 return imports 123 } 124 125 // ---------- Internal helpers ---------- 126 127 // TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos) 128 func tokenFileContainsPos(f *token.File, pos token.Pos) bool { 129 p := int(pos) 130 base := f.Base() 131 return base <= p && p < base+f.Size() 132 }