package service import ( "fmt" "go/ast" "strings" ) var ( inFunctionDepth = 0 ) func incInFunctionDepth() { inFunctionDepth++ } func decInFunctionDepth() { inFunctionDepth-- if inFunctionDepth < 0 { panic(fmt.Sprintf("inFunctionDepth: %v", inFunctionDepth)) } } func IsInFunction() bool { return inFunctionDepth > 0 } func handleFuncDecl(decl ast.Decl) string { incInFunctionDepth() defer decInFunctionDepth() fd := decl.(*ast.FuncDecl) if fd.Recv != nil { return handleMethodDeclaration(fd) } return handleFunctionDeclaration(fd) } func handleFunctionDeclaration(fd *ast.FuncDecl) string { if shouldSkipFunction(fd.Type) { return "" } code := "" name := "" ft := handleFuncDeclType(fd.Type) code += ft code += " " name = handleFuncDeclName(fd.Name) if name == "NewController" { return "" } code += name code += "(" fp := handleFuncDeclParams(fd.Type) code += fp addFunctionDeclaration(ft + " " + name + "(" + fp + ");") code += ") {\n" code += handleBlockStmt(fd.Body) code += "}\n" return code } func shouldSkipFunction(t *ast.FuncType) (res bool) { r := t.Results if r == nil { return } l := r.List if len(l) != 1 { return } p := l[0] if p == nil { return } pt := p.Type if pt == nil { return } i, ok := pt.(*ast.Ident) if !ok { return } if i.Name == "ShouldBeSkipped" { return true } return } func handleFuncDeclType(t *ast.FuncType) string { code := "" if t.Results == nil { return "void" } fl := t.Results if fl.NumFields() == 0 { panic("handleFuncDeclType: fl.NumFields() == 0") } switch ft := fl.List[0].Type.(type) { case *ast.Ident: code += handleIdentExpr(ft) case *ast.StarExpr: code += handleStarExpr(ft) code += "*" } return code } func handleFuncDeclName(ident *ast.Ident) (code string) { if ident == nil { return } code += ident.Name code = RemapCode(code) return } func handleFuncDeclParams(t *ast.FuncType) string { code := "" if t.Params == nil || t.Params.List == nil { return code } values := make([]string, 0) for _, field := range t.Params.List { ftype := "" star := false switch ft := field.Type.(type) { case *ast.Ident: ftype = handleIdentExpr(ft) case *ast.StarExpr: ftype = handleStarExpr(ft) star = true } for _, names := range field.Names { n := ftype if star { n += "*" } n += " " n += names.Name values = append(values, n) } } code += strings.Join(values, ",") return code } func addFunctionDeclaration(f string) { dlock.Lock() defer dlock.Unlock() funcDeclarations = append(funcDeclarations, f) } func handleCallExpr(expr *ast.CallExpr) (code string) { // println() // spew.Dump(expr) if isMemcheckerDeletePointerCall(expr.Fun) { return handleCallAsDelete(expr) } return handleCallExprNormal(expr) } func isMemcheckerDeletePointerCall(expr ast.Expr) bool { // println() // spew.Dump(expr) s, ok := expr.(*ast.SelectorExpr) if !ok { return false } if handleExpr(s.X) != "memchecker" { return false } if handleIdentExpr(s.Sel) != "DeletePointer" { return false } return true } func handleCallAsDelete(expr *ast.CallExpr) (code string) { code += "delete this" return } func handleCallExprNormal(expr *ast.CallExpr) (code string) { code += handleExpr(expr.Fun) code += "(" args := make([]string, 0) for _, arg := range expr.Args { args = append(args, handleExpr(arg)) } code += strings.Join(args, ",") code += ")" return }