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 |