Chromium Code Reviews| 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); | 16 if (b is! num) throw new ArgumentError(b); |
| 21 if (this < b) { | 17 if (this < b) { |
| 22 return -1; | 18 return -1; |
| 23 } else if (this > b) { | 19 } else if (this > b) { |
| 24 return 1; | 20 return 1; |
| 25 } else if (this == b) { | 21 } else if (this == b) { |
| 26 if (this == 0) { | 22 if (this == 0) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 44 | 40 |
| 45 bool get isNaN => JS('bool', r'isNaN(#)', this); | 41 bool get isNaN => JS('bool', r'isNaN(#)', this); |
| 46 | 42 |
| 47 bool get isInfinite { | 43 bool get isInfinite { |
| 48 return JS('bool', r'# == Infinity', this) | 44 return JS('bool', r'# == Infinity', this) |
| 49 || JS('bool', r'# == -Infinity', this); | 45 || JS('bool', r'# == -Infinity', this); |
| 50 } | 46 } |
| 51 | 47 |
| 52 bool get isFinite => JS('bool', r'isFinite(#)', this); | 48 bool get isFinite => JS('bool', r'isFinite(#)', this); |
| 53 | 49 |
| 54 num remainder(num b) { | 50 JSNumber remainder(num b) { |
| 55 checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. | 51 checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. |
| 56 if (b is! num) throw new ArgumentError(b); | 52 if (b is! num) throw new ArgumentError(b); |
| 57 return JS('num', r'# % #', this, b); | 53 return JS('num', r'# % #', this, b); |
| 58 } | 54 } |
| 59 | 55 |
| 60 num abs() => JS('num', r'Math.abs(#)', this); | 56 JSNumber abs() => JS('num', r'Math.abs(#)', this); |
| 61 | 57 |
| 62 num get sign => this > 0 ? 1 : this < 0 ? -1 : this; | 58 JSNumber get sign => this > 0 ? 1 : this < 0 ? -1 : this; |
| 63 | 59 |
| 64 static const int _MIN_INT32 = -0x80000000; | 60 static const int _MIN_INT32 = -0x80000000; |
| 65 static const int _MAX_INT32 = 0x7FFFFFFF; | 61 static const int _MAX_INT32 = 0x7FFFFFFF; |
| 66 | 62 |
| 67 int toInt() { | 63 int toInt() { |
| 68 if (this >= _MIN_INT32 && this <= _MAX_INT32) { | 64 if (this >= _MIN_INT32 && this <= _MAX_INT32) { |
| 69 return JS('int', '# | 0', this); | 65 return JS('int', '# | 0', this); |
| 70 } | 66 } |
| 71 if (JS('bool', r'isFinite(#)', this)) { | 67 if (JS('bool', r'isFinite(#)', this)) { |
| 72 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. | 68 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 98 if (lowerLimit is! num) throw new ArgumentError(lowerLimit); | 94 if (lowerLimit is! num) throw new ArgumentError(lowerLimit); |
| 99 if (upperLimit is! num) throw new ArgumentError(upperLimit); | 95 if (upperLimit is! num) throw new ArgumentError(upperLimit); |
| 100 if (lowerLimit.compareTo(upperLimit) > 0) { | 96 if (lowerLimit.compareTo(upperLimit) > 0) { |
| 101 throw new ArgumentError(lowerLimit); | 97 throw new ArgumentError(lowerLimit); |
| 102 } | 98 } |
| 103 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | 99 if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
| 104 if (this.compareTo(upperLimit) > 0) return upperLimit; | 100 if (this.compareTo(upperLimit) > 0) return upperLimit; |
| 105 return this; | 101 return this; |
| 106 } | 102 } |
| 107 | 103 |
| 108 // The return type is intentionally omitted to avoid type checker warnings | 104 double toDouble() => this; |
| 109 // from assigning JSNumber to double. | |
| 110 toDouble() => this; | |
| 111 | 105 |
| 112 String toStringAsFixed(int fractionDigits) { | 106 String toStringAsFixed(int fractionDigits) { |
| 113 checkInt(fractionDigits); | 107 checkInt(fractionDigits); |
| 114 if (fractionDigits < 0 || fractionDigits > 20) { | 108 if (fractionDigits < 0 || fractionDigits > 20) { |
| 115 throw new RangeError(fractionDigits); | 109 throw new RangeError(fractionDigits); |
| 116 } | 110 } |
| 117 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); | 111 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); |
| 118 if (this == 0 && isNegative) return "-$result"; | 112 if (this == 0 && isNegative) return "-$result"; |
| 119 return result; | 113 return result; |
| 120 } | 114 } |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 String toString() { | 173 String toString() { |
| 180 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { | 174 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { |
| 181 return '-0.0'; | 175 return '-0.0'; |
| 182 } else { | 176 } else { |
| 183 return JS('String', r'"" + (#)', this); | 177 return JS('String', r'"" + (#)', this); |
| 184 } | 178 } |
| 185 } | 179 } |
| 186 | 180 |
| 187 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | 181 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); |
| 188 | 182 |
| 189 num operator -() => JS('num', r'-#', this); | 183 JSNumber operator -() => JS('num', r'-#', this); |
| 190 | 184 |
| 191 num operator +(num other) { | 185 JSNumber operator +(num other) { |
| 192 if (other is !num) throw new ArgumentError(other); | 186 if (other is !num) throw new ArgumentError(other); |
| 193 return JS('num', '# + #', this, other); | 187 return JS('num', '# + #', this, other); |
| 194 } | 188 } |
| 195 | 189 |
| 196 num operator -(num other) { | 190 JSNumber operator -(num other) { |
| 197 if (other is !num) throw new ArgumentError(other); | 191 if (other is !num) throw new ArgumentError(other); |
| 198 return JS('num', '# - #', this, other); | 192 return JS('num', '# - #', this, other); |
| 199 } | 193 } |
| 200 | 194 |
| 201 double operator /(num other) { | 195 double operator /(num other) { |
| 202 if (other is !num) throw new ArgumentError(other); | 196 if (other is !num) throw new ArgumentError(other); |
| 203 return JS('double', '# / #', this, other); | 197 return JS('double', '# / #', this, other); |
| 204 } | 198 } |
| 205 | 199 |
| 206 num operator *(num other) { | 200 JSNumber operator *(num other) { |
| 207 if (other is !num) throw new ArgumentError(other); | 201 if (other is !num) throw new ArgumentError(other); |
| 208 return JS('num', '# * #', this, other); | 202 return JS('num', '# * #', this, other); |
| 209 } | 203 } |
| 210 | 204 |
| 211 num operator %(num other) { | 205 JSNumber operator %(num other) { |
| 212 if (other is !num) throw new ArgumentError(other); | 206 if (other is !num) throw new ArgumentError(other); |
| 213 // Euclidean Modulo. | 207 // Euclidean Modulo. |
| 214 num result = JS('num', r'# % #', this, other); | 208 num result = JS('num', r'# % #', this, other); |
| 215 if (result == 0) return 0; // Make sure we don't return -0.0. | 209 if (result == 0) return (0 as JSNumber); // Make sure we don't return -0.0. |
| 216 if (result > 0) return result; | 210 if (result > 0) return result; |
| 217 if (JS('num', '#', other) < 0) { | 211 if (JS('num', '#', other) < 0) { |
| 218 return result - JS('num', '#', other); | 212 return result - JS('num', '#', other); |
| 219 } else { | 213 } else { |
| 220 return result + JS('num', '#', other); | 214 return result + JS('num', '#', other); |
| 221 } | 215 } |
| 222 } | 216 } |
| 223 | 217 |
| 224 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | 218 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); |
| 225 | 219 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 241 int _tdivSlow(num other) { | 235 int _tdivSlow(num other) { |
| 242 if (other is !num) throw new ArgumentError(other); | 236 if (other is !num) throw new ArgumentError(other); |
| 243 return (JS('num', r'# / #', this, other)).toInt(); | 237 return (JS('num', r'# / #', this, other)).toInt(); |
| 244 } | 238 } |
| 245 | 239 |
| 246 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | 240 // TODO(ngeoffray): Move the bit operations below to [JSInt] and |
| 247 // make them take an int. Because this will make operations slower, | 241 // 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 | 242 // we define these methods on number for now but we need to decide |
| 249 // the grain at which we do the type checks. | 243 // the grain at which we do the type checks. |
| 250 | 244 |
| 251 num operator <<(num other) { | 245 int operator <<(num other) { |
| 252 if (other is !num) throw new ArgumentError(other); | 246 if (other is !num) throw new ArgumentError(other); |
|
Leaf
2015/09/18 22:37:49
I don't know how much we want to assume DDC strong
Jennifer Messerly
2015/09/18 22:42:27
ah, good point. Interesting that isn't a hint (dea
Jennifer Messerly
2015/09/18 23:53:06
fixed kind of ... we still need the null checks th
| |
| 253 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | 247 if (JS('num', '#', other) < 0) throw new ArgumentError(other); |
| 254 return _shlPositive(other); | 248 return _shlPositive(other); |
| 255 } | 249 } |
| 256 | 250 |
| 257 num _shlPositive(num other) { | 251 int _shlPositive(num other) { |
| 258 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | 252 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting |
| 259 // by 33 is hence equivalent to a shift by 1. | 253 // by 33 is hence equivalent to a shift by 1. |
| 260 return JS('bool', r'# > 31', other) | 254 return JS('bool', r'# > 31', other) |
| 261 ? 0 | 255 ? 0 |
| 262 : JS('JSUInt32', r'(# << #) >>> 0', this, other); | 256 : JS('int', r'(# << #) >>> 0', this, other); |
| 263 } | 257 } |
| 264 | 258 |
| 265 num operator >>(num other) { | 259 int operator >>(num other) { |
| 260 // TODO(jmesserly): what's this from? Delete? | |
| 266 if (false) _shrReceiverPositive(other); | 261 if (false) _shrReceiverPositive(other); |
| 267 if (other is !num) throw new ArgumentError(other); | 262 if (other is !num) throw new ArgumentError(other); |
| 268 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | 263 if (JS('num', '#', other) < 0) throw new ArgumentError(other); |
| 269 return _shrOtherPositive(other); | 264 return _shrOtherPositive(other); |
| 270 } | 265 } |
| 271 | 266 |
| 272 num _shrOtherPositive(num other) { | 267 int _shrOtherPositive(num other) { |
| 273 return JS('num', '#', this) > 0 | 268 return JS('num', '#', this) > 0 |
| 274 ? _shrBothPositive(other) | 269 ? _shrBothPositive(other) |
| 275 // For negative numbers we just clamp the shift-by amount. | 270 // For negative numbers we just clamp the shift-by amount. |
| 276 // `this` could be negative but not have its 31st bit set. | 271 // `this` could be negative but not have its 31st bit set. |
| 277 // The ">>" would then shift in 0s instead of 1s. Therefore | 272 // The ">>" would then shift in 0s instead of 1s. Therefore |
| 278 // we cannot simply return 0xFFFFFFFF. | 273 // we cannot simply return 0xFFFFFFFF. |
| 279 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | 274 : JS('int', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); |
| 280 } | 275 } |
| 281 | 276 |
| 282 num _shrReceiverPositive(num other) { | 277 int _shrReceiverPositive(num other) { |
| 283 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | 278 if (JS('num', '#', other) < 0) throw new ArgumentError(other); |
| 284 return _shrBothPositive(other); | 279 return _shrBothPositive(other); |
| 285 } | 280 } |
| 286 | 281 |
| 287 num _shrBothPositive(num other) { | 282 int _shrBothPositive(num other) { |
| 288 return JS('bool', r'# > 31', other) | 283 return JS('bool', r'# > 31', other) |
| 289 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | 284 // 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 | 285 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the |
| 291 // computation when that happens. | 286 // computation when that happens. |
| 292 ? 0 | 287 ? 0 |
| 293 // Given that `this` is positive we must not use '>>'. Otherwise a | 288 // 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 | 289 // number that has the 31st bit set would be treated as negative and |
| 295 // shift in ones. | 290 // shift in ones. |
| 296 : JS('JSUInt32', r'# >>> #', this, other); | 291 : JS('int', r'# >>> #', this, other); |
| 297 } | 292 } |
| 298 | 293 |
| 299 num operator &(num other) { | 294 int operator &(num other) { |
| 300 if (other is !num) throw new ArgumentError(other); | 295 if (other is !num) throw new ArgumentError(other); |
| 301 return JS('JSUInt32', r'(# & #) >>> 0', this, other); | 296 return JS('int', r'(# & #) >>> 0', this, other); |
| 302 } | 297 } |
| 303 | 298 |
| 304 num operator |(num other) { | 299 int operator |(num other) { |
| 305 if (other is !num) throw new ArgumentError(other); | 300 if (other is !num) throw new ArgumentError(other); |
| 306 return JS('JSUInt32', r'(# | #) >>> 0', this, other); | 301 return JS('int', r'(# | #) >>> 0', this, other); |
| 307 } | 302 } |
| 308 | 303 |
| 309 num operator ^(num other) { | 304 int operator ^(num other) { |
| 310 if (other is !num) throw new ArgumentError(other); | 305 if (other is !num) throw new ArgumentError(other); |
| 311 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); | 306 return JS('int', r'(# ^ #) >>> 0', this, other); |
| 312 } | 307 } |
| 313 | 308 |
| 314 bool operator <(num other) { | 309 bool operator <(num other) { |
| 315 if (other is !num) throw new ArgumentError(other); | 310 if (other is !num) throw new ArgumentError(other); |
| 316 return JS('bool', '# < #', this, other); | 311 return JS('bool', '# < #', this, other); |
| 317 } | 312 } |
| 318 | 313 |
| 319 bool operator >(num other) { | 314 bool operator >(num other) { |
| 320 if (other is !num) throw new ArgumentError(other); | 315 if (other is !num) throw new ArgumentError(other); |
| 321 return JS('bool', '# > #', this, other); | 316 return JS('bool', '# > #', this, other); |
| 322 } | 317 } |
| 323 | 318 |
| 324 bool operator <=(num other) { | 319 bool operator <=(num other) { |
| 325 if (other is !num) throw new ArgumentError(other); | 320 if (other is !num) throw new ArgumentError(other); |
| 326 return JS('bool', '# <= #', this, other); | 321 return JS('bool', '# <= #', this, other); |
| 327 } | 322 } |
| 328 | 323 |
| 329 bool operator >=(num other) { | 324 bool operator >=(num other) { |
| 330 if (other is !num) throw new ArgumentError(other); | 325 if (other is !num) throw new ArgumentError(other); |
| 331 return JS('bool', '# >= #', this, other); | 326 return JS('bool', '# >= #', this, other); |
| 332 } | 327 } |
| 333 | 328 |
| 334 Type get runtimeType => num; | 329 // int members. |
| 335 } | 330 // TODO(jmesserly): all numbers will have these in dynamic dispatch. |
| 336 | 331 // We can fix by checking it at dispatch time but we'd need to structure them |
| 337 /** | 332 // 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 | 333 |
| 352 bool get isEven => (this & 1) == 0; | 334 bool get isEven => (this & 1) == 0; |
| 353 | 335 |
| 354 bool get isOdd => (this & 1) == 1; | 336 bool get isOdd => (this & 1) == 1; |
| 355 | 337 |
| 356 int toUnsigned(int width) { | 338 int toUnsigned(int width) { |
| 357 return this & ((1 << width) - 1); | 339 return this & ((1 << width) - 1); |
| 358 } | 340 } |
| 359 | 341 |
| 360 int toSigned(int width) { | 342 int toSigned(int width) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 405 // Assumes i is <= 32-bit | 387 // Assumes i is <= 32-bit |
| 406 static int _spread(int i) { | 388 static int _spread(int i) { |
| 407 i = _ors(i, _shrs(i, 1)); | 389 i = _ors(i, _shrs(i, 1)); |
| 408 i = _ors(i, _shrs(i, 2)); | 390 i = _ors(i, _shrs(i, 2)); |
| 409 i = _ors(i, _shrs(i, 4)); | 391 i = _ors(i, _shrs(i, 4)); |
| 410 i = _ors(i, _shrs(i, 8)); | 392 i = _ors(i, _shrs(i, 8)); |
| 411 i = _shru(_ors(i, _shrs(i, 16)), 0); | 393 i = _shru(_ors(i, _shrs(i, 16)), 0); |
| 412 return i; | 394 return i; |
| 413 } | 395 } |
| 414 | 396 |
| 415 Type get runtimeType => int; | 397 int operator ~() => JS('int', r'(~#) >>> 0', this); |
| 416 | |
| 417 int operator ~() => JS('JSUInt32', r'(~#) >>> 0', this); | |
| 418 } | 398 } |
| 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 |