| 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 | } |
| 853 |
Members