| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package api holds the main interface to the api language libraries. |
| // It provides functions for going from api files to abstract syntax trees and |
| // processed semantic trees. |
| package api |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "sort" |
| "sync" |
| |
| "android.googlesource.com/platform/tools/gpu/api/ast" |
| "android.googlesource.com/platform/tools/gpu/api/parser" |
| "android.googlesource.com/platform/tools/gpu/api/resolver" |
| "android.googlesource.com/platform/tools/gpu/api/semantic" |
| "android.googlesource.com/platform/tools/gpu/framework/parse" |
| ) |
| |
| // ParseResult holds the result of parsing a file. |
| type ParseResult struct { |
| API *ast.API |
| Errs parse.ErrorList |
| } |
| |
| // ResolveResult holds the result of parsing a file. |
| type ResolveResult struct { |
| API *semantic.API |
| Errs parse.ErrorList |
| } |
| |
| // Processor holds the state when resolving multiple api files. |
| type Processor struct { |
| *resolver.Mappings |
| Loader func(path string) ([]byte, error) |
| Parsed map[string]ParseResult // guarded by parsedLock |
| Resolved map[string]ResolveResult |
| ResolveOnParseError bool // If true, resolving will be attempted even if parsing failed. |
| |
| parsedLock sync.Mutex |
| } |
| |
| // NewProcessor returns a new initialized Processor. |
| func NewProcessor() *Processor { |
| return &Processor{ |
| Mappings: resolver.NewMappings(), |
| Loader: ioutil.ReadFile, |
| Parsed: map[string]ParseResult{}, |
| Resolved: map[string]ResolveResult{}, |
| } |
| } |
| |
| // Parse parses the api file with a default Processor. |
| // See Processor.Parse for details. |
| func Parse(apiname string) (*ast.API, parse.ErrorList) { |
| return NewProcessor().Parse(apiname) |
| } |
| |
| // Parse returns an ast that represents the supplied filename. |
| // It if the file has already been parsed, the cached ast will be returned, |
| // otherwise it invokes parser.Parse on the content of the supplied file name. |
| // It is safe to parse multiple files simultaniously. |
| func (p *Processor) Parse(path string) (*ast.API, parse.ErrorList) { |
| p.parsedLock.Lock() |
| res, ok := p.Parsed[path] |
| p.parsedLock.Unlock() |
| if ok { |
| return res.API, res.Errs |
| } |
| |
| info, err := p.Loader(path) |
| if err != nil { |
| return nil, parse.ErrorList{parse.Error{Message: err.Error()}} |
| } |
| api, errs := parser.Parse(path, string(info), p) |
| p.parsedLock.Lock() |
| p.Parsed[path] = ParseResult{api, errs} |
| p.parsedLock.Unlock() |
| return api, errs |
| } |
| |
| // Resolve resolves the api file with a default Processor. |
| // See Processor.Resolve for details. |
| func Resolve(apiname string) (*semantic.API, parse.ErrorList) { |
| return NewProcessor().Resolve(apiname) |
| } |
| |
| // Resolve returns a semantic.API that represents the supplied api file name. |
| // If the file has already been resolved, the cached semantic tree is returned, |
| // otherwise the file and all dependant files are parsed using Processor.Parse. |
| // Recursive calls are made to Resolve for all named imports, and then finally |
| // the ast and all included ast's are handed to resolver.Resolve to do semantic |
| // processing. |
| func (p *Processor) Resolve(apiname string) (*semantic.API, parse.ErrorList) { |
| absname, err := filepath.Abs(apiname) |
| if err != nil { |
| return nil, parse.ErrorList{parse.Error{Message: err.Error()}} |
| } |
| wd, name := filepath.Split(absname) |
| return p.resolve(wd, name) |
| } |
| |
| func (p *Processor) resolve(wd, name string) (*semantic.API, parse.ErrorList) { |
| absname := filepath.Join(wd, name) |
| if res, ok := p.Resolved[absname]; ok { |
| if res.API == nil && res.Errs == nil { // reentry detected |
| return nil, parse.ErrorList{parse.Error{ |
| Message: fmt.Sprintf("Recursive import %s", absname), |
| }} |
| } |
| return res.API, res.Errs |
| } |
| p.Resolved[absname] = ResolveResult{} // mark to prevent reentry |
| // Parse the API file and gather all the includes |
| includes := map[string]*ast.API{} |
| allErrs := p.parseIncludesResursive(wd, name, includes) |
| // Build a sorted list of includes |
| names := make(sort.StringSlice, 0, len(includes)) |
| for name := range includes { |
| names = append(names, name) |
| } |
| names.Sort() |
| list := make([]*ast.API, len(names)) |
| for i, name := range names { |
| list[i] = includes[name] |
| } |
| // Resolve all the named imports |
| imports := &semantic.Symbols{} |
| importPaths := map[string]string{} |
| for _, api := range list { |
| for _, i := range api.Imports { |
| if i.Name == nil { |
| // unnamed imports have already been included |
| continue |
| } |
| path := filepath.Join(wd, i.Path.Value) |
| if importedPath, seen := importPaths[i.Name.Value]; seen { |
| if path == importedPath { |
| // import with same path and name already included |
| continue |
| } |
| return nil, parse.ErrorList{parse.Error{ |
| Message: fmt.Sprintf("Import name '%s' used for different paths (%s != %s)", |
| i.Name.Value, path, importedPath)}, |
| } |
| } |
| wd, name := filepath.Split(path) |
| api, errs := p.resolve(wd, name) |
| if len(errs) > 0 { |
| allErrs = append(errs, allErrs...) |
| } |
| if api != nil { |
| imports.Add(i.Name.Value, api) |
| importPaths[i.Name.Value] = path |
| } |
| } |
| } |
| if len(allErrs) > 0 && !p.ResolveOnParseError { |
| return nil, allErrs |
| } |
| // Now resolve the api set as a single unit |
| api, errs := resolver.Resolve(list, imports, p.Mappings) |
| if len(errs) > 0 { |
| allErrs = append(errs, allErrs...) |
| } |
| nameNoExt := name[:len(name)-len(filepath.Ext(name))] |
| api.Named = semantic.Named(nameNoExt) |
| p.Resolved[absname] = ResolveResult{api, allErrs} |
| return api, allErrs |
| } |
| |
| // parseUnnamedIncludesResursive resursively parses the unnamed includes from |
| // apiname in wd. The full list of includes (named and unnamed) is added to |
| // includes. |
| func (p *Processor) parseIncludesResursive(wd string, name string, includes map[string]*ast.API) parse.ErrorList { |
| path, err := filepath.Abs(filepath.Join(wd, name)) |
| if err != nil { |
| return parse.ErrorList{parse.Error{Message: err.Error()}} |
| } |
| if _, seen := includes[path]; seen { |
| return nil |
| } |
| api, allErrs := p.Parse(path) |
| if api == nil { |
| return allErrs |
| } |
| includes[path] = api |
| for _, i := range api.Imports { |
| if i.Name != nil { |
| // named imports don't get merged |
| continue |
| } |
| path := filepath.Join(wd, i.Path.Value) |
| wd, name := filepath.Split(path) |
| if errs := p.parseIncludesResursive(wd, name, includes); len(errs) > 0 { |
| allErrs = append(allErrs, errs...) |
| } |
| } |
| return allErrs |
| } |
| |
| // CheckErrors will, if len(errs) > 0, print each of the error messages for the |
| // specified api and then return the list as a single error. If errs is zero length, |
| // CheckErrors does nothing and returns nil. |
| func CheckErrors(apiName string, errs parse.ErrorList, maxErrors int) error { |
| if len(errs) == 0 { |
| return nil |
| } |
| if len(errs) > maxErrors { |
| errs = errs[:maxErrors] |
| } |
| for _, e := range errs { |
| if e.At != nil { |
| filename := e.At.Token().Source.Filename |
| line, column := e.At.Token().Cursor() |
| fmt.Fprintf(os.Stderr, "%s:%v:%v: %s\n", filename, line, column, e.Message) |
| } else { |
| fmt.Fprintf(os.Stderr, "%s: %s\n", apiName, e.Message) |
| } |
| } |
| if len(errs) > maxErrors { |
| fmt.Fprintf(os.Stderr, "And %d more errors\n", len(errs)-maxErrors) |
| } |
| fmt.Fprintf(os.Stderr, "Stack of first error:\n%s\n", errs[0].Stack) |
| return errs |
| } |