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 |