| 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 |
| 11 // because they'd get the right name collision behavior, conversions, | 11 // because they'd get the right name collision behavior, conversions, |
| 12 // include-if-used, etc for free. Not sure if it's worth doing that. | 12 // include-if-used, etc for free. Not sure if it's worth doing that. |
| 13 class CoreJs { | 13 class CoreJs { |
| 14 // These values track if the helper is actually used. If it is we generate it. | 14 // These values track if the helper is actually used. If it is we generate it. |
| 15 bool useThrow = false; | 15 bool useThrow = false; |
| 16 bool useAssert = false; | |
| 17 bool useNotNullBool = false; | 16 bool useNotNullBool = false; |
| 18 bool useIndex = false; | 17 bool useIndex = false; |
| 19 bool useSetIndex = false; | 18 bool useSetIndex = false; |
| 20 | 19 |
| 21 bool useWrap0 = false; | 20 bool useWrap0 = false; |
| 22 bool useWrap1 = false; | 21 bool useWrap1 = false; |
| 23 bool useIsolates = false; | 22 bool useIsolates = false; |
| 24 | 23 |
| 25 // These helpers had to switch to a new pattern, because they can be generated | 24 // These helpers had to switch to a new pattern, because they can be generated |
| 26 // after everything else. | 25 // after everything else. |
| 27 bool _generatedTypeNameOf = false; | 26 bool _generatedTypeNameOf = false; |
| 28 bool _generatedDynamicProto = false; | 27 bool _generatedDynamicProto = false; |
| 29 bool _generatedInherits = false; | 28 bool _generatedInherits = false; |
| 30 | 29 |
| 30 |
| 31 Map<String, String> _usedOperators; | 31 Map<String, String> _usedOperators; |
| 32 | 32 |
| 33 CodeWriter writer; | 33 CodeWriter writer; |
| 34 | 34 |
| 35 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); | 35 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); |
| 36 | 36 |
| 37 /** | 37 /** |
| 38 * Generates the special operator method, e.g. $add. | 38 * Generates the special operator method, e.g. $add. |
| 39 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. | 39 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. |
| 40 * Same idea for the other methods. | 40 * Same idea for the other methods. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 59 case ':negate': | 59 case ':negate': |
| 60 code = _NEGATE_FUNCTION; | 60 code = _NEGATE_FUNCTION; |
| 61 break; | 61 break; |
| 62 | 62 |
| 63 case ':add': | 63 case ':add': |
| 64 code = _ADD_FUNCTION; | 64 code = _ADD_FUNCTION; |
| 65 break; | 65 break; |
| 66 | 66 |
| 67 case ':truncdiv': | 67 case ':truncdiv': |
| 68 useThrow = true; | 68 useThrow = true; |
| 69 // TODO(jimhug): Only do this once! |
| 70 world.gen.markTypeUsed( |
| 71 world.corelib.types['IntegerDivisionByZeroException']); |
| 69 code = _TRUNCDIV_FUNCTION; | 72 code = _TRUNCDIV_FUNCTION; |
| 70 break; | 73 break; |
| 71 | 74 |
| 72 case ':mod': | 75 case ':mod': |
| 73 code = _MOD_FUNCTION; | 76 code = _MOD_FUNCTION; |
| 74 break; | 77 break; |
| 75 | 78 |
| 76 default: | 79 default: |
| 77 // All of the other helpers are generated the same way | 80 // All of the other helpers are generated the same way |
| 78 var op = TokenKind.rawOperatorFromMethod(name); | 81 var op = TokenKind.rawOperatorFromMethod(name); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 if (useNotNullBool) { | 120 if (useNotNullBool) { |
| 118 useThrow = true; | 121 useThrow = true; |
| 119 w.writeln(_NOTNULL_BOOL_FUNCTION); | 122 w.writeln(_NOTNULL_BOOL_FUNCTION); |
| 120 } | 123 } |
| 121 | 124 |
| 122 if (useThrow) { | 125 if (useThrow) { |
| 123 w.writeln(_THROW_FUNCTION); | 126 w.writeln(_THROW_FUNCTION); |
| 124 } | 127 } |
| 125 | 128 |
| 126 if (useIndex) { | 129 if (useIndex) { |
| 127 w.writeln(_INDEX_OPERATORS); | 130 w.writeln(options.disableBoundsChecks ? |
| 131 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); |
| 128 } | 132 } |
| 129 | 133 |
| 130 if (useSetIndex) { | 134 if (useSetIndex) { |
| 131 w.writeln(_SETINDEX_OPERATORS); | 135 w.writeln(options.disableBoundsChecks ? |
| 136 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); |
| 132 } | 137 } |
| 133 | 138 |
| 134 if (useIsolates) { | 139 if (useIsolates) { |
| 135 if (useWrap0) { | 140 if (useWrap0) { |
| 136 w.writeln(_WRAP_CALL0_FUNCTION); | 141 w.writeln(_WRAP_CALL0_FUNCTION); |
| 137 } | 142 } |
| 138 if (useWrap1) { | 143 if (useWrap1) { |
| 139 w.writeln(_WRAP_CALL1_FUNCTION); | 144 w.writeln(_WRAP_CALL1_FUNCTION); |
| 140 } | 145 } |
| 141 w.writeln(_ISOLATE_INIT_CODE); | 146 w.writeln(_ISOLATE_INIT_CODE); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 179 /** Snippet for `$eq`. */ | 184 /** Snippet for `$eq`. */ |
| 180 final String _EQ_FUNCTION = @""" | 185 final String _EQ_FUNCTION = @""" |
| 181 function $eq(x, y) { | 186 function $eq(x, y) { |
| 182 if (x == null) return y == null; | 187 if (x == null) return y == null; |
| 183 return (typeof(x) == 'number' && typeof(y) == 'number') || | 188 return (typeof(x) == 'number' && typeof(y) == 'number') || |
| 184 (typeof(x) == 'boolean' && typeof(y) == 'boolean') || | 189 (typeof(x) == 'boolean' && typeof(y) == 'boolean') || |
| 185 (typeof(x) == 'string' && typeof(y) == 'string') | 190 (typeof(x) == 'string' && typeof(y) == 'string') |
| 186 ? x == y : x.$eq(y); | 191 ? x == y : x.$eq(y); |
| 187 } | 192 } |
| 188 // TODO(jimhug): Should this or should it not match equals? | 193 // TODO(jimhug): Should this or should it not match equals? |
| 189 Object.defineProperty(Object.prototype, '$eq', { value: function(other) { | 194 Object.defineProperty(Object.prototype, '$eq', { value: function(other) { |
| 190 return this === other; | 195 return this === other; |
| 191 }, enumerable: false, writable: true, configurable: true });"""; | 196 }, enumerable: false, writable: true, configurable: true });"""; |
| 192 | 197 |
| 193 /** Snippet for `$bit_not`. */ | 198 /** Snippet for `$bit_not`. */ |
| 194 final String _BIT_NOT_FUNCTION = @""" | 199 final String _BIT_NOT_FUNCTION = @""" |
| 195 function $bit_not(x) { | 200 function $bit_not(x) { |
| 196 return (typeof(x) == 'number') ? ~x : x.$bit_not(); | 201 return (typeof(x) == 'number') ? ~x : x.$bit_not(); |
| 197 }"""; | 202 }"""; |
| 198 | 203 |
| 199 /** Snippet for `$negate`. */ | 204 /** Snippet for `$negate`. */ |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 if (entry.map.hasOwnProperty(tag)) { | 280 if (entry.map.hasOwnProperty(tag)) { |
| 276 method = methods[entry.tag]; | 281 method = methods[entry.tag]; |
| 277 if (method) break; | 282 if (method) break; |
| 278 } | 283 } |
| 279 } | 284 } |
| 280 } | 285 } |
| 281 method = method || methods.Object; | 286 method = method || methods.Object; |
| 282 var proto = Object.getPrototypeOf(obj); | 287 var proto = Object.getPrototypeOf(obj); |
| 283 if (!proto.hasOwnProperty(name)) { | 288 if (!proto.hasOwnProperty(name)) { |
| 284 Object.defineProperty(proto, name, | 289 Object.defineProperty(proto, name, |
| 285 { value: method, enumerable: false, writable: true, | 290 { value: method, enumerable: false, writable: true, |
| 286 configurable: true }); | 291 configurable: true }); |
| 287 } | 292 } |
| 288 | 293 |
| 289 return method.apply(this, Array.prototype.slice.call(arguments)); | 294 return method.apply(this, Array.prototype.slice.call(arguments)); |
| 290 }; | 295 }; |
| 291 $dynamicBind.methods = methods; | 296 $dynamicBind.methods = methods; |
| 292 Object.defineProperty(Object.prototype, name, { value: $dynamicBind, | 297 Object.defineProperty(Object.prototype, name, { value: $dynamicBind, |
| 293 enumerable: false, writable: true, configurable: true}); | 298 enumerable: false, writable: true, configurable: true}); |
| 294 return methods; | 299 return methods; |
| 295 } | 300 } |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 388 // prototype. TODO(jmesserly): make this go away by handling index more | 393 // prototype. TODO(jmesserly): make this go away by handling index more |
| 389 // like a normal method. | 394 // like a normal method. |
| 390 final String _INDEX_OPERATORS = @""" | 395 final String _INDEX_OPERATORS = @""" |
| 391 Object.defineProperty(Object.prototype, '$index', { value: function(i) { | 396 Object.defineProperty(Object.prototype, '$index', { value: function(i) { |
| 392 var proto = Object.getPrototypeOf(this); | 397 var proto = Object.getPrototypeOf(this); |
| 393 if (proto !== Object) { | 398 if (proto !== Object) { |
| 394 proto.$index = function(i) { return this[i]; } | 399 proto.$index = function(i) { return this[i]; } |
| 395 } | 400 } |
| 396 return this[i]; | 401 return this[i]; |
| 397 }, enumerable: false, writable: true, configurable: true}); | 402 }, enumerable: false, writable: true, configurable: true}); |
| 398 Object.defineProperty(Array.prototype, '$index', { value: function(i) { | 403 Object.defineProperty(Array.prototype, '$index', { value: function(i) { |
| 399 return this[i]; | 404 return this[i]; |
| 400 }, enumerable: false, writable: true, configurable: true}); | 405 }, enumerable: false, writable: true, configurable: true}); |
| 401 Object.defineProperty(String.prototype, '$index', { value: function(i) { | 406 Object.defineProperty(String.prototype, '$index', { value: function(i) { |
| 402 return this[i]; | 407 return this[i]; |
| 403 }, enumerable: false, writable: true, configurable: true});"""; | 408 }, enumerable: false, writable: true, configurable: true});"""; |
| 404 | 409 |
| 410 final String _CHECKED_INDEX_OPERATORS = @""" |
| 411 Object.defineProperty(Object.prototype, '$index', { value: function(i) { |
| 412 var proto = Object.getPrototypeOf(this); |
| 413 if (proto !== Object) { |
| 414 proto.$index = function(i) { return this[i]; } |
| 415 } |
| 416 return this[i]; |
| 417 }, enumerable: false, writable: true, configurable: true}); |
| 418 Object.defineProperty(Array.prototype, '$index', { value: function(index) { |
| 419 var i = index | 0; |
| 420 if (i !== index) { |
| 421 throw new IllegalArgumentException('index is not int'); |
| 422 } else if (i < 0 || i >= this.length) { |
| 423 throw new IndexOutOfRangeException(index); |
| 424 } |
| 425 return this[i]; |
| 426 }, enumerable: false, writable: true, configurable: true}); |
| 427 Object.defineProperty(String.prototype, '$index', { value: function(i) { |
| 428 return this[i]; |
| 429 }, enumerable: false, writable: true, configurable: true});"""; |
| 430 |
| 431 |
| 432 |
| 405 /** Snippet for `$setindex` in Object, Array, and String. */ | 433 /** Snippet for `$setindex` in Object, Array, and String. */ |
| 406 // TODO(jimhug): Add array bounds checking in checked mode | |
| 407 /* | |
| 408 function $inlineArrayIndexCheck(array, index) { | |
| 409 if (index >= 0 && index < array.length) { | |
| 410 return index; | |
| 411 } | |
| 412 native__ArrayJsUtil__throwIndexOutOfRangeException(index); | |
| 413 }*/ | |
| 414 final String _SETINDEX_OPERATORS = @""" | 434 final String _SETINDEX_OPERATORS = @""" |
| 415 Object.defineProperty(Object.prototype, '$setindex', { value: function(i, value)
{ | 435 Object.defineProperty(Object.prototype, '$setindex', { value: function(i, value)
{ |
| 416 var proto = Object.getPrototypeOf(this); | 436 var proto = Object.getPrototypeOf(this); |
| 417 if (proto !== Object) { | 437 if (proto !== Object) { |
| 418 proto.$setindex = function(i, value) { return this[i] = value; } | 438 proto.$setindex = function(i, value) { return this[i] = value; } |
| 419 } | 439 } |
| 420 return this[i] = value; | 440 return this[i] = value; |
| 421 }, enumerable: false, writable: true, configurable: true}); | 441 }, enumerable: false, writable: true, configurable: true}); |
| 422 Object.defineProperty(Array.prototype, '$setindex', { value: function(i, value)
{ | 442 Object.defineProperty(Array.prototype, '$setindex', { value: function(i, value)
{ |
| 423 return this[i] = value; }, enumerable: false, writable: true, | 443 return this[i] = value; }, enumerable: false, writable: true, |
| 444 configurable: true});"""; |
| 445 |
| 446 final String _CHECKED_SETINDEX_OPERATORS = @""" |
| 447 Object.defineProperty(Object.prototype, '$setindex', { value: function(i, value)
{ |
| 448 var proto = Object.getPrototypeOf(this); |
| 449 if (proto !== Object) { |
| 450 proto.$setindex = function(i, value) { return this[i] = value; } |
| 451 } |
| 452 return this[i] = value; |
| 453 }, enumerable: false, writable: true, configurable: true}); |
| 454 Object.defineProperty(Array.prototype, '$setindex', { value: function(index, val
ue) { |
| 455 var i = index | 0; |
| 456 if (i !== index) { |
| 457 throw new IllegalArgumentException('index is not int'); |
| 458 } else if (i < 0 || i >= this.length) { |
| 459 throw new IndexOutOfRangeException(index); |
| 460 } |
| 461 return this[i] = value; }, enumerable: false, writable: true, |
| 424 configurable: true});"""; | 462 configurable: true});"""; |
| 425 | 463 |
| 426 /** Snippet for `$wrap_call$0`. */ | 464 /** Snippet for `$wrap_call$0`. */ |
| 427 final String _WRAP_CALL0_FUNCTION = @""" | 465 final String _WRAP_CALL0_FUNCTION = @""" |
| 428 // Wrap a 0-arg dom-callback to bind it with the current isolate: | 466 // Wrap a 0-arg dom-callback to bind it with the current isolate: |
| 429 function $wrap_call$0(fn) { return fn && fn.wrap$call$0(); } | 467 function $wrap_call$0(fn) { return fn && fn.wrap$call$0(); } |
| 430 Function.prototype.wrap$call$0 = function() { | 468 Function.prototype.wrap$call$0 = function() { |
| 431 var isolateContext = $globalState.currentContext; | 469 var isolateContext = $globalState.currentContext; |
| 432 var self = this; | 470 var self = this; |
| 433 this.wrap$0 = function() { | 471 this.wrap$0 = function() { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 459 | 497 |
| 460 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ | 498 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ |
| 461 final String _EMPTY_WRAP_CALL1_FUNCTION = | 499 final String _EMPTY_WRAP_CALL1_FUNCTION = |
| 462 @"function $wrap_call$1(fn) { return fn; }"; | 500 @"function $wrap_call$1(fn) { return fn; }"; |
| 463 | 501 |
| 464 /** Snippet that initializes the isolates state. */ | 502 /** Snippet that initializes the isolates state. */ |
| 465 final String _ISOLATE_INIT_CODE = @""" | 503 final String _ISOLATE_INIT_CODE = @""" |
| 466 var $globalThis = this; | 504 var $globalThis = this; |
| 467 var $globals = null; | 505 var $globals = null; |
| 468 var $globalState = null;"""; | 506 var $globalState = null;"""; |
| OLD | NEW |