func initParserMode() { parserMode = parser.Mode(0) parserMode |= parser.ParseComments if *allErrors { parserMode |= parser.AllErrors } }
func GrokDir(dir string) { pkgs, err := parser.ParseDir(fset, dir, FilterDotGo, parser.Mode(0)) if err != nil { panic(Sprintf("ERROR <%q> IN DIR <%s>", err, dir)) } // Find the subdir that follows /pkg/ subdir := AfterFirstDelim(dir, "/pkg/", "") if len(subdir) == 0 { panic("dir does not contain /pkg/ plus a tail: " + dir) } // Trim trailing /, if any. if subdir[len(subdir)-1] == '/' { subdir = subdir[:len(subdir)-1] } for pk, pv := range pkgs { // For dirpkg, remove final piece of subdir // and replace with stated package name. // Often these are the same, but not always. updir := BeforeFinalDelim(subdir, "/", "") dirpkg := Cond(updir == "", pk, updir+"/"+pk) Printf("#dirpkg %#v\n", dirpkg) Printf("#pv %#v\n", pv) for _, fv := range pv.Files { for i, dcl := range fv.Decls { doDecl(dcl, i, dirpkg) } } } }
func goInitParserMode() { goParserMode = parser.Mode(0) if *comments { goParserMode |= parser.ParseComments } goParserMode |= parser.AllErrors }
func TestBump(t *testing.T) { fset := token.NewFileSet() pkgs, err := parser.ParseDir(fset, "testdata/test1", nil, parser.Mode(0)) if err != nil { t.Fatal(err) } conf := Config{ MinorDelta: 1, } for _, pkg := range pkgs { for _, f := range pkg.Files { vers, err := conf.ProcessNode(fset, f) if err != nil { t.Errorf("got error: %s", err) } if _, ok := vers["version"]; !ok { t.Errorf("should detect `version`") } if _, ok := vers["VERSION"]; !ok { t.Errorf("should detect `VERSION`") } if vers["version"] != "1.1.0" { t.Errorf("expected %v: got %v", "1.1.0", vers["version"]) } if vers["VERSION"] != "2.1.0" { t.Errorf("expected %v: got %v", "2.1.0", vers["VERSION"]) } } } }
// fastQueryPos parses the position string and returns a queryPos. // It parses only a single file and does not run the type checker. func fastQueryPos(ctxt *build.Context, pos string) (*queryPos, error) { filename, startOffset, endOffset, err := parsePos(pos) if err != nil { return nil, err } // Parse the file, opening it the file via the build.Context // so that we observe the effects of the -modified flag. fset := token.NewFileSet() cwd, _ := os.Getwd() f, err := buildutil.ParseFile(fset, ctxt, nil, cwd, filename, parser.Mode(0)) // ParseFile usually returns a partial file along with an error. // Only fail if there is no file. if f == nil { return nil, err } if !f.Pos().IsValid() { return nil, fmt.Errorf("%s is not a Go source file", filename) } start, end, err := fileOffsetToPos(fset.File(f.Pos()), startOffset, endOffset) if err != nil { return nil, err } path, exact := astutil.PathEnclosingInterval(f, start, end) if path == nil { return nil, fmt.Errorf("no syntax here") } return &queryPos{fset, start, end, path, exact, nil}, nil }
func initParserMode() { parserMode = parser.Mode(0) if *comments { parserMode |= parser.ParseComments } if *allErrors { parserMode |= parser.SpuriousErrors } }
// importPackages includes packages defined on external file into main file func (s *Session) importPackages(src []byte) error { astf, err := parser.ParseFile(s.Fset, "", src, parser.Mode(0)) if err != nil { return err } for _, imt := range astf.Imports { debugf("import package: %s", imt.Path.Value) actionImport(s, imt.Path.Value) } return nil }
// applyAroundAdvice uses code from gofmt to wrap any after advice // essentially this is the same stuff you could do w/the cmdline tool, // gofmt // // FIXME - mv to CallExpr // // looks for call joinpoints && provides around advice capability // // this is currently a hack to support deferpanic's http lib func (w *Weave) applyAroundAdvice(fname string) string { stuff := fileAsStr(fname) importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if aspect.advize.around != "" { pk := aspect.pointkut around_advice := aspect.advize.around fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, stuff, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } // needs match groups // wildcards of d,s...etc. p := parseExpr(pk.def) val := parseExpr(around_advice) file = rewriteFile2(p, val, file) buf := new(bytes.Buffer) err = format.Node(buf, fset, file) if err != nil { w.flog.Println("Failed to format post-replace source: %v", err) } actual := buf.String() w.writeOut(fname, actual) stuff = actual for t := 0; t < len(aspect.importz); t++ { importsNeeded = append(importsNeeded, aspect.importz[t]) } } } if len(importsNeeded) > 0 { // add any imports for this piece of advice stuff = w.writeMissingImports(fname, stuff, importsNeeded) } return stuff }
// findPackageMember returns the type and position of the declaration of // pkg.member by loading and parsing the files of that package. // srcdir is the directory in which the import appears. func findPackageMember(ctxt *build.Context, fset *token.FileSet, srcdir, pkg, member string) (token.Token, token.Pos, error) { bp, err := ctxt.Import(pkg, srcdir, 0) if err != nil { return 0, token.NoPos, err // no files for package } // TODO(adonovan): opt: parallelize. for _, fname := range bp.GoFiles { filename := filepath.Join(bp.Dir, fname) // Parse the file, opening it the file via the build.Context // so that we observe the effects of the -modified flag. f, _ := buildutil.ParseFile(fset, ctxt, nil, ".", filename, parser.Mode(0)) if f == nil { continue } // Find a package-level decl called 'member'. for _, decl := range f.Decls { switch decl := decl.(type) { case *ast.GenDecl: for _, spec := range decl.Specs { switch spec := spec.(type) { case *ast.ValueSpec: // const or var for _, id := range spec.Names { if id.Name == member { return decl.Tok, id.Pos(), nil } } case *ast.TypeSpec: if spec.Name.Name == member { return token.TYPE, spec.Name.Pos(), nil } case *ast.AliasSpec: if spec.Name.Name == member { return decl.Tok, spec.Name.Pos(), nil } } } case *ast.FuncDecl: if decl.Recv == nil && decl.Name.Name == member { return token.FUNC, decl.Name.Pos(), nil } } } } return 0, token.NoPos, fmt.Errorf("couldn't find declaration of %s in %q", member, pkg) }
// applyGlobalAdvice applies any global scoped advice // if advice has already been placed in this pkg than we skip // no support for sub-pkgs yet // FIXME func (w *Weave) applyGlobalAdvice(fname string, stuff string) string { if w.appliedGlobal { return stuff } rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if aspect.pointkut.def != "*" { continue } before_advice := aspect.advize.before after_advice := aspect.advize.after w.appliedGlobal = true fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } // endOfImport returns the line after the import statement // used to add global advice // just a guess here - doubt this is ordered ilen := len(file.Imports) s := file.Imports[ilen-1].End() ei := fset.Position(s).Line + 1 if before_advice != "" { rout = w.writeAtLine(fname, ei, before_advice) } if after_advice != "" { rout = w.writeAtLine(fname, ei, after_advice) } } if len(importsNeeded) > 0 { rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }
func (s *Session) reset() error { source, err := s.source(false) if err != nil { return err } file, err := parser.ParseFile(s.Fset, "gore_session.go", source, parser.Mode(0)) if err != nil { return err } s.File = file s.mainBody = s.mainFunc().Body return nil }
func TestDeadCode(t *testing.T) { // We'll use dead code detection to verify the CFG. fset := token.NewFileSet() f, err := parser.ParseFile(fset, "dummy.go", src, parser.Mode(0)) if err != nil { t.Fatal(err) } for _, decl := range f.Decls { if decl, ok := decl.(*ast.FuncDecl); ok { g := New(decl.Body, mayReturn) // Mark blocks reachable from entry. live := make(map[*Block]bool) var visit func(*Block) visit = func(b *Block) { if !live[b] { live[b] = true for _, succ := range b.Succs { visit(succ) } } } visit(g.Blocks[0]) // Print statements in unreachable blocks // (in order determined by builder). var buf bytes.Buffer for _, b := range g.Blocks { if !live[b] { for _, n := range b.Nodes { fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n)) } } } // Check that the result contains "dead" at least once but not "live". if !bytes.Contains(buf.Bytes(), []byte("dead")) || bytes.Contains(buf.Bytes(), []byte("live")) { t.Errorf("unexpected dead statements in function %s:\n%s", decl.Name.Name, &buf) t.Logf("control flow graph:\n%s", g.Format(fset)) } } } }
// importFile adds external golang file to goRun target to use its function func (s *Session) importFile(src []byte) error { // Don't need to same directory tmp, err := ioutil.TempFile(filepath.Dir(s.FilePath), "gore_extarnal_") if err != nil { return err } ext := tmp.Name() + ".go" f, err := parser.ParseFile(s.Fset, ext, src, parser.Mode(0)) if err != nil { return err } // rewrite to package main f.Name.Name = "main" // remove func main() for i, decl := range f.Decls { if funcDecl, ok := decl.(*ast.FuncDecl); ok { if isNamedIdent(funcDecl.Name, "main") { f.Decls = append(f.Decls[0:i], f.Decls[i+1:]...) // main() removed from this file, we may have to // remove some unsed import's quickfix.QuickFix(s.Fset, []*ast.File{f}) break } } } out, err := os.Create(ext) if err != nil { return err } defer out.Close() err = printer.Fprint(out, s.Fset, f) if err != nil { return err } debugf("import file: %s", ext) s.ExtraFilePaths = append(s.ExtraFilePaths, ext) s.ExtraFiles = append(s.ExtraFiles, f) return nil }
func doDir(name string) { notests := func(info os.FileInfo) bool { if !info.IsDir() && strings.HasSuffix(info.Name(), ".go") && (!strings.HasSuffix(info.Name(), "_test.go") || *includeTests) { return true } return false } fs := token.NewFileSet() pkgs, err := parser.ParseDir(fs, name, notests, parser.Mode(0)) if err != nil { errorf("%s", err) return } for _, pkg := range pkgs { doPackage(fs, pkg) } }
// fixImports formats and adjusts imports for the current AST. func (s *Session) fixImports() error { var buf bytes.Buffer err := printer.Fprint(&buf, s.Fset, s.File) if err != nil { return err } formatted, err := imports.Process("", buf.Bytes(), nil) if err != nil { return err } s.File, err = parser.ParseFile(s.Fset, "", formatted, parser.Mode(0)) if err != nil { return err } s.mainBody = s.mainFunc().Body return nil }
func NewSession() (*Session, error) { var err error s := &Session{ Fset: token.NewFileSet(), Types: &types.Config{ Packages: make(map[string]*types.Package), }, } s.FilePath, err = tempFile() if err != nil { return nil, err } var initialSource string for _, pp := range printerPkgs { _, err := types.DefaultImport(s.Types.Packages, pp.path) if err == nil { initialSource = fmt.Sprintf(initialSourceTemplate, pp.path, pp.code) break } debugf("could not import %q: %s", pp.path, err) } if initialSource == "" { return nil, fmt.Errorf(`Could not load pretty printing package (even "fmt"; something is wrong)`) } s.File, err = parser.ParseFile(s.Fset, "gore_session.go", initialSource, parser.Mode(0)) if err != nil { return nil, err } s.mainBody = s.mainFunc().Body return s, nil }
// NewSession initiates a new REPL func NewSession() (*Session, error) { s := &Session{ Fset: token.NewFileSet(), Types: &types.Config{ Importer: importer.Default(), }, } var err error s.FilePath, err = tempFile() if err != nil { return nil, err } var initialSource string for _, pp := range printerPkgs { _, err := importer.Default().Import(pp.path) if err == nil { initialSource = fmt.Sprintf(initialSourceTemplate, pp.path, pp.code) break } debugf("could not import %q: %s", pp.path, err) } if initialSource == "" { return nil, fmt.Errorf("Could not load pretty printing package") } s.File, err = parser.ParseFile(s.Fset, "gophernotes_session.go", initialSource, parser.Mode(0)) if err != nil { return nil, err } s.mainBody = s.mainFunc().Body return s, nil }
func (s *Session) evalStmt(in string) error { src := fmt.Sprintf("package P; func F() { %s }", in) f, err := parser.ParseFile(s.Fset, "stmt.go", src, parser.Mode(0)) if err != nil { return err } enclosingFunc := f.Scope.Lookup("F").Decl.(*ast.FuncDecl) stmts := enclosingFunc.Body.List if len(stmts) > 0 { debugf("evalStmt :: %s", showNode(s.Fset, stmts)) lastStmt := stmts[len(stmts)-1] // print last assigned/defined values if assign, ok := lastStmt.(*ast.AssignStmt); ok { vs := []ast.Expr{} for _, v := range assign.Lhs { if !isNamedIdent(v, "_") { vs = append(vs, v) } } if len(vs) > 0 { printLastValues := &ast.ExprStmt{ X: &ast.CallExpr{ Fun: ast.NewIdent(printerName), Args: vs, }, } stmts = append(stmts, printLastValues) } } } s.appendStatements(stmts...) return nil }
// applySetJP applies any advice for set joinpoints func (w *Weave) applySetJP(fname string, stuff string) string { rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if !aspect.pointkut.isSet() { continue } pk := aspect.pointkut.def fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } linecnt := 0 for _, decl := range file.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok { continue } for x := 0; x < len(fn.Body.List); x++ { begin := 0 after := 0 as, ok2 := fn.Body.List[x].(*ast.SendStmt) if !ok2 { // look for assignment //*ast.AssignStmt as, ok3 := fn.Body.List[x].(*ast.AssignStmt) if !ok3 { continue } // no multiple-return support yet blah := as.Lhs[0].(*ast.Ident).Name if pk != blah { continue } begin = fset.Position(as.Pos()).Line - 1 after = fset.Position(as.End()).Line + 1 } else { // look for channel as3, ok3 := as.Chan.(*ast.Ident) if !ok3 { continue } if as3.Name != pk { continue } begin = fset.Position(as.Pos()).Line - 1 after = fset.Position(as.End()).Line + 1 } // figure out type // begin := fset.Position(as.Pos()).Line - 1 // after := fset.Position(as.End()).Line + 1 before_advice := aspect.advize.before after_advice := aspect.advize.after if before_advice != "" { rout = w.writeAtLine(fname, begin+linecnt, before_advice) linecnt += strings.Count(before_advice, "\n") + 1 } if after_advice != "" { rout = w.writeAtLine(fname, after+linecnt-1, after_advice) linecnt += strings.Count(after_advice, "\n") + 1 } } } } if len(importsNeeded) > 0 { // add any imports for this piece of advice rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }
// parse parses src, which was read from filename, // as a Go source file or statement list. func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) { parserMode := parser.Mode(0) if opt.Comments { parserMode |= parser.ParseComments } if opt.AllErrors { parserMode |= parser.AllErrors } // Try as whole source file. file, err := parser.ParseFile(fset, filename, src, parserMode) if err == nil { return file, nil, nil } // If the error is that the source file didn't begin with a // package line and we accept fragmented input, fall through to // try as a source fragment. Stop and return on any other error. if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") { return nil, nil, err } // If this is a declaration list, make it a source file // by inserting a package clause. // Insert using a ;, not a newline, so that the line numbers // in psrc match the ones in src. psrc := append([]byte("package main;"), src...) file, err = parser.ParseFile(fset, filename, psrc, parserMode) if err == nil { // If a main function exists, we will assume this is a main // package and leave the file. if containsMainFunc(file) { return file, nil, nil } adjust := func(orig, src []byte) []byte { // Remove the package clause. // Gofmt has turned the ; into a \n. src = src[len("package main\n"):] return matchSpace(orig, src) } return file, adjust, nil } // If the error is that the source file didn't begin with a // declaration, fall through to try as a statement list. // Stop and return on any other error. if !strings.Contains(err.Error(), "expected declaration") { return nil, nil, err } // If this is a statement list, make it a source file // by inserting a package clause and turning the list // into a function body. This handles expressions too. // Insert using a ;, not a newline, so that the line numbers // in fsrc match the ones in src. fsrc := append(append([]byte("package p; func _() {"), src...), '}') file, err = parser.ParseFile(fset, filename, fsrc, parserMode) if err == nil { adjust := func(orig, src []byte) []byte { // Remove the wrapping. // Gofmt has turned the ; into a \n\n. src = src[len("package p\n\nfunc _() {"):] src = src[:len(src)-len("}\n")] // Gofmt has also indented the function body one level. // Remove that indent. src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) return matchSpace(orig, src) } return file, adjust, nil } // Failed, and out of options. return nil, nil, err }
// applyCallAdvice applies call advice before/after a call // around advice is currently hacked in via applyAroundAdvice func (w *Weave) applyCallAdvice(fname string, stuff string) string { rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if aspect.pointkut.kind != 1 { continue } linecnt := 0 pk := aspect.pointkut.def before_advice := aspect.advize.before after_advice := aspect.advize.after fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } // look for call expressions - call joinpoints // bend keeps track of tailing binaryExprs // if a call-expression's end is not > then we use this lastbinstart := 0 lastbinend := 0 ast.Inspect(file, func(n ast.Node) bool { switch stmt := n.(type) { case *ast.CompositeLit: // log.Printf("found a composite lit\n") lbs := fset.Position(stmt.Lbrace).Line lbe := fset.Position(stmt.Rbrace).Line if lbs > lastbinstart { lastbinstart = lbs } if lbe > lastbinend { lastbinend = lbe } case *ast.BinaryExpr: // child nodes traversed in DFS - so we want the max // it's ok when newer nodes re-write this out of scope lbs := fset.Position(stmt.X.Pos()).Line lbe := fset.Position(stmt.Y.Pos()).Line if lbs > lastbinstart { lastbinstart = lbs } if lbe > lastbinend { lastbinend = lbe } // log.Printf("last bin start %d, last bin end %d", lastbinstart, lastbinend) case *ast.CallExpr: var name string switch x := stmt.Fun.(type) { case *ast.Ident: name = x.Name case *ast.SelectorExpr: name = x.Sel.Name default: name = "WRONG" } pk = strings.Split(pk, "(")[0] // fixme - hack - we need support for identifying call // expressions w/pkgs if strings.Contains(pk, ".") { pk = strings.Split(pk, ".")[1] } // are we part of a bigger expression? // if so grab our lines so we don't erroneously set them if (string(name) != pk) && (len(stmt.Args) > 1) { // log.Printf("found comma..") // child nodes traversed in DFS - so we want the max // it's ok when newer nodes re-write this out of scope lbs := fset.Position(stmt.Lparen).Line lbe := fset.Position(stmt.Rparen).Line // log.Printf("lbs: %d, lbe: %d\n", lbs, lbe) if lbs > 0 { //lastbinstart { lastbinstart = lbs } if lbe > lastbinend { lastbinend = lbe } } if string(name) == pk { begin := fset.Position(stmt.Lparen).Line end := fset.Position(stmt.Rparen).Line // log.Printf("found expression on line %d\n", begin) //log.Printf("lbs: %d, lbe: %d\n", lbs, lbe) // adjust begin if begin > lastbinend { // log.Printf("using this funcs start %d", begin) } else { if lastbinstart < begin { begin = lastbinstart } // log.Printf("using binexps' start %d", begin) } if end > lastbinend { // log.Printf("using this funcs begin %d", begin) } else { if lastbinstart < begin { begin = lastbinstart } // log.Printf("using binexps' start %d", begin) } // adjust end if lastbinend > end { end = lastbinend } if before_advice != "" { rout = w.writeAtLine(fname, begin+linecnt-1, before_advice) // log.Println(rout) // log.Printf("writing at line %d", begin+linecnt-1) linecnt += strings.Count(before_advice, "\n") + 1 } if after_advice != "" { rout = w.writeAtLine(fname, end+linecnt, after_advice) // log.Println(rout) // log.Printf("writing at line %d", end+linecnt) linecnt += strings.Count(after_advice, "\n") + 1 } for t := 0; t < len(aspect.importz); t++ { importsNeeded = append(importsNeeded, aspect.importz[t]) } } } return true }) } if len(importsNeeded) > 0 { rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }
func (s *Session) evalStmt(in string, noPrint bool) error { src := fmt.Sprintf("package P; func F() { %s }", in) f, err := parser.ParseFile(s.Fset, "stmt.go", src, parser.Mode(0)) if err != nil { debugf("stmt :: err = %s", err) // try to import this as a proxy function and correct for any imports appendForImport := `package main ` f, err := os.Create(string(filepath.Dir(s.FilePath)) + "/func_proxy.go") if err != nil { return err } _, err = f.Write([]byte(appendForImport + in)) if err != nil { return err } f.Close() b := new(bytes.Buffer) cmd := exec.Command("goimports", "-w", string(filepath.Dir(s.FilePath))+"/func_proxy.go") cmd.Stdout = b cmd.Stderr = b err = cmd.Run() if err != nil { err = errors.New(b.String()) return err } functproxy, err := ioutil.ReadFile(string(filepath.Dir(s.FilePath)) + "/func_proxy.go") if err != nil { return err } if err = s.importFile(functproxy); err != nil { errorf("%s", err.Error()) if _, ok := err.(scanner.ErrorList); ok { return ErrContinue } } } enclosingFunc := f.Scope.Lookup("F").Decl.(*ast.FuncDecl) stmts := enclosingFunc.Body.List if len(stmts) > 0 { debugf("evalStmt :: %s", showNode(s.Fset, stmts)) lastStmt := stmts[len(stmts)-1] // print last assigned/defined values if !noPrint { if assign, ok := lastStmt.(*ast.AssignStmt); ok { vs := []ast.Expr{} for _, v := range assign.Lhs { if !isNamedIdent(v, "_") { vs = append(vs, v) } } if len(vs) > 0 { printLastValues := &ast.ExprStmt{ X: &ast.CallExpr{ Fun: ast.NewIdent(printerName), Args: vs, }, } stmts = append(stmts, printLastValues) } } } } s.appendStatements(stmts...) return nil }
// applyGetJP applies any advice for get joinpoints func (w *Weave) applyGetJP(fname string, stuff string) string { rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if !aspect.pointkut.isGet() { continue } pk := aspect.pointkut.def fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } linecnt := 0 for _, decl := range file.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok { continue } for x := 0; x < len(fn.Body.List); x++ { as, ok2 := fn.Body.List[x].(*ast.ExprStmt) if !ok2 { continue } blah, ok3 := as.X.(*ast.CallExpr) if !ok3 { continue } // can either be a unary || a ident (so far) fn2, ok4 := blah.Args[0].(*ast.UnaryExpr) if !ok4 { // look for ident fn3, ok5 := blah.Args[0].(*ast.Ident) if !ok5 { continue } if pk != fn3.Name { continue } } else { // look for channel blah2, ok4 := fn2.X.(*ast.Ident) if !ok4 { continue } if pk != blah2.Name { continue } } begin := fset.Position(as.Pos()).Line - 1 after := fset.Position(as.End()).Line + 1 before_advice := aspect.advize.before after_advice := aspect.advize.after if before_advice != "" { rout = w.writeAtLine(fname, begin+linecnt, before_advice) linecnt += strings.Count(before_advice, "\n") + 1 } if after_advice != "" { rout = w.writeAtLine(fname, after+linecnt-1, after_advice) linecnt += strings.Count(after_advice, "\n") + 1 } } } } if len(importsNeeded) > 0 { // add any imports for this piece of advice rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }
// applyWithinJP applies any advice for within joinpoints // right now this expects both 'before && after' func (w *Weave) applyWithinJP(fname string, stuff string) string { rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if !aspect.pointkut.isWithin() { continue } pk := aspect.pointkut.def fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } linecnt := 0 // look for function declarations - ala look for execution // joinpoints for _, decl := range file.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok { continue } fpk := strings.Split(pk, "(")[0] // if function name missing --> wildcard if fpk == "" { fpk = fn.Name.Name } if fn.Name.Name == fpk && containArgs(pk, fn.Type.Params.List) { wb := WithinBlock{ name: fn.Name.Name, fname: fname, stmts: fn.Body.List, linecnt: linecnt, importsNeeded: importsNeeded, aspect: aspect, fset: fset, } rout, linecnt, importsNeeded = wb.iterateBodyStatements(w) } } } if len(importsNeeded) > 0 { // add any imports for this piece of advice rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }
// CreateTestMainPackage creates and returns a synthetic "testmain" // package for the specified package if it defines tests, benchmarks or // executable examples, or nil otherwise. The new package is named // "main" and provides a function named "main" that runs the tests, // similar to the one that would be created by the 'go test' tool. // // Subsequent calls to prog.AllPackages include the new package. // The package pkg must belong to the program prog. func (prog *Program) CreateTestMainPackage(pkg *Package) *Package { if pkg.Prog != prog { log.Fatal("Package does not belong to Program") } // Template data var data struct { Pkg *Package Tests, Benchmarks, Examples []*Function Main *Function Go18 bool } data.Pkg = pkg // Enumerate tests. data.Tests, data.Benchmarks, data.Examples, data.Main = FindTests(pkg) if data.Main == nil && data.Tests == nil && data.Benchmarks == nil && data.Examples == nil { return nil } // Synthesize source for testmain package. path := pkg.Pkg.Path() + "$testmain" tmpl := testmainTmpl if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { // In Go 1.8, testing.MainStart's first argument is an interface, not a func. data.Go18 = types.IsInterface(testingPkg.Func("MainStart").Signature.Params().At(0).Type()) } else { // The program does not import "testing", but FindTests // returned non-nil, which must mean there were Examples // but no Test, Benchmark, or TestMain functions. // We'll simply call them from testmain.main; this will // ensure they don't panic, but will not check any // "Output:" comments. // (We should not execute an Example that has no // "Output:" comment, but it's impossible to tell here.) tmpl = examplesOnlyTmpl } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { log.Fatalf("internal error expanding template for %s: %v", path, err) } if false { // debugging fmt.Fprintln(os.Stderr, buf.String()) } // Parse and type-check the testmain package. f, err := parser.ParseFile(prog.Fset, path+".go", &buf, parser.Mode(0)) if err != nil { log.Fatalf("internal error parsing %s: %v", path, err) } conf := types.Config{ DisableUnusedImportCheck: true, Importer: importer{pkg}, } files := []*ast.File{f} info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), } testmainPkg, err := conf.Check(path, prog.Fset, files, info) if err != nil { log.Fatalf("internal error type-checking %s: %v", path, err) } // Create and build SSA code. testmain := prog.CreatePackage(testmainPkg, files, info, false) testmain.SetDebugMode(false) testmain.Build() testmain.Func("main").Synthetic = "test main function" testmain.Func("init").Synthetic = "package initializer" return testmain }
// applyExecutionJP applies any advice for execution joinpoints func (w *Weave) applyExecutionJP(fname string, stuff string) string { rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if aspect.pointkut.kind != 2 { continue } pk := aspect.pointkut.def before_advice := aspect.advize.before after_advice := aspect.advize.after fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } linecnt := 0 // look for function declarations - ala look for execution // joinpoints for _, decl := range file.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok { continue } fpk := strings.Split(pk, "(")[0] // if function name missing --> wildcard if fpk == "" { fpk = fn.Name.Name } if fn.Name.Name == fpk && containArgs(pk, fn.Type.Params.List) { // begin line begin := fset.Position(fn.Body.Lbrace).Line after := fset.Position(fn.Body.Rbrace).Line // until this is refactored - any lines we add in our // advice need to be accounted for w/begin if before_advice != "" { rout = w.writeAtLine(fname, begin+linecnt, before_advice) linecnt += strings.Count(before_advice, "\n") + 1 } if after_advice != "" { if fn.Type.Results != nil { lcnt := strings.Count(after_advice, "\n") + 1 rout = w.writeAtLine(fname, after+linecnt-1-lcnt, after_advice) } else { rout = w.writeAtLine(fname, after+linecnt-1, after_advice) } linecnt += strings.Count(after_advice, "\n") + 1 } for t := 0; t < len(aspect.importz); t++ { importsNeeded = append(importsNeeded, aspect.importz[t]) } } } } if len(importsNeeded) > 0 { // add any imports for this piece of advice applyExecutionJP rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }
// applyDeclarationJP applies any advice for set joinpoints // currently expects a channel type // need some extra help here to be agnostic // maybe some helper functions that determine what type it is and // associated meta-data about it // // this is currently a TEST - it only works for specific channels at the // moment func (w *Weave) applyDeclarationJP(fname string, stuff string) string { rout := stuff importsNeeded := []string{} for i := 0; i < len(w.aspects); i++ { aspect := w.aspects[i] if !aspect.pointkut.isDeclaration() { continue } pk := aspect.pointkut.def fset := token.NewFileSet() file, err := parser.ParseFile(fset, fname, rout, parser.Mode(0)) if err != nil { w.flog.Println("Failed to parse source: %s", err.Error()) } linecnt := 0 for _, decl := range file.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok { continue } for x := 0; x < len(fn.Body.List); x++ { as, ok2 := fn.Body.List[x].(*ast.AssignStmt) if !ok2 { continue } // ll-cool-j blah := as.Lhs[0].(*ast.Ident).Name if pk != blah { continue } // figure out type // ll-cool-j once again if len(as.Rhs[0].(*ast.CallExpr).Args) == 2 { _, k := (as.Rhs[0].(*ast.CallExpr).Args[0]).(*ast.ChanType) if !k { continue } r2, k2 := (as.Rhs[0].(*ast.CallExpr).Args[1]).(*ast.BasicLit) if !k2 { continue } avar := AbstractVar{ Kind: "channel", Val: r2.Value, } begin := fset.Position(as.Pos()).Line - 1 after := fset.Position(as.End()).Line + 1 // this needs to do var subsitution like we do for // within advice // // before_advice := formatAdvice(aspect.advize.before, mName) // after_advice := formatAdvice(aspect.advize.after, mName) before_advice := aspect.advize.before after_advice := aspect.advize.after if before_advice != "" { rout = w.writeAtLine(fname, begin+linecnt, before_advice) linecnt += strings.Count(before_advice, "\n") + 1 } if after_advice != "" { rout = w.writeAtLine(fname, after+linecnt-1, after_advice) linecnt += strings.Count(after_advice, "\n") + 1 } mAvars = append(mAvars, avar) } } } } if len(importsNeeded) > 0 { // add any imports for this piece of advice rout = w.writeMissingImports(fname, rout, importsNeeded) } return rout }