Chromium Code Reviews| 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 26 matching lines...) Expand all Loading... | |
| 37 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); | 37 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); |
| 38 | 38 |
| 39 /** | 39 /** |
| 40 * Generates the special operator method, e.g. $add. | 40 * 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. | 41 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. |
| 42 * Same idea for the other methods. | 42 * Same idea for the other methods. |
| 43 */ | 43 */ |
| 44 void useOperator(String name) { | 44 void useOperator(String name) { |
| 45 if (_usedOperators[name] != null) return; | 45 if (_usedOperators[name] != null) return; |
| 46 | 46 |
| 47 if (name != ':ne' && name != ':eq') { | |
| 48 // TODO(jimhug): Only do this once! | |
| 49 world.gen.markTypeUsed(world.corelib.types['NoSuchMethodException']); | |
|
kasperl
2012/02/15 11:54:49
How about adding a helper for this statement?
floitsch
2012/02/15 13:11:43
Done.
| |
| 50 } | |
| 51 if (name != ':bit_not' && name != ':negate') { | |
| 52 // TODO(jimhug): Only do this once! | |
| 53 world.gen.markTypeUsed(world.corelib.types['IllegalArgumentException']); | |
| 54 } | |
| 55 | |
| 47 var code; | 56 var code; |
| 48 switch (name) { | 57 switch (name) { |
| 49 case ':ne': | 58 case ':ne': |
| 50 code = _NE_FUNCTION; | 59 code = _NE_FUNCTION; |
| 51 break; | 60 break; |
| 52 | 61 |
| 53 case ':eq': | 62 case ':eq': |
| 54 ensureDefProp(); | 63 ensureDefProp(); |
| 55 code = _EQ_FUNCTION; | 64 code = _EQ_FUNCTION; |
| 56 break; | 65 break; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 138 if (useNotNullBool) { | 147 if (useNotNullBool) { |
| 139 useThrow = true; | 148 useThrow = true; |
| 140 w.writeln(_NOTNULL_BOOL_FUNCTION); | 149 w.writeln(_NOTNULL_BOOL_FUNCTION); |
| 141 } | 150 } |
| 142 | 151 |
| 143 if (useThrow) { | 152 if (useThrow) { |
| 144 w.writeln(_THROW_FUNCTION); | 153 w.writeln(_THROW_FUNCTION); |
| 145 } | 154 } |
| 146 | 155 |
| 147 if (useIndex) { | 156 if (useIndex) { |
| 157 world.gen.markTypeUsed(world.corelib.types['NoSuchMethodException']); | |
| 148 ensureDefProp(); | 158 ensureDefProp(); |
| 149 w.writeln(options.disableBoundsChecks ? | 159 w.writeln(options.disableBoundsChecks ? |
| 150 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); | 160 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); |
| 151 } | 161 } |
| 152 | 162 |
| 153 if (useSetIndex) { | 163 if (useSetIndex) { |
| 164 world.gen.markTypeUsed(world.corelib.types['NoSuchMethodException']); | |
| 154 ensureDefProp(); | 165 ensureDefProp(); |
| 155 w.writeln(options.disableBoundsChecks ? | 166 w.writeln(options.disableBoundsChecks ? |
| 156 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); | 167 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); |
| 157 } | 168 } |
| 158 | 169 |
| 159 if (useIsolates) { | 170 if (useIsolates) { |
| 160 if (useWrap0) { | 171 if (useWrap0) { |
| 161 w.writeln(_WRAP_CALL0_FUNCTION); | 172 w.writeln(_WRAP_CALL0_FUNCTION); |
| 162 } | 173 } |
| 163 if (useWrap1) { | 174 if (useWrap1) { |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 188 'Object.prototype.\$typeNameOf);'); | 199 'Object.prototype.\$typeNameOf);'); |
| 189 } | 200 } |
| 190 } | 201 } |
| 191 } | 202 } |
| 192 | 203 |
| 193 | 204 |
| 194 /** Snippet for `$ne`. */ | 205 /** Snippet for `$ne`. */ |
| 195 final String _NE_FUNCTION = @""" | 206 final String _NE_FUNCTION = @""" |
| 196 function $ne(x, y) { | 207 function $ne(x, y) { |
| 197 if (x == null) return y != null; | 208 if (x == null) return y != null; |
| 198 return (typeof(x) == 'number' && typeof(y) == 'number') || | 209 return (typeof(x) != 'object') ? x !== y : !x.$eq(y); |
|
kasperl
2012/02/15 11:54:49
This changes things for functions, right? I guess
floitsch
2012/02/15 13:11:43
AFAICS functions don't override the $eq method and
| |
| 199 (typeof(x) == 'boolean' && typeof(y) == 'boolean') || | |
| 200 (typeof(x) == 'string' && typeof(y) == 'string') | |
| 201 ? x != y : !x.$eq(y); | |
| 202 }"""; | 210 }"""; |
| 203 | 211 |
| 204 /** Snippet for `$eq`. */ | 212 /** Snippet for `$eq`. */ |
| 205 final String _EQ_FUNCTION = @""" | 213 final String _EQ_FUNCTION = @""" |
| 206 function $eq(x, y) { | 214 function $eq(x, y) { |
| 207 if (x == null) return y == null; | 215 if (x == null) return y == null; |
| 208 return (typeof(x) == 'number' && typeof(y) == 'number') || | 216 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 } | 217 } |
| 213 // TODO(jimhug): Should this or should it not match equals? | 218 // TODO(jimhug): Should this or should it not match equals? |
| 214 $defProp(Object.prototype, '$eq', function(other) { | 219 $defProp(Object.prototype, '$eq', function(other) { |
| 215 return this === other; | 220 return this === other; |
| 216 });"""; | 221 });"""; |
| 217 | 222 |
| 218 /** Snippet for `$bit_not`. */ | 223 /** Snippet for `$bit_not`. */ |
| 219 final String _BIT_NOT_FUNCTION = @""" | 224 final String _BIT_NOT_FUNCTION = @""" |
| 220 function $bit_not(x) { | 225 function $bit_not(x) { |
| 221 return (typeof(x) == 'number') ? ~x : x.$bit_not(); | 226 if (typeof(x) == 'number') return ~x; |
| 227 if (typeof(x) == 'object') return x.$bit_not(); | |
| 228 $throw(new NoSuchMethodException(x, "operator ~", [])); | |
| 222 }"""; | 229 }"""; |
| 223 | 230 |
| 224 /** Snippet for `$negate`. */ | 231 /** Snippet for `$negate`. */ |
| 225 final String _NEGATE_FUNCTION = @""" | 232 final String _NEGATE_FUNCTION = @""" |
| 226 function $negate(x) { | 233 function $negate(x) { |
| 227 return (typeof(x) == 'number') ? -x : x.$negate(); | 234 if (typeof(x) == 'number') return -x; |
| 235 if (typeof(x) == 'object') return x.$negate(); | |
| 236 $throw(new NoSuchMethodException(x, "operator negate", [])); | |
| 228 }"""; | 237 }"""; |
| 229 | 238 |
| 230 /** Snippet for `$add`. This relies on JS's string "+" to match Dart's. */ | 239 /** Snippet for `$add`. This relies on JS's string "+" to match Dart's. */ |
| 231 final String _ADD_FUNCTION = @""" | 240 final String _ADD_FUNCTION = @""" |
| 232 function $add(x, y) { | 241 function $add(x, y) { |
| 233 return ((typeof(x) == 'number' && typeof(y) == 'number') || | 242 if (typeof(x) == 'number') { |
|
kasperl
2012/02/15 11:54:49
It's hard to understand what this does for perform
floitsch
2012/02/15 13:11:43
Done.
| |
| 234 (typeof(x) == 'string')) | 243 if (typeof(y) == 'number') return x + y; |
| 235 ? x + y : x.$add(y); | 244 $throw(new IllegalArgumentException(y)); |
| 245 } else if (typeof(x) == 'string') { | |
| 246 var str = (y == null) ? 'null' : y.toString(); | |
| 247 if (typeof(str) != 'string') { | |
| 248 throw new Error("calling toString() on right hand operand of operator " + | |
| 249 "+ did not return a String"); | |
| 250 } | |
| 251 return x + str; | |
| 252 } else if (typeof(x) == 'object') { | |
| 253 return x.$add(y); | |
| 254 } else { | |
| 255 $throw(new NoSuchMethodException(x, "operator +", [y])); | |
| 256 } | |
| 236 }"""; | 257 }"""; |
| 237 | 258 |
| 238 /** Snippet for `$truncdiv`. This uses `$throw`. */ | 259 /** Snippet for `$truncdiv`. This uses `$throw`. */ |
| 239 final String _TRUNCDIV_FUNCTION = @""" | 260 final String _TRUNCDIV_FUNCTION = @""" |
| 240 function $truncdiv(x, y) { | 261 function $truncdiv(x, y) { |
| 241 if (typeof(x) == 'number' && typeof(y) == 'number') { | 262 if (typeof(x) == 'number') { |
| 242 if (y == 0) $throw(new IntegerDivisionByZeroException()); | 263 if (typeof(y) == 'number') { |
|
kasperl
2012/02/15 11:54:49
if ( -> if (
floitsch
2012/02/15 13:11:43
Done.
| |
| 243 var tmp = x / y; | 264 if (y == 0) $throw(new IntegerDivisionByZeroException()); |
| 244 return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); | 265 var tmp = x / y; |
| 266 return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); | |
| 267 } else { | |
| 268 $throw(new IllegalArgumentException(y)); | |
| 269 } | |
| 270 } else if (typeof(x) == 'object') { | |
| 271 return x.$truncdiv(y); | |
| 245 } else { | 272 } else { |
| 246 return x.$truncdiv(y); | 273 $throw(new NoSuchMethodException(x, "operator ~/", [y])); |
| 247 } | 274 } |
| 248 }"""; | 275 }"""; |
| 249 | 276 |
| 250 /** Snippet for `$mod`. */ | 277 /** Snippet for `$mod`. */ |
| 251 final String _MOD_FUNCTION = @""" | 278 final String _MOD_FUNCTION = @""" |
| 252 function $mod(x, y) { | 279 function $mod(x, y) { |
| 253 if (typeof(x) == 'number' && typeof(y) == 'number') { | 280 if (typeof(x) == 'number') { |
| 254 var result = x % y; | 281 if (typeof(y) == 'number') { |
| 255 if (result == 0) { | 282 var result = x % y; |
| 256 return 0; // Make sure we don't return -0.0. | 283 if (result == 0) { |
| 257 } else if (result < 0) { | 284 return 0; // Make sure we don't return -0.0. |
| 258 if (y < 0) { | 285 } else if (result < 0) { |
| 259 return result - y; | 286 if (y < 0) { |
| 260 } else { | 287 return result - y; |
| 261 return result + y; | 288 } else { |
| 289 return result + y; | |
| 290 } | |
| 262 } | 291 } |
| 292 return result; | |
| 293 } else { | |
| 294 $throw(new IllegalArgumentException(y)); | |
| 263 } | 295 } |
| 264 return result; | 296 } else if (typeof(x) == 'object') { |
| 297 return x.$mod(y); | |
| 265 } else { | 298 } else { |
| 266 return x.$mod(y); | 299 $throw(new NoSuchMethodException(x, "operator %", [y])); |
| 267 } | 300 }h |
| 268 }"""; | 301 }"""; |
| 269 | 302 |
| 270 /** Code snippet for all other operators. */ | 303 /** Code snippet for all other operators. */ |
| 271 String _otherOperator(String jsname, String op) { | 304 String _otherOperator(String jsname, String op) { |
| 272 return """ | 305 return """ |
| 273 function $jsname(x, y) { | 306 function $jsname(x, y) { |
| 274 return (typeof(x) == 'number' && typeof(y) == 'number') | 307 if (typeof(x) == 'number') { |
| 275 ? x $op y : x.$jsname(y); | 308 if (typeof(y) == 'number') { |
| 309 return x $op y; | |
| 310 } else { | |
| 311 \$throw(new IllegalArgumentException(y)); | |
| 312 } | |
| 313 } else if (typeof(x) == 'object') { | |
| 314 return x.$jsname(y); | |
| 315 } else { | |
| 316 \$throw(new NoSuchMethodException(x, "operator $op", [y])); | |
| 317 } | |
| 276 }"""; | 318 }"""; |
| 277 } | 319 } |
| 278 | 320 |
| 279 /** | 321 /** |
| 280 * Snippet for `$dynamic`. Usage: | 322 * Snippet for `$dynamic`. Usage: |
| 281 * $dynamic(name).SomeTypeName = ... method ...; | 323 * $dynamic(name).SomeTypeName = ... method ...; |
| 282 * $dynamic(name).Object = ... noSuchMethod ...; | 324 * $dynamic(name).Object = ... noSuchMethod ...; |
| 283 */ | 325 */ |
| 284 final String _DYNAMIC_FUNCTION = @""" | 326 final String _DYNAMIC_FUNCTION = @""" |
| 285 function $dynamic(name) { | 327 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). | 458 // 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 | 459 // TODO(jmesserly): do perf analysis, figure out if this is worth it and |
| 418 // what the cost of $index $setindex is on all browsers | 460 // what the cost of $index $setindex is on all browsers |
| 419 | 461 |
| 420 // Performance of Object.prototype methods can go down because there are | 462 // 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 | 463 // 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 | 464 // prototype. TODO(jmesserly): make this go away by handling index more |
| 423 // like a normal method. | 465 // like a normal method. |
| 424 final String _INDEX_OPERATORS = @""" | 466 final String _INDEX_OPERATORS = @""" |
| 425 $defProp(Object.prototype, '$index', function(i) { | 467 $defProp(Object.prototype, '$index', function(i) { |
| 426 var proto = Object.getPrototypeOf(this); | 468 $throw(new NoSuchMethodException(this, "operator []", [i])); |
| 427 if (proto !== Object) { | |
| 428 proto.$index = function(i) { return this[i]; } | |
| 429 } | |
| 430 return this[i]; | |
| 431 }); | 469 }); |
| 432 $defProp(Array.prototype, '$index', function(i) { | 470 $defProp(Array.prototype, '$index', function(i) { |
| 433 return this[i]; | 471 return this[i]; |
| 434 }); | 472 }); |
| 435 $defProp(String.prototype, '$index', function(i) { | 473 $defProp(String.prototype, '$index', function(i) { |
| 436 return this[i]; | 474 return this[i]; |
| 437 });"""; | 475 });"""; |
| 438 | 476 |
| 439 final String _CHECKED_INDEX_OPERATORS = @""" | 477 final String _CHECKED_INDEX_OPERATORS = @""" |
| 440 $defProp(Object.prototype, '$index', function(i) { | 478 $defProp(Object.prototype, '$index', function(i) { |
| 441 var proto = Object.getPrototypeOf(this); | 479 $throw(new NoSuchMethodException(this, "operator []", [i])); |
| 442 if (proto !== Object) { | |
| 443 proto.$index = function(i) { return this[i]; } | |
| 444 } | |
| 445 return this[i]; | |
| 446 }); | 480 }); |
| 447 $defProp(Array.prototype, '$index', function(index) { | 481 $defProp(Array.prototype, '$index', function(index) { |
| 448 var i = index | 0; | 482 var i = index | 0; |
| 449 if (i !== index) { | 483 if (i !== index) { |
| 450 throw new IllegalArgumentException('index is not int'); | 484 throw new IllegalArgumentException('index is not int'); |
| 451 } else if (i < 0 || i >= this.length) { | 485 } else if (i < 0 || i >= this.length) { |
| 452 throw new IndexOutOfRangeException(index); | 486 throw new IndexOutOfRangeException(index); |
| 453 } | 487 } |
| 454 return this[i]; | 488 return this[i]; |
| 455 }); | 489 }); |
| 456 $defProp(String.prototype, '$index', function(i) { | 490 $defProp(String.prototype, '$index', function(i) { |
| 457 return this[i]; | 491 return this[i]; |
| 458 });"""; | 492 });"""; |
| 459 | 493 |
| 460 | 494 |
| 461 | 495 |
| 462 /** Snippet for `$setindex` in Object, Array, and String. */ | 496 /** Snippet for `$setindex` in Object, Array, and String. */ |
| 463 final String _SETINDEX_OPERATORS = @""" | 497 final String _SETINDEX_OPERATORS = @""" |
| 464 $defProp(Object.prototype, '$setindex', function(i, value) { | 498 $defProp(Object.prototype, '$setindex', function(i, value) { |
| 465 var proto = Object.getPrototypeOf(this); | 499 $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 }); | 500 }); |
| 471 $defProp(Array.prototype, '$setindex', | 501 $defProp(Array.prototype, '$setindex', |
| 472 function(i, value) { return this[i] = value; });"""; | 502 function(i, value) { return this[i] = value; });"""; |
| 473 | 503 |
| 474 final String _CHECKED_SETINDEX_OPERATORS = @""" | 504 final String _CHECKED_SETINDEX_OPERATORS = @""" |
| 475 $defProp(Object.prototype, '$setindex', function(i, value) { | 505 $defProp(Object.prototype, '$setindex', function(i, value) { |
| 476 var proto = Object.getPrototypeOf(this); | 506 $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 }); | 507 }); |
| 482 $defProp(Array.prototype, '$setindex', function(index, value) { | 508 $defProp(Array.prototype, '$setindex', function(index, value) { |
| 483 var i = index | 0; | 509 var i = index | 0; |
| 484 if (i !== index) { | 510 if (i !== index) { |
| 485 throw new IllegalArgumentException('index is not int'); | 511 throw new IllegalArgumentException('index is not int'); |
| 486 } else if (i < 0 || i >= this.length) { | 512 } else if (i < 0 || i >= this.length) { |
| 487 throw new IndexOutOfRangeException(index); | 513 throw new IndexOutOfRangeException(index); |
| 488 } | 514 } |
| 489 return this[i] = value; | 515 return this[i] = value; |
| 490 });"""; | 516 });"""; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 525 | 551 |
| 526 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ | 552 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ |
| 527 final String _EMPTY_WRAP_CALL1_FUNCTION = | 553 final String _EMPTY_WRAP_CALL1_FUNCTION = |
| 528 @"function $wrap_call$1(fn) { return fn; }"; | 554 @"function $wrap_call$1(fn) { return fn; }"; |
| 529 | 555 |
| 530 /** Snippet that initializes the isolates state. */ | 556 /** Snippet that initializes the isolates state. */ |
| 531 final String _ISOLATE_INIT_CODE = @""" | 557 final String _ISOLATE_INIT_CODE = @""" |
| 532 var $globalThis = this; | 558 var $globalThis = this; |
| 533 var $globals = null; | 559 var $globals = null; |
| 534 var $globalState = null;"""; | 560 var $globalState = null;"""; |
| OLD | NEW |