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