OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 part of _interceptors; | |
6 | |
7 /** | |
8 * The super interceptor class for [JSInt] and [JSDouble]. The compiler | |
9 * recognizes this class as an interceptor, and changes references to | |
10 * [:this:] to actually use the receiver of the method, which is | |
11 * generated as an extra argument added to each member. | |
12 * | |
13 * Note that none of the methods here delegate to a method defined on JSInt or | |
14 * JSDouble. This is exploited in [tryComputeConstantInterceptor]. | |
15 */ | |
16 class JSNumber extends Interceptor implements num { | |
17 const JSNumber(); | |
18 | |
19 int compareTo(num b) { | |
20 if (b is! num) throw new ArgumentError(b); | |
21 if (this < b) { | |
22 return -1; | |
23 } else if (this > b) { | |
24 return 1; | |
25 } else if (this == b) { | |
26 if (this == 0) { | |
27 bool bIsNegative = b.isNegative; | |
28 if (isNegative == bIsNegative) return 0; | |
29 if (isNegative) return -1; | |
30 return 1; | |
31 } | |
32 return 0; | |
33 } else if (isNaN) { | |
34 if (b.isNaN) { | |
35 return 0; | |
36 } | |
37 return 1; | |
38 } else { | |
39 return -1; | |
40 } | |
41 } | |
42 | |
43 bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; | |
44 | |
45 bool get isNaN => JS('bool', r'isNaN(#)', this); | |
46 | |
47 bool get isInfinite { | |
48 return JS('bool', r'# == Infinity', this) | |
49 || JS('bool', r'# == -Infinity', this); | |
50 } | |
51 | |
52 bool get isFinite => JS('bool', r'isFinite(#)', this); | |
53 | |
54 num remainder(num b) { | |
55 checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. | |
56 if (b is! num) throw new ArgumentError(b); | |
57 return JS('num', r'# % #', this, b); | |
58 } | |
59 | |
60 num abs() => JS('num', r'Math.abs(#)', this); | |
61 | |
62 num get sign => this > 0 ? 1 : this < 0 ? -1 : this; | |
63 | |
64 static const int _MIN_INT32 = -0x80000000; | |
65 static const int _MAX_INT32 = 0x7FFFFFFF; | |
66 | |
67 int toInt() { | |
68 if (this >= _MIN_INT32 && this <= _MAX_INT32) { | |
69 return JS('int', '# | 0', this); | |
70 } | |
71 if (JS('bool', r'isFinite(#)', this)) { | |
72 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. | |
73 } | |
74 // This is either NaN, Infinity or -Infinity. | |
75 throw new UnsupportedError(JS("String", "''+#", this)); | |
76 } | |
77 | |
78 int truncate() => toInt(); | |
79 int ceil() => ceilToDouble().toInt(); | |
80 int floor() => floorToDouble().toInt(); | |
81 int round() => roundToDouble().toInt(); | |
82 | |
83 double ceilToDouble() => JS('num', r'Math.ceil(#)', this); | |
84 | |
85 double floorToDouble() => JS('num', r'Math.floor(#)', this); | |
86 | |
87 double roundToDouble() { | |
88 if (this < 0) { | |
89 return JS('num', r'-Math.round(-#)', this); | |
90 } else { | |
91 return JS('num', r'Math.round(#)', this); | |
92 } | |
93 } | |
94 | |
95 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); | |
96 | |
97 num clamp(lowerLimit, upperLimit) { | |
98 if (lowerLimit is! num) throw new ArgumentError(lowerLimit); | |
99 if (upperLimit is! num) throw new ArgumentError(upperLimit); | |
100 if (lowerLimit.compareTo(upperLimit) > 0) { | |
101 throw new ArgumentError(lowerLimit); | |
102 } | |
103 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | |
104 if (this.compareTo(upperLimit) > 0) return upperLimit; | |
105 return this; | |
106 } | |
107 | |
108 // The return type is intentionally omitted to avoid type checker warnings | |
109 // from assigning JSNumber to double. | |
110 toDouble() => this; | |
111 | |
112 String toStringAsFixed(int fractionDigits) { | |
113 checkNum(fractionDigits); | |
114 // TODO(floitsch): fractionDigits must be an integer. | |
115 if (fractionDigits < 0 || fractionDigits > 20) { | |
116 throw new RangeError(fractionDigits); | |
117 } | |
118 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); | |
119 if (this == 0 && isNegative) return "-$result"; | |
120 return result; | |
121 } | |
122 | |
123 String toStringAsExponential([int fractionDigits]) { | |
124 String result; | |
125 if (fractionDigits != null) { | |
126 // TODO(floitsch): fractionDigits must be an integer. | |
127 checkNum(fractionDigits); | |
128 if (fractionDigits < 0 || fractionDigits > 20) { | |
129 throw new RangeError(fractionDigits); | |
130 } | |
131 result = JS('String', r'#.toExponential(#)', this, fractionDigits); | |
132 } else { | |
133 result = JS('String', r'#.toExponential()', this); | |
134 } | |
135 if (this == 0 && isNegative) return "-$result"; | |
136 return result; | |
137 } | |
138 | |
139 String toStringAsPrecision(int precision) { | |
140 // TODO(floitsch): precision must be an integer. | |
141 checkNum(precision); | |
142 if (precision < 1 || precision > 21) { | |
143 throw new RangeError(precision); | |
144 } | |
145 String result = JS('String', r'#.toPrecision(#)', | |
146 this, precision); | |
147 if (this == 0 && isNegative) return "-$result"; | |
148 return result; | |
149 } | |
150 | |
151 String toRadixString(int radix) { | |
152 checkNum(radix); | |
153 if (radix < 2 || radix > 36) throw new RangeError(radix); | |
154 return JS('String', r'#.toString(#)', this, radix); | |
155 } | |
156 | |
157 // Note: if you change this, also change the function [S]. | |
158 String toString() { | |
159 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { | |
160 return '-0.0'; | |
161 } else { | |
162 return JS('String', r'"" + (#)', this); | |
163 } | |
164 } | |
165 | |
166 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | |
167 | |
168 num operator -() => JS('num', r'-#', this); | |
169 | |
170 num operator +(num other) { | |
171 if (other is !num) throw new ArgumentError(other); | |
172 return JS('num', '# + #', this, other); | |
173 } | |
174 | |
175 num operator -(num other) { | |
176 if (other is !num) throw new ArgumentError(other); | |
177 return JS('num', '# - #', this, other); | |
178 } | |
179 | |
180 num operator /(num other) { | |
181 if (other is !num) throw new ArgumentError(other); | |
182 return JS('num', '# / #', this, other); | |
183 } | |
184 | |
185 num operator *(num other) { | |
186 if (other is !num) throw new ArgumentError(other); | |
187 return JS('num', '# * #', this, other); | |
188 } | |
189 | |
190 num operator %(num other) { | |
191 if (other is !num) throw new ArgumentError(other); | |
192 // Euclidean Modulo. | |
193 num result = JS('num', r'# % #', this, other); | |
194 if (result == 0) return 0; // Make sure we don't return -0.0. | |
195 if (result > 0) return result; | |
196 if (JS('num', '#', other) < 0) { | |
197 return result - JS('num', '#', other); | |
198 } else { | |
199 return result + JS('num', '#', other); | |
200 } | |
201 } | |
202 | |
203 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | |
204 | |
205 num operator ~/(num other) { | |
206 if (false) _tdivFast(other); // Ensure resolution. | |
207 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { | |
208 return JS('num', r'(# / #) | 0', this, other); | |
209 } else { | |
210 return _tdivSlow(other); | |
211 } | |
212 } | |
213 | |
214 num _tdivFast(num other) { | |
215 return _isInt32(this) | |
216 ? JS('num', r'(# / #) | 0', this, other) | |
217 : (JS('num', r'# / #', this, other)).toInt(); | |
218 } | |
219 | |
220 num _tdivSlow(num other) { | |
221 if (other is !num) throw new ArgumentError(other); | |
222 return (JS('num', r'# / #', this, other)).toInt(); | |
223 } | |
224 | |
225 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | |
226 // make them take an int. Because this will make operations slower, | |
227 // we define these methods on number for now but we need to decide | |
228 // the grain at which we do the type checks. | |
229 | |
230 num operator <<(num other) { | |
231 if (other is !num) throw new ArgumentError(other); | |
232 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
233 return _shlPositive(other); | |
234 } | |
235 | |
236 num _shlPositive(num other) { | |
237 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | |
238 // by 33 is hence equivalent to a shift by 1. | |
239 return JS('bool', r'# > 31', other) | |
240 ? 0 | |
241 : JS('JSUInt32', r'(# << #) >>> 0', this, other); | |
242 } | |
243 | |
244 num operator >>(num other) { | |
245 if (false) _shrReceiverPositive(other); | |
246 if (other is !num) throw new ArgumentError(other); | |
247 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
248 return _shrOtherPositive(other); | |
249 } | |
250 | |
251 num _shrOtherPositive(num other) { | |
252 return JS('num', '#', this) > 0 | |
253 ? _shrBothPositive(other) | |
254 // For negative numbers we just clamp the shift-by amount. | |
255 // `this` could be negative but not have its 31st bit set. | |
256 // The ">>" would then shift in 0s instead of 1s. Therefore | |
257 // we cannot simply return 0xFFFFFFFF. | |
258 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | |
259 } | |
260 | |
261 num _shrReceiverPositive(num other) { | |
262 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
263 return _shrBothPositive(other); | |
264 } | |
265 | |
266 num _shrBothPositive(num other) { | |
267 return JS('bool', r'# > 31', other) | |
268 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | |
269 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the | |
270 // computation when that happens. | |
271 ? 0 | |
272 // Given that `this` is positive we must not use '>>'. Otherwise a | |
273 // number that has the 31st bit set would be treated as negative and | |
274 // shift in ones. | |
275 : JS('JSUInt32', r'# >>> #', this, other); | |
276 } | |
277 | |
278 num operator &(num other) { | |
279 if (other is !num) throw new ArgumentError(other); | |
280 return JS('JSUInt32', r'(# & #) >>> 0', this, other); | |
281 } | |
282 | |
283 num operator |(num other) { | |
284 if (other is !num) throw new ArgumentError(other); | |
285 return JS('JSUInt32', r'(# | #) >>> 0', this, other); | |
286 } | |
287 | |
288 num operator ^(num other) { | |
289 if (other is !num) throw new ArgumentError(other); | |
290 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); | |
291 } | |
292 | |
293 bool operator <(num other) { | |
294 if (other is !num) throw new ArgumentError(other); | |
295 return JS('bool', '# < #', this, other); | |
296 } | |
297 | |
298 bool operator >(num other) { | |
299 if (other is !num) throw new ArgumentError(other); | |
300 return JS('bool', '# > #', this, other); | |
301 } | |
302 | |
303 bool operator <=(num other) { | |
304 if (other is !num) throw new ArgumentError(other); | |
305 return JS('bool', '# <= #', this, other); | |
306 } | |
307 | |
308 bool operator >=(num other) { | |
309 if (other is !num) throw new ArgumentError(other); | |
310 return JS('bool', '# >= #', this, other); | |
311 } | |
312 | |
313 Type get runtimeType => num; | |
314 } | |
315 | |
316 /** | |
317 * The interceptor class for [int]s. | |
318 * | |
319 * This class implements double since in JavaScript all numbers are doubles, so | |
320 * while we want to treat `2.0` as an integer for some operations, its | |
321 * interceptor should answer `true` to `is double`. | |
322 */ | |
323 class JSInt extends JSNumber implements int, double { | |
324 const JSInt(); | |
325 | |
326 bool get isEven => (this & 1) == 0; | |
327 | |
328 bool get isOdd => (this & 1) == 1; | |
329 | |
330 int toUnsigned(int width) { | |
331 return this & ((1 << width) - 1); | |
332 } | |
333 | |
334 int toSigned(int width) { | |
335 int signMask = 1 << (width - 1); | |
336 return (this & (signMask - 1)) - (this & signMask); | |
337 } | |
338 | |
339 int get bitLength { | |
340 int nonneg = this < 0 ? -this - 1 : this; | |
341 if (nonneg >= 0x100000000) { | |
342 nonneg = nonneg ~/ 0x100000000; | |
343 return _bitCount(_spread(nonneg)) + 32; | |
344 } | |
345 return _bitCount(_spread(nonneg)); | |
346 } | |
347 | |
348 // Assumes i is <= 32-bit and unsigned. | |
349 static int _bitCount(int i) { | |
350 // See "Hacker's Delight", section 5-1, "Counting 1-Bits". | |
351 | |
352 // The basic strategy is to use "divide and conquer" to | |
353 // add pairs (then quads, etc.) of bits together to obtain | |
354 // sub-counts. | |
355 // | |
356 // A straightforward approach would look like: | |
357 // | |
358 // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); | |
359 // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); | |
360 // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); | |
361 // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); | |
362 // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); | |
363 // | |
364 // The code below removes unnecessary &'s and uses a | |
365 // trick to remove one instruction in the first line. | |
366 | |
367 i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); | |
368 i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); | |
369 i = 0x0F0F0F0F & (i + _shru(i, 4)); | |
370 i += _shru(i, 8); | |
371 i += _shru(i, 16); | |
372 return (i & 0x0000003F); | |
373 } | |
374 | |
375 static _shru(int value, int shift) => JS('int', '# >>> #', value, shift); | |
376 static _shrs(int value, int shift) => JS('int', '# >> #', value, shift); | |
377 static _ors(int a, int b) => JS('int', '# | #', a, b); | |
378 | |
379 // Assumes i is <= 32-bit | |
380 static int _spread(int i) { | |
381 i = _ors(i, _shrs(i, 1)); | |
382 i = _ors(i, _shrs(i, 2)); | |
383 i = _ors(i, _shrs(i, 4)); | |
384 i = _ors(i, _shrs(i, 8)); | |
385 i = _shru(_ors(i, _shrs(i, 16)), 0); | |
386 return i; | |
387 } | |
388 | |
389 Type get runtimeType => int; | |
390 | |
391 int operator ~() => JS('JSUInt32', r'(~#) >>> 0', this); | |
392 } | |
393 | |
394 class JSDouble extends JSNumber implements double { | |
395 const JSDouble(); | |
396 Type get runtimeType => double; | |
397 } | |
398 | |
399 class JSPositiveInt extends JSInt {} | |
400 class JSUInt32 extends JSPositiveInt {} | |
401 class JSUInt31 extends JSUInt32 {} | |
OLD | NEW |