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  }