| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.util; | 5 library dart2js.diagnostics.spannable; |
| 6 | |
| 7 import 'util_implementation.dart'; | |
| 8 import 'characters.dart'; | |
| 9 | |
| 10 export 'setlet.dart'; | |
| 11 export 'maplet.dart'; | |
| 12 export 'emptyset.dart'; | |
| 13 | |
| 14 part 'indentation.dart'; | |
| 15 part 'link.dart'; | |
| 16 | |
| 17 /// Helper functions for creating hash codes. | |
| 18 class Hashing { | |
| 19 /// If an integer is masked by this constant, the result is guaranteed to be | |
| 20 /// in Smi range. | |
| 21 static const int SMI_MASK = 0x3fffffff; | |
| 22 | |
| 23 /// Mix the bits of [value] and merge them with [existing]. | |
| 24 static int mixHashCodeBits(int existing, int value) { | |
| 25 // Spread the bits of value. Try to stay in the 30-bit range to | |
| 26 // avoid overflowing into a more expensive integer representation. | |
| 27 int h = value & 0x1fffffff; | |
| 28 h += ((h & 0x3fff) << 15) ^ 0x1fffcd7d; | |
| 29 h ^= (h >> 10); | |
| 30 h += ((h & 0x3ffffff) << 3); | |
| 31 h ^= (h >> 6); | |
| 32 h += ((h & 0x7ffffff) << 2) + ((h & 0x7fff) << 14); | |
| 33 h ^= (h >> 16); | |
| 34 // Combine the two hash values. | |
| 35 int high = existing >> 15; | |
| 36 int low = existing & 0x7fff; | |
| 37 return ((high * 13) ^ (low * 997) ^ h) & SMI_MASK; | |
| 38 } | |
| 39 | |
| 40 /// Mix the bits of `object.hashCode` with [existing]. | |
| 41 static int objectHash(Object object, [int existing = 0]) { | |
| 42 return mixHashCodeBits(existing, object.hashCode); | |
| 43 } | |
| 44 | |
| 45 /// Mix the bits of the element hash codes of [list] with [existing]. | |
| 46 static int listHash(List list, [int existing = 0]) { | |
| 47 int h = existing; | |
| 48 int length = list.length; | |
| 49 for (int i = 0; i < length; i++) { | |
| 50 h = mixHashCodeBits(h, list[i].hashCode); | |
| 51 } | |
| 52 return h; | |
| 53 } | |
| 54 | |
| 55 /// Mix the bits of the key/value hash codes from [map] with [existing]. | |
| 56 static int mapHash(Map map, [int existing = 0]) { | |
| 57 int h = existing; | |
| 58 for (var key in map.keys) { | |
| 59 h = mixHashCodeBits(h, key.hashCode); | |
| 60 h = mixHashCodeBits(h, map[key].hashCode); | |
| 61 } | |
| 62 return h; | |
| 63 } | |
| 64 } | |
| 65 | 6 |
| 66 /** | 7 /** |
| 67 * Tagging interface for classes from which source spans can be generated. | 8 * Tagging interface for classes from which source spans can be generated. |
| 68 */ | 9 */ |
| 69 // TODO(johnniwinther): Find a better name. | 10 // TODO(johnniwinther): Find a better name. |
| 70 // TODO(ahe): How about "Bolt"? | 11 // TODO(ahe): How about "Bolt"? |
| 71 abstract class Spannable {} | 12 abstract class Spannable {} |
| 72 | 13 |
| 73 class _SpannableSentinel implements Spannable { | 14 class _SpannableSentinel implements Spannable { |
| 74 final String name; | 15 final String name; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 91 const _SpannableSentinel("No location"); | 32 const _SpannableSentinel("No location"); |
| 92 | 33 |
| 93 class SpannableAssertionFailure { | 34 class SpannableAssertionFailure { |
| 94 final Spannable node; | 35 final Spannable node; |
| 95 final String message; | 36 final String message; |
| 96 SpannableAssertionFailure(this.node, this.message); | 37 SpannableAssertionFailure(this.node, this.message); |
| 97 | 38 |
| 98 String toString() => 'Assertion failure' | 39 String toString() => 'Assertion failure' |
| 99 '${message != null ? ': $message' : ''}'; | 40 '${message != null ? ': $message' : ''}'; |
| 100 } | 41 } |
| 101 | |
| 102 bool equalElements(List a, List b) { | |
| 103 if (a.length != b.length) return false; | |
| 104 for (int index = 0; index < a.length; index++) { | |
| 105 if (a[index] != b[index]) { | |
| 106 return false; | |
| 107 } | |
| 108 } | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 /** | |
| 113 * File name prefix used to shorten the file name in stack traces printed by | |
| 114 * [trace]. | |
| 115 */ | |
| 116 String stackTraceFilePrefix = null; | |
| 117 | |
| 118 /// Writes the characters of [string] on [buffer]. The characters | |
| 119 /// are escaped as suitable for JavaScript and JSON. [buffer] is | |
| 120 /// anything which supports [:write:] and [:writeCharCode:], for example, | |
| 121 /// [StringBuffer]. Note that JS supports \xnn and \unnnn whereas JSON only | |
| 122 /// supports the \unnnn notation. Therefore we use the \unnnn notation. | |
| 123 void writeJsonEscapedCharsOn(String string, buffer) { | |
| 124 void addCodeUnitEscaped(var buffer, int code) { | |
| 125 assert(code < 0x10000); | |
| 126 buffer.write(r'\u'); | |
| 127 if (code < 0x1000) { | |
| 128 buffer.write('0'); | |
| 129 if (code < 0x100) { | |
| 130 buffer.write('0'); | |
| 131 if (code < 0x10) { | |
| 132 buffer.write('0'); | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 buffer.write(code.toRadixString(16)); | |
| 137 } | |
| 138 | |
| 139 void writeEscapedOn(String string, var buffer) { | |
| 140 for (int i = 0; i < string.length; i++) { | |
| 141 int code = string.codeUnitAt(i); | |
| 142 if (code == $DQ) { | |
| 143 buffer.write(r'\"'); | |
| 144 } else if (code == $TAB) { | |
| 145 buffer.write(r'\t'); | |
| 146 } else if (code == $LF) { | |
| 147 buffer.write(r'\n'); | |
| 148 } else if (code == $CR) { | |
| 149 buffer.write(r'\r'); | |
| 150 } else if (code == $DEL) { | |
| 151 addCodeUnitEscaped(buffer, $DEL); | |
| 152 } else if (code == $LS) { | |
| 153 // This Unicode line terminator and $PS are invalid in JS string | |
| 154 // literals. | |
| 155 addCodeUnitEscaped(buffer, $LS); // 0x2028. | |
| 156 } else if (code == $PS) { | |
| 157 addCodeUnitEscaped(buffer, $PS); // 0x2029. | |
| 158 } else if (code == $BACKSLASH) { | |
| 159 buffer.write(r'\\'); | |
| 160 } else { | |
| 161 if (code < 0x20) { | |
| 162 addCodeUnitEscaped(buffer, code); | |
| 163 // We emit DEL (ASCII 0x7f) as an escape because it would be confusing | |
| 164 // to have it unescaped in a string literal. We also escape | |
| 165 // everything above 0x7f because that means we don't have to worry | |
| 166 // about whether the web server serves it up as Latin1 or UTF-8. | |
| 167 } else if (code < 0x7f) { | |
| 168 buffer.writeCharCode(code); | |
| 169 } else { | |
| 170 // This will output surrogate pairs in the form \udxxx\udyyy, rather | |
| 171 // than the more logical \u{zzzzzz}. This should work in JavaScript | |
| 172 // (especially old UCS-2 based implementations) and is the only | |
| 173 // format that is allowed in JSON. | |
| 174 addCodeUnitEscaped(buffer, code); | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 for (int i = 0; i < string.length; i++) { | |
| 181 int code = string.codeUnitAt(i); | |
| 182 if (code < 0x20 || code == $DEL || code == $DQ || code == $LS || | |
| 183 code == $PS || code == $BACKSLASH || code >= 0x80) { | |
| 184 writeEscapedOn(string, buffer); | |
| 185 return; | |
| 186 } | |
| 187 } | |
| 188 buffer.write(string); | |
| 189 } | |
| 190 | |
| 191 int computeHashCode(part1, [part2, part3, part4, part5]) { | |
| 192 return (part1.hashCode | |
| 193 ^ part2.hashCode | |
| 194 ^ part3.hashCode | |
| 195 ^ part4.hashCode | |
| 196 ^ part5.hashCode) & 0x3fffffff; | |
| 197 } | |
| 198 | |
| 199 String modifiersToString({bool isStatic: false, | |
| 200 bool isAbstract: false, | |
| 201 bool isFinal: false, | |
| 202 bool isVar: false, | |
| 203 bool isConst: false, | |
| 204 bool isFactory: false, | |
| 205 bool isExternal: false}) { | |
| 206 LinkBuilder<String> builder = new LinkBuilder<String>(); | |
| 207 if (isStatic) builder.addLast('static'); | |
| 208 if (isAbstract) builder.addLast('abstract'); | |
| 209 if (isFinal) builder.addLast('final'); | |
| 210 if (isVar) builder.addLast('var'); | |
| 211 if (isConst) builder.addLast('const'); | |
| 212 if (isFactory) builder.addLast('factory'); | |
| 213 if (isExternal) builder.addLast('external'); | |
| 214 StringBuffer buffer = new StringBuffer(); | |
| 215 builder.toLink().printOn(buffer, ', '); | |
| 216 return buffer.toString(); | |
| 217 } | |
| 218 | |
| 219 class Pair<A, B> { | |
| 220 final A a; | |
| 221 final B b; | |
| 222 | |
| 223 Pair(this.a, this.b); | |
| 224 | |
| 225 int get hashCode => 13 * a.hashCode + 17 * b.hashCode; | |
| 226 | |
| 227 bool operator ==(var other) { | |
| 228 if (identical(this, other)) return true; | |
| 229 if (other is! Pair) return false; | |
| 230 return a == other.a && b == other.b; | |
| 231 } | |
| 232 | |
| 233 String toString() => '($a,$b)'; | |
| 234 } | |
| 235 | |
| 236 | |
| 237 int longestCommonPrefixLength(List a, List b) { | |
| 238 int index = 0; | |
| 239 for ( ; index < a.length && index < b.length; index++) { | |
| 240 if (a[index] != b[index]) { | |
| 241 break; | |
| 242 } | |
| 243 } | |
| 244 return index; | |
| 245 } | |
| 246 | |
| 247 /// Returns [suggestedName] if it is not in [usedNames]. Otherwise concatenates | |
| 248 /// the smallest number that makes it not appear in [usedNames]. | |
| 249 /// | |
| 250 /// Adds the result to [usedNames]. | |
| 251 String makeUnique(String suggestedName, Set<String> usedNames) { | |
| 252 String result = suggestedName; | |
| 253 if (usedNames.contains(suggestedName)) { | |
| 254 int counter = 0; | |
| 255 while (usedNames.contains(result)) { | |
| 256 counter++; | |
| 257 result = "$suggestedName$counter"; | |
| 258 } | |
| 259 } | |
| 260 usedNames.add(result); | |
| 261 return result; | |
| 262 } | |
| OLD | NEW |