| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library dart2js.util; | |
| 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 /// If an integer is masked by this constant, the result is guaranteed to be in | |
| 18 /// Smi range. | |
| 19 const int SMI_MASK = 0x3fffffff; | |
| 20 | |
| 21 /** | |
| 22 * Tagging interface for classes from which source spans can be generated. | |
| 23 */ | |
| 24 // TODO(johnniwinther): Find a better name. | |
| 25 // TODO(ahe): How about "Bolt"? | |
| 26 abstract class Spannable {} | |
| 27 | |
| 28 class _SpannableSentinel implements Spannable { | |
| 29 final String name; | |
| 30 | |
| 31 const _SpannableSentinel(this.name); | |
| 32 | |
| 33 String toString() => name; | |
| 34 } | |
| 35 | |
| 36 /// Sentinel spannable used to mark that diagnostics should point to the | |
| 37 /// current element. Note that the diagnostic reporting will fail if the current | |
| 38 /// element is `null`. | |
| 39 const Spannable CURRENT_ELEMENT_SPANNABLE = | |
| 40 const _SpannableSentinel("Current element"); | |
| 41 | |
| 42 /// Sentinel spannable used to mark that there might be no location for the | |
| 43 /// diagnostic. Use this only when it is not an error not to have a current | |
| 44 /// element. | |
| 45 const Spannable NO_LOCATION_SPANNABLE = | |
| 46 const _SpannableSentinel("No location"); | |
| 47 | |
| 48 class SpannableAssertionFailure { | |
| 49 final Spannable node; | |
| 50 final String message; | |
| 51 SpannableAssertionFailure(this.node, this.message); | |
| 52 | |
| 53 String toString() => 'Assertion failure' | |
| 54 '${message != null ? ': $message' : ''}'; | |
| 55 } | |
| 56 | |
| 57 bool equalElements(List a, List b) { | |
| 58 if (a.length != b.length) return false; | |
| 59 for (int index = 0; index < a.length; index++) { | |
| 60 if (a[index] != b[index]) { | |
| 61 return false; | |
| 62 } | |
| 63 } | |
| 64 return true; | |
| 65 } | |
| 66 | |
| 67 /** | |
| 68 * File name prefix used to shorten the file name in stack traces printed by | |
| 69 * [trace]. | |
| 70 */ | |
| 71 String stackTraceFilePrefix = null; | |
| 72 | |
| 73 /// Writes the characters of [string] on [buffer]. The characters | |
| 74 /// are escaped as suitable for JavaScript and JSON. [buffer] is | |
| 75 /// anything which supports [:write:] and [:writeCharCode:], for example, | |
| 76 /// [StringBuffer]. Note that JS supports \xnn and \unnnn whereas JSON only | |
| 77 /// supports the \unnnn notation. Therefore we use the \unnnn notation. | |
| 78 void writeJsonEscapedCharsOn(String string, buffer) { | |
| 79 void addCodeUnitEscaped(var buffer, int code) { | |
| 80 assert(code < 0x10000); | |
| 81 buffer.write(r'\u'); | |
| 82 if (code < 0x1000) { | |
| 83 buffer.write('0'); | |
| 84 if (code < 0x100) { | |
| 85 buffer.write('0'); | |
| 86 if (code < 0x10) { | |
| 87 buffer.write('0'); | |
| 88 } | |
| 89 } | |
| 90 } | |
| 91 buffer.write(code.toRadixString(16)); | |
| 92 } | |
| 93 | |
| 94 void writeEscapedOn(String string, var buffer) { | |
| 95 for (int i = 0; i < string.length; i++) { | |
| 96 int code = string.codeUnitAt(i); | |
| 97 if (code == $DQ) { | |
| 98 buffer.write(r'\"'); | |
| 99 } else if (code == $TAB) { | |
| 100 buffer.write(r'\t'); | |
| 101 } else if (code == $LF) { | |
| 102 buffer.write(r'\n'); | |
| 103 } else if (code == $CR) { | |
| 104 buffer.write(r'\r'); | |
| 105 } else if (code == $DEL) { | |
| 106 addCodeUnitEscaped(buffer, $DEL); | |
| 107 } else if (code == $LS) { | |
| 108 // This Unicode line terminator and $PS are invalid in JS string | |
| 109 // literals. | |
| 110 addCodeUnitEscaped(buffer, $LS); // 0x2028. | |
| 111 } else if (code == $PS) { | |
| 112 addCodeUnitEscaped(buffer, $PS); // 0x2029. | |
| 113 } else if (code == $BACKSLASH) { | |
| 114 buffer.write(r'\\'); | |
| 115 } else { | |
| 116 if (code < 0x20) { | |
| 117 addCodeUnitEscaped(buffer, code); | |
| 118 // We emit DEL (ASCII 0x7f) as an escape because it would be confusing | |
| 119 // to have it unescaped in a string literal. We also escape | |
| 120 // everything above 0x7f because that means we don't have to worry | |
| 121 // about whether the web server serves it up as Latin1 or UTF-8. | |
| 122 } else if (code < 0x7f) { | |
| 123 buffer.writeCharCode(code); | |
| 124 } else { | |
| 125 // This will output surrogate pairs in the form \udxxx\udyyy, rather | |
| 126 // than the more logical \u{zzzzzz}. This should work in JavaScript | |
| 127 // (especially old UCS-2 based implementations) and is the only | |
| 128 // format that is allowed in JSON. | |
| 129 addCodeUnitEscaped(buffer, code); | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 for (int i = 0; i < string.length; i++) { | |
| 136 int code = string.codeUnitAt(i); | |
| 137 if (code < 0x20 || code == $DEL || code == $DQ || code == $LS || | |
| 138 code == $PS || code == $BACKSLASH || code >= 0x80) { | |
| 139 writeEscapedOn(string, buffer); | |
| 140 return; | |
| 141 } | |
| 142 } | |
| 143 buffer.write(string); | |
| 144 } | |
| 145 | |
| 146 int computeHashCode(part1, [part2, part3, part4, part5]) { | |
| 147 return (part1.hashCode | |
| 148 ^ part2.hashCode | |
| 149 ^ part3.hashCode | |
| 150 ^ part4.hashCode | |
| 151 ^ part5.hashCode) & 0x3fffffff; | |
| 152 } | |
| 153 | |
| 154 String modifiersToString({bool isStatic: false, | |
| 155 bool isAbstract: false, | |
| 156 bool isFinal: false, | |
| 157 bool isVar: false, | |
| 158 bool isConst: false, | |
| 159 bool isFactory: false, | |
| 160 bool isExternal: false}) { | |
| 161 LinkBuilder<String> builder = new LinkBuilder<String>(); | |
| 162 if (isStatic) builder.addLast('static'); | |
| 163 if (isAbstract) builder.addLast('abstract'); | |
| 164 if (isFinal) builder.addLast('final'); | |
| 165 if (isVar) builder.addLast('var'); | |
| 166 if (isConst) builder.addLast('const'); | |
| 167 if (isFactory) builder.addLast('factory'); | |
| 168 if (isExternal) builder.addLast('external'); | |
| 169 StringBuffer buffer = new StringBuffer(); | |
| 170 builder.toLink().printOn(buffer, ', '); | |
| 171 return buffer.toString(); | |
| 172 } | |
| 173 | |
| 174 class Pair<A, B> { | |
| 175 final A a; | |
| 176 final B b; | |
| 177 | |
| 178 Pair(this.a, this.b); | |
| 179 | |
| 180 int get hashCode => 13 * a.hashCode + 17 * b.hashCode; | |
| 181 | |
| 182 bool operator ==(var other) { | |
| 183 if (identical(this, other)) return true; | |
| 184 if (other is! Pair) return false; | |
| 185 return a == other.a && b == other.b; | |
| 186 } | |
| 187 | |
| 188 String toString() => '($a,$b)'; | |
| 189 } | |
| 190 | |
| 191 | |
| 192 int longestCommonPrefixLength(List a, List b) { | |
| 193 int index = 0; | |
| 194 for ( ; index < a.length && index < b.length; index++) { | |
| 195 if (a[index] != b[index]) { | |
| 196 break; | |
| 197 } | |
| 198 } | |
| 199 return index; | |
| 200 } | |
| 201 | |
| 202 /// Returns [suggestedName] if it is not in [usedNames]. Otherwise concatenates | |
| 203 /// the smallest number that makes it not appear in [usedNames]. | |
| 204 /// | |
| 205 /// Adds the result to [usedNames]. | |
| 206 String makeUnique(String suggestedName, Set<String> usedNames) { | |
| 207 String result = suggestedName; | |
| 208 if (usedNames.contains(suggestedName)) { | |
| 209 int counter = 0; | |
| 210 while (usedNames.contains(result)) { | |
| 211 counter++; | |
| 212 result = "$suggestedName$counter"; | |
| 213 } | |
| 214 } | |
| 215 usedNames.add(result); | |
| 216 return result; | |
| 217 } | |
| OLD | NEW |