OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 part of dart._interceptors; | 5 part of dart._interceptors; |
6 | 6 |
7 /** | 7 /** |
8 * The super interceptor class for [JSInt] and [JSDouble]. The compiler | 8 * The implementation of Dart's int & double methods. |
9 * recognizes this class as an interceptor, and changes references to | 9 * These are made available as extension methods on `Number` in JS. |
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 */ | 10 */ |
16 class JSNumber extends Interceptor implements num { | 11 @JsPeerInterface(name: 'Number') |
| 12 class JSNumber extends Interceptor implements int, double { |
17 const JSNumber(); | 13 const JSNumber(); |
18 | 14 |
19 int compareTo(num b) { | 15 int compareTo(num b) { |
20 if (b is! num) throw new ArgumentError(b); | |
21 if (this < b) { | 16 if (this < b) { |
22 return -1; | 17 return -1; |
23 } else if (this > b) { | 18 } else if (this > b) { |
24 return 1; | 19 return 1; |
25 } else if (this == b) { | 20 } else if (this == b) { |
26 if (this == 0) { | 21 if (this == 0) { |
27 bool bIsNegative = b.isNegative; | 22 bool bIsNegative = b.isNegative; |
28 if (isNegative == bIsNegative) return 0; | 23 if (isNegative == bIsNegative) return 0; |
29 if (isNegative) return -1; | 24 if (isNegative) return -1; |
30 return 1; | 25 return 1; |
(...skipping 13 matching lines...) Expand all Loading... |
44 | 39 |
45 bool get isNaN => JS('bool', r'isNaN(#)', this); | 40 bool get isNaN => JS('bool', r'isNaN(#)', this); |
46 | 41 |
47 bool get isInfinite { | 42 bool get isInfinite { |
48 return JS('bool', r'# == Infinity', this) | 43 return JS('bool', r'# == Infinity', this) |
49 || JS('bool', r'# == -Infinity', this); | 44 || JS('bool', r'# == -Infinity', this); |
50 } | 45 } |
51 | 46 |
52 bool get isFinite => JS('bool', r'isFinite(#)', this); | 47 bool get isFinite => JS('bool', r'isFinite(#)', this); |
53 | 48 |
54 num remainder(num b) { | 49 JSNumber remainder(num b) { |
55 checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. | 50 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); | 51 return JS('num', r'# % #', this, b); |
58 } | 52 } |
59 | 53 |
60 num abs() => JS('num', r'Math.abs(#)', this); | 54 JSNumber abs() => JS('num', r'Math.abs(#)', this); |
61 | 55 |
62 num get sign => this > 0 ? 1 : this < 0 ? -1 : this; | 56 JSNumber get sign => this > 0 ? 1 : this < 0 ? -1 : this; |
63 | 57 |
64 static const int _MIN_INT32 = -0x80000000; | 58 static const int _MIN_INT32 = -0x80000000; |
65 static const int _MAX_INT32 = 0x7FFFFFFF; | 59 static const int _MAX_INT32 = 0x7FFFFFFF; |
66 | 60 |
67 int toInt() { | 61 int toInt() { |
68 if (this >= _MIN_INT32 && this <= _MAX_INT32) { | 62 if (this >= _MIN_INT32 && this <= _MAX_INT32) { |
69 return JS('int', '# | 0', this); | 63 return JS('int', '# | 0', this); |
70 } | 64 } |
71 if (JS('bool', r'isFinite(#)', this)) { | 65 if (JS('bool', r'isFinite(#)', this)) { |
72 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. | 66 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. |
(...skipping 15 matching lines...) Expand all Loading... |
88 if (this < 0) { | 82 if (this < 0) { |
89 return JS('num', r'-Math.round(-#)', this); | 83 return JS('num', r'-Math.round(-#)', this); |
90 } else { | 84 } else { |
91 return JS('num', r'Math.round(#)', this); | 85 return JS('num', r'Math.round(#)', this); |
92 } | 86 } |
93 } | 87 } |
94 | 88 |
95 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); | 89 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); |
96 | 90 |
97 num clamp(num lowerLimit, num upperLimit) { | 91 num clamp(num lowerLimit, num 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) { | 92 if (lowerLimit.compareTo(upperLimit) > 0) { |
101 throw new ArgumentError(lowerLimit); | 93 throw new ArgumentError(lowerLimit); |
102 } | 94 } |
103 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | 95 if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
104 if (this.compareTo(upperLimit) > 0) return upperLimit; | 96 if (this.compareTo(upperLimit) > 0) return upperLimit; |
105 return this; | 97 return this; |
106 } | 98 } |
107 | 99 |
108 // The return type is intentionally omitted to avoid type checker warnings | 100 double toDouble() => this; |
109 // from assigning JSNumber to double. | |
110 toDouble() => this; | |
111 | 101 |
112 String toStringAsFixed(int fractionDigits) { | 102 String toStringAsFixed(int fractionDigits) { |
113 checkInt(fractionDigits); | 103 checkInt(fractionDigits); |
114 if (fractionDigits < 0 || fractionDigits > 20) { | 104 if (fractionDigits < 0 || fractionDigits > 20) { |
115 throw new RangeError(fractionDigits); | 105 throw new RangeError(fractionDigits); |
116 } | 106 } |
117 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); | 107 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); |
118 if (this == 0 && isNegative) return "-$result"; | 108 if (this == 0 && isNegative) return "-$result"; |
119 return result; | 109 return result; |
120 } | 110 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 String toString() { | 169 String toString() { |
180 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { | 170 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { |
181 return '-0.0'; | 171 return '-0.0'; |
182 } else { | 172 } else { |
183 return JS('String', r'"" + (#)', this); | 173 return JS('String', r'"" + (#)', this); |
184 } | 174 } |
185 } | 175 } |
186 | 176 |
187 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | 177 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); |
188 | 178 |
189 num operator -() => JS('num', r'-#', this); | 179 JSNumber operator -() => JS('num', r'-#', this); |
190 | 180 |
191 num operator +(num other) { | 181 JSNumber operator +(num other) { |
192 if (other is !num) throw new ArgumentError(other); | 182 checkNull(other); |
193 return JS('num', '# + #', this, other); | 183 return JS('num', '# + #', this, other); |
194 } | 184 } |
195 | 185 |
196 num operator -(num other) { | 186 JSNumber operator -(num other) { |
197 if (other is !num) throw new ArgumentError(other); | 187 checkNull(other); |
198 return JS('num', '# - #', this, other); | 188 return JS('num', '# - #', this, other); |
199 } | 189 } |
200 | 190 |
201 double operator /(num other) { | 191 double operator /(num other) { |
202 if (other is !num) throw new ArgumentError(other); | 192 checkNull(other); |
203 return JS('double', '# / #', this, other); | 193 return JS('double', '# / #', this, other); |
204 } | 194 } |
205 | 195 |
206 num operator *(num other) { | 196 JSNumber operator *(num other) { |
207 if (other is !num) throw new ArgumentError(other); | 197 checkNull(other); |
208 return JS('num', '# * #', this, other); | 198 return JS('num', '# * #', this, other); |
209 } | 199 } |
210 | 200 |
211 num operator %(num other) { | 201 JSNumber operator %(num other) { |
212 if (other is !num) throw new ArgumentError(other); | 202 checkNull(other); |
213 // Euclidean Modulo. | 203 // Euclidean Modulo. |
214 num result = JS('num', r'# % #', this, other); | 204 num result = JS('num', r'# % #', this, other); |
215 if (result == 0) return 0; // Make sure we don't return -0.0. | 205 if (result == 0) return (0 as JSNumber); // Make sure we don't return -0.0. |
216 if (result > 0) return result; | 206 if (result > 0) return result; |
217 if (JS('num', '#', other) < 0) { | 207 if (JS('num', '#', other) < 0) { |
218 return result - JS('num', '#', other); | 208 return result - JS('num', '#', other); |
219 } else { | 209 } else { |
220 return result + JS('num', '#', other); | 210 return result + JS('num', '#', other); |
221 } | 211 } |
222 } | 212 } |
223 | 213 |
224 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | 214 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); |
225 | 215 |
226 int operator ~/(num other) { | 216 int operator ~/(num other) { |
227 if (false) _tdivFast(other); // Ensure resolution. | |
228 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { | 217 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { |
229 return JS('int', r'(# / #) | 0', this, other); | 218 return JS('int', r'(# / #) | 0', this, other); |
230 } else { | 219 } else { |
231 return _tdivSlow(other); | 220 return _tdivSlow(other); |
232 } | 221 } |
233 } | 222 } |
234 | 223 |
235 int _tdivFast(num other) { | |
236 return _isInt32(this) | |
237 ? JS('int', r'(# / #) | 0', this, other) | |
238 : (JS('num', r'# / #', this, other)).toInt(); | |
239 } | |
240 | |
241 int _tdivSlow(num other) { | 224 int _tdivSlow(num other) { |
242 if (other is !num) throw new ArgumentError(other); | 225 checkNull(other); |
243 return (JS('num', r'# / #', this, other)).toInt(); | 226 return (JS('num', r'# / #', this, other)).toInt(); |
244 } | 227 } |
245 | 228 |
246 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | 229 // TODO(ngeoffray): Move the bit operations below to [JSInt] and |
247 // make them take an int. Because this will make operations slower, | 230 // make them take an int. Because this will make operations slower, |
248 // we define these methods on number for now but we need to decide | 231 // we define these methods on number for now but we need to decide |
249 // the grain at which we do the type checks. | 232 // the grain at which we do the type checks. |
250 | 233 |
251 num operator <<(num other) { | 234 int operator <<(num other) { |
252 if (other is !num) throw new ArgumentError(other); | 235 if (other < 0) throw new ArgumentError(other); |
253 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
254 return _shlPositive(other); | 236 return _shlPositive(other); |
255 } | 237 } |
256 | 238 |
257 num _shlPositive(num other) { | 239 int _shlPositive(num other) { |
258 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | 240 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting |
259 // by 33 is hence equivalent to a shift by 1. | 241 // by 33 is hence equivalent to a shift by 1. |
260 return JS('bool', r'# > 31', other) | 242 return JS('bool', r'# > 31', other) |
261 ? 0 | 243 ? 0 |
262 : JS('JSUInt32', r'(# << #) >>> 0', this, other); | 244 : JS('int', r'(# << #) >>> 0', this, other); |
263 } | 245 } |
264 | 246 |
265 num operator >>(num other) { | 247 int operator >>(num other) { |
266 if (false) _shrReceiverPositive(other); | 248 if (other < 0) throw new ArgumentError(other); |
267 if (other is !num) throw new ArgumentError(other); | |
268 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
269 return _shrOtherPositive(other); | 249 return _shrOtherPositive(other); |
270 } | 250 } |
271 | 251 |
272 num _shrOtherPositive(num other) { | 252 int _shrOtherPositive(num other) { |
273 return JS('num', '#', this) > 0 | 253 return JS('num', '#', this) > 0 |
274 ? _shrBothPositive(other) | 254 ? _shrBothPositive(other) |
275 // For negative numbers we just clamp the shift-by amount. | 255 // For negative numbers we just clamp the shift-by amount. |
276 // `this` could be negative but not have its 31st bit set. | 256 // `this` could be negative but not have its 31st bit set. |
277 // The ">>" would then shift in 0s instead of 1s. Therefore | 257 // The ">>" would then shift in 0s instead of 1s. Therefore |
278 // we cannot simply return 0xFFFFFFFF. | 258 // we cannot simply return 0xFFFFFFFF. |
279 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | 259 : JS('int', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); |
280 } | 260 } |
281 | 261 |
282 num _shrReceiverPositive(num other) { | 262 int _shrBothPositive(num other) { |
283 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
284 return _shrBothPositive(other); | |
285 } | |
286 | |
287 num _shrBothPositive(num other) { | |
288 return JS('bool', r'# > 31', other) | 263 return JS('bool', r'# > 31', other) |
289 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | 264 // JavaScript only looks at the last 5 bits of the shift-amount. In JS |
290 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the | 265 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the |
291 // computation when that happens. | 266 // computation when that happens. |
292 ? 0 | 267 ? 0 |
293 // Given that `this` is positive we must not use '>>'. Otherwise a | 268 // Given that `this` is positive we must not use '>>'. Otherwise a |
294 // number that has the 31st bit set would be treated as negative and | 269 // number that has the 31st bit set would be treated as negative and |
295 // shift in ones. | 270 // shift in ones. |
296 : JS('JSUInt32', r'# >>> #', this, other); | 271 : JS('int', r'# >>> #', this, other); |
297 } | 272 } |
298 | 273 |
299 num operator &(num other) { | 274 int operator &(num other) { |
300 if (other is !num) throw new ArgumentError(other); | 275 checkNull(other); |
301 return JS('JSUInt32', r'(# & #) >>> 0', this, other); | 276 return JS('int', r'(# & #) >>> 0', this, other); |
302 } | 277 } |
303 | 278 |
304 num operator |(num other) { | 279 int operator |(num other) { |
305 if (other is !num) throw new ArgumentError(other); | 280 checkNull(other); |
306 return JS('JSUInt32', r'(# | #) >>> 0', this, other); | 281 return JS('int', r'(# | #) >>> 0', this, other); |
307 } | 282 } |
308 | 283 |
309 num operator ^(num other) { | 284 int operator ^(num other) { |
310 if (other is !num) throw new ArgumentError(other); | 285 checkNull(other); |
311 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); | 286 return JS('int', r'(# ^ #) >>> 0', this, other); |
312 } | 287 } |
313 | 288 |
314 bool operator <(num other) { | 289 bool operator <(num other) { |
315 if (other is !num) throw new ArgumentError(other); | 290 checkNull(other); |
316 return JS('bool', '# < #', this, other); | 291 return JS('bool', '# < #', this, other); |
317 } | 292 } |
318 | 293 |
319 bool operator >(num other) { | 294 bool operator >(num other) { |
320 if (other is !num) throw new ArgumentError(other); | 295 checkNull(other); |
321 return JS('bool', '# > #', this, other); | 296 return JS('bool', '# > #', this, other); |
322 } | 297 } |
323 | 298 |
324 bool operator <=(num other) { | 299 bool operator <=(num other) { |
325 if (other is !num) throw new ArgumentError(other); | 300 checkNull(other); |
326 return JS('bool', '# <= #', this, other); | 301 return JS('bool', '# <= #', this, other); |
327 } | 302 } |
328 | 303 |
329 bool operator >=(num other) { | 304 bool operator >=(num other) { |
330 if (other is !num) throw new ArgumentError(other); | 305 checkNull(other); |
331 return JS('bool', '# >= #', this, other); | 306 return JS('bool', '# >= #', this, other); |
332 } | 307 } |
333 | 308 |
334 Type get runtimeType => num; | 309 // int members. |
335 } | 310 // TODO(jmesserly): all numbers will have these in dynamic dispatch. |
336 | 311 // We can fix by checking it at dispatch time but we'd need to structure them |
337 /** | 312 // differently. |
338 * The interceptor class for [int]s. | |
339 * | |
340 * This class implements double since in JavaScript all numbers are doubles, so | |
341 * while we want to treat `2.0` as an integer for some operations, its | |
342 * interceptor should answer `true` to `is double`. | |
343 */ | |
344 // TODO(jmesserly): for dev_compiler all numbers will get `int` members at | |
345 // runtime for dynamic dispatch. We can fix by checking it at dispatch time. | |
346 // TODO(jmesserly): merge with JSNumber? That would simplify generated code, | |
347 // and dart_runtime's extension mechanism. | |
348 @JsPeerInterface(name: 'Number') | |
349 class JSInt extends JSNumber implements int, double { | |
350 const JSInt(); | |
351 | 313 |
352 bool get isEven => (this & 1) == 0; | 314 bool get isEven => (this & 1) == 0; |
353 | 315 |
354 bool get isOdd => (this & 1) == 1; | 316 bool get isOdd => (this & 1) == 1; |
355 | 317 |
356 int toUnsigned(int width) { | 318 int toUnsigned(int width) { |
357 return this & ((1 << width) - 1); | 319 return this & ((1 << width) - 1); |
358 } | 320 } |
359 | 321 |
360 int toSigned(int width) { | 322 int toSigned(int width) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 // Assumes i is <= 32-bit | 367 // Assumes i is <= 32-bit |
406 static int _spread(int i) { | 368 static int _spread(int i) { |
407 i = _ors(i, _shrs(i, 1)); | 369 i = _ors(i, _shrs(i, 1)); |
408 i = _ors(i, _shrs(i, 2)); | 370 i = _ors(i, _shrs(i, 2)); |
409 i = _ors(i, _shrs(i, 4)); | 371 i = _ors(i, _shrs(i, 4)); |
410 i = _ors(i, _shrs(i, 8)); | 372 i = _ors(i, _shrs(i, 8)); |
411 i = _shru(_ors(i, _shrs(i, 16)), 0); | 373 i = _shru(_ors(i, _shrs(i, 16)), 0); |
412 return i; | 374 return i; |
413 } | 375 } |
414 | 376 |
415 Type get runtimeType => int; | 377 int operator ~() => JS('int', r'(~#) >>> 0', this); |
416 | |
417 int operator ~() => JS('JSUInt32', r'(~#) >>> 0', this); | |
418 } | 378 } |
419 | |
420 class JSDouble extends JSNumber implements double { | |
421 const JSDouble(); | |
422 Type get runtimeType => double; | |
423 } | |
424 | |
425 class JSPositiveInt extends JSInt {} | |
426 class JSUInt32 extends JSPositiveInt {} | |
427 class JSUInt31 extends JSUInt32 {} | |
OLD | NEW |