github.com/nibnait/go-learn@v0.0.0-20220227013611-dfa47ea6d2da/src/pkg/mod/golang.org/x/sys@v0.0.0-20210630005230-0f9fa26af87c/windows/mkwinsyscall/mkwinsyscall.go (about) 1 // Copyright 2013 The Go 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 /* 6 mkwinsyscall generates windows system call bodies 7 8 It parses all files specified on command line containing function 9 prototypes (like syscall_windows.go) and prints system call bodies 10 to standard output. 11 12 The prototypes are marked by lines beginning with "//sys" and read 13 like func declarations if //sys is replaced by func, but: 14 15 * The parameter lists must give a name for each argument. This 16 includes return parameters. 17 18 * The parameter lists must give a type for each argument: 19 the (x, y, z int) shorthand is not allowed. 20 21 * If the return parameter is an error number, it must be named err. 22 23 * If go func name needs to be different from its winapi dll name, 24 the winapi name could be specified at the end, after "=" sign, like 25 //sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA 26 27 * Each function that returns err needs to supply a condition, that 28 return value of winapi will be tested against to detect failure. 29 This would set err to windows "last-error", otherwise it will be nil. 30 The value can be provided at end of //sys declaration, like 31 //sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA 32 and is [failretval==0] by default. 33 34 * If the function name ends in a "?", then the function not existing is non- 35 fatal, and an error will be returned instead of panicking. 36 37 Usage: 38 mkwinsyscall [flags] [path ...] 39 40 The flags are: 41 -output 42 Specify output file name (outputs to console if blank). 43 -trace 44 Generate print statement after every syscall. 45 */ 46 package main 47 48 import ( 49 "bufio" 50 "bytes" 51 "errors" 52 "flag" 53 "fmt" 54 "go/format" 55 "go/parser" 56 "go/token" 57 "io" 58 "io/ioutil" 59 "log" 60 "os" 61 "path/filepath" 62 "runtime" 63 "sort" 64 "strconv" 65 "strings" 66 "text/template" 67 ) 68 69 var ( 70 filename = flag.String("output", "", "output file name (standard output if omitted)") 71 printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall") 72 systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory") 73 ) 74 75 func trim(s string) string { 76 return strings.Trim(s, " \t") 77 } 78 79 var packageName string 80 81 func packagename() string { 82 return packageName 83 } 84 85 func windowsdot() string { 86 if packageName == "windows" { 87 return "" 88 } 89 return "windows." 90 } 91 92 func syscalldot() string { 93 if packageName == "syscall" { 94 return "" 95 } 96 return "syscall." 97 } 98 99 // Param is function parameter 100 type Param struct { 101 Name string 102 Type string 103 fn *Fn 104 tmpVarIdx int 105 } 106 107 // tmpVar returns temp variable name that will be used to represent p during syscall. 108 func (p *Param) tmpVar() string { 109 if p.tmpVarIdx < 0 { 110 p.tmpVarIdx = p.fn.curTmpVarIdx 111 p.fn.curTmpVarIdx++ 112 } 113 return fmt.Sprintf("_p%d", p.tmpVarIdx) 114 } 115 116 // BoolTmpVarCode returns source code for bool temp variable. 117 func (p *Param) BoolTmpVarCode() string { 118 const code = `var %[1]s uint32 119 if %[2]s { 120 %[1]s = 1 121 }` 122 return fmt.Sprintf(code, p.tmpVar(), p.Name) 123 } 124 125 // BoolPointerTmpVarCode returns source code for bool temp variable. 126 func (p *Param) BoolPointerTmpVarCode() string { 127 const code = `var %[1]s uint32 128 if *%[2]s { 129 %[1]s = 1 130 }` 131 return fmt.Sprintf(code, p.tmpVar(), p.Name) 132 } 133 134 // SliceTmpVarCode returns source code for slice temp variable. 135 func (p *Param) SliceTmpVarCode() string { 136 const code = `var %s *%s 137 if len(%s) > 0 { 138 %s = &%s[0] 139 }` 140 tmp := p.tmpVar() 141 return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name) 142 } 143 144 // StringTmpVarCode returns source code for string temp variable. 145 func (p *Param) StringTmpVarCode() string { 146 errvar := p.fn.Rets.ErrorVarName() 147 if errvar == "" { 148 errvar = "_" 149 } 150 tmp := p.tmpVar() 151 const code = `var %s %s 152 %s, %s = %s(%s)` 153 s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name) 154 if errvar == "-" { 155 return s 156 } 157 const morecode = ` 158 if %s != nil { 159 return 160 }` 161 return s + fmt.Sprintf(morecode, errvar) 162 } 163 164 // TmpVarCode returns source code for temp variable. 165 func (p *Param) TmpVarCode() string { 166 switch { 167 case p.Type == "bool": 168 return p.BoolTmpVarCode() 169 case p.Type == "*bool": 170 return p.BoolPointerTmpVarCode() 171 case strings.HasPrefix(p.Type, "[]"): 172 return p.SliceTmpVarCode() 173 default: 174 return "" 175 } 176 } 177 178 // TmpVarReadbackCode returns source code for reading back the temp variable into the original variable. 179 func (p *Param) TmpVarReadbackCode() string { 180 switch { 181 case p.Type == "*bool": 182 return fmt.Sprintf("*%s = %s != 0", p.Name, p.tmpVar()) 183 default: 184 return "" 185 } 186 } 187 188 // TmpVarHelperCode returns source code for helper's temp variable. 189 func (p *Param) TmpVarHelperCode() string { 190 if p.Type != "string" { 191 return "" 192 } 193 return p.StringTmpVarCode() 194 } 195 196 // SyscallArgList returns source code fragments representing p parameter 197 // in syscall. Slices are translated into 2 syscall parameters: pointer to 198 // the first element and length. 199 func (p *Param) SyscallArgList() []string { 200 t := p.HelperType() 201 var s string 202 switch { 203 case t == "*bool": 204 s = fmt.Sprintf("unsafe.Pointer(&%s)", p.tmpVar()) 205 case t[0] == '*': 206 s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name) 207 case t == "bool": 208 s = p.tmpVar() 209 case strings.HasPrefix(t, "[]"): 210 return []string{ 211 fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()), 212 fmt.Sprintf("uintptr(len(%s))", p.Name), 213 } 214 default: 215 s = p.Name 216 } 217 return []string{fmt.Sprintf("uintptr(%s)", s)} 218 } 219 220 // IsError determines if p parameter is used to return error. 221 func (p *Param) IsError() bool { 222 return p.Name == "err" && p.Type == "error" 223 } 224 225 // HelperType returns type of parameter p used in helper function. 226 func (p *Param) HelperType() string { 227 if p.Type == "string" { 228 return p.fn.StrconvType() 229 } 230 return p.Type 231 } 232 233 // join concatenates parameters ps into a string with sep separator. 234 // Each parameter is converted into string by applying fn to it 235 // before conversion. 236 func join(ps []*Param, fn func(*Param) string, sep string) string { 237 if len(ps) == 0 { 238 return "" 239 } 240 a := make([]string, 0) 241 for _, p := range ps { 242 a = append(a, fn(p)) 243 } 244 return strings.Join(a, sep) 245 } 246 247 // Rets describes function return parameters. 248 type Rets struct { 249 Name string 250 Type string 251 ReturnsError bool 252 FailCond string 253 fnMaybeAbsent bool 254 } 255 256 // ErrorVarName returns error variable name for r. 257 func (r *Rets) ErrorVarName() string { 258 if r.ReturnsError { 259 return "err" 260 } 261 if r.Type == "error" { 262 return r.Name 263 } 264 return "" 265 } 266 267 // ToParams converts r into slice of *Param. 268 func (r *Rets) ToParams() []*Param { 269 ps := make([]*Param, 0) 270 if len(r.Name) > 0 { 271 ps = append(ps, &Param{Name: r.Name, Type: r.Type}) 272 } 273 if r.ReturnsError { 274 ps = append(ps, &Param{Name: "err", Type: "error"}) 275 } 276 return ps 277 } 278 279 // List returns source code of syscall return parameters. 280 func (r *Rets) List() string { 281 s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ") 282 if len(s) > 0 { 283 s = "(" + s + ")" 284 } else if r.fnMaybeAbsent { 285 s = "(err error)" 286 } 287 return s 288 } 289 290 // PrintList returns source code of trace printing part correspondent 291 // to syscall return values. 292 func (r *Rets) PrintList() string { 293 return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) 294 } 295 296 // SetReturnValuesCode returns source code that accepts syscall return values. 297 func (r *Rets) SetReturnValuesCode() string { 298 if r.Name == "" && !r.ReturnsError { 299 return "" 300 } 301 retvar := "r0" 302 if r.Name == "" { 303 retvar = "r1" 304 } 305 errvar := "_" 306 if r.ReturnsError { 307 errvar = "e1" 308 } 309 return fmt.Sprintf("%s, _, %s := ", retvar, errvar) 310 } 311 312 func (r *Rets) useLongHandleErrorCode(retvar string) string { 313 const code = `if %s { 314 err = errnoErr(e1) 315 }` 316 cond := retvar + " == 0" 317 if r.FailCond != "" { 318 cond = strings.Replace(r.FailCond, "failretval", retvar, 1) 319 } 320 return fmt.Sprintf(code, cond) 321 } 322 323 // SetErrorCode returns source code that sets return parameters. 324 func (r *Rets) SetErrorCode() string { 325 const code = `if r0 != 0 { 326 %s = %sErrno(r0) 327 }` 328 const ntstatus = `if r0 != 0 { 329 ntstatus = %sNTStatus(r0) 330 }` 331 if r.Name == "" && !r.ReturnsError { 332 return "" 333 } 334 if r.Name == "" { 335 return r.useLongHandleErrorCode("r1") 336 } 337 if r.Type == "error" && r.Name == "ntstatus" { 338 return fmt.Sprintf(ntstatus, windowsdot()) 339 } 340 if r.Type == "error" { 341 return fmt.Sprintf(code, r.Name, syscalldot()) 342 } 343 s := "" 344 switch { 345 case r.Type[0] == '*': 346 s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type) 347 case r.Type == "bool": 348 s = fmt.Sprintf("%s = r0 != 0", r.Name) 349 default: 350 s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type) 351 } 352 if !r.ReturnsError { 353 return s 354 } 355 return s + "\n\t" + r.useLongHandleErrorCode(r.Name) 356 } 357 358 // Fn describes syscall function. 359 type Fn struct { 360 Name string 361 Params []*Param 362 Rets *Rets 363 PrintTrace bool 364 dllname string 365 dllfuncname string 366 src string 367 // TODO: get rid of this field and just use parameter index instead 368 curTmpVarIdx int // insure tmp variables have uniq names 369 } 370 371 // extractParams parses s to extract function parameters. 372 func extractParams(s string, f *Fn) ([]*Param, error) { 373 s = trim(s) 374 if s == "" { 375 return nil, nil 376 } 377 a := strings.Split(s, ",") 378 ps := make([]*Param, len(a)) 379 for i := range ps { 380 s2 := trim(a[i]) 381 b := strings.Split(s2, " ") 382 if len(b) != 2 { 383 b = strings.Split(s2, "\t") 384 if len(b) != 2 { 385 return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"") 386 } 387 } 388 ps[i] = &Param{ 389 Name: trim(b[0]), 390 Type: trim(b[1]), 391 fn: f, 392 tmpVarIdx: -1, 393 } 394 } 395 return ps, nil 396 } 397 398 // extractSection extracts text out of string s starting after start 399 // and ending just before end. found return value will indicate success, 400 // and prefix, body and suffix will contain correspondent parts of string s. 401 func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) { 402 s = trim(s) 403 if strings.HasPrefix(s, string(start)) { 404 // no prefix 405 body = s[1:] 406 } else { 407 a := strings.SplitN(s, string(start), 2) 408 if len(a) != 2 { 409 return "", "", s, false 410 } 411 prefix = a[0] 412 body = a[1] 413 } 414 a := strings.SplitN(body, string(end), 2) 415 if len(a) != 2 { 416 return "", "", "", false 417 } 418 return prefix, a[0], a[1], true 419 } 420 421 // newFn parses string s and return created function Fn. 422 func newFn(s string) (*Fn, error) { 423 s = trim(s) 424 f := &Fn{ 425 Rets: &Rets{}, 426 src: s, 427 PrintTrace: *printTraceFlag, 428 } 429 // function name and args 430 prefix, body, s, found := extractSection(s, '(', ')') 431 if !found || prefix == "" { 432 return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"") 433 } 434 f.Name = prefix 435 var err error 436 f.Params, err = extractParams(body, f) 437 if err != nil { 438 return nil, err 439 } 440 // return values 441 _, body, s, found = extractSection(s, '(', ')') 442 if found { 443 r, err := extractParams(body, f) 444 if err != nil { 445 return nil, err 446 } 447 switch len(r) { 448 case 0: 449 case 1: 450 if r[0].IsError() { 451 f.Rets.ReturnsError = true 452 } else { 453 f.Rets.Name = r[0].Name 454 f.Rets.Type = r[0].Type 455 } 456 case 2: 457 if !r[1].IsError() { 458 return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"") 459 } 460 f.Rets.ReturnsError = true 461 f.Rets.Name = r[0].Name 462 f.Rets.Type = r[0].Type 463 default: 464 return nil, errors.New("Too many return values in \"" + f.src + "\"") 465 } 466 } 467 // fail condition 468 _, body, s, found = extractSection(s, '[', ']') 469 if found { 470 f.Rets.FailCond = body 471 } 472 // dll and dll function names 473 s = trim(s) 474 if s == "" { 475 return f, nil 476 } 477 if !strings.HasPrefix(s, "=") { 478 return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") 479 } 480 s = trim(s[1:]) 481 a := strings.Split(s, ".") 482 switch len(a) { 483 case 1: 484 f.dllfuncname = a[0] 485 case 2: 486 f.dllname = a[0] 487 f.dllfuncname = a[1] 488 default: 489 return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") 490 } 491 if n := f.dllfuncname; strings.HasSuffix(n, "?") { 492 f.dllfuncname = n[:len(n)-1] 493 f.Rets.fnMaybeAbsent = true 494 } 495 return f, nil 496 } 497 498 // DLLName returns DLL name for function f. 499 func (f *Fn) DLLName() string { 500 if f.dllname == "" { 501 return "kernel32" 502 } 503 return f.dllname 504 } 505 506 // DLLName returns DLL function name for function f. 507 func (f *Fn) DLLFuncName() string { 508 if f.dllfuncname == "" { 509 return f.Name 510 } 511 return f.dllfuncname 512 } 513 514 // ParamList returns source code for function f parameters. 515 func (f *Fn) ParamList() string { 516 return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ") 517 } 518 519 // HelperParamList returns source code for helper function f parameters. 520 func (f *Fn) HelperParamList() string { 521 return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ") 522 } 523 524 // ParamPrintList returns source code of trace printing part correspondent 525 // to syscall input parameters. 526 func (f *Fn) ParamPrintList() string { 527 return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) 528 } 529 530 // ParamCount return number of syscall parameters for function f. 531 func (f *Fn) ParamCount() int { 532 n := 0 533 for _, p := range f.Params { 534 n += len(p.SyscallArgList()) 535 } 536 return n 537 } 538 539 // SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/... 540 // to use. It returns parameter count for correspondent SyscallX function. 541 func (f *Fn) SyscallParamCount() int { 542 n := f.ParamCount() 543 switch { 544 case n <= 3: 545 return 3 546 case n <= 6: 547 return 6 548 case n <= 9: 549 return 9 550 case n <= 12: 551 return 12 552 case n <= 15: 553 return 15 554 default: 555 panic("too many arguments to system call") 556 } 557 } 558 559 // Syscall determines which SyscallX function to use for function f. 560 func (f *Fn) Syscall() string { 561 c := f.SyscallParamCount() 562 if c == 3 { 563 return syscalldot() + "Syscall" 564 } 565 return syscalldot() + "Syscall" + strconv.Itoa(c) 566 } 567 568 // SyscallParamList returns source code for SyscallX parameters for function f. 569 func (f *Fn) SyscallParamList() string { 570 a := make([]string, 0) 571 for _, p := range f.Params { 572 a = append(a, p.SyscallArgList()...) 573 } 574 for len(a) < f.SyscallParamCount() { 575 a = append(a, "0") 576 } 577 return strings.Join(a, ", ") 578 } 579 580 // HelperCallParamList returns source code of call into function f helper. 581 func (f *Fn) HelperCallParamList() string { 582 a := make([]string, 0, len(f.Params)) 583 for _, p := range f.Params { 584 s := p.Name 585 if p.Type == "string" { 586 s = p.tmpVar() 587 } 588 a = append(a, s) 589 } 590 return strings.Join(a, ", ") 591 } 592 593 // MaybeAbsent returns source code for handling functions that are possibly unavailable. 594 func (p *Fn) MaybeAbsent() string { 595 if !p.Rets.fnMaybeAbsent { 596 return "" 597 } 598 const code = `%[1]s = proc%[2]s.Find() 599 if %[1]s != nil { 600 return 601 }` 602 errorVar := p.Rets.ErrorVarName() 603 if errorVar == "" { 604 errorVar = "err" 605 } 606 return fmt.Sprintf(code, errorVar, p.DLLFuncName()) 607 } 608 609 // IsUTF16 is true, if f is W (utf16) function. It is false 610 // for all A (ascii) functions. 611 func (f *Fn) IsUTF16() bool { 612 s := f.DLLFuncName() 613 return s[len(s)-1] == 'W' 614 } 615 616 // StrconvFunc returns name of Go string to OS string function for f. 617 func (f *Fn) StrconvFunc() string { 618 if f.IsUTF16() { 619 return syscalldot() + "UTF16PtrFromString" 620 } 621 return syscalldot() + "BytePtrFromString" 622 } 623 624 // StrconvType returns Go type name used for OS string for f. 625 func (f *Fn) StrconvType() string { 626 if f.IsUTF16() { 627 return "*uint16" 628 } 629 return "*byte" 630 } 631 632 // HasStringParam is true, if f has at least one string parameter. 633 // Otherwise it is false. 634 func (f *Fn) HasStringParam() bool { 635 for _, p := range f.Params { 636 if p.Type == "string" { 637 return true 638 } 639 } 640 return false 641 } 642 643 // HelperName returns name of function f helper. 644 func (f *Fn) HelperName() string { 645 if !f.HasStringParam() { 646 return f.Name 647 } 648 return "_" + f.Name 649 } 650 651 // Source files and functions. 652 type Source struct { 653 Funcs []*Fn 654 DLLFuncNames []*Fn 655 Files []string 656 StdLibImports []string 657 ExternalImports []string 658 } 659 660 func (src *Source) Import(pkg string) { 661 src.StdLibImports = append(src.StdLibImports, pkg) 662 sort.Strings(src.StdLibImports) 663 } 664 665 func (src *Source) ExternalImport(pkg string) { 666 src.ExternalImports = append(src.ExternalImports, pkg) 667 sort.Strings(src.ExternalImports) 668 } 669 670 // ParseFiles parses files listed in fs and extracts all syscall 671 // functions listed in sys comments. It returns source files 672 // and functions collection *Source if successful. 673 func ParseFiles(fs []string) (*Source, error) { 674 src := &Source{ 675 Funcs: make([]*Fn, 0), 676 Files: make([]string, 0), 677 StdLibImports: []string{ 678 "unsafe", 679 }, 680 ExternalImports: make([]string, 0), 681 } 682 for _, file := range fs { 683 if err := src.ParseFile(file); err != nil { 684 return nil, err 685 } 686 } 687 src.DLLFuncNames = make([]*Fn, 0, len(src.Funcs)) 688 uniq := make(map[string]bool, len(src.Funcs)) 689 for _, fn := range src.Funcs { 690 name := fn.DLLFuncName() 691 if !uniq[name] { 692 src.DLLFuncNames = append(src.DLLFuncNames, fn) 693 uniq[name] = true 694 } 695 } 696 return src, nil 697 } 698 699 // DLLs return dll names for a source set src. 700 func (src *Source) DLLs() []string { 701 uniq := make(map[string]bool) 702 r := make([]string, 0) 703 for _, f := range src.Funcs { 704 name := f.DLLName() 705 if _, found := uniq[name]; !found { 706 uniq[name] = true 707 r = append(r, name) 708 } 709 } 710 sort.Strings(r) 711 return r 712 } 713 714 // ParseFile adds additional file path to a source set src. 715 func (src *Source) ParseFile(path string) error { 716 file, err := os.Open(path) 717 if err != nil { 718 return err 719 } 720 defer file.Close() 721 722 s := bufio.NewScanner(file) 723 for s.Scan() { 724 t := trim(s.Text()) 725 if len(t) < 7 { 726 continue 727 } 728 if !strings.HasPrefix(t, "//sys") { 729 continue 730 } 731 t = t[5:] 732 if !(t[0] == ' ' || t[0] == '\t') { 733 continue 734 } 735 f, err := newFn(t[1:]) 736 if err != nil { 737 return err 738 } 739 src.Funcs = append(src.Funcs, f) 740 } 741 if err := s.Err(); err != nil { 742 return err 743 } 744 src.Files = append(src.Files, path) 745 sort.Slice(src.Funcs, func(i, j int) bool { 746 fi, fj := src.Funcs[i], src.Funcs[j] 747 if fi.DLLName() == fj.DLLName() { 748 return fi.DLLFuncName() < fj.DLLFuncName() 749 } 750 return fi.DLLName() < fj.DLLName() 751 }) 752 753 // get package name 754 fset := token.NewFileSet() 755 _, err = file.Seek(0, 0) 756 if err != nil { 757 return err 758 } 759 pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly) 760 if err != nil { 761 return err 762 } 763 packageName = pkg.Name.Name 764 765 return nil 766 } 767 768 // IsStdRepo reports whether src is part of standard library. 769 func (src *Source) IsStdRepo() (bool, error) { 770 if len(src.Files) == 0 { 771 return false, errors.New("no input files provided") 772 } 773 abspath, err := filepath.Abs(src.Files[0]) 774 if err != nil { 775 return false, err 776 } 777 goroot := runtime.GOROOT() 778 if runtime.GOOS == "windows" { 779 abspath = strings.ToLower(abspath) 780 goroot = strings.ToLower(goroot) 781 } 782 sep := string(os.PathSeparator) 783 if !strings.HasSuffix(goroot, sep) { 784 goroot += sep 785 } 786 return strings.HasPrefix(abspath, goroot), nil 787 } 788 789 // Generate output source file from a source set src. 790 func (src *Source) Generate(w io.Writer) error { 791 const ( 792 pkgStd = iota // any package in std library 793 pkgXSysWindows // x/sys/windows package 794 pkgOther 795 ) 796 isStdRepo, err := src.IsStdRepo() 797 if err != nil { 798 return err 799 } 800 var pkgtype int 801 switch { 802 case isStdRepo: 803 pkgtype = pkgStd 804 case packageName == "windows": 805 // TODO: this needs better logic than just using package name 806 pkgtype = pkgXSysWindows 807 default: 808 pkgtype = pkgOther 809 } 810 if *systemDLL { 811 switch pkgtype { 812 case pkgStd: 813 src.Import("internal/syscall/windows/sysdll") 814 case pkgXSysWindows: 815 default: 816 src.ExternalImport("golang.org/x/sys/windows") 817 } 818 } 819 if packageName != "syscall" { 820 src.Import("syscall") 821 } 822 funcMap := template.FuncMap{ 823 "packagename": packagename, 824 "syscalldot": syscalldot, 825 "newlazydll": func(dll string) string { 826 arg := "\"" + dll + ".dll\"" 827 if !*systemDLL { 828 return syscalldot() + "NewLazyDLL(" + arg + ")" 829 } 830 switch pkgtype { 831 case pkgStd: 832 return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))" 833 case pkgXSysWindows: 834 return "NewLazySystemDLL(" + arg + ")" 835 default: 836 return "windows.NewLazySystemDLL(" + arg + ")" 837 } 838 }, 839 } 840 t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate)) 841 err = t.Execute(w, src) 842 if err != nil { 843 return errors.New("Failed to execute template: " + err.Error()) 844 } 845 return nil 846 } 847 848 func usage() { 849 fmt.Fprintf(os.Stderr, "usage: mkwinsyscall [flags] [path ...]\n") 850 flag.PrintDefaults() 851 os.Exit(1) 852 } 853 854 func main() { 855 flag.Usage = usage 856 flag.Parse() 857 if len(flag.Args()) <= 0 { 858 fmt.Fprintf(os.Stderr, "no files to parse provided\n") 859 usage() 860 } 861 862 src, err := ParseFiles(flag.Args()) 863 if err != nil { 864 log.Fatal(err) 865 } 866 867 var buf bytes.Buffer 868 if err := src.Generate(&buf); err != nil { 869 log.Fatal(err) 870 } 871 872 data, err := format.Source(buf.Bytes()) 873 if err != nil { 874 log.Fatal(err) 875 } 876 if *filename == "" { 877 _, err = os.Stdout.Write(data) 878 } else { 879 err = ioutil.WriteFile(*filename, data, 0644) 880 } 881 if err != nil { 882 log.Fatal(err) 883 } 884 } 885 886 // TODO: use println instead to print in the following template 887 const srcTemplate = ` 888 889 {{define "main"}}// Code generated by 'go generate'; DO NOT EDIT. 890 891 package {{packagename}} 892 893 import ( 894 {{range .StdLibImports}}"{{.}}" 895 {{end}} 896 897 {{range .ExternalImports}}"{{.}}" 898 {{end}} 899 ) 900 901 var _ unsafe.Pointer 902 903 // Do the interface allocations only once for common 904 // Errno values. 905 const ( 906 errnoERROR_IO_PENDING = 997 907 ) 908 909 var ( 910 errERROR_IO_PENDING error = {{syscalldot}}Errno(errnoERROR_IO_PENDING) 911 errERROR_EINVAL error = {{syscalldot}}EINVAL 912 ) 913 914 // errnoErr returns common boxed Errno values, to prevent 915 // allocations at runtime. 916 func errnoErr(e {{syscalldot}}Errno) error { 917 switch e { 918 case 0: 919 return errERROR_EINVAL 920 case errnoERROR_IO_PENDING: 921 return errERROR_IO_PENDING 922 } 923 // TODO: add more here, after collecting data on the common 924 // error values see on Windows. (perhaps when running 925 // all.bat?) 926 return e 927 } 928 929 var ( 930 {{template "dlls" .}} 931 {{template "funcnames" .}}) 932 {{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}} 933 {{end}} 934 935 {{/* help functions */}} 936 937 {{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}} 938 {{end}}{{end}} 939 940 {{define "funcnames"}}{{range .DLLFuncNames}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}") 941 {{end}}{{end}} 942 943 {{define "helperbody"}} 944 func {{.Name}}({{.ParamList}}) {{template "results" .}}{ 945 {{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}}) 946 } 947 {{end}} 948 949 {{define "funcbody"}} 950 func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{ 951 {{template "maybeabsent" .}} {{template "tmpvars" .}} {{template "syscall" .}} {{template "tmpvarsreadback" .}} 952 {{template "seterror" .}}{{template "printtrace" .}} return 953 } 954 {{end}} 955 956 {{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}} 957 {{end}}{{end}}{{end}} 958 959 {{define "maybeabsent"}}{{if .MaybeAbsent}}{{.MaybeAbsent}} 960 {{end}}{{end}} 961 962 {{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}} 963 {{end}}{{end}}{{end}} 964 965 {{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}} 966 967 {{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}} 968 969 {{define "tmpvarsreadback"}}{{range .Params}}{{if .TmpVarReadbackCode}} 970 {{.TmpVarReadbackCode}}{{end}}{{end}}{{end}} 971 972 {{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}} 973 {{end}}{{end}} 974 975 {{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n") 976 {{end}}{{end}} 977 978 `