github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/gcimporter/bexport.go (about) 1 // Copyright 2016 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 // Binary package export. 6 // This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; 7 // see that file for specification of the format. 8 9 package gcimporter 10 11 import ( 12 "bytes" 13 "encoding/binary" 14 "fmt" 15 "go/constant" 16 "go/token" 17 "go/types" 18 "math" 19 "math/big" 20 "sort" 21 "strings" 22 ) 23 24 // If debugFormat is set, each integer and string value is preceded by a marker 25 // and position information in the encoding. This mechanism permits an importer 26 // to recognize immediately when it is out of sync. The importer recognizes this 27 // mode automatically (i.e., it can import export data produced with debugging 28 // support even if debugFormat is not set at the time of import). This mode will 29 // lead to massively larger export data (by a factor of 2 to 3) and should only 30 // be enabled during development and debugging. 31 // 32 // NOTE: This flag is the first flag to enable if importing dies because of 33 // (suspected) format errors, and whenever a change is made to the format. 34 const debugFormat = false // default: false 35 36 // Current export format version. Increase with each format change. 37 // 38 // Note: The latest binary (non-indexed) export format is at version 6. 39 // This exporter is still at level 4, but it doesn't matter since 40 // the binary importer can handle older versions just fine. 41 // 42 // 6: package height (CL 105038) -- NOT IMPLEMENTED HERE 43 // 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE 44 // 4: type name objects support type aliases, uses aliasTag 45 // 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) 46 // 2: removed unused bool in ODCL export (compiler only) 47 // 1: header format change (more regular), export package for _ struct fields 48 // 0: Go1.7 encoding 49 const exportVersion = 4 50 51 // trackAllTypes enables cycle tracking for all types, not just named 52 // types. The existing compiler invariants assume that unnamed types 53 // that are not completely set up are not used, or else there are spurious 54 // errors. 55 // If disabled, only named types are tracked, possibly leading to slightly 56 // less efficient encoding in rare cases. It also prevents the export of 57 // some corner-case type declarations (but those are not handled correctly 58 // with with the textual export format either). 59 // TODO(gri) enable and remove once issues caused by it are fixed 60 const trackAllTypes = false 61 62 type exporter struct { 63 fset *token.FileSet 64 out bytes.Buffer 65 66 // object -> index maps, indexed in order of serialization 67 strIndex map[string]int 68 pkgIndex map[*types.Package]int 69 typIndex map[types.Type]int 70 71 // position encoding 72 posInfoFormat bool 73 prevFile string 74 prevLine int 75 76 // debugging support 77 written int // bytes written 78 indent int // for trace 79 } 80 81 // internalError represents an error generated inside this package. 82 type internalError string 83 84 func (e internalError) Error() string { return "gcimporter: " + string(e) } 85 86 func internalErrorf(format string, args ...interface{}) error { 87 return internalError(fmt.Sprintf(format, args...)) 88 } 89 90 // BExportData returns binary export data for pkg. 91 // If no file set is provided, position info will be missing. 92 func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { 93 if !debug { 94 defer func() { 95 if e := recover(); e != nil { 96 if ierr, ok := e.(internalError); ok { 97 err = ierr 98 return 99 } 100 // Not an internal error; panic again. 101 panic(e) 102 } 103 }() 104 } 105 106 p := exporter{ 107 fset: fset, 108 strIndex: map[string]int{"": 0}, // empty string is mapped to 0 109 pkgIndex: make(map[*types.Package]int), 110 typIndex: make(map[types.Type]int), 111 posInfoFormat: true, // TODO(gri) might become a flag, eventually 112 } 113 114 // write version info 115 // The version string must start with "version %d" where %d is the version 116 // number. Additional debugging information may follow after a blank; that 117 // text is ignored by the importer. 118 p.rawStringln(fmt.Sprintf("version %d", exportVersion)) 119 var debug string 120 if debugFormat { 121 debug = "debug" 122 } 123 p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly 124 p.bool(trackAllTypes) 125 p.bool(p.posInfoFormat) 126 127 // --- generic export data --- 128 129 // populate type map with predeclared "known" types 130 for index, typ := range predeclared() { 131 p.typIndex[typ] = index 132 } 133 if len(p.typIndex) != len(predeclared()) { 134 return nil, internalError("duplicate entries in type map?") 135 } 136 137 // write package data 138 p.pkg(pkg, true) 139 if trace { 140 p.tracef("\n") 141 } 142 143 // write objects 144 objcount := 0 145 scope := pkg.Scope() 146 for _, name := range scope.Names() { 147 if !token.IsExported(name) { 148 continue 149 } 150 if trace { 151 p.tracef("\n") 152 } 153 p.obj(scope.Lookup(name)) 154 objcount++ 155 } 156 157 // indicate end of list 158 if trace { 159 p.tracef("\n") 160 } 161 p.tag(endTag) 162 163 // for self-verification only (redundant) 164 p.int(objcount) 165 166 if trace { 167 p.tracef("\n") 168 } 169 170 // --- end of export data --- 171 172 return p.out.Bytes(), nil 173 } 174 175 func (p *exporter) pkg(pkg *types.Package, emptypath bool) { 176 if pkg == nil { 177 panic(internalError("unexpected nil pkg")) 178 } 179 180 // if we saw the package before, write its index (>= 0) 181 if i, ok := p.pkgIndex[pkg]; ok { 182 p.index('P', i) 183 return 184 } 185 186 // otherwise, remember the package, write the package tag (< 0) and package data 187 if trace { 188 p.tracef("P%d = { ", len(p.pkgIndex)) 189 defer p.tracef("} ") 190 } 191 p.pkgIndex[pkg] = len(p.pkgIndex) 192 193 p.tag(packageTag) 194 p.string(pkg.Name()) 195 if emptypath { 196 p.string("") 197 } else { 198 p.string(pkg.Path()) 199 } 200 } 201 202 func (p *exporter) obj(obj types.Object) { 203 switch obj := obj.(type) { 204 case *types.Const: 205 p.tag(constTag) 206 p.pos(obj) 207 p.qualifiedName(obj) 208 p.typ(obj.Type()) 209 p.value(obj.Val()) 210 211 case *types.TypeName: 212 if obj.IsAlias() { 213 p.tag(aliasTag) 214 p.pos(obj) 215 p.qualifiedName(obj) 216 } else { 217 p.tag(typeTag) 218 } 219 p.typ(obj.Type()) 220 221 case *types.Var: 222 p.tag(varTag) 223 p.pos(obj) 224 p.qualifiedName(obj) 225 p.typ(obj.Type()) 226 227 case *types.Func: 228 p.tag(funcTag) 229 p.pos(obj) 230 p.qualifiedName(obj) 231 sig := obj.Type().(*types.Signature) 232 p.paramList(sig.Params(), sig.Variadic()) 233 p.paramList(sig.Results(), false) 234 235 default: 236 panic(internalErrorf("unexpected object %v (%T)", obj, obj)) 237 } 238 } 239 240 func (p *exporter) pos(obj types.Object) { 241 if !p.posInfoFormat { 242 return 243 } 244 245 file, line := p.fileLine(obj) 246 if file == p.prevFile { 247 // common case: write line delta 248 // delta == 0 means different file or no line change 249 delta := line - p.prevLine 250 p.int(delta) 251 if delta == 0 { 252 p.int(-1) // -1 means no file change 253 } 254 } else { 255 // different file 256 p.int(0) 257 // Encode filename as length of common prefix with previous 258 // filename, followed by (possibly empty) suffix. Filenames 259 // frequently share path prefixes, so this can save a lot 260 // of space and make export data size less dependent on file 261 // path length. The suffix is unlikely to be empty because 262 // file names tend to end in ".go". 263 n := commonPrefixLen(p.prevFile, file) 264 p.int(n) // n >= 0 265 p.string(file[n:]) // write suffix only 266 p.prevFile = file 267 p.int(line) 268 } 269 p.prevLine = line 270 } 271 272 func (p *exporter) fileLine(obj types.Object) (file string, line int) { 273 if p.fset != nil { 274 pos := p.fset.Position(obj.Pos()) 275 file = pos.Filename 276 line = pos.Line 277 } 278 return 279 } 280 281 func commonPrefixLen(a, b string) int { 282 if len(a) > len(b) { 283 a, b = b, a 284 } 285 // len(a) <= len(b) 286 i := 0 287 for i < len(a) && a[i] == b[i] { 288 i++ 289 } 290 return i 291 } 292 293 func (p *exporter) qualifiedName(obj types.Object) { 294 p.string(obj.Name()) 295 p.pkg(obj.Pkg(), false) 296 } 297 298 func (p *exporter) typ(t types.Type) { 299 if t == nil { 300 panic(internalError("nil type")) 301 } 302 303 // Possible optimization: Anonymous pointer types *T where 304 // T is a named type are common. We could canonicalize all 305 // such types *T to a single type PT = *T. This would lead 306 // to at most one *T entry in typIndex, and all future *T's 307 // would be encoded as the respective index directly. Would 308 // save 1 byte (pointerTag) per *T and reduce the typIndex 309 // size (at the cost of a canonicalization map). We can do 310 // this later, without encoding format change. 311 312 // if we saw the type before, write its index (>= 0) 313 if i, ok := p.typIndex[t]; ok { 314 p.index('T', i) 315 return 316 } 317 318 // otherwise, remember the type, write the type tag (< 0) and type data 319 if trackAllTypes { 320 if trace { 321 p.tracef("T%d = {>\n", len(p.typIndex)) 322 defer p.tracef("<\n} ") 323 } 324 p.typIndex[t] = len(p.typIndex) 325 } 326 327 switch t := t.(type) { 328 case *types.Named: 329 if !trackAllTypes { 330 // if we don't track all types, track named types now 331 p.typIndex[t] = len(p.typIndex) 332 } 333 334 p.tag(namedTag) 335 p.pos(t.Obj()) 336 p.qualifiedName(t.Obj()) 337 p.typ(t.Underlying()) 338 if !types.IsInterface(t) { 339 p.assocMethods(t) 340 } 341 342 case *types.Array: 343 p.tag(arrayTag) 344 p.int64(t.Len()) 345 p.typ(t.Elem()) 346 347 case *types.Slice: 348 p.tag(sliceTag) 349 p.typ(t.Elem()) 350 351 case *dddSlice: 352 p.tag(dddTag) 353 p.typ(t.elem) 354 355 case *types.Struct: 356 p.tag(structTag) 357 p.fieldList(t) 358 359 case *types.Pointer: 360 p.tag(pointerTag) 361 p.typ(t.Elem()) 362 363 case *types.Signature: 364 p.tag(signatureTag) 365 p.paramList(t.Params(), t.Variadic()) 366 p.paramList(t.Results(), false) 367 368 case *types.Interface: 369 p.tag(interfaceTag) 370 p.iface(t) 371 372 case *types.Map: 373 p.tag(mapTag) 374 p.typ(t.Key()) 375 p.typ(t.Elem()) 376 377 case *types.Chan: 378 p.tag(chanTag) 379 p.int(int(3 - t.Dir())) // hack 380 p.typ(t.Elem()) 381 382 default: 383 panic(internalErrorf("unexpected type %T: %s", t, t)) 384 } 385 } 386 387 func (p *exporter) assocMethods(named *types.Named) { 388 // Sort methods (for determinism). 389 var methods []*types.Func 390 for i := 0; i < named.NumMethods(); i++ { 391 methods = append(methods, named.Method(i)) 392 } 393 sort.Sort(methodsByName(methods)) 394 395 p.int(len(methods)) 396 397 if trace && methods != nil { 398 p.tracef("associated methods {>\n") 399 } 400 401 for i, m := range methods { 402 if trace && i > 0 { 403 p.tracef("\n") 404 } 405 406 p.pos(m) 407 name := m.Name() 408 p.string(name) 409 if !exported(name) { 410 p.pkg(m.Pkg(), false) 411 } 412 413 sig := m.Type().(*types.Signature) 414 p.paramList(types.NewTuple(sig.Recv()), false) 415 p.paramList(sig.Params(), sig.Variadic()) 416 p.paramList(sig.Results(), false) 417 p.int(0) // dummy value for go:nointerface pragma - ignored by importer 418 } 419 420 if trace && methods != nil { 421 p.tracef("<\n} ") 422 } 423 } 424 425 type methodsByName []*types.Func 426 427 func (x methodsByName) Len() int { return len(x) } 428 func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 429 func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } 430 431 func (p *exporter) fieldList(t *types.Struct) { 432 if trace && t.NumFields() > 0 { 433 p.tracef("fields {>\n") 434 defer p.tracef("<\n} ") 435 } 436 437 p.int(t.NumFields()) 438 for i := 0; i < t.NumFields(); i++ { 439 if trace && i > 0 { 440 p.tracef("\n") 441 } 442 p.field(t.Field(i)) 443 p.string(t.Tag(i)) 444 } 445 } 446 447 func (p *exporter) field(f *types.Var) { 448 if !f.IsField() { 449 panic(internalError("field expected")) 450 } 451 452 p.pos(f) 453 p.fieldName(f) 454 p.typ(f.Type()) 455 } 456 457 func (p *exporter) iface(t *types.Interface) { 458 // TODO(gri): enable importer to load embedded interfaces, 459 // then emit Embeddeds and ExplicitMethods separately here. 460 p.int(0) 461 462 n := t.NumMethods() 463 if trace && n > 0 { 464 p.tracef("methods {>\n") 465 defer p.tracef("<\n} ") 466 } 467 p.int(n) 468 for i := 0; i < n; i++ { 469 if trace && i > 0 { 470 p.tracef("\n") 471 } 472 p.method(t.Method(i)) 473 } 474 } 475 476 func (p *exporter) method(m *types.Func) { 477 sig := m.Type().(*types.Signature) 478 if sig.Recv() == nil { 479 panic(internalError("method expected")) 480 } 481 482 p.pos(m) 483 p.string(m.Name()) 484 if m.Name() != "_" && !token.IsExported(m.Name()) { 485 p.pkg(m.Pkg(), false) 486 } 487 488 // interface method; no need to encode receiver. 489 p.paramList(sig.Params(), sig.Variadic()) 490 p.paramList(sig.Results(), false) 491 } 492 493 func (p *exporter) fieldName(f *types.Var) { 494 name := f.Name() 495 496 if f.Anonymous() { 497 // anonymous field - we distinguish between 3 cases: 498 // 1) field name matches base type name and is exported 499 // 2) field name matches base type name and is not exported 500 // 3) field name doesn't match base type name (alias name) 501 bname := basetypeName(f.Type()) 502 if name == bname { 503 if token.IsExported(name) { 504 name = "" // 1) we don't need to know the field name or package 505 } else { 506 name = "?" // 2) use unexported name "?" to force package export 507 } 508 } else { 509 // 3) indicate alias and export name as is 510 // (this requires an extra "@" but this is a rare case) 511 p.string("@") 512 } 513 } 514 515 p.string(name) 516 if name != "" && !token.IsExported(name) { 517 p.pkg(f.Pkg(), false) 518 } 519 } 520 521 func basetypeName(typ types.Type) string { 522 switch typ := deref(typ).(type) { 523 case *types.Basic: 524 return typ.Name() 525 case *types.Named: 526 return typ.Obj().Name() 527 default: 528 return "" // unnamed type 529 } 530 } 531 532 func (p *exporter) paramList(params *types.Tuple, variadic bool) { 533 // use negative length to indicate unnamed parameters 534 // (look at the first parameter only since either all 535 // names are present or all are absent) 536 n := params.Len() 537 if n > 0 && params.At(0).Name() == "" { 538 n = -n 539 } 540 p.int(n) 541 for i := 0; i < params.Len(); i++ { 542 q := params.At(i) 543 t := q.Type() 544 if variadic && i == params.Len()-1 { 545 t = &dddSlice{t.(*types.Slice).Elem()} 546 } 547 p.typ(t) 548 if n > 0 { 549 name := q.Name() 550 p.string(name) 551 if name != "_" { 552 p.pkg(q.Pkg(), false) 553 } 554 } 555 p.string("") // no compiler-specific info 556 } 557 } 558 559 func (p *exporter) value(x constant.Value) { 560 if trace { 561 p.tracef("= ") 562 } 563 564 switch x.Kind() { 565 case constant.Bool: 566 tag := falseTag 567 if constant.BoolVal(x) { 568 tag = trueTag 569 } 570 p.tag(tag) 571 572 case constant.Int: 573 if v, exact := constant.Int64Val(x); exact { 574 // common case: x fits into an int64 - use compact encoding 575 p.tag(int64Tag) 576 p.int64(v) 577 return 578 } 579 // uncommon case: large x - use float encoding 580 // (powers of 2 will be encoded efficiently with exponent) 581 p.tag(floatTag) 582 p.float(constant.ToFloat(x)) 583 584 case constant.Float: 585 p.tag(floatTag) 586 p.float(x) 587 588 case constant.Complex: 589 p.tag(complexTag) 590 p.float(constant.Real(x)) 591 p.float(constant.Imag(x)) 592 593 case constant.String: 594 p.tag(stringTag) 595 p.string(constant.StringVal(x)) 596 597 case constant.Unknown: 598 // package contains type errors 599 p.tag(unknownTag) 600 601 default: 602 panic(internalErrorf("unexpected value %v (%T)", x, x)) 603 } 604 } 605 606 func (p *exporter) float(x constant.Value) { 607 if x.Kind() != constant.Float { 608 panic(internalErrorf("unexpected constant %v, want float", x)) 609 } 610 // extract sign (there is no -0) 611 sign := constant.Sign(x) 612 if sign == 0 { 613 // x == 0 614 p.int(0) 615 return 616 } 617 // x != 0 618 619 var f big.Float 620 if v, exact := constant.Float64Val(x); exact { 621 // float64 622 f.SetFloat64(v) 623 } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { 624 // TODO(gri): add big.Rat accessor to constant.Value. 625 r := valueToRat(num) 626 f.SetRat(r.Quo(r, valueToRat(denom))) 627 } else { 628 // Value too large to represent as a fraction => inaccessible. 629 // TODO(gri): add big.Float accessor to constant.Value. 630 f.SetFloat64(math.MaxFloat64) // FIXME 631 } 632 633 // extract exponent such that 0.5 <= m < 1.0 634 var m big.Float 635 exp := f.MantExp(&m) 636 637 // extract mantissa as *big.Int 638 // - set exponent large enough so mant satisfies mant.IsInt() 639 // - get *big.Int from mant 640 m.SetMantExp(&m, int(m.MinPrec())) 641 mant, acc := m.Int(nil) 642 if acc != big.Exact { 643 panic(internalError("internal error")) 644 } 645 646 p.int(sign) 647 p.int(exp) 648 p.string(string(mant.Bytes())) 649 } 650 651 func valueToRat(x constant.Value) *big.Rat { 652 // Convert little-endian to big-endian. 653 // I can't believe this is necessary. 654 bytes := constant.Bytes(x) 655 for i := 0; i < len(bytes)/2; i++ { 656 bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] 657 } 658 return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) 659 } 660 661 func (p *exporter) bool(b bool) bool { 662 if trace { 663 p.tracef("[") 664 defer p.tracef("= %v] ", b) 665 } 666 667 x := 0 668 if b { 669 x = 1 670 } 671 p.int(x) 672 return b 673 } 674 675 // ---------------------------------------------------------------------------- 676 // Low-level encoders 677 678 func (p *exporter) index(marker byte, index int) { 679 if index < 0 { 680 panic(internalError("invalid index < 0")) 681 } 682 if debugFormat { 683 p.marker('t') 684 } 685 if trace { 686 p.tracef("%c%d ", marker, index) 687 } 688 p.rawInt64(int64(index)) 689 } 690 691 func (p *exporter) tag(tag int) { 692 if tag >= 0 { 693 panic(internalError("invalid tag >= 0")) 694 } 695 if debugFormat { 696 p.marker('t') 697 } 698 if trace { 699 p.tracef("%s ", tagString[-tag]) 700 } 701 p.rawInt64(int64(tag)) 702 } 703 704 func (p *exporter) int(x int) { 705 p.int64(int64(x)) 706 } 707 708 func (p *exporter) int64(x int64) { 709 if debugFormat { 710 p.marker('i') 711 } 712 if trace { 713 p.tracef("%d ", x) 714 } 715 p.rawInt64(x) 716 } 717 718 func (p *exporter) string(s string) { 719 if debugFormat { 720 p.marker('s') 721 } 722 if trace { 723 p.tracef("%q ", s) 724 } 725 // if we saw the string before, write its index (>= 0) 726 // (the empty string is mapped to 0) 727 if i, ok := p.strIndex[s]; ok { 728 p.rawInt64(int64(i)) 729 return 730 } 731 // otherwise, remember string and write its negative length and bytes 732 p.strIndex[s] = len(p.strIndex) 733 p.rawInt64(-int64(len(s))) 734 for i := 0; i < len(s); i++ { 735 p.rawByte(s[i]) 736 } 737 } 738 739 // marker emits a marker byte and position information which makes 740 // it easy for a reader to detect if it is "out of sync". Used for 741 // debugFormat format only. 742 func (p *exporter) marker(m byte) { 743 p.rawByte(m) 744 // Enable this for help tracking down the location 745 // of an incorrect marker when running in debugFormat. 746 if false && trace { 747 p.tracef("#%d ", p.written) 748 } 749 p.rawInt64(int64(p.written)) 750 } 751 752 // rawInt64 should only be used by low-level encoders. 753 func (p *exporter) rawInt64(x int64) { 754 var tmp [binary.MaxVarintLen64]byte 755 n := binary.PutVarint(tmp[:], x) 756 for i := 0; i < n; i++ { 757 p.rawByte(tmp[i]) 758 } 759 } 760 761 // rawStringln should only be used to emit the initial version string. 762 func (p *exporter) rawStringln(s string) { 763 for i := 0; i < len(s); i++ { 764 p.rawByte(s[i]) 765 } 766 p.rawByte('\n') 767 } 768 769 // rawByte is the bottleneck interface to write to p.out. 770 // rawByte escapes b as follows (any encoding does that 771 // hides '$'): 772 // 773 // '$' => '|' 'S' 774 // '|' => '|' '|' 775 // 776 // Necessary so other tools can find the end of the 777 // export data by searching for "$$". 778 // rawByte should only be used by low-level encoders. 779 func (p *exporter) rawByte(b byte) { 780 switch b { 781 case '$': 782 // write '$' as '|' 'S' 783 b = 'S' 784 fallthrough 785 case '|': 786 // write '|' as '|' '|' 787 p.out.WriteByte('|') 788 p.written++ 789 } 790 p.out.WriteByte(b) 791 p.written++ 792 } 793 794 // tracef is like fmt.Printf but it rewrites the format string 795 // to take care of indentation. 796 func (p *exporter) tracef(format string, args ...interface{}) { 797 if strings.ContainsAny(format, "<>\n") { 798 var buf bytes.Buffer 799 for i := 0; i < len(format); i++ { 800 // no need to deal with runes 801 ch := format[i] 802 switch ch { 803 case '>': 804 p.indent++ 805 continue 806 case '<': 807 p.indent-- 808 continue 809 } 810 buf.WriteByte(ch) 811 if ch == '\n' { 812 for j := p.indent; j > 0; j-- { 813 buf.WriteString(". ") 814 } 815 } 816 } 817 format = buf.String() 818 } 819 fmt.Printf(format, args...) 820 } 821 822 // Debugging support. 823 // (tagString is only used when tracing is enabled) 824 var tagString = [...]string{ 825 // Packages 826 -packageTag: "package", 827 828 // Types 829 -namedTag: "named type", 830 -arrayTag: "array", 831 -sliceTag: "slice", 832 -dddTag: "ddd", 833 -structTag: "struct", 834 -pointerTag: "pointer", 835 -signatureTag: "signature", 836 -interfaceTag: "interface", 837 -mapTag: "map", 838 -chanTag: "chan", 839 840 // Values 841 -falseTag: "false", 842 -trueTag: "true", 843 -int64Tag: "int64", 844 -floatTag: "float", 845 -fractionTag: "fraction", 846 -complexTag: "complex", 847 -stringTag: "string", 848 -unknownTag: "unknown", 849 850 // Type aliases 851 -aliasTag: "alias", 852 }