github.com/neugram/ng@v0.0.0-20180309130942-d472ff93d872/eval/gowrap/genwrap/genwrap.go (about) 1 // Copyright 2017 The Neugram Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package genwrap 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/ast" 11 "go/format" 12 "go/types" 13 "log" 14 "strings" 15 "text/template" 16 17 "neugram.io/ng/gotool" 18 ) 19 20 func quotePkgPath(path string) string { 21 return "wrap_" + strings.NewReplacer( 22 "/", "_", 23 ".", "_", 24 "-", "_", 25 ).Replace(path) 26 } 27 28 func buildDataPkg(pkgPath string, pkg *types.Package) DataPkg { 29 quotedPkgPath := quotePkgPath(pkgPath) 30 scope := pkg.Scope() 31 exports := map[string]string{} 32 for _, name := range scope.Names() { 33 if !ast.IsExported(name) { 34 continue 35 } 36 obj := scope.Lookup(name) 37 switch obj.(type) { 38 case *types.TypeName: 39 if _, ok := obj.Type().Underlying().(*types.Interface); ok { 40 exports[name] = "reflect.ValueOf(reflect.TypeOf((*" + quotedPkgPath + "." + name + ")(nil)).Elem())" 41 } else { 42 exports[name] = "reflect.ValueOf(reflect.TypeOf(" + quotedPkgPath + "." + name + nilexpr(obj.Type()) + "))" 43 } 44 case *types.Var: 45 exports[name] = "reflect.ValueOf(&" + quotedPkgPath + "." + name + ").Elem()" 46 case *types.Func, *types.Const: 47 exports[name] = "reflect.ValueOf(" + quotedPkgPath + "." + name + ")" 48 default: 49 log.Printf("genwrap: unexpected obj: %T\n", obj) 50 } 51 } 52 53 return DataPkg{ 54 Name: pkg.Path(), 55 QuotedName: quotedPkgPath, 56 Exports: exports, 57 } 58 } 59 60 // GenGo generates a wrapper package naemd outPkgName that 61 // registers the exported symbols of pkgPath with the global 62 // map gopkg.Pkgs. 63 // 64 // Any other packages that pkgPath depends on for defining its 65 // exported symbols are also registered, unless skipDeps is set. 66 func GenGo(pkgPath, outPkgName string, skipDeps bool) ([]byte, error) { 67 pkg, err := gotool.M.ImportGo(pkgPath) 68 if err != nil { 69 return nil, err 70 } 71 pkgs := make(map[string]DataPkg) 72 pkgs[pkgPath] = buildDataPkg(pkgPath, pkg) 73 imports := []*types.Package{} 74 if !skipDeps { 75 for _, imp := range pkg.Imports() { 76 // Re-import package to get all exported symbols. 77 imppkg, err := gotool.M.ImportGo(imp.Path()) 78 if err != nil { 79 return nil, err 80 } 81 imports = append(imports, imppkg) 82 } 83 } 84 85 importsLoop: 86 for i := 0; i < len(imports); i++ { // imports grows as we loop 87 path := imports[i].Path() 88 if _, exists := pkgs[path]; exists { 89 continue 90 } 91 for _, dir := range strings.Split(path, "/") { 92 if dir == "internal" || dir == "vendor" { 93 continue importsLoop 94 } 95 } 96 pkgs[path] = buildDataPkg(path, imports[i]) 97 } 98 data := Data{ 99 OutPkgName: outPkgName, 100 } 101 for _, dataPkg := range pkgs { 102 data.Pkgs = append(data.Pkgs, dataPkg) 103 } 104 105 buf := new(bytes.Buffer) 106 err = tmpl.Execute(buf, data) 107 if err != nil { 108 return nil, fmt.Errorf("genwrap: %v", err) 109 } 110 res, err := format.Source(buf.Bytes()) 111 if err != nil { 112 lines := new(bytes.Buffer) 113 for i, line := range strings.Split(buf.String(), "\n") { 114 fmt.Fprintf(lines, "%3d: %s\n", i, line) 115 } 116 return nil, fmt.Errorf("genwrap: bad generated source: %v\n%s", err, lines.String()) 117 } 118 return res, nil 119 } 120 121 func nilexpr(t types.Type) string { 122 t = t.Underlying() 123 switch t := t.(type) { 124 case *types.Basic: 125 switch t.Kind() { 126 case types.Bool: 127 return "(false)" 128 case types.String: 129 return `("")` 130 case types.UnsafePointer: 131 return "(nil)" 132 default: 133 return "(0)" 134 } 135 case *types.Array, *types.Struct: 136 return "{}" 137 case *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature: 138 return "(nil)" 139 default: 140 return fmt.Sprintf("(unexpected type: %T)", t) 141 } 142 } 143 144 type Data struct { 145 OutPkgName string 146 Pkgs []DataPkg 147 } 148 149 type DataPkg struct { 150 Name string 151 QuotedName string 152 Exports map[string]string 153 } 154 155 var tmpl = template.Must(template.New("genwrap").Parse(` 156 // Generated file, do not edit. 157 158 package {{.OutPkgName}} 159 160 import ( 161 "reflect" 162 163 "neugram.io/ng/eval/gowrap" 164 165 {{range .Pkgs}} 166 {{.QuotedName}} "{{.Name}}" 167 {{end}} 168 ) 169 170 {{range .Pkgs}} 171 var pkg_{{.QuotedName}} = &gowrap.Pkg{ 172 Exports: map[string]reflect.Value{ 173 {{with $data := .}} 174 {{range $name, $export := $data.Exports}} 175 "{{$name}}": {{$export}},{{end}} 176 {{end}} 177 }, 178 } 179 {{end}} 180 181 {{range .Pkgs}} 182 func init() { 183 if gowrap.Pkgs["{{.Name}}"] == nil { 184 gowrap.Pkgs["{{.Name}}"] = pkg_{{.QuotedName}} 185 } 186 } 187 {{end}} 188 `))