1 package minify 2 3 import ( 4 "sort" 5 6 "vimagination.zapto.org/javascript" 7 "vimagination.zapto.org/javascript/scope" 8 ) 9 10 type binding struct { 11 Name, OriginalName string 12 Scope *scope.Scope 13 NameSet bool 14 } 15 16 func orderedScope(s *scope.Scope) []binding { 17 b := walkScope(s, nil) 18 sort.Slice(b, func(i, j int) bool { 19 if b[i].NameSet { 20 if !b[j].NameSet { 21 return false 22 } 23 } else if b[j].NameSet { 24 return true 25 } 26 il := len(b[i].Scope.Bindings[b[i].Name]) 27 jl := len(b[j].Scope.Bindings[b[j].Name]) 28 29 if il == jl { 30 return b[i].Name < b[j].Name 31 } 32 return il > jl 33 }) 34 return b 35 } 36 37 func walkScope(s *scope.Scope, b []binding) []binding { 38 for name := range s.Bindings { 39 if name == "this" || name == "arguments" { 40 continue 41 } 42 b = append(b, binding{ 43 Name: name, 44 OriginalName: name, 45 Scope: s, 46 NameSet: s.Bindings[name][0].BindingType == scope.BindingRef, 47 }) 48 } 49 for _, cs := range s.Scopes { 50 b = walkScope(cs, b) 51 } 52 return b 53 } 54 55 func renameIdentifiers(m *javascript.Module) error { 56 s, err := scope.ModuleScope(m, nil) 57 if err != nil { 58 return err 59 } 60 bindings := orderedScope(s) 61 for n, binding := range bindings { 62 if binding.NameSet { 63 break 64 } 65 identifiersInScope := make(map[string]struct{}) 66 for _, checkBinding := range bindings { 67 if !checkBinding.NameSet || checkBinding == binding { 68 continue 69 } 70 if binding.Scope == checkBinding.Scope { 71 identifiersInScope[checkBinding.Name] = struct{}{} 72 } else if isParentScope(binding.Scope, checkBinding.Scope) { 73 for _, scope := range binding.Scope.Bindings[binding.OriginalName] { 74 if isParentScope(checkBinding.Scope, scope.Scope) { 75 identifiersInScope[checkBinding.Name] = struct{}{} 76 break 77 } 78 } 79 } else if isParentScope(checkBinding.Scope, binding.Scope) { 80 for _, scope := range checkBinding.Scope.Bindings[checkBinding.OriginalName] { 81 if isParentScope(binding.Scope, scope.Scope) { 82 identifiersInScope[checkBinding.Name] = struct{}{} 83 break 84 } 85 } 86 } 87 } 88 name := makeUniqueName(identifiersInScope) 89 for _, b := range binding.Scope.Bindings[binding.Name] { 90 b.Data = name 91 } 92 bindings[n].Name = name 93 bindings[n].NameSet = true 94 } 95 return nil 96 } 97 98 func isParentScope(a, b *scope.Scope) bool { 99 for b != nil { 100 if b == a { 101 return true 102 } 103 b = b.Parent 104 } 105 return false 106 } 107 108 var ( 109 extraChars = []byte("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$") 110 startChars = extraChars[10:] 111 ) 112 113 func makeUniqueName(exclude map[string]struct{}) string { 114 name := make([]byte, 8) 115 parts := make([][]byte, 8) 116 for l := 0; ; l++ { 117 parts = parts[:1] 118 parts[0] = startChars 119 for n := 0; n < l; n++ { 120 parts = append(parts, extraChars) 121 } 122 L: 123 for { 124 name = name[:0] 125 for i := 0; i <= l; i++ { 126 name = append(name, parts[i][0]) 127 } 128 if _, ok := exclude[string(name)]; !ok { 129 return string(name) 130 } 131 for i := l; i >= 0; i-- { 132 parts[i] = parts[i][1:] 133 if len(parts[i]) > 0 { 134 break 135 } 136 if i == 0 { 137 break L 138 } 139 parts[i] = extraChars 140 } 141 } 142 } 143 } 144