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 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 37Usage: 38 mkwinsyscall [flags] [path ...] 39 40The flags are: 41 -output 42 Specify output file name (outputs to console if blank). 43 -trace 44 Generate print statement after every syscall. 45*/ 46package main 47 48import ( 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 69var ( 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 75func trim(s string) string { 76 return strings.Trim(s, " \t") 77} 78 79var packageName string 80 81func packagename() string { 82 return packageName 83} 84 85func windowsdot() string { 86 if packageName == "windows" { 87 return "" 88 } 89 return "windows." 90} 91 92func syscalldot() string { 93 if packageName == "syscall" { 94 return "" 95 } 96 return "syscall." 97} 98 99// Param is function parameter 100type 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. 108func (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. 117func (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. 126func (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. 135func (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. 145func (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. 165func (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. 179func (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. 189func (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. 199func (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. 221func (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. 226func (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. 236func 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. 248type 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. 257func (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. 268func (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. 280func (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. 292func (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. 297func (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 312func (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. 324func (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. 359type 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. 372func 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. 401func 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. 422func 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. 499func (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. 507func (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. 515func (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. 520func (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. 526func (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. 531func (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. 541func (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. 560func (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. 569func (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. 581func (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. 594func (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. 611func (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. 617func (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. 625func (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. 634func (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. 644func (f *Fn) HelperName() string { 645 if !f.HasStringParam() { 646 return f.Name 647 } 648 return "_" + f.Name 649} 650 651// Source files and functions. 652type Source struct { 653 Funcs []*Fn 654 DLLFuncNames []*Fn 655 Files []string 656 StdLibImports []string 657 ExternalImports []string 658} 659 660func (src *Source) Import(pkg string) { 661 src.StdLibImports = append(src.StdLibImports, pkg) 662 sort.Strings(src.StdLibImports) 663} 664 665func (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. 673func 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. 700func (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. 715func (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. 769func (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. 790func (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 848func usage() { 849 fmt.Fprintf(os.Stderr, "usage: mkwinsyscall [flags] [path ...]\n") 850 flag.PrintDefaults() 851 os.Exit(1) 852} 853 854func 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 887const srcTemplate = ` 888 889{{define "main"}}// Code generated by 'go generate'; DO NOT EDIT. 890 891package {{packagename}} 892 893import ( 894{{range .StdLibImports}}"{{.}}" 895{{end}} 896 897{{range .ExternalImports}}"{{.}}" 898{{end}} 899) 900 901var _ unsafe.Pointer 902 903// Do the interface allocations only once for common 904// Errno values. 905const ( 906 errnoERROR_IO_PENDING = 997 907) 908 909var ( 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. 916func 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 929var ( 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"}} 944func {{.Name}}({{.ParamList}}) {{template "results" .}}{ 945{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}}) 946} 947{{end}} 948 949{{define "funcbody"}} 950func {{.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` 979