Skip to content

Commit cae7f02

Browse files
committed
go-aah/aah#157 inspect program improvement and extracted as lib
1 parent 1ebb571 commit cae7f02

File tree

7 files changed

+640
-266
lines changed

7 files changed

+640
-266
lines changed

ainsp.go

+38-266
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@ import (
1212
"errors"
1313
"fmt"
1414
"go/ast"
15-
"go/build"
1615
"go/parser"
1716
"go/scanner"
1817
"go/token"
1918
"os"
2019
"path/filepath"
20+
"reflect"
2121

2222
"aahframework.org/essentials.v0"
23-
"aahframework.org/log.v0"
2423
)
2524

2625
var (
@@ -61,17 +60,17 @@ var (
6160

6261
// Inspect method processes the Go source code for the given directory and its
6362
// sub-directories.
64-
func Inspect(path string, excludes ess.Excludes, registeredActions map[string]map[string]uint8) (*program, []error) {
65-
if err := validateInput(path); err != nil {
66-
return nil, append([]error{}, err)
67-
}
68-
69-
prg := &program{
63+
func Inspect(path string, excludes ess.Excludes, registeredActions map[string]map[string]uint8) (*Program, []error) {
64+
prg := &Program{
7065
Path: path,
7166
Packages: []*packageInfo{},
7267
RegisteredActions: registeredActions,
7368
}
7469

70+
if err := validateInput(path); err != nil {
71+
return prg, append([]error{}, err)
72+
}
73+
7574
var (
7675
pkgs map[string]*ast.Package
7776
errs []error
@@ -138,281 +137,54 @@ func Inspect(path string, excludes ess.Excludes, registeredActions map[string]ma
138137
return prg, errs
139138
}
140139

141-
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
142-
// Program struct and its methods
143-
//______________________________________________________________________________
144-
145-
// Program holds all details loaded from the Go source code for given Path.
146-
type program struct {
147-
Path string
148-
Packages []*packageInfo
149-
RegisteredActions map[string]map[string]uint8
150-
}
151-
152-
// Process method processes all packages in the program for `Type`,
153-
// `Embedded Type`, `Method`, etc.
154-
func (prg *program) Process() {
155-
for _, pkgInfo := range prg.Packages {
156-
pkgInfo.Types = map[string]*typeInfo{}
157-
158-
// Each source file
159-
for name, file := range pkgInfo.Pkg.Files {
160-
pkgInfo.Files = append(pkgInfo.Files, filepath.Base(name))
161-
fileImports := make(map[string]string)
162-
163-
for _, decl := range file.Decls {
164-
// Processing imports
165-
pkgInfo.processImports(decl, fileImports)
166-
167-
// Processing types
168-
pkgInfo.processTypes(decl, fileImports)
169-
170-
// Processing methods
171-
processMethods(pkgInfo, prg.RegisteredActions, decl, fileImports)
172-
}
173-
}
140+
// FindFieldIndexes method does breadth-first search on struct
141+
// anonymous field to find given type `struct` discover index positions.
142+
//
143+
// For e.g.: `aah.Context`, `ws.Context`, etc.
144+
func FindFieldIndexes(targetTyp reflect.Type, searchTyp reflect.Type) [][]int {
145+
var indexes [][]int
146+
type nodeType struct {
147+
val reflect.Value
148+
index []int
174149
}
175-
}
176150

177-
// FindTypeByEmbeddedType method returns all the typeInfo that has directly or
178-
// indirectly embedded by given type name. Type name must be fully qualified
179-
// type name. E.g.: aahframework.org/aah.Controller
180-
func (prg *program) FindTypeByEmbeddedType(qualifiedTypeName string) []*typeInfo {
181-
var (
182-
queue = []string{qualifiedTypeName}
183-
processed []string
184-
result []*typeInfo
185-
)
151+
queue := []nodeType{{reflect.New(targetTyp), []int{}}}
186152

187153
for len(queue) > 0 {
188-
typeName := queue[0]
189-
queue = queue[1:]
190-
processed = append(processed, typeName)
191-
192-
// search within all packages in the program
193-
for _, p := range prg.Packages {
194-
// search within all struct type in the package
195-
for _, t := range p.Types {
196-
// If this one has been processed or is already in queue, then move on.
197-
if ess.IsSliceContainsString(processed, t.FullyQualifiedName()) ||
198-
ess.IsSliceContainsString(queue, t.FullyQualifiedName()) {
199-
continue
200-
}
201-
202-
// search through the embedded types to see if the current type is among them.
203-
for _, et := range t.EmbeddedTypes {
204-
// If so, add this type's FullyQualifiedName into queue,
205-
// and it's typeInfo into result.
206-
if typeName == et.FullyQualifiedName() {
207-
queue = append(queue, t.FullyQualifiedName())
208-
result = append(result, t)
209-
break
210-
}
211-
}
212-
}
213-
}
214-
}
215-
216-
return result
217-
}
218-
219-
// CreateImportPaths method returns unique package alias with import path.
220-
func (prg *program) CreateImportPaths(types []*typeInfo) map[string]string {
221-
importPaths := map[string]string{}
222-
for _, t := range types {
223-
createAlias(t.PackageName(), t.ImportPath, importPaths)
224-
for _, m := range t.Methods {
225-
for _, p := range m.Parameters {
226-
if !p.Type.IsBuiltIn {
227-
createAlias(p.Type.PackageName, p.ImportPath, importPaths)
228-
}
229-
}
230-
}
231-
}
232-
233-
return importPaths
234-
}
154+
var (
155+
node = queue[0]
156+
elem = node.val
157+
elemType = elem.Type()
158+
)
235159

236-
func createAlias(packageName, importPath string, importPaths map[string]string) {
237-
importPath = filepath.ToSlash(importPath)
238-
if _, found := importPaths[importPath]; !found {
239-
cnt := 0
240-
pkgAlias := packageName
241-
242-
for isPkgAliasExists(importPaths, pkgAlias) {
243-
pkgAlias = fmt.Sprintf("%s%d", packageName, cnt)
244-
cnt++
245-
}
246-
247-
if !ess.IsStrEmpty(pkgAlias) && !ess.IsStrEmpty(importPath) {
248-
importPaths[importPath] = pkgAlias
160+
if elemType.Kind() == reflect.Ptr {
161+
elem = elem.Elem()
162+
elemType = elem.Type()
249163
}
250-
}
251-
}
252164

253-
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
254-
// PackageInfo struct and its methods
255-
//______________________________________________________________________________
256-
257-
// PackageInfo holds the single paackge information.
258-
type packageInfo struct {
259-
Fset *token.FileSet
260-
Pkg *ast.Package
261-
Types map[string]*typeInfo
262-
ImportPath string
263-
FilePath string
264-
Files []string
265-
}
266-
267-
// Name method return package name
268-
func (p *packageInfo) Name() string {
269-
return filepath.Base(p.ImportPath)
270-
}
271-
272-
func (p *packageInfo) processTypes(decl ast.Decl, imports map[string]string) {
273-
genDecl, ok := decl.(*ast.GenDecl)
274-
if !ok || !isTypeTok(genDecl) || len(genDecl.Specs) == 0 {
275-
return
276-
}
277-
278-
spec := genDecl.Specs[0].(*ast.TypeSpec)
279-
st, ok := spec.Type.(*ast.StructType)
280-
if !ok {
281-
// Not a struct type
282-
return
283-
}
284-
285-
typeName := spec.Name.Name
286-
ty := &typeInfo{
287-
Name: typeName,
288-
ImportPath: filepath.ToSlash(p.ImportPath),
289-
Methods: make([]*methodInfo, 0),
290-
EmbeddedTypes: make([]*typeInfo, 0),
291-
}
292-
293-
for _, field := range st.Fields.List {
294-
// If field.Names is set, it's not an embedded type.
295-
if field.Names != nil && len(field.Names) > 0 {
296-
continue
297-
}
298-
299-
fPkgName, fTypeName := parseStructFieldExpr(field.Type)
300-
if ess.IsStrEmpty(fTypeName) {
165+
queue = queue[1:]
166+
if elemType.Kind() != reflect.Struct {
301167
continue
302168
}
303169

304-
// Find the import path for embedded type. If it was referenced without
305-
// a package name, use the current package import path otherwise
306-
// get the import path by package name.
307-
var eTypeImportPath string
308-
if ess.IsStrEmpty(fPkgName) {
309-
eTypeImportPath = ty.ImportPath
310-
} else {
311-
var found bool
312-
if eTypeImportPath, found = imports[fPkgName]; !found {
313-
log.Errorf("AST: Unable to find import path for %s.%s", fPkgName, fTypeName)
170+
for i := 0; i < elem.NumField(); i++ {
171+
// skip non-anonymous fields
172+
field := elemType.Field(i)
173+
if !field.Anonymous {
314174
continue
315175
}
316-
}
317-
318-
ty.EmbeddedTypes = append(ty.EmbeddedTypes, &typeInfo{Name: fTypeName, ImportPath: eTypeImportPath})
319-
}
320176

321-
p.Types[typeName] = ty
322-
}
323-
324-
func (p *packageInfo) processImports(decl ast.Decl, imports map[string]string) {
325-
genDecl, ok := decl.(*ast.GenDecl)
326-
if !ok || !isImportTok(genDecl) {
327-
return
328-
}
329-
330-
for _, dspec := range genDecl.Specs {
331-
spec := dspec.(*ast.ImportSpec)
332-
var pkgAlias string
333-
if spec.Name != nil {
334-
if spec.Name.Name == "_" {
177+
// If it's a search type then record the field index and move on
178+
if field.Type == searchTyp {
179+
indexes = append(indexes, append(node.index, i))
335180
continue
336181
}
337182

338-
pkgAlias = spec.Name.Name
183+
fieldValue := elem.Field(i)
184+
queue = append(queue,
185+
nodeType{fieldValue, append(append([]int{}, node.index...), i)})
339186
}
340-
341-
importPath := spec.Path.Value[1 : len(spec.Path.Value)-1]
342-
if ess.IsStrEmpty(pkgAlias) {
343-
if alias, found := buildImportCache[importPath]; found {
344-
pkgAlias = alias
345-
} else { // build cache
346-
pkg, err := build.Import(importPath, p.FilePath, 0)
347-
if err != nil {
348-
log.Errorf("AST: Unable to find import path: %s", importPath)
349-
continue
350-
}
351-
pkgAlias = pkg.Name
352-
buildImportCache[importPath] = pkg.Name
353-
}
354-
}
355-
356-
imports[pkgAlias] = importPath
357-
}
358-
}
359-
360-
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
361-
// TypeInfo struct and its methods
362-
//______________________________________________________________________________
363-
364-
// TypeInfo holds the information about Controller Name, Methods,
365-
// Embedded types etc.
366-
type typeInfo struct {
367-
Name string
368-
ImportPath string
369-
Methods []*methodInfo
370-
EmbeddedTypes []*typeInfo
371-
}
372-
373-
// MethodInfo holds the information of single method and it's Parameters.
374-
type methodInfo struct {
375-
Name string
376-
StructName string
377-
Parameters []*parameterInfo
378-
}
379-
380-
// ParameterInfo holds the information of single Parameter in the method.
381-
type parameterInfo struct {
382-
Name string
383-
ImportPath string
384-
Type *typeExpr
385-
}
386-
387-
// FullyQualifiedName method returns the fully qualified type name.
388-
func (t *typeInfo) FullyQualifiedName() string {
389-
return fmt.Sprintf("%s.%s", t.ImportPath, t.Name)
390-
}
391-
392-
// PackageName method returns types package name from import path.
393-
func (t *typeInfo) PackageName() string {
394-
return filepath.Base(t.ImportPath)
395-
}
396-
397-
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
398-
// TypeExpr struct and its methods
399-
//______________________________________________________________________________
400-
401-
// TypeExpr holds the information of single parameter data type.
402-
type typeExpr struct {
403-
Expr string
404-
IsBuiltIn bool
405-
PackageName string
406-
ImportPath string
407-
PackageIndex uint8
408-
Valid bool
409-
}
410-
411-
// Name method returns type name for expression.
412-
func (te *typeExpr) Name() string {
413-
if te.IsBuiltIn || ess.IsStrEmpty(te.PackageName) {
414-
return te.Expr
415187
}
416188

417-
return fmt.Sprintf("%s%s.%s", te.Expr[:te.PackageIndex], te.PackageName, te.Expr[te.PackageIndex:])
189+
return indexes
418190
}

0 commit comments

Comments
 (0)