You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

private-gen.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. // +build ignore
  2. package main
  3. import (
  4. "bytes"
  5. "fmt"
  6. "go/ast"
  7. "go/parser"
  8. "go/printer"
  9. "go/token"
  10. "io"
  11. "io/ioutil"
  12. "log"
  13. "os"
  14. "reflect"
  15. "strings"
  16. "unicode"
  17. "unicode/utf8"
  18. )
  19. var inFiles = []string{"cpuid.go", "cpuid_test.go"}
  20. var copyFiles = []string{"cpuid_amd64.s", "cpuid_386.s", "detect_ref.go", "detect_intel.go"}
  21. var fileSet = token.NewFileSet()
  22. var reWrites = []rewrite{
  23. initRewrite("CPUInfo -> cpuInfo"),
  24. initRewrite("Vendor -> vendor"),
  25. initRewrite("Flags -> flags"),
  26. initRewrite("Detect -> detect"),
  27. initRewrite("CPU -> cpu"),
  28. }
  29. var excludeNames = map[string]bool{"string": true, "join": true, "trim": true,
  30. // cpuid_test.go
  31. "t": true, "println": true, "logf": true, "log": true, "fatalf": true, "fatal": true,
  32. }
  33. var excludePrefixes = []string{"test", "benchmark"}
  34. func main() {
  35. Package := "private"
  36. parserMode := parser.ParseComments
  37. exported := make(map[string]rewrite)
  38. for _, file := range inFiles {
  39. in, err := os.Open(file)
  40. if err != nil {
  41. log.Fatalf("opening input", err)
  42. }
  43. src, err := ioutil.ReadAll(in)
  44. if err != nil {
  45. log.Fatalf("reading input", err)
  46. }
  47. astfile, err := parser.ParseFile(fileSet, file, src, parserMode)
  48. if err != nil {
  49. log.Fatalf("parsing input", err)
  50. }
  51. for _, rw := range reWrites {
  52. astfile = rw(astfile)
  53. }
  54. // Inspect the AST and print all identifiers and literals.
  55. var startDecl token.Pos
  56. var endDecl token.Pos
  57. ast.Inspect(astfile, func(n ast.Node) bool {
  58. var s string
  59. switch x := n.(type) {
  60. case *ast.Ident:
  61. if x.IsExported() {
  62. t := strings.ToLower(x.Name)
  63. for _, pre := range excludePrefixes {
  64. if strings.HasPrefix(t, pre) {
  65. return true
  66. }
  67. }
  68. if excludeNames[t] != true {
  69. //if x.Pos() > startDecl && x.Pos() < endDecl {
  70. exported[x.Name] = initRewrite(x.Name + " -> " + t)
  71. }
  72. }
  73. case *ast.GenDecl:
  74. if x.Tok == token.CONST && x.Lparen > 0 {
  75. startDecl = x.Lparen
  76. endDecl = x.Rparen
  77. // fmt.Printf("Decl:%s -> %s\n", fileSet.Position(startDecl), fileSet.Position(endDecl))
  78. }
  79. }
  80. if s != "" {
  81. fmt.Printf("%s:\t%s\n", fileSet.Position(n.Pos()), s)
  82. }
  83. return true
  84. })
  85. for _, rw := range exported {
  86. astfile = rw(astfile)
  87. }
  88. var buf bytes.Buffer
  89. printer.Fprint(&buf, fileSet, astfile)
  90. // Remove package documentation and insert information
  91. s := buf.String()
  92. ind := strings.Index(buf.String(), "\npackage cpuid")
  93. s = s[ind:]
  94. s = "// Generated, DO NOT EDIT,\n" +
  95. "// but copy it to your own project and rename the package.\n" +
  96. "// See more at http://github.com/klauspost/cpuid\n" +
  97. s
  98. outputName := Package + string(os.PathSeparator) + file
  99. err = ioutil.WriteFile(outputName, []byte(s), 0644)
  100. if err != nil {
  101. log.Fatalf("writing output: %s", err)
  102. }
  103. log.Println("Generated", outputName)
  104. }
  105. for _, file := range copyFiles {
  106. dst := ""
  107. if strings.HasPrefix(file, "cpuid") {
  108. dst = Package + string(os.PathSeparator) + file
  109. } else {
  110. dst = Package + string(os.PathSeparator) + "cpuid_" + file
  111. }
  112. err := copyFile(file, dst)
  113. if err != nil {
  114. log.Fatalf("copying file: %s", err)
  115. }
  116. log.Println("Copied", dst)
  117. }
  118. }
  119. // CopyFile copies a file from src to dst. If src and dst files exist, and are
  120. // the same, then return success. Copy the file contents from src to dst.
  121. func copyFile(src, dst string) (err error) {
  122. sfi, err := os.Stat(src)
  123. if err != nil {
  124. return
  125. }
  126. if !sfi.Mode().IsRegular() {
  127. // cannot copy non-regular files (e.g., directories,
  128. // symlinks, devices, etc.)
  129. return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
  130. }
  131. dfi, err := os.Stat(dst)
  132. if err != nil {
  133. if !os.IsNotExist(err) {
  134. return
  135. }
  136. } else {
  137. if !(dfi.Mode().IsRegular()) {
  138. return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
  139. }
  140. if os.SameFile(sfi, dfi) {
  141. return
  142. }
  143. }
  144. err = copyFileContents(src, dst)
  145. return
  146. }
  147. // copyFileContents copies the contents of the file named src to the file named
  148. // by dst. The file will be created if it does not already exist. If the
  149. // destination file exists, all it's contents will be replaced by the contents
  150. // of the source file.
  151. func copyFileContents(src, dst string) (err error) {
  152. in, err := os.Open(src)
  153. if err != nil {
  154. return
  155. }
  156. defer in.Close()
  157. out, err := os.Create(dst)
  158. if err != nil {
  159. return
  160. }
  161. defer func() {
  162. cerr := out.Close()
  163. if err == nil {
  164. err = cerr
  165. }
  166. }()
  167. if _, err = io.Copy(out, in); err != nil {
  168. return
  169. }
  170. err = out.Sync()
  171. return
  172. }
  173. type rewrite func(*ast.File) *ast.File
  174. // Mostly copied from gofmt
  175. func initRewrite(rewriteRule string) rewrite {
  176. f := strings.Split(rewriteRule, "->")
  177. if len(f) != 2 {
  178. fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
  179. os.Exit(2)
  180. }
  181. pattern := parseExpr(f[0], "pattern")
  182. replace := parseExpr(f[1], "replacement")
  183. return func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) }
  184. }
  185. // parseExpr parses s as an expression.
  186. // It might make sense to expand this to allow statement patterns,
  187. // but there are problems with preserving formatting and also
  188. // with what a wildcard for a statement looks like.
  189. func parseExpr(s, what string) ast.Expr {
  190. x, err := parser.ParseExpr(s)
  191. if err != nil {
  192. fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err)
  193. os.Exit(2)
  194. }
  195. return x
  196. }
  197. // Keep this function for debugging.
  198. /*
  199. func dump(msg string, val reflect.Value) {
  200. fmt.Printf("%s:\n", msg)
  201. ast.Print(fileSet, val.Interface())
  202. fmt.Println()
  203. }
  204. */
  205. // rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file.
  206. func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
  207. cmap := ast.NewCommentMap(fileSet, p, p.Comments)
  208. m := make(map[string]reflect.Value)
  209. pat := reflect.ValueOf(pattern)
  210. repl := reflect.ValueOf(replace)
  211. var rewriteVal func(val reflect.Value) reflect.Value
  212. rewriteVal = func(val reflect.Value) reflect.Value {
  213. // don't bother if val is invalid to start with
  214. if !val.IsValid() {
  215. return reflect.Value{}
  216. }
  217. for k := range m {
  218. delete(m, k)
  219. }
  220. val = apply(rewriteVal, val)
  221. if match(m, pat, val) {
  222. val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos()))
  223. }
  224. return val
  225. }
  226. r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File)
  227. r.Comments = cmap.Filter(r).Comments() // recreate comments list
  228. return r
  229. }
  230. // set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y.
  231. func set(x, y reflect.Value) {
  232. // don't bother if x cannot be set or y is invalid
  233. if !x.CanSet() || !y.IsValid() {
  234. return
  235. }
  236. defer func() {
  237. if x := recover(); x != nil {
  238. if s, ok := x.(string); ok &&
  239. (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
  240. // x cannot be set to y - ignore this rewrite
  241. return
  242. }
  243. panic(x)
  244. }
  245. }()
  246. x.Set(y)
  247. }
  248. // Values/types for special cases.
  249. var (
  250. objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
  251. scopePtrNil = reflect.ValueOf((*ast.Scope)(nil))
  252. identType = reflect.TypeOf((*ast.Ident)(nil))
  253. objectPtrType = reflect.TypeOf((*ast.Object)(nil))
  254. positionType = reflect.TypeOf(token.NoPos)
  255. callExprType = reflect.TypeOf((*ast.CallExpr)(nil))
  256. scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
  257. )
  258. // apply replaces each AST field x in val with f(x), returning val.
  259. // To avoid extra conversions, f operates on the reflect.Value form.
  260. func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
  261. if !val.IsValid() {
  262. return reflect.Value{}
  263. }
  264. // *ast.Objects introduce cycles and are likely incorrect after
  265. // rewrite; don't follow them but replace with nil instead
  266. if val.Type() == objectPtrType {
  267. return objectPtrNil
  268. }
  269. // similarly for scopes: they are likely incorrect after a rewrite;
  270. // replace them with nil
  271. if val.Type() == scopePtrType {
  272. return scopePtrNil
  273. }
  274. switch v := reflect.Indirect(val); v.Kind() {
  275. case reflect.Slice:
  276. for i := 0; i < v.Len(); i++ {
  277. e := v.Index(i)
  278. set(e, f(e))
  279. }
  280. case reflect.Struct:
  281. for i := 0; i < v.NumField(); i++ {
  282. e := v.Field(i)
  283. set(e, f(e))
  284. }
  285. case reflect.Interface:
  286. e := v.Elem()
  287. set(v, f(e))
  288. }
  289. return val
  290. }
  291. func isWildcard(s string) bool {
  292. rune, size := utf8.DecodeRuneInString(s)
  293. return size == len(s) && unicode.IsLower(rune)
  294. }
  295. // match returns true if pattern matches val,
  296. // recording wildcard submatches in m.
  297. // If m == nil, match checks whether pattern == val.
  298. func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
  299. // Wildcard matches any expression. If it appears multiple
  300. // times in the pattern, it must match the same expression
  301. // each time.
  302. if m != nil && pattern.IsValid() && pattern.Type() == identType {
  303. name := pattern.Interface().(*ast.Ident).Name
  304. if isWildcard(name) && val.IsValid() {
  305. // wildcards only match valid (non-nil) expressions.
  306. if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() {
  307. if old, ok := m[name]; ok {
  308. return match(nil, old, val)
  309. }
  310. m[name] = val
  311. return true
  312. }
  313. }
  314. }
  315. // Otherwise, pattern and val must match recursively.
  316. if !pattern.IsValid() || !val.IsValid() {
  317. return !pattern.IsValid() && !val.IsValid()
  318. }
  319. if pattern.Type() != val.Type() {
  320. return false
  321. }
  322. // Special cases.
  323. switch pattern.Type() {
  324. case identType:
  325. // For identifiers, only the names need to match
  326. // (and none of the other *ast.Object information).
  327. // This is a common case, handle it all here instead
  328. // of recursing down any further via reflection.
  329. p := pattern.Interface().(*ast.Ident)
  330. v := val.Interface().(*ast.Ident)
  331. return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
  332. case objectPtrType, positionType:
  333. // object pointers and token positions always match
  334. return true
  335. case callExprType:
  336. // For calls, the Ellipsis fields (token.Position) must
  337. // match since that is how f(x) and f(x...) are different.
  338. // Check them here but fall through for the remaining fields.
  339. p := pattern.Interface().(*ast.CallExpr)
  340. v := val.Interface().(*ast.CallExpr)
  341. if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
  342. return false
  343. }
  344. }
  345. p := reflect.Indirect(pattern)
  346. v := reflect.Indirect(val)
  347. if !p.IsValid() || !v.IsValid() {
  348. return !p.IsValid() && !v.IsValid()
  349. }
  350. switch p.Kind() {
  351. case reflect.Slice:
  352. if p.Len() != v.Len() {
  353. return false
  354. }
  355. for i := 0; i < p.Len(); i++ {
  356. if !match(m, p.Index(i), v.Index(i)) {
  357. return false
  358. }
  359. }
  360. return true
  361. case reflect.Struct:
  362. for i := 0; i < p.NumField(); i++ {
  363. if !match(m, p.Field(i), v.Field(i)) {
  364. return false
  365. }
  366. }
  367. return true
  368. case reflect.Interface:
  369. return match(m, p.Elem(), v.Elem())
  370. }
  371. // Handle token integers, etc.
  372. return p.Interface() == v.Interface()
  373. }
  374. // subst returns a copy of pattern with values from m substituted in place
  375. // of wildcards and pos used as the position of tokens from the pattern.
  376. // if m == nil, subst returns a copy of pattern and doesn't change the line
  377. // number information.
  378. func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
  379. if !pattern.IsValid() {
  380. return reflect.Value{}
  381. }
  382. // Wildcard gets replaced with map value.
  383. if m != nil && pattern.Type() == identType {
  384. name := pattern.Interface().(*ast.Ident).Name
  385. if isWildcard(name) {
  386. if old, ok := m[name]; ok {
  387. return subst(nil, old, reflect.Value{})
  388. }
  389. }
  390. }
  391. if pos.IsValid() && pattern.Type() == positionType {
  392. // use new position only if old position was valid in the first place
  393. if old := pattern.Interface().(token.Pos); !old.IsValid() {
  394. return pattern
  395. }
  396. return pos
  397. }
  398. // Otherwise copy.
  399. switch p := pattern; p.Kind() {
  400. case reflect.Slice:
  401. v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
  402. for i := 0; i < p.Len(); i++ {
  403. v.Index(i).Set(subst(m, p.Index(i), pos))
  404. }
  405. return v
  406. case reflect.Struct:
  407. v := reflect.New(p.Type()).Elem()
  408. for i := 0; i < p.NumField(); i++ {
  409. v.Field(i).Set(subst(m, p.Field(i), pos))
  410. }
  411. return v
  412. case reflect.Ptr:
  413. v := reflect.New(p.Type()).Elem()
  414. if elem := p.Elem(); elem.IsValid() {
  415. v.Set(subst(m, elem, pos).Addr())
  416. }
  417. return v
  418. case reflect.Interface:
  419. v := reflect.New(p.Type()).Elem()
  420. if elem := p.Elem(); elem.IsValid() {
  421. v.Set(subst(m, elem, pos))
  422. }
  423. return v
  424. }
  425. return pattern
  426. }