Skip to content

Commit

Permalink
File position (#67)
Browse files Browse the repository at this point in the history
Co-authored-by: alan <[email protected]>
Co-authored-by: Ondrej Fabry <[email protected]>
Co-authored-by: adieUkid <[email protected]>
  • Loading branch information
4 people authored Dec 29, 2020
1 parent fbe7e9f commit e6144a9
Show file tree
Hide file tree
Showing 5 changed files with 401 additions and 311 deletions.
276 changes: 238 additions & 38 deletions analysis.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,77 @@
package main

import (
"errors"
"fmt"
"io"
"go/types"
"os"
"strings"
"net/http"
"path/filepath"
"log"

"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)

var Analysis *analysis
//==[ type def/func: analysis ]===============================================
type renderOpts struct {
cacheDir string
focus string
group []string
ignore []string
include []string
limit []string
nointer bool
refresh bool
nostd bool
}

// mainPackages returns the main packages to analyze.
// Each resulting package is named "main" and has a main function.
func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
var mains []*ssa.Package
for _, p := range pkgs {
if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil {
mains = append(mains, p)
}
}
if len(mains) == 0 {
return nil, fmt.Errorf("no main packages")
}
return mains, nil
}

//==[ type def/func: analysis ]===============================================
type analysis struct {
opts *renderOpts
prog *ssa.Program
pkgs []*ssa.Package
mains []*ssa.Package
result *pointer.Result
}

func doAnalysis(dir string, tests bool, args []string) error {
var Analysis *analysis

func (a *analysis) DoAnalysis(
dir string,
tests bool,
args []string,
) error {
cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Tests: tests,
Dir: dir,
}

initial, err := packages.Load(cfg, args...)
if err != nil {
return err
}

if packages.PrintErrors(initial) > 0 {
return fmt.Errorf("packages contain errors")
}
Expand All @@ -43,79 +84,143 @@ func doAnalysis(dir string, tests bool, args []string) error {
if err != nil {
return err
}

config := &pointer.Config{
Mains: mains,
BuildCallGraph: true,
}

result, err := pointer.Analyze(config)
if err != nil {
return err // internal error in pointer analysis
}
//cg.DeleteSyntheticNodes()

Analysis = &analysis{
prog: prog,
pkgs: pkgs,
mains: mains,
result: result,
}
a.prog = prog
a.pkgs = pkgs
a.mains = mains
a.result = result
return nil
}

// mainPackages returns the main packages to analyze.
// Each resulting package is named "main" and has a main function.
func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
var mains []*ssa.Package
for _, p := range pkgs {
if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil {
mains = append(mains, p)
func (a *analysis) OptsSetup() {
a.opts = &renderOpts{
cacheDir: *cacheDir,
focus: *focusFlag,
group: []string{*groupFlag},
ignore: []string{*ignoreFlag},
include: []string{*includeFlag},
limit: []string{*limitFlag},
nointer: *nointerFlag,
nostd: *nostdFlag,
}
}

func (a *analysis) ProcessListArgs() (e error) {
var groupBy []string
var ignorePaths []string
var includePaths []string
var limitPaths []string

for _, g := range strings.Split(a.opts.group[0], ",") {
g := strings.TrimSpace(g)
if g == "" {
continue
}
if g != "pkg" && g != "type" {
e = errors.New("invalid group option")
return
}
groupBy = append(groupBy, g)
}
if len(mains) == 0 {
return nil, fmt.Errorf("no main packages")

for _, p := range strings.Split(a.opts.ignore[0], ",") {
p = strings.TrimSpace(p)
if p != "" {
ignorePaths = append(ignorePaths, p)
}
}
return mains, nil

for _, p := range strings.Split(a.opts.include[0], ",") {
p = strings.TrimSpace(p)
if p != "" {
includePaths = append(includePaths, p)
}
}

for _, p := range strings.Split(a.opts.limit[0], ",") {
p = strings.TrimSpace(p)
if p != "" {
limitPaths = append(limitPaths, p)
}
}

a.opts.group = groupBy
a.opts.ignore = ignorePaths
a.opts.include = includePaths
a.opts.limit = limitPaths

return
}

type renderOpts struct {
cacheDir string
focus string
group []string
ignore []string
include []string
limit []string
nointer bool
refresh bool
nostd bool
func (a *analysis) OverrideByHTTP(r *http.Request) () {
if f := r.FormValue("f"); f == "all" {
a.opts.focus = ""
} else if f != "" {
a.opts.focus = f
}
if std := r.FormValue("std"); std != "" {
a.opts.nostd = false
}
if inter := r.FormValue("nointer"); inter != "" {
a.opts.nointer = true
}
if refresh := r.FormValue("refresh"); refresh != "" {
a.opts.refresh = true
}
if g := r.FormValue("group"); g != "" {
a.opts.group[0] = g
}
if l := r.FormValue("limit"); l != "" {
a.opts.limit[0] = l
}
if ign := r.FormValue("ignore"); ign != "" {
a.opts.ignore[0] = ign
}
if inc := r.FormValue("include"); inc != "" {
a.opts.include[0] = inc
}
return
}

func (a *analysis) render(opts *renderOpts) ([]byte, error) {
// basically do printOutput() with previously checking
// focus option and respective package
func (a *analysis) Render() ([]byte, error) {
var (
err error
ssaPkg *ssa.Package
focusPkg *types.Package
)

if opts.focus != "" {
ssaPkg = a.prog.ImportedPackage(opts.focus)
if ssaPkg == nil {
if strings.Contains(opts.focus, "/") {
if a.opts.focus != "" {
if ssaPkg = a.prog.ImportedPackage(a.opts.focus); ssaPkg == nil {
if strings.Contains(a.opts.focus, "/") {
return nil, fmt.Errorf("focus failed: %v", err)
}
// try to find package by name
var foundPaths []string
for _, p := range a.pkgs {
if p.Pkg.Name() == opts.focus {
if p.Pkg.Name() == a.opts.focus {
foundPaths = append(foundPaths, p.Pkg.Path())
}
}
if len(foundPaths) == 0 {
return nil, fmt.Errorf("focus failed, could not find package: %v", opts.focus)
return nil, fmt.Errorf("focus failed, could not find package: %v", a.opts.focus)
} else if len(foundPaths) > 1 {
for _, p := range foundPaths {
fmt.Fprintf(os.Stderr, " - %s\n", p)
}
return nil, fmt.Errorf("focus failed, found multiple packages with name: %v", opts.focus)
return nil, fmt.Errorf("focus failed, found multiple packages with name: %v", a.opts.focus)
}
// found single package
if ssaPkg = a.prog.ImportedPackage(foundPaths[0]); ssaPkg == nil {
Expand All @@ -126,11 +231,106 @@ func (a *analysis) render(opts *renderOpts) ([]byte, error) {
logf("focusing: %v", focusPkg.Path())
}

dot, err := printOutput(a.prog, a.mains[0].Pkg, a.result.CallGraph,
focusPkg, opts.limit, opts.ignore, opts.include, opts.group, opts.nostd, opts.nointer)
dot, err := printOutput(
a.prog,
a.mains[0].Pkg,
a.result.CallGraph,
focusPkg,
a.opts.limit,
a.opts.ignore,
a.opts.include,
a.opts.group,
a.opts.nostd,
a.opts.nointer,
)
if err != nil {
return nil, fmt.Errorf("processing failed: %v", err)
}

return dot, nil
}

func (a *analysis) FindCachedImg() string {
if a.opts.cacheDir == "" || a.opts.refresh {
return ""
}

focus := a.opts.focus
if focus == "" {
focus = "all"
}
focusFilePath := focus + "." + *outputFormat
absFilePath := filepath.Join(a.opts.cacheDir, focusFilePath)

if exists, err := pathExists(absFilePath); err != nil || !exists {
log.Println("not cached img:", absFilePath)
return ""
}

log.Println("hit cached img")
return absFilePath
}

func (a *analysis) CacheImg(img string) error {
if a.opts.cacheDir == "" || img == "" {
return nil
}

focus := a.opts.focus
if focus == "" {
focus = "all"
}
absCacheDirPrefix := filepath.Join(a.opts.cacheDir, focus)
absCacheDirPath := strings.TrimRightFunc(absCacheDirPrefix, func(r rune) bool {
return r != '\\' && r != '/'
})
err := os.MkdirAll(absCacheDirPath, os.ModePerm)
if err != nil {
return err
}

absFilePath := absCacheDirPrefix + "." + *outputFormat
_, err = copyFile(img, absFilePath)
if err != nil {
return err
}

return nil
}

func pathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}

func copyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)

if err != nil {
return 0, err
}

if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}

source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()

destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
Loading

0 comments on commit e6144a9

Please sign in to comment.