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