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