mkwinsyscall.go 22 KB

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