github.com/evanw/esbuild@v0.21.4/internal/ast/ast.go (about) 1 package ast 2 3 // This file contains data structures that are used with the AST packages for 4 // both JavaScript and CSS. This helps the bundler treat both AST formats in 5 // a somewhat format-agnostic manner. 6 7 import ( 8 "sort" 9 10 "github.com/evanw/esbuild/internal/helpers" 11 "github.com/evanw/esbuild/internal/logger" 12 ) 13 14 type ImportKind uint8 15 16 const ( 17 // An entry point provided by the user 18 ImportEntryPoint ImportKind = iota 19 20 // An ES6 import or re-export statement 21 ImportStmt 22 23 // A call to "require()" 24 ImportRequire 25 26 // An "import()" expression with a string argument 27 ImportDynamic 28 29 // A call to "require.resolve()" 30 ImportRequireResolve 31 32 // A CSS "@import" rule 33 ImportAt 34 35 // A CSS "composes" declaration 36 ImportComposesFrom 37 38 // A CSS "url(...)" token 39 ImportURL 40 ) 41 42 func (kind ImportKind) StringForMetafile() string { 43 switch kind { 44 case ImportStmt: 45 return "import-statement" 46 case ImportRequire: 47 return "require-call" 48 case ImportDynamic: 49 return "dynamic-import" 50 case ImportRequireResolve: 51 return "require-resolve" 52 case ImportAt: 53 return "import-rule" 54 case ImportComposesFrom: 55 return "composes-from" 56 case ImportURL: 57 return "url-token" 58 case ImportEntryPoint: 59 return "entry-point" 60 default: 61 panic("Internal error") 62 } 63 } 64 65 func (kind ImportKind) IsFromCSS() bool { 66 switch kind { 67 case ImportAt, ImportComposesFrom, ImportURL: 68 return true 69 } 70 return false 71 } 72 73 func (kind ImportKind) MustResolveToCSS() bool { 74 switch kind { 75 case ImportAt, ImportComposesFrom: 76 return true 77 } 78 return false 79 } 80 81 type ImportRecordFlags uint16 82 83 const ( 84 // Sometimes the parser creates an import record and decides it isn't needed. 85 // For example, TypeScript code may have import statements that later turn 86 // out to be type-only imports after analyzing the whole file. 87 IsUnused ImportRecordFlags = 1 << iota 88 89 // If this is true, the import contains syntax like "* as ns". This is used 90 // to determine whether modules that have no exports need to be wrapped in a 91 // CommonJS wrapper or not. 92 ContainsImportStar 93 94 // If this is true, the import contains an import for the alias "default", 95 // either via the "import x from" or "import {default as x} from" syntax. 96 ContainsDefaultAlias 97 98 // If this is true, the import contains an import for the alias "__esModule", 99 // via the "import {__esModule} from" syntax. 100 ContainsESModuleAlias 101 102 // If true, this "export * from 'path'" statement is evaluated at run-time by 103 // calling the "__reExport()" helper function 104 CallsRunTimeReExportFn 105 106 // Tell the printer to wrap this call to "require()" in "__toESM(...)" 107 WrapWithToESM 108 109 // Tell the printer to wrap this ESM exports object in "__toCJS(...)" 110 WrapWithToCJS 111 112 // Tell the printer to use the runtime "__require()" instead of "require()" 113 CallRuntimeRequire 114 115 // True for the following cases: 116 // 117 // try { require('x') } catch { handle } 118 // try { await import('x') } catch { handle } 119 // try { require.resolve('x') } catch { handle } 120 // import('x').catch(handle) 121 // import('x').then(_, handle) 122 // 123 // In these cases we shouldn't generate an error if the path could not be 124 // resolved. 125 HandlesImportErrors 126 127 // If true, this was originally written as a bare "import 'file'" statement 128 WasOriginallyBareImport 129 130 // If true, this import can be removed if it's unused 131 IsExternalWithoutSideEffects 132 133 // If true, "assert { type: 'json' }" was present 134 AssertTypeJSON 135 136 // If true, do not generate "external": true in the metafile 137 ShouldNotBeExternalInMetafile 138 139 // CSS "@import" of an empty file should be removed 140 WasLoadedWithEmptyLoader 141 142 // Unique keys are randomly-generated strings that are used to replace paths 143 // in the source code after it's printed. These must not ever be split apart. 144 ContainsUniqueKey 145 ) 146 147 func (flags ImportRecordFlags) Has(flag ImportRecordFlags) bool { 148 return (flags & flag) != 0 149 } 150 151 type ImportRecord struct { 152 AssertOrWith *ImportAssertOrWith 153 GlobPattern *GlobPattern 154 Path logger.Path 155 Range logger.Range 156 157 // If the "HandlesImportErrors" flag is present, then this is the location 158 // of the error handler. This is used for error reporting. 159 ErrorHandlerLoc logger.Loc 160 161 // The resolved source index for an internal import (within the bundle) or 162 // invalid for an external import (not included in the bundle) 163 SourceIndex Index32 164 165 // Files imported via the "copy" loader use this instead of "SourceIndex" 166 // because they are sort of like external imports, and are not bundled. 167 CopySourceIndex Index32 168 169 Flags ImportRecordFlags 170 Kind ImportKind 171 } 172 173 type AssertOrWithKeyword uint8 174 175 const ( 176 AssertKeyword AssertOrWithKeyword = iota 177 WithKeyword 178 ) 179 180 func (kw AssertOrWithKeyword) String() string { 181 if kw == AssertKeyword { 182 return "assert" 183 } 184 return "with" 185 } 186 187 type ImportAssertOrWith struct { 188 Entries []AssertOrWithEntry 189 KeywordLoc logger.Loc 190 InnerOpenBraceLoc logger.Loc 191 InnerCloseBraceLoc logger.Loc 192 OuterOpenBraceLoc logger.Loc 193 OuterCloseBraceLoc logger.Loc 194 Keyword AssertOrWithKeyword 195 } 196 197 type AssertOrWithEntry struct { 198 Key []uint16 // An identifier or a string 199 Value []uint16 // Always a string 200 KeyLoc logger.Loc 201 ValueLoc logger.Loc 202 PreferQuotedKey bool 203 } 204 205 func FindAssertOrWithEntry(assertions []AssertOrWithEntry, name string) *AssertOrWithEntry { 206 for _, assertion := range assertions { 207 if helpers.UTF16EqualsString(assertion.Key, name) { 208 return &assertion 209 } 210 } 211 return nil 212 } 213 214 type GlobPattern struct { 215 Parts []helpers.GlobPart 216 ExportAlias string 217 Kind ImportKind 218 } 219 220 // This stores a 32-bit index where the zero value is an invalid index. This is 221 // a better alternative to storing the index as a pointer since that has the 222 // same properties but takes up more space and costs an extra pointer traversal. 223 type Index32 struct { 224 flippedBits uint32 225 } 226 227 func MakeIndex32(index uint32) Index32 { 228 return Index32{flippedBits: ^index} 229 } 230 231 func (i Index32) IsValid() bool { 232 return i.flippedBits != 0 233 } 234 235 func (i Index32) GetIndex() uint32 { 236 return ^i.flippedBits 237 } 238 239 type SymbolKind uint8 240 241 const ( 242 // An unbound symbol is one that isn't declared in the file it's referenced 243 // in. For example, using "window" without declaring it will be unbound. 244 SymbolUnbound SymbolKind = iota 245 246 // This has special merging behavior. You're allowed to re-declare these 247 // symbols more than once in the same scope. These symbols are also hoisted 248 // out of the scope they are declared in to the closest containing function 249 // or module scope. These are the symbols with this kind: 250 // 251 // - Function arguments 252 // - Function statements 253 // - Variables declared using "var" 254 // 255 SymbolHoisted 256 SymbolHoistedFunction 257 258 // There's a weird special case where catch variables declared using a simple 259 // identifier (i.e. not a binding pattern) block hoisted variables instead of 260 // becoming an error: 261 // 262 // var e = 0; 263 // try { throw 1 } catch (e) { 264 // print(e) // 1 265 // var e = 2 266 // print(e) // 2 267 // } 268 // print(e) // 0 (since the hoisting stops at the catch block boundary) 269 // 270 // However, other forms are still a syntax error: 271 // 272 // try {} catch (e) { let e } 273 // try {} catch ({e}) { var e } 274 // 275 // This symbol is for handling this weird special case. 276 SymbolCatchIdentifier 277 278 // Generator and async functions are not hoisted, but still have special 279 // properties such as being able to overwrite previous functions with the 280 // same name 281 SymbolGeneratorOrAsyncFunction 282 283 // This is the special "arguments" variable inside functions 284 SymbolArguments 285 286 // Classes can merge with TypeScript namespaces. 287 SymbolClass 288 289 // Class names are not allowed to be referenced by computed property keys 290 SymbolClassInComputedPropertyKey 291 292 // A class-private identifier (i.e. "#foo"). 293 SymbolPrivateField 294 SymbolPrivateMethod 295 SymbolPrivateGet 296 SymbolPrivateSet 297 SymbolPrivateGetSetPair 298 SymbolPrivateStaticField 299 SymbolPrivateStaticMethod 300 SymbolPrivateStaticGet 301 SymbolPrivateStaticSet 302 SymbolPrivateStaticGetSetPair 303 304 // Labels are in their own namespace 305 SymbolLabel 306 307 // TypeScript enums can merge with TypeScript namespaces and other TypeScript 308 // enums. 309 SymbolTSEnum 310 311 // TypeScript namespaces can merge with classes, functions, TypeScript enums, 312 // and other TypeScript namespaces. 313 SymbolTSNamespace 314 315 // In TypeScript, imports are allowed to silently collide with symbols within 316 // the module. Presumably this is because the imports may be type-only. 317 SymbolImport 318 319 // Assigning to a "const" symbol will throw a TypeError at runtime 320 SymbolConst 321 322 // Injected symbols can be overridden by provided defines 323 SymbolInjected 324 325 // Properties can optionally be renamed to shorter names 326 SymbolMangledProp 327 328 // CSS identifiers that are never renamed 329 SymbolGlobalCSS 330 331 // CSS identifiers that are renamed to be unique to the file they are in 332 SymbolLocalCSS 333 334 // This annotates all other symbols that don't have special behavior 335 SymbolOther 336 ) 337 338 func (kind SymbolKind) IsPrivate() bool { 339 return kind >= SymbolPrivateField && kind <= SymbolPrivateStaticGetSetPair 340 } 341 342 func (kind SymbolKind) IsHoisted() bool { 343 return kind == SymbolHoisted || kind == SymbolHoistedFunction 344 } 345 346 func (kind SymbolKind) IsHoistedOrFunction() bool { 347 return kind.IsHoisted() || kind == SymbolGeneratorOrAsyncFunction 348 } 349 350 func (kind SymbolKind) IsFunction() bool { 351 return kind == SymbolHoistedFunction || kind == SymbolGeneratorOrAsyncFunction 352 } 353 354 func (kind SymbolKind) IsUnboundOrInjected() bool { 355 return kind == SymbolUnbound || kind == SymbolInjected 356 } 357 358 var InvalidRef Ref = Ref{^uint32(0), ^uint32(0)} 359 360 // Files are parsed in parallel for speed. We want to allow each parser to 361 // generate symbol IDs that won't conflict with each other. We also want to be 362 // able to quickly merge symbol tables from all files into one giant symbol 363 // table. 364 // 365 // We can accomplish both goals by giving each symbol ID two parts: a source 366 // index that is unique to the parser goroutine, and an inner index that 367 // increments as the parser generates new symbol IDs. Then a symbol map can 368 // be an array of arrays indexed first by source index, then by inner index. 369 // The maps can be merged quickly by creating a single outer array containing 370 // all inner arrays from all parsed files. 371 type Ref struct { 372 SourceIndex uint32 373 InnerIndex uint32 374 } 375 376 type LocRef struct { 377 Loc logger.Loc 378 Ref Ref 379 } 380 381 type ImportItemStatus uint8 382 383 const ( 384 ImportItemNone ImportItemStatus = iota 385 386 // The linker doesn't report import/export mismatch errors 387 ImportItemGenerated 388 389 // The printer will replace this import with "undefined" 390 ImportItemMissing 391 ) 392 393 type SymbolFlags uint16 394 395 const ( 396 // Certain symbols must not be renamed or minified. For example, the 397 // "arguments" variable is declared by the runtime for every function. 398 // Renaming can also break any identifier used inside a "with" statement. 399 MustNotBeRenamed SymbolFlags = 1 << iota 400 401 // In React's version of JSX, lower-case names are strings while upper-case 402 // names are identifiers. If we are preserving JSX syntax (i.e. not 403 // transforming it), then we need to be careful to name the identifiers 404 // something with a capital letter so further JSX processing doesn't treat 405 // them as strings instead. 406 MustStartWithCapitalLetterForJSX 407 408 // If true, this symbol is the target of a "__name" helper function call. 409 // This call is special because it deliberately doesn't count as a use 410 // of the symbol (otherwise keeping names would disable tree shaking) 411 // so "UseCountEstimate" is not incremented. This flag helps us know to 412 // avoid optimizing this symbol when "UseCountEstimate" is 1 in this case. 413 DidKeepName 414 415 // Sometimes we lower private symbols even if they are supported. For example, 416 // consider the following TypeScript code: 417 // 418 // class Foo { 419 // #foo = 123 420 // bar = this.#foo 421 // } 422 // 423 // If "useDefineForClassFields: false" is set in "tsconfig.json", then "bar" 424 // must use assignment semantics instead of define semantics. We can compile 425 // that to this code: 426 // 427 // class Foo { 428 // constructor() { 429 // this.#foo = 123; 430 // this.bar = this.#foo; 431 // } 432 // #foo; 433 // } 434 // 435 // However, we can't do the same for static fields: 436 // 437 // class Foo { 438 // static #foo = 123 439 // static bar = this.#foo 440 // } 441 // 442 // Compiling these static fields to something like this would be invalid: 443 // 444 // class Foo { 445 // static #foo; 446 // } 447 // Foo.#foo = 123; 448 // Foo.bar = Foo.#foo; 449 // 450 // Thus "#foo" must be lowered even though it's supported. Another case is 451 // when we're converting top-level class declarations to class expressions 452 // to avoid the TDZ and the class shadowing symbol is referenced within the 453 // class body: 454 // 455 // class Foo { 456 // static #foo = Foo 457 // } 458 // 459 // This cannot be converted into something like this: 460 // 461 // var Foo = class { 462 // static #foo; 463 // }; 464 // Foo.#foo = Foo; 465 // 466 PrivateSymbolMustBeLowered 467 468 // This is used to remove the all but the last function re-declaration if a 469 // function is re-declared multiple times like this: 470 // 471 // function foo() { console.log(1) } 472 // function foo() { console.log(2) } 473 // 474 RemoveOverwrittenFunctionDeclaration 475 476 // This flag is to avoid warning about this symbol more than once. It only 477 // applies to the "module" and "exports" unbound symbols. 478 DidWarnAboutCommonJSInESM 479 480 // If this is present, the symbol could potentially be overwritten. This means 481 // it's not safe to make assumptions about this symbol from the initializer. 482 CouldPotentiallyBeMutated 483 484 // This flags all symbols that were exported from the module using the ES6 485 // "export" keyword, either directly on the declaration or using "export {}". 486 WasExported 487 488 // This means the symbol is a normal function that has no body statements. 489 IsEmptyFunction 490 491 // This means the symbol is a normal function that takes a single argument 492 // and returns that argument. 493 IsIdentityFunction 494 495 // If true, calls to this symbol can be unwrapped (i.e. removed except for 496 // argument side effects) if the result is unused. 497 CallCanBeUnwrappedIfUnused 498 ) 499 500 func (flags SymbolFlags) Has(flag SymbolFlags) bool { 501 return (flags & flag) != 0 502 } 503 504 // Note: the order of values in this struct matters to reduce struct size. 505 type Symbol struct { 506 // This is used for symbols that represent items in the import clause of an 507 // ES6 import statement. These should always be referenced by EImportIdentifier 508 // instead of an EIdentifier. When this is present, the expression should 509 // be printed as a property access off the namespace instead of as a bare 510 // identifier. 511 // 512 // For correctness, this must be stored on the symbol instead of indirectly 513 // associated with the Ref for the symbol somehow. In ES6 "flat bundling" 514 // mode, re-exported symbols are collapsed using MergeSymbols() and renamed 515 // symbols from other files that end up at this symbol must be able to tell 516 // if it has a namespace alias. 517 NamespaceAlias *NamespaceAlias 518 519 // This is the name that came from the parser. Printed names may be renamed 520 // during minification or to avoid name collisions. Do not use the original 521 // name during printing. 522 OriginalName string 523 524 // Used by the parser for single pass parsing. Symbols that have been merged 525 // form a linked-list where the last link is the symbol to use. This link is 526 // an invalid ref if it's the last link. If this isn't invalid, you need to 527 // FollowSymbols to get the real one. 528 Link Ref 529 530 // An estimate of the number of uses of this symbol. This is used to detect 531 // whether a symbol is used or not. For example, TypeScript imports that are 532 // unused must be removed because they are probably type-only imports. This 533 // is an estimate and may not be completely accurate due to oversights in the 534 // code. But it should always be non-zero when the symbol is used. 535 UseCountEstimate uint32 536 537 // This is for generating cross-chunk imports and exports for code splitting. 538 ChunkIndex Index32 539 540 // This is used for minification. Symbols that are declared in sibling scopes 541 // can share a name. A good heuristic (from Google Closure Compiler) is to 542 // assign names to symbols from sibling scopes in declaration order. That way 543 // local variable names are reused in each global function like this, which 544 // improves gzip compression: 545 // 546 // function x(a, b) { ... } 547 // function y(a, b, c) { ... } 548 // 549 // The parser fills this in for symbols inside nested scopes. There are three 550 // slot namespaces: regular symbols, label symbols, and private symbols. 551 NestedScopeSlot Index32 552 553 // Boolean values should all be flags instead to save space 554 Flags SymbolFlags 555 556 Kind SymbolKind 557 558 // We automatically generate import items for property accesses off of 559 // namespace imports. This lets us remove the expensive namespace imports 560 // while bundling in many cases, replacing them with a cheap import item 561 // instead: 562 // 563 // import * as ns from 'path' 564 // ns.foo() 565 // 566 // That can often be replaced by this, which avoids needing the namespace: 567 // 568 // import {foo} from 'path' 569 // foo() 570 // 571 // However, if the import is actually missing then we don't want to report a 572 // compile-time error like we do for real import items. This status lets us 573 // avoid this. We also need to be able to replace such import items with 574 // undefined, which this status is also used for. 575 ImportItemStatus ImportItemStatus 576 } 577 578 // You should call "MergeSymbols" instead of calling this directly 579 func (newSymbol *Symbol) MergeContentsWith(oldSymbol *Symbol) { 580 newSymbol.UseCountEstimate += oldSymbol.UseCountEstimate 581 if oldSymbol.Flags.Has(MustNotBeRenamed) && !newSymbol.Flags.Has(MustNotBeRenamed) { 582 newSymbol.OriginalName = oldSymbol.OriginalName 583 newSymbol.Flags |= MustNotBeRenamed 584 } 585 if oldSymbol.Flags.Has(MustStartWithCapitalLetterForJSX) { 586 newSymbol.Flags |= MustStartWithCapitalLetterForJSX 587 } 588 } 589 590 type SlotNamespace uint8 591 592 const ( 593 SlotDefault SlotNamespace = iota 594 SlotLabel 595 SlotPrivateName 596 SlotMangledProp 597 SlotMustNotBeRenamed 598 ) 599 600 func (s *Symbol) SlotNamespace() SlotNamespace { 601 if s.Kind == SymbolUnbound || s.Flags.Has(MustNotBeRenamed) { 602 return SlotMustNotBeRenamed 603 } 604 if s.Kind.IsPrivate() { 605 return SlotPrivateName 606 } 607 if s.Kind == SymbolLabel { 608 return SlotLabel 609 } 610 if s.Kind == SymbolMangledProp { 611 return SlotMangledProp 612 } 613 return SlotDefault 614 } 615 616 type SlotCounts [4]uint32 617 618 func (a *SlotCounts) UnionMax(b SlotCounts) { 619 for i := range *a { 620 ai := &(*a)[i] 621 bi := b[i] 622 if *ai < bi { 623 *ai = bi 624 } 625 } 626 } 627 628 type NamespaceAlias struct { 629 Alias string 630 NamespaceRef Ref 631 } 632 633 type SymbolMap struct { 634 // This could be represented as a "map[Ref]Symbol" but a two-level array was 635 // more efficient in profiles. This appears to be because it doesn't involve 636 // a hash. This representation also makes it trivial to quickly merge symbol 637 // maps from multiple files together. Each file only generates symbols in a 638 // single inner array, so you can join the maps together by just make a 639 // single outer array containing all of the inner arrays. See the comment on 640 // "Ref" for more detail. 641 SymbolsForSource [][]Symbol 642 } 643 644 func NewSymbolMap(sourceCount int) SymbolMap { 645 return SymbolMap{make([][]Symbol, sourceCount)} 646 } 647 648 func (sm SymbolMap) Get(ref Ref) *Symbol { 649 return &sm.SymbolsForSource[ref.SourceIndex][ref.InnerIndex] 650 } 651 652 // Returns the canonical ref that represents the ref for the provided symbol. 653 // This may not be the provided ref if the symbol has been merged with another 654 // symbol. 655 func FollowSymbols(symbols SymbolMap, ref Ref) Ref { 656 symbol := symbols.Get(ref) 657 if symbol.Link == InvalidRef { 658 return ref 659 } 660 661 link := FollowSymbols(symbols, symbol.Link) 662 663 // Only write if needed to avoid concurrent map update hazards 664 if symbol.Link != link { 665 symbol.Link = link 666 } 667 668 return link 669 } 670 671 // Use this before calling "FollowSymbols" from separate threads to avoid 672 // concurrent map update hazards. In Go, mutating a map is not threadsafe 673 // but reading from a map is. Calling "FollowAllSymbols" first ensures that 674 // all mutation is done up front. 675 func FollowAllSymbols(symbols SymbolMap) { 676 for sourceIndex, inner := range symbols.SymbolsForSource { 677 for symbolIndex := range inner { 678 FollowSymbols(symbols, Ref{uint32(sourceIndex), uint32(symbolIndex)}) 679 } 680 } 681 } 682 683 // Makes "old" point to "new" by joining the linked lists for the two symbols 684 // together. That way "FollowSymbols" on both "old" and "new" will result in 685 // the same ref. 686 func MergeSymbols(symbols SymbolMap, old Ref, new Ref) Ref { 687 if old == new { 688 return new 689 } 690 691 oldSymbol := symbols.Get(old) 692 if oldSymbol.Link != InvalidRef { 693 oldSymbol.Link = MergeSymbols(symbols, oldSymbol.Link, new) 694 return oldSymbol.Link 695 } 696 697 newSymbol := symbols.Get(new) 698 if newSymbol.Link != InvalidRef { 699 newSymbol.Link = MergeSymbols(symbols, old, newSymbol.Link) 700 return newSymbol.Link 701 } 702 703 oldSymbol.Link = new 704 newSymbol.MergeContentsWith(oldSymbol) 705 return new 706 } 707 708 // This is a histogram of character frequencies for minification 709 type CharFreq [64]int32 710 711 func (freq *CharFreq) Scan(text string, delta int32) { 712 if delta == 0 { 713 return 714 } 715 716 // This matches the order in "DefaultNameMinifier" 717 for i, n := 0, len(text); i < n; i++ { 718 c := text[i] 719 switch { 720 case c >= 'a' && c <= 'z': 721 (*freq)[c-'a'] += delta 722 case c >= 'A' && c <= 'Z': 723 (*freq)[c-('A'-26)] += delta 724 case c >= '0' && c <= '9': 725 (*freq)[c+(52-'0')] += delta 726 case c == '_': 727 (*freq)[62] += delta 728 case c == '$': 729 (*freq)[63] += delta 730 } 731 } 732 } 733 734 func (freq *CharFreq) Include(other *CharFreq) { 735 for i := 0; i < 64; i++ { 736 (*freq)[i] += (*other)[i] 737 } 738 } 739 740 type NameMinifier struct { 741 head string 742 tail string 743 } 744 745 var DefaultNameMinifierJS = NameMinifier{ 746 head: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$", 747 tail: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$", 748 } 749 750 var DefaultNameMinifierCSS = NameMinifier{ 751 head: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", 752 tail: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", 753 } 754 755 type charAndCount struct { 756 char string 757 count int32 758 index byte 759 } 760 761 // This type is just so we can use Go's native sort function 762 type charAndCountArray []charAndCount 763 764 func (a charAndCountArray) Len() int { return len(a) } 765 func (a charAndCountArray) Swap(i int, j int) { a[i], a[j] = a[j], a[i] } 766 767 func (a charAndCountArray) Less(i int, j int) bool { 768 ai := a[i] 769 aj := a[j] 770 return ai.count > aj.count || (ai.count == aj.count && ai.index < aj.index) 771 } 772 773 func (source NameMinifier) ShuffleByCharFreq(freq CharFreq) NameMinifier { 774 // Sort the histogram in descending order by count 775 array := make(charAndCountArray, 64) 776 for i := 0; i < len(source.tail); i++ { 777 array[i] = charAndCount{ 778 char: source.tail[i : i+1], 779 index: byte(i), 780 count: freq[i], 781 } 782 } 783 sort.Sort(array) 784 785 // Compute the identifier start and identifier continue sequences 786 minifier := NameMinifier{} 787 for _, item := range array { 788 if item.char < "0" || item.char > "9" { 789 minifier.head += item.char 790 } 791 minifier.tail += item.char 792 } 793 return minifier 794 } 795 796 func (minifier NameMinifier) NumberToMinifiedName(i int) string { 797 n_head := len(minifier.head) 798 n_tail := len(minifier.tail) 799 800 j := i % n_head 801 name := minifier.head[j : j+1] 802 i = i / n_head 803 804 for i > 0 { 805 i-- 806 j := i % n_tail 807 name += minifier.tail[j : j+1] 808 i = i / n_tail 809 } 810 811 return name 812 }