| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 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 | 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 /** | 5 /** |
| 6 * Generates JS helpers for dart:core. This used to be in a file "core.js". | 6 * Generates JS helpers for dart:core. This used to be in a file "core.js". |
| 7 * Having them in Dart code means we can easily control which are generated. | 7 * Having them in Dart code means we can easily control which are generated. |
| 8 */ | 8 */ |
| 9 // TODO(jmesserly): one idea to make this cleaner: put these as private "native" | 9 // TODO(jmesserly): one idea to make this cleaner: put these as private "native" |
| 10 // methods somewhere in a library that we import. This would be rather elegant | 10 // methods somewhere in a library that we import. This would be rather elegant |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 bool _generatedInherits = false; | 29 bool _generatedInherits = false; |
| 30 bool _generatedDefProp = false; | 30 bool _generatedDefProp = false; |
| 31 | 31 |
| 32 | 32 |
| 33 Map<String, String> _usedOperators; | 33 Map<String, String> _usedOperators; |
| 34 | 34 |
| 35 CodeWriter writer; | 35 CodeWriter writer; |
| 36 | 36 |
| 37 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); | 37 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); |
| 38 | 38 |
| 39 void markCorelibTypeUsed(String typeName) { |
| 40 world.gen.markTypeUsed(world.corelib.types[typeName]); |
| 41 } |
| 42 |
| 39 /** | 43 /** |
| 40 * Generates the special operator method, e.g. $add. | 44 * Generates the special operator method, e.g. $add. |
| 41 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. | 45 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. |
| 42 * Same idea for the other methods. | 46 * Same idea for the other methods. |
| 43 */ | 47 */ |
| 44 void useOperator(String name) { | 48 void useOperator(String name) { |
| 45 if (_usedOperators[name] != null) return; | 49 if (_usedOperators[name] != null) return; |
| 46 | 50 |
| 51 if (name != ':ne' && name != ':eq') { |
| 52 // TODO(jimhug): Only do this once! |
| 53 markCorelibTypeUsed('NoSuchMethodException'); |
| 54 } |
| 55 if (name != ':bit_not' && name != ':negate') { |
| 56 // TODO(jimhug): Only do this once! |
| 57 markCorelibTypeUsed('IllegalArgumentException'); |
| 58 } |
| 59 |
| 47 var code; | 60 var code; |
| 48 switch (name) { | 61 switch (name) { |
| 49 case ':ne': | 62 case ':ne': |
| 50 code = _NE_FUNCTION; | 63 code = _NE_FUNCTION; |
| 51 break; | 64 break; |
| 52 | 65 |
| 53 case ':eq': | 66 case ':eq': |
| 54 ensureDefProp(); | 67 ensureDefProp(); |
| 55 code = _EQ_FUNCTION; | 68 code = _EQ_FUNCTION; |
| 56 break; | 69 break; |
| 57 | 70 |
| 58 case ':bit_not': | 71 case ':bit_not': |
| 59 code = _BIT_NOT_FUNCTION; | 72 code = _BIT_NOT_FUNCTION; |
| 60 break; | 73 break; |
| 61 | 74 |
| 62 case ':negate': | 75 case ':negate': |
| 63 code = _NEGATE_FUNCTION; | 76 code = _NEGATE_FUNCTION; |
| 64 break; | 77 break; |
| 65 | 78 |
| 66 case ':add': | 79 case ':add': |
| 67 code = _ADD_FUNCTION; | 80 code = _ADD_FUNCTION; |
| 68 break; | 81 break; |
| 69 | 82 |
| 70 case ':truncdiv': | 83 case ':truncdiv': |
| 71 useThrow = true; | 84 useThrow = true; |
| 72 // TODO(jimhug): Only do this once! | 85 // TODO(jimhug): Only do this once! |
| 73 world.gen.markTypeUsed( | 86 markCorelibTypeUsed('IntegerDivisionByZeroException'); |
| 74 world.corelib.types['IntegerDivisionByZeroException']); | |
| 75 code = _TRUNCDIV_FUNCTION; | 87 code = _TRUNCDIV_FUNCTION; |
| 76 break; | 88 break; |
| 77 | 89 |
| 78 case ':mod': | 90 case ':mod': |
| 79 code = _MOD_FUNCTION; | 91 code = _MOD_FUNCTION; |
| 80 break; | 92 break; |
| 81 | 93 |
| 82 default: | 94 default: |
| 83 // All of the other helpers are generated the same way | 95 // All of the other helpers are generated the same way |
| 84 var op = TokenKind.rawOperatorFromMethod(name); | 96 var op = TokenKind.rawOperatorFromMethod(name); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 if (useNotNullBool) { | 150 if (useNotNullBool) { |
| 139 useThrow = true; | 151 useThrow = true; |
| 140 w.writeln(_NOTNULL_BOOL_FUNCTION); | 152 w.writeln(_NOTNULL_BOOL_FUNCTION); |
| 141 } | 153 } |
| 142 | 154 |
| 143 if (useThrow) { | 155 if (useThrow) { |
| 144 w.writeln(_THROW_FUNCTION); | 156 w.writeln(_THROW_FUNCTION); |
| 145 } | 157 } |
| 146 | 158 |
| 147 if (useIndex) { | 159 if (useIndex) { |
| 160 markCorelibTypeUsed('NoSuchMethodException'); |
| 148 ensureDefProp(); | 161 ensureDefProp(); |
| 149 w.writeln(options.disableBoundsChecks ? | 162 w.writeln(options.disableBoundsChecks ? |
| 150 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); | 163 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); |
| 151 } | 164 } |
| 152 | 165 |
| 153 if (useSetIndex) { | 166 if (useSetIndex) { |
| 167 markCorelibTypeUsed('NoSuchMethodException'); |
| 154 ensureDefProp(); | 168 ensureDefProp(); |
| 155 w.writeln(options.disableBoundsChecks ? | 169 w.writeln(options.disableBoundsChecks ? |
| 156 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); | 170 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); |
| 157 } | 171 } |
| 158 | 172 |
| 159 if (useIsolates) { | 173 if (useIsolates) { |
| 160 if (useWrap0) { | 174 if (useWrap0) { |
| 161 w.writeln(_WRAP_CALL0_FUNCTION); | 175 w.writeln(_WRAP_CALL0_FUNCTION); |
| 162 } | 176 } |
| 163 if (useWrap1) { | 177 if (useWrap1) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 188 'Object.prototype.\$typeNameOf);'); | 202 'Object.prototype.\$typeNameOf);'); |
| 189 } | 203 } |
| 190 } | 204 } |
| 191 } | 205 } |
| 192 | 206 |
| 193 | 207 |
| 194 /** Snippet for `$ne`. */ | 208 /** Snippet for `$ne`. */ |
| 195 final String _NE_FUNCTION = @""" | 209 final String _NE_FUNCTION = @""" |
| 196 function $ne(x, y) { | 210 function $ne(x, y) { |
| 197 if (x == null) return y != null; | 211 if (x == null) return y != null; |
| 198 return (typeof(x) == 'number' && typeof(y) == 'number') || | 212 return (typeof(x) != 'object') ? x !== y : !x.$eq(y); |
| 199 (typeof(x) == 'boolean' && typeof(y) == 'boolean') || | |
| 200 (typeof(x) == 'string' && typeof(y) == 'string') | |
| 201 ? x != y : !x.$eq(y); | |
| 202 }"""; | 213 }"""; |
| 203 | 214 |
| 204 /** Snippet for `$eq`. */ | 215 /** Snippet for `$eq`. */ |
| 205 final String _EQ_FUNCTION = @""" | 216 final String _EQ_FUNCTION = @""" |
| 206 function $eq(x, y) { | 217 function $eq(x, y) { |
| 207 if (x == null) return y == null; | 218 if (x == null) return y == null; |
| 208 return (typeof(x) == 'number' && typeof(y) == 'number') || | 219 return (typeof(x) != 'object') ? x === y : x.$eq(y); |
| 209 (typeof(x) == 'boolean' && typeof(y) == 'boolean') || | |
| 210 (typeof(x) == 'string' && typeof(y) == 'string') | |
| 211 ? x == y : x.$eq(y); | |
| 212 } | 220 } |
| 213 // TODO(jimhug): Should this or should it not match equals? | 221 // TODO(jimhug): Should this or should it not match equals? |
| 214 $defProp(Object.prototype, '$eq', function(other) { | 222 $defProp(Object.prototype, '$eq', function(other) { |
| 215 return this === other; | 223 return this === other; |
| 216 });"""; | 224 });"""; |
| 217 | 225 |
| 218 /** Snippet for `$bit_not`. */ | 226 /** Snippet for `$bit_not`. */ |
| 219 final String _BIT_NOT_FUNCTION = @""" | 227 final String _BIT_NOT_FUNCTION = @""" |
| 220 function $bit_not(x) { | 228 function $bit_not(x) { |
| 221 return (typeof(x) == 'number') ? ~x : x.$bit_not(); | 229 if (typeof(x) == 'number') return ~x; |
| 230 if (typeof(x) == 'object') return x.$bit_not(); |
| 231 $throw(new NoSuchMethodException(x, "operator ~", [])); |
| 222 }"""; | 232 }"""; |
| 223 | 233 |
| 224 /** Snippet for `$negate`. */ | 234 /** Snippet for `$negate`. */ |
| 225 final String _NEGATE_FUNCTION = @""" | 235 final String _NEGATE_FUNCTION = @""" |
| 226 function $negate(x) { | 236 function $negate(x) { |
| 227 return (typeof(x) == 'number') ? -x : x.$negate(); | 237 if (typeof(x) == 'number') return -x; |
| 238 if (typeof(x) == 'object') return x.$negate(); |
| 239 $throw(new NoSuchMethodException(x, "operator negate", [])); |
| 228 }"""; | 240 }"""; |
| 229 | 241 |
| 230 /** Snippet for `$add`. This relies on JS's string "+" to match Dart's. */ | 242 /** Snippet for `$add`. This relies on JS's string "+" to match Dart's. */ |
| 231 final String _ADD_FUNCTION = @""" | 243 final String _ADD_FUNCTION = @""" |
| 244 function $add$complex(x, y) { |
| 245 if (typeof(x) == 'number') { |
| 246 $throw(new IllegalArgumentException(y)); |
| 247 } else if (typeof(x) == 'string') { |
| 248 var str = (y == null) ? 'null' : y.toString(); |
| 249 if (typeof(str) != 'string') { |
| 250 throw new Error("calling toString() on right hand operand of operator " + |
| 251 "+ did not return a String"); |
| 252 } |
| 253 return x + str; |
| 254 } else if (typeof(x) == 'object') { |
| 255 return x.$add(y); |
| 256 } else { |
| 257 $throw(new NoSuchMethodException(x, "operator +", [y])); |
| 258 } |
| 259 } |
| 260 |
| 232 function $add(x, y) { | 261 function $add(x, y) { |
| 233 return ((typeof(x) == 'number' && typeof(y) == 'number') || | 262 if (typeof(x) == 'number' && typeof(y) == 'number') return x + y; |
| 234 (typeof(x) == 'string')) | 263 return $add$complex(x, y); |
| 235 ? x + y : x.$add(y); | |
| 236 }"""; | 264 }"""; |
| 237 | 265 |
| 238 /** Snippet for `$truncdiv`. This uses `$throw`. */ | 266 /** Snippet for `$truncdiv`. This uses `$throw`. */ |
| 239 final String _TRUNCDIV_FUNCTION = @""" | 267 final String _TRUNCDIV_FUNCTION = @""" |
| 240 function $truncdiv(x, y) { | 268 function $truncdiv(x, y) { |
| 241 if (typeof(x) == 'number' && typeof(y) == 'number') { | 269 if (typeof(x) == 'number') { |
| 242 if (y == 0) $throw(new IntegerDivisionByZeroException()); | 270 if (typeof(y) == 'number') { |
| 243 var tmp = x / y; | 271 if (y == 0) $throw(new IntegerDivisionByZeroException()); |
| 244 return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); | 272 var tmp = x / y; |
| 273 return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); |
| 274 } else { |
| 275 $throw(new IllegalArgumentException(y)); |
| 276 } |
| 277 } else if (typeof(x) == 'object') { |
| 278 return x.$truncdiv(y); |
| 245 } else { | 279 } else { |
| 246 return x.$truncdiv(y); | 280 $throw(new NoSuchMethodException(x, "operator ~/", [y])); |
| 247 } | 281 } |
| 248 }"""; | 282 }"""; |
| 249 | 283 |
| 250 /** Snippet for `$mod`. */ | 284 /** Snippet for `$mod`. */ |
| 251 final String _MOD_FUNCTION = @""" | 285 final String _MOD_FUNCTION = @""" |
| 252 function $mod(x, y) { | 286 function $mod(x, y) { |
| 253 if (typeof(x) == 'number' && typeof(y) == 'number') { | 287 if (typeof(x) == 'number') { |
| 254 var result = x % y; | 288 if (typeof(y) == 'number') { |
| 255 if (result == 0) { | 289 var result = x % y; |
| 256 return 0; // Make sure we don't return -0.0. | 290 if (result == 0) { |
| 257 } else if (result < 0) { | 291 return 0; // Make sure we don't return -0.0. |
| 258 if (y < 0) { | 292 } else if (result < 0) { |
| 259 return result - y; | 293 if (y < 0) { |
| 260 } else { | 294 return result - y; |
| 261 return result + y; | 295 } else { |
| 296 return result + y; |
| 297 } |
| 262 } | 298 } |
| 299 return result; |
| 300 } else { |
| 301 $throw(new IllegalArgumentException(y)); |
| 263 } | 302 } |
| 264 return result; | 303 } else if (typeof(x) == 'object') { |
| 304 return x.$mod(y); |
| 265 } else { | 305 } else { |
| 266 return x.$mod(y); | 306 $throw(new NoSuchMethodException(x, "operator %", [y])); |
| 267 } | 307 } |
| 268 }"""; | 308 }"""; |
| 269 | 309 |
| 270 /** Code snippet for all other operators. */ | 310 /** Code snippet for all other operators. */ |
| 271 String _otherOperator(String jsname, String op) { | 311 String _otherOperator(String jsname, String op) { |
| 272 return """ | 312 return """ |
| 313 function $jsname\$complex(x, y) { |
| 314 if (typeof(x) == 'number') { |
| 315 \$throw(new IllegalArgumentException(y)); |
| 316 } else if (typeof(x) == 'object') { |
| 317 return x.$jsname(y); |
| 318 } else { |
| 319 \$throw(new NoSuchMethodException(x, "operator $op", [y])); |
| 320 } |
| 321 } |
| 273 function $jsname(x, y) { | 322 function $jsname(x, y) { |
| 274 return (typeof(x) == 'number' && typeof(y) == 'number') | 323 if (typeof(x) == 'number' && typeof(y) == 'number') return x $op y; |
| 275 ? x $op y : x.$jsname(y); | 324 return $jsname\$complex(x, y); |
| 276 }"""; | 325 }"""; |
| 277 } | 326 } |
| 278 | 327 |
| 279 /** | 328 /** |
| 280 * Snippet for `$dynamic`. Usage: | 329 * Snippet for `$dynamic`. Usage: |
| 281 * $dynamic(name).SomeTypeName = ... method ...; | 330 * $dynamic(name).SomeTypeName = ... method ...; |
| 282 * $dynamic(name).Object = ... noSuchMethod ...; | 331 * $dynamic(name).Object = ... noSuchMethod ...; |
| 283 */ | 332 */ |
| 284 final String _DYNAMIC_FUNCTION = @""" | 333 final String _DYNAMIC_FUNCTION = @""" |
| 285 function $dynamic(name) { | 334 function $dynamic(name) { |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 // to get the right errors - at least in checked mode (once we have that). | 465 // to get the right errors - at least in checked mode (once we have that). |
| 417 // TODO(jmesserly): do perf analysis, figure out if this is worth it and | 466 // TODO(jmesserly): do perf analysis, figure out if this is worth it and |
| 418 // what the cost of $index $setindex is on all browsers | 467 // what the cost of $index $setindex is on all browsers |
| 419 | 468 |
| 420 // Performance of Object.prototype methods can go down because there are | 469 // Performance of Object.prototype methods can go down because there are |
| 421 // so many of them. Instead, first time we hit it, put it on the derived | 470 // so many of them. Instead, first time we hit it, put it on the derived |
| 422 // prototype. TODO(jmesserly): make this go away by handling index more | 471 // prototype. TODO(jmesserly): make this go away by handling index more |
| 423 // like a normal method. | 472 // like a normal method. |
| 424 final String _INDEX_OPERATORS = @""" | 473 final String _INDEX_OPERATORS = @""" |
| 425 $defProp(Object.prototype, '$index', function(i) { | 474 $defProp(Object.prototype, '$index', function(i) { |
| 426 var proto = Object.getPrototypeOf(this); | 475 $throw(new NoSuchMethodException(this, "operator []", [i])); |
| 427 if (proto !== Object) { | |
| 428 proto.$index = function(i) { return this[i]; } | |
| 429 } | |
| 430 return this[i]; | |
| 431 }); | 476 }); |
| 432 $defProp(Array.prototype, '$index', function(i) { | 477 $defProp(Array.prototype, '$index', function(i) { |
| 433 return this[i]; | 478 return this[i]; |
| 434 }); | 479 }); |
| 435 $defProp(String.prototype, '$index', function(i) { | 480 $defProp(String.prototype, '$index', function(i) { |
| 436 return this[i]; | 481 return this[i]; |
| 437 });"""; | 482 });"""; |
| 438 | 483 |
| 439 final String _CHECKED_INDEX_OPERATORS = @""" | 484 final String _CHECKED_INDEX_OPERATORS = @""" |
| 440 $defProp(Object.prototype, '$index', function(i) { | 485 $defProp(Object.prototype, '$index', function(i) { |
| 441 var proto = Object.getPrototypeOf(this); | 486 $throw(new NoSuchMethodException(this, "operator []", [i])); |
| 442 if (proto !== Object) { | |
| 443 proto.$index = function(i) { return this[i]; } | |
| 444 } | |
| 445 return this[i]; | |
| 446 }); | 487 }); |
| 447 $defProp(Array.prototype, '$index', function(index) { | 488 $defProp(Array.prototype, '$index', function(index) { |
| 448 var i = index | 0; | 489 var i = index | 0; |
| 449 if (i !== index) { | 490 if (i !== index) { |
| 450 throw new IllegalArgumentException('index is not int'); | 491 throw new IllegalArgumentException('index is not int'); |
| 451 } else if (i < 0 || i >= this.length) { | 492 } else if (i < 0 || i >= this.length) { |
| 452 throw new IndexOutOfRangeException(index); | 493 throw new IndexOutOfRangeException(index); |
| 453 } | 494 } |
| 454 return this[i]; | 495 return this[i]; |
| 455 }); | 496 }); |
| 456 $defProp(String.prototype, '$index', function(i) { | 497 $defProp(String.prototype, '$index', function(i) { |
| 457 return this[i]; | 498 return this[i]; |
| 458 });"""; | 499 });"""; |
| 459 | 500 |
| 460 | 501 |
| 461 | 502 |
| 462 /** Snippet for `$setindex` in Object, Array, and String. */ | 503 /** Snippet for `$setindex` in Object, Array, and String. */ |
| 463 final String _SETINDEX_OPERATORS = @""" | 504 final String _SETINDEX_OPERATORS = @""" |
| 464 $defProp(Object.prototype, '$setindex', function(i, value) { | 505 $defProp(Object.prototype, '$setindex', function(i, value) { |
| 465 var proto = Object.getPrototypeOf(this); | 506 $throw(new NoSuchMethodException(this, "operator []=", [i, value])); |
| 466 if (proto !== Object) { | |
| 467 proto.$setindex = function(i, value) { return this[i] = value; } | |
| 468 } | |
| 469 return this[i] = value; | |
| 470 }); | 507 }); |
| 471 $defProp(Array.prototype, '$setindex', | 508 $defProp(Array.prototype, '$setindex', |
| 472 function(i, value) { return this[i] = value; });"""; | 509 function(i, value) { return this[i] = value; });"""; |
| 473 | 510 |
| 474 final String _CHECKED_SETINDEX_OPERATORS = @""" | 511 final String _CHECKED_SETINDEX_OPERATORS = @""" |
| 475 $defProp(Object.prototype, '$setindex', function(i, value) { | 512 $defProp(Object.prototype, '$setindex', function(i, value) { |
| 476 var proto = Object.getPrototypeOf(this); | 513 $throw(new NoSuchMethodException(this, "operator []=", [i, value])); |
| 477 if (proto !== Object) { | |
| 478 proto.$setindex = function(i, value) { return this[i] = value; } | |
| 479 } | |
| 480 return this[i] = value; | |
| 481 }); | 514 }); |
| 482 $defProp(Array.prototype, '$setindex', function(index, value) { | 515 $defProp(Array.prototype, '$setindex', function(index, value) { |
| 483 var i = index | 0; | 516 var i = index | 0; |
| 484 if (i !== index) { | 517 if (i !== index) { |
| 485 throw new IllegalArgumentException('index is not int'); | 518 throw new IllegalArgumentException('index is not int'); |
| 486 } else if (i < 0 || i >= this.length) { | 519 } else if (i < 0 || i >= this.length) { |
| 487 throw new IndexOutOfRangeException(index); | 520 throw new IndexOutOfRangeException(index); |
| 488 } | 521 } |
| 489 return this[i] = value; | 522 return this[i] = value; |
| 490 });"""; | 523 });"""; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 525 | 558 |
| 526 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ | 559 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ |
| 527 final String _EMPTY_WRAP_CALL1_FUNCTION = | 560 final String _EMPTY_WRAP_CALL1_FUNCTION = |
| 528 @"function $wrap_call$1(fn) { return fn; }"; | 561 @"function $wrap_call$1(fn) { return fn; }"; |
| 529 | 562 |
| 530 /** Snippet that initializes the isolates state. */ | 563 /** Snippet that initializes the isolates state. */ |
| 531 final String _ISOLATE_INIT_CODE = @""" | 564 final String _ISOLATE_INIT_CODE = @""" |
| 532 var $globalThis = this; | 565 var $globalThis = this; |
| 533 var $globals = null; | 566 var $globals = null; |
| 534 var $globalState = null;"""; | 567 var $globalState = null;"""; |
| OLD | NEW |