github.com/status-im/status-go@v1.1.0/cmd/library/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/parser" 7 "go/token" 8 "os" 9 "strings" 10 "unicode" 11 ) 12 13 func isCodeFile(info os.FileInfo) bool { 14 return !strings.HasSuffix(info.Name(), "test.go") 15 } 16 17 func main() { 18 var output = prelude 19 20 fset := token.NewFileSet() 21 22 // Parse the whole `mobile/` directory, excluding test files 23 parsedAST, err := parser.ParseDir(fset, "mobile/", isCodeFile, parser.AllErrors) 24 if err != nil { 25 fmt.Printf("Error parsing directory: %+v\n", err) 26 os.Exit(1) 27 } 28 29 for _, a := range parsedAST { 30 for _, file := range a.Files { 31 // handle each file and append the output 32 output += handleFile(file) 33 } 34 } 35 36 // To free memory allocated to strings 37 output += "//export Free\n" 38 output += "func Free (param unsafe.Pointer){\n" 39 output += "C.free(param);\n" 40 output += "}\n" 41 42 fmt.Println(output) 43 } 44 45 func handleFunction(name string, funcDecl *ast.FuncDecl) string { 46 params := funcDecl.Type.Params.List 47 results := funcDecl.Type.Results 48 49 // add export tag 50 output := fmt.Sprintf("//export %s\n", name) 51 // add initial func declaration 52 output += fmt.Sprintf("func %s (", name) 53 54 // iterate over parameters and correctly add the C type 55 paramCount := 0 56 for _, p := range params { 57 for _, paramIdentity := range p.Names { 58 if paramCount != 0 { 59 output += ", " 60 } 61 paramCount++ 62 output += paramIdentity.Name 63 64 typeString := fmt.Sprint(paramIdentity.Obj.Decl.(*ast.Field).Type) 65 // We match against the stringified type, 66 // could not find a better way to match this 67 switch typeString { 68 case stringType: 69 output += " *C.char" 70 case intType, boolType: 71 output += " C.int" 72 case unsafePointerType: 73 output += " unsafe.Pointer" 74 default: 75 // ignore if the type is any different 76 return "" 77 } 78 } 79 } 80 81 output += ")" 82 83 // check if it has a return value, convert to CString if so and return 84 if results != nil { 85 output += " *C.char {\nreturn C.CString(" 86 } else { 87 output += " {\n" 88 89 } 90 91 // call the mobile equivalent function 92 output += fmt.Sprintf("mobile.%s(", name) 93 94 // iterate through the parameters, convert to go types and close 95 // the function call 96 paramCount = 0 97 for _, p := range params { 98 for _, paramIdentity := range p.Names { 99 if paramCount != 0 { 100 output += ", " 101 } 102 paramCount++ 103 typeString := fmt.Sprint(paramIdentity.Obj.Decl.(*ast.Field).Type) 104 switch typeString { 105 case stringType: 106 output += fmt.Sprintf("C.GoString(%s)", paramIdentity.Name) 107 case intType: 108 output += fmt.Sprintf("int(%s)", paramIdentity.Name) 109 case unsafePointerType: 110 output += paramIdentity.Name 111 case boolType: 112 output += paramIdentity.Name 113 // convert int to bool 114 output += " == 1" 115 default: 116 // ignore otherwise 117 return "" 118 } 119 } 120 } 121 122 // close function call 123 output += ")" 124 125 // close conversion to CString 126 if results != nil { 127 output += ")\n" 128 } 129 130 // close function declaration 131 output += "}\n" 132 return output 133 } 134 135 func handleFile(parsedAST *ast.File) string { 136 output := "" 137 for name, obj := range parsedAST.Scope.Objects { 138 // Ignore non-functions or non exported fields 139 if obj.Kind != ast.Fun || !unicode.IsUpper(rune(name[0])) { 140 continue 141 } 142 output += handleFunction(name, obj.Decl.(*ast.FuncDecl)) 143 } 144 145 return output 146 }