github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/cmd/gtrace/writer.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "container/list" 6 "fmt" 7 "go/build" 8 "go/token" 9 "go/types" 10 "io" 11 "os" 12 "path/filepath" 13 "runtime" 14 "sort" 15 "strconv" 16 "strings" 17 "sync" 18 "unicode" 19 "unicode/utf8" 20 ) 21 22 //nolint:maligned 23 type Writer struct { 24 Output io.Writer 25 Context build.Context 26 27 once sync.Once 28 bw *bufio.Writer 29 30 atEOL bool 31 depth int 32 scope *list.List 33 34 pkg *types.Package 35 std map[string]bool 36 } 37 38 func (w *Writer) Write(p Package) error { 39 w.pkg = p.Package 40 41 w.init() 42 w.line(`// Code generated by gtrace. DO NOT EDIT.`) 43 44 for i, line := range p.BuildConstraints { 45 if i == 0 { 46 w.line() 47 } 48 w.line(line) 49 } 50 w.line() 51 w.line(`package `, p.Name()) 52 w.line() 53 54 var deps []dep 55 for _, trace := range p.Traces { 56 deps = w.traceImports(deps, trace) 57 } 58 w.importDeps(deps) 59 60 w.newScope(func() { 61 for _, trace := range p.Traces { 62 w.options(trace) 63 w.compose(trace) 64 if trace.Nested { 65 w.isZero(trace) 66 } 67 for _, hook := range trace.Hooks { 68 w.hook(trace, hook) 69 } 70 } 71 for _, trace := range p.Traces { 72 for _, hook := range trace.Hooks { 73 w.hookShortcut(trace, hook) 74 } 75 } 76 }) 77 78 return w.bw.Flush() 79 } 80 81 func (w *Writer) init() { 82 w.once.Do(func() { 83 w.bw = bufio.NewWriter(w.Output) 84 w.scope = list.New() 85 }) 86 } 87 88 func (w *Writer) mustDeclare(name string) { 89 s := w.scope.Back().Value.(*scope) 90 if !s.set(name) { 91 where := s.where(name) 92 panic(fmt.Sprintf( 93 "gtrace: can't declare identifier: %q: already defined at %q", 94 name, where, 95 )) 96 } 97 } 98 99 func (w *Writer) declare(name string) string { 100 if isPredeclared(name) { 101 name = firstChar(name) 102 } 103 s := w.scope.Back().Value.(*scope) 104 for i := 0; ; i++ { 105 v := name 106 if i > 0 { 107 v += strconv.Itoa(i) 108 } 109 if token.IsKeyword(v) { 110 continue 111 } 112 if w.isGlobalScope() && w.pkg.Scope().Lookup(v) != nil { 113 continue 114 } 115 if s.set(v) { 116 return v 117 } 118 } 119 } 120 121 func isPredeclared(name string) bool { 122 return types.Universe.Lookup(name) != nil 123 } 124 125 func (w *Writer) isGlobalScope() bool { 126 return w.scope.Back().Prev() == nil 127 } 128 129 func (w *Writer) capture(vars ...string) { 130 s := w.scope.Back().Value.(*scope) 131 for _, v := range vars { 132 if !s.set(v) { 133 panic(fmt.Sprintf("can't capture variable %q", v)) 134 } 135 } 136 } 137 138 type dep struct { 139 pkgPath string 140 pkgName string 141 typName string 142 } 143 144 func (w *Writer) typeImports(dst []dep, t types.Type) []dep { 145 if p, ok := t.(*types.Pointer); ok { 146 return w.typeImports(dst, p.Elem()) 147 } 148 n, ok := t.(*types.Named) 149 if !ok { 150 return dst 151 } 152 var ( 153 obj = n.Obj() 154 pkg = obj.Pkg() 155 ) 156 if pkg != nil && pkg.Path() != w.pkg.Path() { 157 return append(dst, dep{ 158 pkgPath: pkg.Path(), 159 pkgName: pkg.Name(), 160 typName: obj.Name(), 161 }) 162 } 163 164 return dst 165 } 166 167 func forEachField(s *types.Struct, fn func(*types.Var)) { 168 for i := 0; i < s.NumFields(); i++ { 169 fn(s.Field(i)) 170 } 171 } 172 173 func unwrapStruct(t types.Type) (n *types.Named, s *types.Struct) { 174 var ok bool 175 n, ok = t.(*types.Named) 176 if ok { 177 s, _ = n.Underlying().(*types.Struct) 178 } 179 180 return 181 } 182 183 func (w *Writer) funcImports(dst []dep, fn *Func) []dep { 184 for i := range fn.Params { 185 dst = w.typeImports(dst, fn.Params[i].Type) 186 if _, s := unwrapStruct(fn.Params[i].Type); s != nil { 187 forEachField(s, func(v *types.Var) { 188 if v.Exported() { 189 dst = w.typeImports(dst, v.Type()) 190 } 191 }) 192 } 193 } 194 for _, x := range fn.Result { 195 if fn, ok := x.(*Func); ok { 196 dst = w.funcImports(dst, fn) 197 } 198 } 199 200 return dst 201 } 202 203 func (w *Writer) traceImports(dst []dep, t *Trace) []dep { 204 for _, h := range t.Hooks { 205 dst = w.funcImports(dst, h.Func) 206 } 207 208 return dst 209 } 210 211 func (w *Writer) importDeps(deps []dep) { 212 seen := map[string]bool{} 213 for i := 0; i < len(deps); { 214 d := deps[i] 215 if seen[d.pkgPath] { 216 n := len(deps) 217 deps[i], deps[n-1] = deps[n-1], deps[i] 218 deps = deps[:n-1] 219 220 continue 221 } 222 seen[d.pkgPath] = true 223 i++ 224 } 225 if len(deps) == 0 { 226 return 227 } 228 sort.Slice(deps, func(i, j int) bool { 229 var ( 230 d0 = deps[i] 231 d1 = deps[j] 232 std0 = w.isStdLib(d0.pkgPath) 233 std1 = w.isStdLib(d1.pkgPath) 234 ) 235 if std0 != std1 { 236 return std0 237 } 238 239 return d0.pkgPath < d1.pkgPath 240 }) 241 w.line(`import (`) 242 var lastStd bool 243 for i := range deps { 244 if w.isStdLib(deps[i].pkgPath) { 245 lastStd = true 246 } else if lastStd { 247 lastStd = false 248 w.line() 249 } 250 w.line("\t", `"`, deps[i].pkgPath, `"`) 251 } 252 w.line(`)`) 253 w.line() 254 } 255 256 func (w *Writer) isStdLib(pkg string) bool { 257 w.ensureStdLibMapping() 258 s := strings.Split(pkg, "/")[0] 259 260 return w.std[s] 261 } 262 263 func (w *Writer) ensureStdLibMapping() { 264 if w.std != nil { 265 return 266 } 267 w.std = make(map[string]bool) 268 269 src := filepath.Join(w.Context.GOROOT, "src") 270 files, err := os.ReadDir(src) 271 if err != nil { 272 panic(fmt.Sprintf("can't list GOROOT's src: %v", err)) 273 } 274 for _, file := range files { 275 if !file.IsDir() { 276 continue 277 } 278 name := filepath.Base(file.Name()) 279 switch name { 280 case "cmd", "internal": 281 // Ignored. 282 283 default: 284 w.std[name] = true 285 } 286 } 287 } 288 289 func (w *Writer) call(args []string) { 290 w.code(`(`) 291 for i, name := range args { 292 if i > 0 { 293 w.code(`, `) 294 } 295 w.code(name) 296 } 297 w.line(`)`) 298 } 299 300 func (w *Writer) isZero(trace *Trace) { 301 w.newScope(func() { 302 t := w.declare("t") 303 w.line(`// isZero checks whether `, t, ` is empty`) 304 w.line(`func (`, t, ` `, trace.Name, `) isZero() bool {`) 305 w.block(func() { 306 for _, hook := range trace.Hooks { 307 w.line(`if `, t, `.`, hook.Name, ` != nil {`) 308 w.block(func() { 309 w.line(`return false`) 310 }) 311 w.line(`}`) 312 } 313 w.line(`return true`) 314 }) 315 w.line(`}`) 316 }) 317 } 318 319 func (w *Writer) compose(trace *Trace) { 320 w.newScope(func() { 321 t := w.declare("t") 322 x := w.declare("x") 323 ret := w.declare("ret") 324 w.line(`// Compose returns a new `, trace.Name, ` which has functional fields composed both from `, 325 t, ` and `, x, `.`, 326 ) 327 w.code(`func (`, t, ` *`, trace.Name, `) Compose(`, x, ` *`, trace.Name, `, opts ...`+trace.Name+`ComposeOption) `) 328 w.line(`*`, trace.Name, ` {`) 329 w.block(func() { 330 w.line(`var `, ret, ` `, trace.Name, ``) 331 if len(trace.Hooks) > 0 { 332 w.line(`options := `, unexported(trace.Name), `ComposeOptions{}`) 333 w.line(`for _, opt := range opts {`) 334 w.block(func() { 335 w.line(`if opt != nil {`) 336 w.block(func() { 337 w.line(`opt(&options)`) 338 }) 339 w.line(`}`) 340 }) 341 w.line(`}`) 342 } 343 for _, hook := range trace.Hooks { 344 w.composeHook(hook, t, x, ret+"."+hook.Name) 345 } 346 w.line(`return &`, ret) 347 }) 348 w.line(`}`) 349 }) 350 } 351 352 func (w *Writer) composeHook(hook Hook, t1, t2, dst string) { 353 w.line(`{`) 354 w.block(func() { 355 h1 := w.declare("h1") 356 h2 := w.declare("h2") 357 w.line(h1, ` := `, t1, `.`, hook.Name) 358 w.line(h2, ` := `, t2, `.`, hook.Name) 359 w.code(dst, ` = `) 360 w.composeHookCall(hook.Func, h1, h2) 361 }) 362 w.line(`}`) 363 } 364 365 func (w *Writer) composeHookCall(fn *Func, h1, h2 string) { 366 w.newScope(func() { 367 w.capture(h1, h2) 368 w.block(func() { 369 w.capture(h1, h2) 370 w.code(`func`) 371 args := w.funcParams(fn.Params) 372 if fn.HasResult() { 373 w.code(` `) 374 } 375 w.funcResults(fn) 376 w.line(` {`) 377 w.line(`if options.panicCallback != nil {`) 378 w.block(func() { 379 w.line("defer func() {") 380 w.block(func() { 381 w.line("if e := recover(); e != nil {") 382 w.block(func() { 383 w.line(`options.panicCallback(e)`) 384 }) 385 w.line("}") 386 }) 387 w.line("}()") 388 }) 389 w.line("}") 390 var ( 391 r1 string 392 r2 string 393 rs []string 394 ) 395 if fn.HasResult() { 396 r1 = w.declare("r") 397 r2 = w.declare("r") 398 rs = []string{r1, r2} 399 w.code("var " + r1 + ", " + r2 + " ") 400 w.funcResults(fn) 401 _ = w.bw.WriteByte('\n') 402 w.atEOL = true 403 } 404 for i, h := range []string{h1, h2} { 405 w.line("if " + h + " != nil {") 406 w.block(func() { 407 if fn.HasResult() { 408 w.code(rs[i], ` = `) 409 } 410 w.code(h) 411 w.call(args) 412 }) 413 w.line("}") 414 } 415 if fn.HasResult() { 416 w.code(`return `) 417 switch x := fn.Result[0].(type) { 418 case *Func: 419 w.composeHookCall(x, r1, r2) 420 case *Trace: 421 w.line(r1, `.Compose(`, r2, `)`) 422 default: 423 panic("unknown result type") 424 } 425 } 426 }) 427 w.line(`}`) 428 }) 429 } 430 431 func (w *Writer) options(trace *Trace) { 432 w.newScope(func() { 433 w.line(fmt.Sprintf(`// %sComposeOptions is a holder of options`, unexported(trace.Name))) 434 w.line(fmt.Sprintf(`type %sComposeOptions struct {`, unexported(trace.Name))) 435 w.block(func() { 436 w.line(`panicCallback func(e interface{})`) 437 }) 438 w.line(`}`) 439 _ = w.bw.WriteByte('\n') 440 }) 441 w.newScope(func() { 442 w.line(fmt.Sprintf(`// %sOption specified %s compose option`, trace.Name, trace.Name)) 443 w.line(fmt.Sprintf(`type %sComposeOption func(o *%sComposeOptions)`, trace.Name, unexported(trace.Name))) 444 _ = w.bw.WriteByte('\n') 445 }) 446 w.newScope(func() { 447 w.line(fmt.Sprintf(`// With%sPanicCallback specified behavior on panic`, trace.Name)) 448 w.line(fmt.Sprintf(`func With%sPanicCallback(cb func(e interface{})) %sComposeOption {`, trace.Name, trace.Name)) 449 w.block(func() { 450 w.line(fmt.Sprintf(`return func(o *%sComposeOptions) {`, unexported(trace.Name))) 451 w.block(func() { 452 w.line(`o.panicCallback = cb`) 453 }) 454 w.line(`}`) 455 }) 456 w.line(`}`) 457 _ = w.bw.WriteByte('\n') 458 }) 459 } 460 461 func (w *Writer) hook(trace *Trace, hook Hook) { 462 w.newScope(func() { 463 t := w.declare("t") 464 fn := w.declare("fn") 465 466 w.code(`func (`, t, ` *`, trace.Name, `) `, unexported(hook.Name)) 467 468 w.code(`(`) 469 var args []string 470 for i := range hook.Func.Params { 471 if i > 0 { 472 w.code(`, `) 473 } 474 args = append(args, w.funcParam(&hook.Func.Params[i])) 475 } 476 w.code(`)`) 477 if hook.Func.HasResult() { 478 w.code(` `) 479 } 480 w.funcResultsFlags(hook.Func, docs) 481 w.line(` {`) 482 w.block(func() { 483 w.line(fn, ` := `, t, `.`, hook.Name) 484 w.line(`if `, fn, ` == nil {`) 485 w.block(func() { 486 w.zeroReturn(hook.Func) 487 }) 488 w.line(`}`) 489 490 w.hookFuncCall(hook.Func, fn, args) 491 }) 492 w.line(`}`) 493 }) 494 } 495 496 func (w *Writer) hookFuncCall(fn *Func, name string, args []string) { 497 var res string 498 if fn.HasResult() { 499 res = w.declare("res") 500 w.code(res, ` := `) 501 } 502 503 w.code(name) 504 w.call(args) 505 506 if !fn.HasResult() { 507 return 508 } 509 510 r, isFunc := fn.Result[0].(*Func) 511 if isFunc { 512 w.line(`if `, res, ` == nil {`) 513 w.block(func() { 514 w.zeroReturn(fn) 515 }) 516 w.line(`}`) 517 518 if r.HasResult() { 519 w.newScope(func() { 520 w.code(`return func`) 521 args := w.funcParams(r.Params) 522 w.code(` `) 523 w.funcResults(r) 524 w.line(` {`) 525 w.block(func() { 526 w.hookFuncCall(r, res, args) 527 }) 528 w.line(`}`) 529 }) 530 531 return 532 } 533 } 534 535 w.line(`return `, res) 536 } 537 538 func nameParam(p *Param) (s string) { 539 s = p.Name 540 if s == "" { 541 s = firstChar(ident(typeBasename(p.Type))) 542 } 543 544 return unexported(s) 545 } 546 547 func (w *Writer) declareParams(src []Param) (names []string) { 548 names = make([]string, len(src)) 549 for i := range src { 550 names[i] = w.declare(nameParam(&src[i])) 551 } 552 553 return names 554 } 555 556 func flattenParams(params []Param) (dst []Param) { 557 for i := range params { 558 _, s := unwrapStruct(params[i].Type) 559 if s != nil { 560 dst = flattenStruct(dst, s) 561 562 continue 563 } 564 dst = append(dst, params[i]) 565 } 566 567 return dst 568 } 569 570 func typeBasename(t types.Type) (name string) { 571 lo, name := rsplit(t.String(), '.') 572 if name == "" { 573 name = lo 574 } 575 576 return name 577 } 578 579 func flattenStruct(dst []Param, s *types.Struct) []Param { 580 forEachField(s, func(f *types.Var) { 581 if !f.Exported() { 582 return 583 } 584 var ( 585 name = f.Name() 586 typ = f.Type() 587 ) 588 if name == typeBasename(typ) { 589 // NOTE: field name essentially be empty for embedded structs or 590 // fields called exactly as type. 591 name = "" 592 } 593 dst = append(dst, Param{ 594 Name: name, 595 Type: typ, 596 }) 597 }) 598 599 return dst 600 } 601 602 func (w *Writer) constructParams(params []Param, names []string) (res []string) { 603 for i := range params { 604 n, s := unwrapStruct(params[i].Type) 605 if s != nil { 606 var v string 607 v, names = w.constructStruct(n, s, names) 608 res = append(res, v) 609 610 continue 611 } 612 name := names[0] 613 names = names[1:] 614 res = append(res, name) 615 } 616 617 return res 618 } 619 620 func (w *Writer) constructStruct(n types.Type, s *types.Struct, vars []string) (string, []string) { 621 p := w.declare("p") 622 // maybe skip pointers from flattening to not allocate anyhing during trace. 623 w.line(`var `, p, ` `, w.typeString(n)) 624 for i := 0; i < s.NumFields(); i++ { 625 v := s.Field(i) 626 if !v.Exported() { 627 continue 628 } 629 name := vars[0] 630 vars = vars[1:] 631 w.line(p, `.`, v.Name(), ` = `, name) 632 } 633 634 return p, vars 635 } 636 637 func (w *Writer) hookShortcut(trace *Trace, hook Hook) { 638 name := exported(tempName(trace.Name, hook.Name)) 639 640 w.mustDeclare(name) 641 642 w.newScope(func() { 643 t := w.declare("t") 644 w.code(`func `, name) 645 w.code(`(`) 646 var ctx string 647 w.code(t, ` *`, trace.Name) 648 649 var ( 650 params = flattenParams(hook.Func.Params) 651 names = w.declareParams(params) 652 ) 653 for i := range params { 654 w.code(`, `) 655 w.code(names[i], ` `, w.typeString(params[i].Type)) 656 } 657 w.code(`)`) 658 if hook.Func.HasResult() { 659 w.code(` `) 660 } 661 w.shortcutFuncResultsFlags(hook.Func, docs) 662 w.line(` {`) 663 w.block(func() { 664 for _, name := range names { 665 w.capture(name) 666 } 667 vars := w.constructParams(hook.Func.Params, names) 668 var res string 669 if hook.Func.HasResult() { 670 res = w.declare("res") 671 w.code(res, ` := `) 672 } 673 w.code(t, `.`, unexported(hook.Name)) 674 if ctx != "" { 675 vars = append([]string{ctx}, vars...) 676 } 677 w.call(vars) 678 if hook.Func.HasResult() { 679 w.code(`return `) 680 r := hook.Func.Result[0] 681 switch x := r.(type) { 682 case *Func: 683 w.hookFuncShortcut(x, res) 684 case *Trace: 685 w.line(res) 686 default: 687 panic("unexpected result type") 688 } 689 } 690 }) 691 w.line(`}`) 692 }) 693 } 694 695 func (w *Writer) hookFuncShortcut(fn *Func, name string) { 696 w.newScope(func() { 697 w.code(`func(`) 698 var ( 699 params = flattenParams(fn.Params) 700 names = w.declareParams(params) 701 ) 702 for i := range params { 703 if i > 0 { 704 w.code(`, `) 705 } 706 w.code(names[i], ` `, w.typeString(params[i].Type)) 707 } 708 w.code(`)`) 709 if fn.HasResult() { 710 w.code(` `) 711 } 712 w.shortcutFuncResults(fn) 713 w.line(` {`) 714 w.block(func() { 715 for _, name := range names { 716 w.capture(name) 717 } 718 params := w.constructParams(fn.Params, names) 719 var res string 720 if fn.HasResult() { 721 res = w.declare("res") 722 w.code(res, ` := `) 723 } 724 w.code(name) 725 w.call(params) 726 if fn.HasResult() { 727 r := fn.Result[0] 728 w.code(`return `) 729 switch x := r.(type) { 730 case *Func: 731 w.hookFuncShortcut(x, res) 732 case *Trace: 733 w.line(res) 734 default: 735 panic("unexpected result type") 736 } 737 } 738 }) 739 w.line(`}`) 740 }) 741 } 742 743 func (w *Writer) zeroReturn(fn *Func) { 744 if !fn.HasResult() { 745 w.line(`return`) 746 747 return 748 } 749 w.code(`return `) 750 switch x := fn.Result[0].(type) { 751 case *Func: 752 w.funcSignature(x) 753 w.line(` {`) 754 w.block(func() { 755 w.zeroReturn(x) 756 }) 757 w.line(`}`) 758 case *Trace: 759 w.line(x.Name, `{}`) 760 default: 761 panic("unexpected result type") 762 } 763 } 764 765 func (w *Writer) funcParams(params []Param) (vars []string) { 766 w.code(`(`) 767 for i := range params { 768 if i > 0 { 769 w.code(`, `) 770 } 771 vars = append(vars, w.funcParam(¶ms[i])) 772 } 773 w.code(`)`) 774 775 return 776 } 777 778 func (w *Writer) funcParam(p *Param) (name string) { 779 name = w.declare(nameParam(p)) 780 w.code(name, ` `) 781 w.code(w.typeString(p.Type)) 782 783 return name 784 } 785 786 func (w *Writer) funcParamSign(p *Param) { 787 name := nameParam(p) 788 if len(name) == 1 || isPredeclared(name) { 789 name = "_" 790 } 791 w.code(name, ` `) 792 w.code(w.typeString(p.Type)) 793 } 794 795 type flags uint8 796 797 func (f flags) has(x flags) bool { 798 return f&x != 0 799 } 800 801 const ( 802 _ flags = 1 << iota >> 1 803 docs 804 ) 805 806 func (w *Writer) funcResultsFlags(fn *Func, flags flags) { 807 for _, r := range fn.Result { 808 switch x := r.(type) { 809 case *Func: 810 w.funcSignatureFlags(x, flags) 811 case *Trace: 812 w.code(x.Name, ` `) 813 default: 814 panic("unexpected result type") 815 } 816 } 817 } 818 819 func (w *Writer) funcResults(fn *Func) { 820 w.funcResultsFlags(fn, 0) 821 } 822 823 func (w *Writer) funcSignatureFlags(fn *Func, flags flags) { 824 haveNames := haveNames(fn.Params) 825 w.code(`func(`) 826 for i := range fn.Params { 827 if i > 0 { 828 w.code(`, `) 829 } 830 if flags.has(docs) && haveNames { 831 w.funcParamSign(&fn.Params[i]) 832 } else { 833 w.code(w.typeString(fn.Params[i].Type)) 834 } 835 } 836 w.code(`)`) 837 if fn.HasResult() { 838 if fn.isFuncResult() { 839 w.code(` `) 840 } 841 w.funcResultsFlags(fn, flags) 842 } 843 } 844 845 func (w *Writer) funcSignature(fn *Func) { 846 w.funcSignatureFlags(fn, 0) 847 } 848 849 func (w *Writer) shortcutFuncSignFlags(fn *Func, flags flags) { 850 var ( 851 params = flattenParams(fn.Params) 852 haveNames = haveNames(params) 853 ) 854 w.code(`func(`) 855 for i := range params { 856 if i > 0 { 857 w.code(`, `) 858 } 859 if flags.has(docs) && haveNames { 860 w.funcParamSign(¶ms[i]) 861 } else { 862 w.code(w.typeString(params[i].Type)) 863 } 864 } 865 w.code(`)`) 866 if fn.HasResult() { 867 if fn.isFuncResult() { 868 w.code(` `) 869 } 870 w.shortcutFuncResultsFlags(fn, flags) 871 } 872 } 873 874 func (w *Writer) shortcutFuncResultsFlags(fn *Func, flags flags) { 875 for _, r := range fn.Result { 876 switch x := r.(type) { 877 case *Func: 878 w.shortcutFuncSignFlags(x, flags) 879 case *Trace: 880 w.code(x.Name, ` `) 881 default: 882 panic("unexpected result type") 883 } 884 } 885 } 886 887 func (w *Writer) shortcutFuncResults(fn *Func) { 888 w.shortcutFuncResultsFlags(fn, 0) 889 } 890 891 func haveNames(params []Param) bool { 892 for i := range params { 893 name := nameParam(¶ms[i]) 894 if len(name) > 1 && !isPredeclared(name) { 895 return true 896 } 897 } 898 899 return false 900 } 901 902 func (w *Writer) typeString(t types.Type) string { 903 return types.TypeString(t, func(pkg *types.Package) string { 904 if pkg.Path() == w.pkg.Path() { 905 return "" // same package; unqualified 906 } 907 908 return pkg.Name() 909 }) 910 } 911 912 func (w *Writer) block(fn func()) { 913 w.depth++ 914 w.newScope(fn) 915 w.depth-- 916 } 917 918 func (w *Writer) newScope(fn func()) { 919 w.scope.PushBack(new(scope)) 920 fn() 921 w.scope.Remove(w.scope.Back()) 922 } 923 924 func (w *Writer) line(args ...string) { 925 w.code(args...) 926 _ = w.bw.WriteByte('\n') 927 w.atEOL = true 928 } 929 930 func (w *Writer) code(args ...string) { 931 if w.atEOL { 932 for i := 0; i < w.depth; i++ { 933 _ = w.bw.WriteByte('\t') 934 } 935 w.atEOL = false 936 } 937 for _, arg := range args { 938 _, _ = w.bw.WriteString(arg) 939 } 940 } 941 942 func exported(s string) string { 943 r, size := utf8.DecodeRuneInString(s) 944 if r == utf8.RuneError { 945 panic("invalid string") 946 } 947 948 return string(unicode.ToUpper(r)) + s[size:] 949 } 950 951 func unexported(s string) string { 952 r, size := utf8.DecodeRuneInString(s) 953 if r == utf8.RuneError { 954 panic("invalid string") 955 } 956 957 return string(unicode.ToLower(r)) + s[size:] 958 } 959 960 func firstChar(s string) string { 961 r, _ := utf8.DecodeRuneInString(s) 962 if r == utf8.RuneError { 963 panic("invalid string") 964 } 965 966 return string(r) 967 } 968 969 func ident(s string) string { 970 // Identifier must not begin with number. 971 for len(s) > 0 { 972 r, size := utf8.DecodeRuneInString(s) 973 if r == utf8.RuneError { 974 panic("invalid string") 975 } 976 if !unicode.IsNumber(r) { 977 break 978 } 979 s = s[size:] 980 } 981 982 // Filter out non letter/number/underscore characters. 983 s = strings.Map(func(r rune) rune { 984 switch { 985 case r == '_' || 986 unicode.IsLetter(r) || 987 unicode.IsNumber(r): 988 989 return r 990 default: 991 return -1 992 } 993 }, s) 994 995 if !token.IsIdentifier(s) { 996 s = "_" + s 997 } 998 999 return s 1000 } 1001 1002 func tempName(names ...string) string { 1003 var sb strings.Builder 1004 for i, name := range names { 1005 if i == 0 { 1006 name = unexported(name) 1007 } else { 1008 name = exported(name) 1009 } 1010 sb.WriteString(name) 1011 } 1012 1013 return sb.String() 1014 } 1015 1016 type decl struct { 1017 where string 1018 } 1019 1020 type scope struct { 1021 vars map[string]decl 1022 } 1023 1024 func (s *scope) set(v string) bool { 1025 if s.vars == nil { 1026 s.vars = make(map[string]decl) 1027 } 1028 if _, has := s.vars[v]; has { 1029 return false 1030 } 1031 _, file, line, _ := runtime.Caller(2) 1032 s.vars[v] = decl{ 1033 where: fmt.Sprintf("%s:%d", file, line), 1034 } 1035 1036 return true 1037 } 1038 1039 func (s *scope) where(v string) string { 1040 d := s.vars[v] 1041 1042 return d.where 1043 }