Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Side by Side Diff: tool/input_sdk/private/js_number.dart

Issue 1348453004: fix some errors in our SDK, mostly around numbers (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tool/input_sdk/private/js_helper.dart ('k') | tool/input_sdk/private/js_string.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 {}
OLDNEW
« no previous file with comments | « tool/input_sdk/private/js_helper.dart ('k') | tool/input_sdk/private/js_string.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698