| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 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. | |
| 8 */ | |
| 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 | |
| 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. | |
| 13 class CoreJs { | |
| 14 // These values track if the helper is actually used. If it is we generate it. | |
| 15 bool useThrow = false; | |
| 16 bool useNotNullBool = false; | |
| 17 bool useIndex = false; | |
| 18 bool useSetIndex = false; | |
| 19 | |
| 20 bool useWrap0 = false; | |
| 21 bool useWrap1 = false; | |
| 22 bool useWrap2 = false; | |
| 23 bool useIsolates = false; | |
| 24 | |
| 25 // These helpers had to switch to a new pattern, because they can be generated | |
| 26 // after everything else. | |
| 27 bool _generatedTypeNameOf = false; | |
| 28 bool _generatedDynamicProto = false; | |
| 29 bool _generatedDynamicSetMetadata = false; | |
| 30 bool _generatedInherits = false; | |
| 31 bool _generatedDefProp = false; | |
| 32 bool _generatedBind = false; | |
| 33 | |
| 34 | |
| 35 Map<String, String> _usedOperators; | |
| 36 | |
| 37 CodeWriter writer; | |
| 38 | |
| 39 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); | |
| 40 | |
| 41 void markCorelibTypeUsed(String typeName) { | |
| 42 world.gen.markTypeUsed(world.corelib.types[typeName]); | |
| 43 } | |
| 44 | |
| 45 _emit(String code) => writer.writeln(code); | |
| 46 | |
| 47 /** | |
| 48 * Generates the special operator method, e.g. $add. | |
| 49 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. | |
| 50 * Same idea for the other methods. | |
| 51 */ | |
| 52 void useOperator(String name) { | |
| 53 if (_usedOperators[name] != null) return; | |
| 54 | |
| 55 if (name != ':ne' && name != ':eq') { | |
| 56 // TODO(jimhug): Only do this once! | |
| 57 markCorelibTypeUsed('NoSuchMethodException'); | |
| 58 } | |
| 59 if (name != ':bit_not' && name != ':negate') { | |
| 60 // TODO(jimhug): Only do this once! | |
| 61 markCorelibTypeUsed('IllegalArgumentException'); | |
| 62 } | |
| 63 | |
| 64 var code; | |
| 65 switch (name) { | |
| 66 case ':ne': | |
| 67 code = _NE_FUNCTION; | |
| 68 break; | |
| 69 | |
| 70 case ':eq': | |
| 71 ensureDefProp(); | |
| 72 code = _EQ_FUNCTION; | |
| 73 break; | |
| 74 | |
| 75 case ':bit_not': | |
| 76 code = _BIT_NOT_FUNCTION; | |
| 77 break; | |
| 78 | |
| 79 case ':negate': | |
| 80 code = _NEGATE_FUNCTION; | |
| 81 break; | |
| 82 | |
| 83 case ':add': | |
| 84 code = _ADD_FUNCTION; | |
| 85 break; | |
| 86 | |
| 87 case ':truncdiv': | |
| 88 useThrow = true; | |
| 89 // TODO(jimhug): Only do this once! | |
| 90 markCorelibTypeUsed('IntegerDivisionByZeroException'); | |
| 91 code = _TRUNCDIV_FUNCTION; | |
| 92 break; | |
| 93 | |
| 94 case ':mod': | |
| 95 code = _MOD_FUNCTION; | |
| 96 break; | |
| 97 | |
| 98 default: | |
| 99 // All of the other helpers are generated the same way | |
| 100 var op = TokenKind.rawOperatorFromMethod(name); | |
| 101 var jsname = world.toJsIdentifier(name); | |
| 102 code = _otherOperator(jsname, op); | |
| 103 break; | |
| 104 } | |
| 105 | |
| 106 _usedOperators[name] = code; | |
| 107 } | |
| 108 | |
| 109 // NOTE: some helpers can't be generated when we generate corelib, | |
| 110 // because we don't discover that we need them until later. | |
| 111 // Generate on-demand instead | |
| 112 void ensureDynamicProto() { | |
| 113 if (_generatedDynamicProto) return; | |
| 114 _generatedDynamicProto = true; | |
| 115 ensureTypeNameOf(); | |
| 116 ensureDefProp(); | |
| 117 _emit(_DYNAMIC_FUNCTION); | |
| 118 } | |
| 119 | |
| 120 void ensureDynamicSetMetadata() { | |
| 121 if (_generatedDynamicSetMetadata) return; | |
| 122 _generatedDynamicSetMetadata = true; | |
| 123 _emit(_DYNAMIC_SET_METADATA_FUNCTION); | |
| 124 } | |
| 125 | |
| 126 void ensureTypeNameOf() { | |
| 127 if (_generatedTypeNameOf) return; | |
| 128 _generatedTypeNameOf = true; | |
| 129 ensureDefProp(); | |
| 130 _emit(_TYPE_NAME_OF_FUNCTION); | |
| 131 } | |
| 132 | |
| 133 /** Generates the $inherits function when it's first used. */ | |
| 134 void ensureInheritsHelper() { | |
| 135 if (_generatedInherits) return; | |
| 136 _generatedInherits = true; | |
| 137 _emit(_INHERITS_FUNCTION); | |
| 138 } | |
| 139 | |
| 140 /** Generates the $defProp function when it's first used. */ | |
| 141 void ensureDefProp() { | |
| 142 if (_generatedDefProp) return; | |
| 143 _generatedDefProp = true; | |
| 144 _emit(_DEF_PROP_FUNCTION); | |
| 145 } | |
| 146 | |
| 147 void ensureBind() { | |
| 148 if (_generatedBind) return; | |
| 149 _generatedBind = true; | |
| 150 _emit(_BIND_CODE); | |
| 151 } | |
| 152 | |
| 153 void generate(CodeWriter w) { | |
| 154 // Write any stuff we had queued up, then replace our writer with a | |
| 155 // subwriter into the one in WorldGenerator so anything we discover that we | |
| 156 // need later on will be generated on-demand. | |
| 157 w.write(writer.text); | |
| 158 writer = w.subWriter(); | |
| 159 | |
| 160 if (useNotNullBool) { | |
| 161 useThrow = true; | |
| 162 _emit(_NOTNULL_BOOL_FUNCTION); | |
| 163 } | |
| 164 | |
| 165 if (useThrow) { | |
| 166 _emit(_THROW_FUNCTION); | |
| 167 } | |
| 168 | |
| 169 if (useIndex) { | |
| 170 markCorelibTypeUsed('NoSuchMethodException'); | |
| 171 ensureDefProp(); | |
| 172 _emit(options.disableBoundsChecks ? | |
| 173 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); | |
| 174 } | |
| 175 | |
| 176 if (useSetIndex) { | |
| 177 markCorelibTypeUsed('NoSuchMethodException'); | |
| 178 ensureDefProp(); | |
| 179 _emit(options.disableBoundsChecks ? | |
| 180 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); | |
| 181 } | |
| 182 | |
| 183 if (!useIsolates) { | |
| 184 if (useWrap0) _emit(_EMPTY_WRAP_CALL0_FUNCTION); | |
| 185 if (useWrap1) _emit(_EMPTY_WRAP_CALL1_FUNCTION); | |
| 186 if (useWrap2) _emit(_EMPTY_WRAP_CALL2_FUNCTION); | |
| 187 } | |
| 188 | |
| 189 // Write operator helpers | |
| 190 for (var opImpl in orderValuesByKeys(_usedOperators)) { | |
| 191 _emit(opImpl); | |
| 192 } | |
| 193 | |
| 194 if (world.dom != null || world.html != null) { | |
| 195 ensureTypeNameOf(); | |
| 196 ensureDefProp(); | |
| 197 // TODO(jmesserly): we need to find a way to avoid conflicts with other | |
| 198 // generated "typeName" fields. Ideally we wouldn't be patching 'Object' | |
| 199 // here. | |
| 200 _emit('\$defProp(Object.prototype, "get\$typeName", ' | |
| 201 'Object.prototype.\$typeNameOf);'); | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 | |
| 207 /** Snippet for `$ne`. */ | |
| 208 final String _NE_FUNCTION = @""" | |
| 209 function $ne$(x, y) { | |
| 210 if (x == null) return y != null; | |
| 211 return (typeof(x) != 'object') ? x !== y : !x.$eq(y); | |
| 212 } | |
| 213 """; | |
| 214 | |
| 215 /** Snippet for `$eq`. */ | |
| 216 final String _EQ_FUNCTION = @""" | |
| 217 function $eq$(x, y) { | |
| 218 if (x == null) return y == null; | |
| 219 return (typeof(x) != 'object') ? x === y : x.$eq(y); | |
| 220 } | |
| 221 // TODO(jimhug): Should this or should it not match equals? | |
| 222 $defProp(Object.prototype, '$eq', function(other) { | |
| 223 return this === other; | |
| 224 }); | |
| 225 """; | |
| 226 | |
| 227 /** Snippet for `$bit_not`. */ | |
| 228 final String _BIT_NOT_FUNCTION = @""" | |
| 229 function $bit_not$(x) { | |
| 230 if (typeof(x) == 'number') return ~x; | |
| 231 if (typeof(x) == 'object') return x.$bit_not(); | |
| 232 $throw(new NoSuchMethodException(x, "operator ~", [])); | |
| 233 } | |
| 234 """; | |
| 235 | |
| 236 /** Snippet for `$negate`. */ | |
| 237 final String _NEGATE_FUNCTION = @""" | |
| 238 function $negate$(x) { | |
| 239 if (typeof(x) == 'number') return -x; | |
| 240 if (typeof(x) == 'object') return x.$negate(); | |
| 241 $throw(new NoSuchMethodException(x, "operator negate", [])); | |
| 242 } | |
| 243 """; | |
| 244 | |
| 245 /** Snippet for `$add`. This relies on JS's string "+" to match Dart's. */ | |
| 246 final String _ADD_FUNCTION = @""" | |
| 247 function $add$complex$(x, y) { | |
| 248 if (typeof(x) == 'number') { | |
| 249 $throw(new IllegalArgumentException(y)); | |
| 250 } else if (typeof(x) == 'string') { | |
| 251 var str = (y == null) ? 'null' : y.toString(); | |
| 252 if (typeof(str) != 'string') { | |
| 253 throw new Error("calling toString() on right hand operand of operator " + | |
| 254 "+ did not return a String"); | |
| 255 } | |
| 256 return x + str; | |
| 257 } else if (typeof(x) == 'object') { | |
| 258 return x.$add(y); | |
| 259 } else { | |
| 260 $throw(new NoSuchMethodException(x, "operator +", [y])); | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 function $add$(x, y) { | |
| 265 if (typeof(x) == 'number' && typeof(y) == 'number') return x + y; | |
| 266 return $add$complex$(x, y); | |
| 267 } | |
| 268 """; | |
| 269 | |
| 270 /** Snippet for `$truncdiv`. This uses `$throw`. */ | |
| 271 final String _TRUNCDIV_FUNCTION = @""" | |
| 272 function $truncdiv$(x, y) { | |
| 273 if (typeof(x) == 'number') { | |
| 274 if (typeof(y) == 'number') { | |
| 275 if (y == 0) $throw(new IntegerDivisionByZeroException()); | |
| 276 var tmp = x / y; | |
| 277 return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); | |
| 278 } else { | |
| 279 $throw(new IllegalArgumentException(y)); | |
| 280 } | |
| 281 } else if (typeof(x) == 'object') { | |
| 282 return x.$truncdiv(y); | |
| 283 } else { | |
| 284 $throw(new NoSuchMethodException(x, "operator ~/", [y])); | |
| 285 } | |
| 286 } | |
| 287 """; | |
| 288 | |
| 289 /** Snippet for `$mod`. */ | |
| 290 final String _MOD_FUNCTION = @""" | |
| 291 function $mod$(x, y) { | |
| 292 if (typeof(x) == 'number') { | |
| 293 if (typeof(y) == 'number') { | |
| 294 var result = x % y; | |
| 295 if (result == 0) { | |
| 296 return 0; // Make sure we don't return -0.0. | |
| 297 } else if (result < 0) { | |
| 298 if (y < 0) { | |
| 299 return result - y; | |
| 300 } else { | |
| 301 return result + y; | |
| 302 } | |
| 303 } | |
| 304 return result; | |
| 305 } else { | |
| 306 $throw(new IllegalArgumentException(y)); | |
| 307 } | |
| 308 } else if (typeof(x) == 'object') { | |
| 309 return x.$mod(y); | |
| 310 } else { | |
| 311 $throw(new NoSuchMethodException(x, "operator %", [y])); | |
| 312 } | |
| 313 } | |
| 314 """; | |
| 315 | |
| 316 /** Code snippet for all other operators. */ | |
| 317 String _otherOperator(String jsname, String op) { | |
| 318 return """ | |
| 319 function $jsname\$complex\$(x, y) { | |
| 320 if (typeof(x) == 'number') { | |
| 321 \$throw(new IllegalArgumentException(y)); | |
| 322 } else if (typeof(x) == 'object') { | |
| 323 return x.$jsname(y); | |
| 324 } else { | |
| 325 \$throw(new NoSuchMethodException(x, "operator $op", [y])); | |
| 326 } | |
| 327 } | |
| 328 function $jsname\$(x, y) { | |
| 329 if (typeof(x) == 'number' && typeof(y) == 'number') return x $op y; | |
| 330 return $jsname\$complex\$(x, y); | |
| 331 } | |
| 332 """; | |
| 333 } | |
| 334 | |
| 335 /** | |
| 336 * Snippet for `$dynamic`. Usage: | |
| 337 * $dynamic(name).SomeTypeName = ... method ...; | |
| 338 * $dynamic(name).Object = ... noSuchMethod ...; | |
| 339 */ | |
| 340 final String _DYNAMIC_FUNCTION = @""" | |
| 341 function $dynamic(name) { | |
| 342 var f = Object.prototype[name]; | |
| 343 if (f && f.methods) return f.methods; | |
| 344 | |
| 345 var methods = {}; | |
| 346 if (f) methods.Object = f; | |
| 347 function $dynamicBind() { | |
| 348 // Find the target method | |
| 349 var obj = this; | |
| 350 var tag = obj.$typeNameOf(); | |
| 351 var method = methods[tag]; | |
| 352 if (!method) { | |
| 353 var table = $dynamicMetadata; | |
| 354 for (var i = 0; i < table.length; i++) { | |
| 355 var entry = table[i]; | |
| 356 if (entry.map.hasOwnProperty(tag)) { | |
| 357 method = methods[entry.tag]; | |
| 358 if (method) break; | |
| 359 } | |
| 360 } | |
| 361 } | |
| 362 method = method || methods.Object; | |
| 363 | |
| 364 var proto = Object.getPrototypeOf(obj); | |
| 365 | |
| 366 if (method == null) { | |
| 367 // Trampoline to throw NoSuchMethodException (TODO: call noSuchMethod). | |
| 368 method = function(){ | |
| 369 // Exact type check to prevent this code shadowing the dispatcher from a | |
| 370 // subclass. | |
| 371 if (Object.getPrototypeOf(this) === proto) { | |
| 372 // TODO(sra): 'name' is the jsname, should be the Dart name. | |
| 373 $throw(new NoSuchMethodException( | |
| 374 obj, name, Array.prototype.slice.call(arguments))); | |
| 375 } | |
| 376 return Object.prototype[name].apply(this, arguments); | |
| 377 }; | |
| 378 } | |
| 379 | |
| 380 if (!proto.hasOwnProperty(name)) { | |
| 381 $defProp(proto, name, method); | |
| 382 } | |
| 383 | |
| 384 return method.apply(this, Array.prototype.slice.call(arguments)); | |
| 385 }; | |
| 386 $dynamicBind.methods = methods; | |
| 387 $defProp(Object.prototype, name, $dynamicBind); | |
| 388 return methods; | |
| 389 } | |
| 390 if (typeof $dynamicMetadata == 'undefined') $dynamicMetadata = []; | |
| 391 """; | |
| 392 | |
| 393 /** | |
| 394 * Snippet for `$dynamicSetMetadata`. | |
| 395 */ | |
| 396 final String _DYNAMIC_SET_METADATA_FUNCTION = @""" | |
| 397 function $dynamicSetMetadata(inputTable) { | |
| 398 // TODO: Deal with light isolates. | |
| 399 var table = []; | |
| 400 for (var i = 0; i < inputTable.length; i++) { | |
| 401 var tag = inputTable[i][0]; | |
| 402 var tags = inputTable[i][1]; | |
| 403 var map = {}; | |
| 404 var tagNames = tags.split('|'); | |
| 405 for (var j = 0; j < tagNames.length; j++) { | |
| 406 map[tagNames[j]] = true; | |
| 407 } | |
| 408 table.push({tag: tag, tags: tags, map: map}); | |
| 409 } | |
| 410 $dynamicMetadata = table; | |
| 411 } | |
| 412 """; | |
| 413 | |
| 414 /** Snippet for `$typeNameOf`. */ | |
| 415 final String _TYPE_NAME_OF_FUNCTION = @""" | |
| 416 $defProp(Object.prototype, '$typeNameOf', (function() { | |
| 417 function constructorNameWithFallback(obj) { | |
| 418 var constructor = obj.constructor; | |
| 419 if (typeof(constructor) == 'function') { | |
| 420 // The constructor isn't null or undefined at this point. Try | |
| 421 // to grab hold of its name. | |
| 422 var name = constructor.name; | |
| 423 // If the name is a non-empty string, we use that as the type | |
| 424 // name of this object. On Firefox, we often get 'Object' as | |
| 425 // the constructor name even for more specialized objects so | |
| 426 // we have to fall through to the toString() based implementation | |
| 427 // below in that case. | |
| 428 if (typeof(name) == 'string' && name && name != 'Object') return name; | |
| 429 } | |
| 430 var string = Object.prototype.toString.call(obj); | |
| 431 return string.substring(8, string.length - 1); | |
| 432 } | |
| 433 | |
| 434 function chrome$typeNameOf() { | |
| 435 var name = this.constructor.name; | |
| 436 if (name == 'Window') return 'DOMWindow'; | |
| 437 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; | |
| 438 return name; | |
| 439 } | |
| 440 | |
| 441 function firefox$typeNameOf() { | |
| 442 var name = constructorNameWithFallback(this); | |
| 443 if (name == 'Window') return 'DOMWindow'; | |
| 444 if (name == 'Document') return 'HTMLDocument'; | |
| 445 if (name == 'XMLDocument') return 'Document'; | |
| 446 if (name == 'WorkerMessageEvent') return 'MessageEvent'; | |
| 447 return name; | |
| 448 } | |
| 449 | |
| 450 function ie$typeNameOf() { | |
| 451 var name = constructorNameWithFallback(this); | |
| 452 if (name == 'Window') return 'DOMWindow'; | |
| 453 // IE calls both HTML and XML documents 'Document', so we check for the | |
| 454 // xmlVersion property, which is the empty string on HTML documents. | |
| 455 if (name == 'Document' && this.xmlVersion) return 'Document'; | |
| 456 if (name == 'Document') return 'HTMLDocument'; | |
| 457 if (name == 'HTMLTableDataCellElement') return 'HTMLTableCellElement'; | |
| 458 if (name == 'HTMLTableHeaderCellElement') return 'HTMLTableCellElement'; | |
| 459 if (name == 'MSStyleCSSProperties') return 'CSSStyleDeclaration'; | |
| 460 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; | |
| 461 if (name == 'HTMLPhraseElement') return 'HTMLElement'; | |
| 462 if (name == 'MouseWheelEvent') return 'WheelEvent'; | |
| 463 return name; | |
| 464 } | |
| 465 | |
| 466 // If we're not in the browser, we're almost certainly running on v8. | |
| 467 if (typeof(navigator) != 'object') return chrome$typeNameOf; | |
| 468 | |
| 469 var userAgent = navigator.userAgent; | |
| 470 if (/Chrome|DumpRenderTree/.test(userAgent)) return chrome$typeNameOf; | |
| 471 if (/Firefox/.test(userAgent)) return firefox$typeNameOf; | |
| 472 if (/MSIE/.test(userAgent)) return ie$typeNameOf; | |
| 473 return function() { return constructorNameWithFallback(this); }; | |
| 474 })()); | |
| 475 """; | |
| 476 | |
| 477 /** Snippet for `$inherits`. */ | |
| 478 final String _INHERITS_FUNCTION = @""" | |
| 479 /** Implements extends for Dart classes on JavaScript prototypes. */ | |
| 480 function $inherits(child, parent) { | |
| 481 if (child.prototype.__proto__) { | |
| 482 child.prototype.__proto__ = parent.prototype; | |
| 483 } else { | |
| 484 function tmp() {}; | |
| 485 tmp.prototype = parent.prototype; | |
| 486 child.prototype = new tmp(); | |
| 487 child.prototype.constructor = child; | |
| 488 } | |
| 489 } | |
| 490 """; | |
| 491 | |
| 492 /** Snippet for `$defProp`. */ | |
| 493 final String _DEF_PROP_FUNCTION = @""" | |
| 494 function $defProp(obj, prop, value) { | |
| 495 Object.defineProperty(obj, prop, | |
| 496 {value: value, enumerable: false, writable: true, configurable: true}); | |
| 497 } | |
| 498 """; | |
| 499 | |
| 500 /** Snippet for `$stackTraceOf`. */ | |
| 501 final String _STACKTRACEOF_FUNCTION = @""" | |
| 502 function $stackTraceOf(e) { | |
| 503 // TODO(jmesserly): we shouldn't be relying on the e.stack property. | |
| 504 // Need to mangle it. | |
| 505 return (e && e.stack) ? e.stack : null; | |
| 506 } | |
| 507 """; | |
| 508 | |
| 509 /** | |
| 510 * Snippet for `$notnull_bool`. This pattern chosen because IE9 does really | |
| 511 * badly with typeof, and it's still decent on other browsers. | |
| 512 */ | |
| 513 final String _NOTNULL_BOOL_FUNCTION = @""" | |
| 514 function $notnull_bool(test) { | |
| 515 if (test === true || test === false) return test; | |
| 516 $throw(new TypeError(test, 'bool')); | |
| 517 } | |
| 518 """; | |
| 519 | |
| 520 /** Snippet for `$throw`. */ | |
| 521 final String _THROW_FUNCTION = @""" | |
| 522 function $throw(e) { | |
| 523 // If e is not a value, we can use V8's captureStackTrace utility method. | |
| 524 // TODO(jmesserly): capture the stack trace on other JS engines. | |
| 525 if (e && (typeof e == 'object') && Error.captureStackTrace) { | |
| 526 // TODO(jmesserly): this will clobber the e.stack property | |
| 527 Error.captureStackTrace(e, $throw); | |
| 528 } | |
| 529 throw e; | |
| 530 } | |
| 531 """; | |
| 532 | |
| 533 /** | |
| 534 * Snippet for `$index` in Object, Array, and String. If not overridden, | |
| 535 * `$index` and `$setindex` fall back to JS [] and []= accessors. | |
| 536 */ | |
| 537 // TODO(jimhug): This fallback could be very confusing in a few cases - | |
| 538 // because of the bizare default [] rules in JS. We need to revisit this | |
| 539 // to get the right errors - at least in checked mode (once we have that). | |
| 540 // TODO(jmesserly): do perf analysis, figure out if this is worth it and | |
| 541 // what the cost of $index $setindex is on all browsers | |
| 542 | |
| 543 // Performance of Object.prototype methods can go down because there are | |
| 544 // so many of them. Instead, first time we hit it, put it on the derived | |
| 545 // prototype. TODO(jmesserly): make this go away by handling index more | |
| 546 // like a normal method. | |
| 547 final String _INDEX_OPERATORS = @""" | |
| 548 $defProp(Object.prototype, '$index', function(i) { | |
| 549 $throw(new NoSuchMethodException(this, "operator []", [i])); | |
| 550 }); | |
| 551 $defProp(Array.prototype, '$index', function(i) { | |
| 552 return this[i]; | |
| 553 }); | |
| 554 $defProp(String.prototype, '$index', function(i) { | |
| 555 return this[i]; | |
| 556 }); | |
| 557 """; | |
| 558 | |
| 559 final String _CHECKED_INDEX_OPERATORS = @""" | |
| 560 $defProp(Object.prototype, '$index', function(i) { | |
| 561 $throw(new NoSuchMethodException(this, "operator []", [i])); | |
| 562 }); | |
| 563 $defProp(Array.prototype, '$index', function(index) { | |
| 564 var i = index | 0; | |
| 565 if (i !== index) { | |
| 566 throw new IllegalArgumentException('index is not int'); | |
| 567 } else if (i < 0 || i >= this.length) { | |
| 568 throw new IndexOutOfRangeException(index); | |
| 569 } | |
| 570 return this[i]; | |
| 571 }); | |
| 572 $defProp(String.prototype, '$index', function(i) { | |
| 573 return this[i]; | |
| 574 }); | |
| 575 """; | |
| 576 | |
| 577 | |
| 578 | |
| 579 /** Snippet for `$setindex` in Object, Array, and String. */ | |
| 580 final String _SETINDEX_OPERATORS = @""" | |
| 581 $defProp(Object.prototype, '$setindex', function(i, value) { | |
| 582 $throw(new NoSuchMethodException(this, "operator []=", [i, value])); | |
| 583 }); | |
| 584 $defProp(Array.prototype, '$setindex', | |
| 585 function(i, value) { return this[i] = value; });"""; | |
| 586 | |
| 587 final String _CHECKED_SETINDEX_OPERATORS = @""" | |
| 588 $defProp(Object.prototype, '$setindex', function(i, value) { | |
| 589 $throw(new NoSuchMethodException(this, "operator []=", [i, value])); | |
| 590 }); | |
| 591 $defProp(Array.prototype, '$setindex', function(index, value) { | |
| 592 var i = index | 0; | |
| 593 if (i !== index) { | |
| 594 throw new IllegalArgumentException('index is not int'); | |
| 595 } else if (i < 0 || i >= this.length) { | |
| 596 throw new IndexOutOfRangeException(index); | |
| 597 } | |
| 598 return this[i] = value; | |
| 599 }); | |
| 600 """; | |
| 601 | |
| 602 /** Snippet for `$wrap_call$0`, in case it was not necessary. */ | |
| 603 final String _EMPTY_WRAP_CALL0_FUNCTION = @""" | |
| 604 function $wrap_call$0(fn) { return fn; } | |
| 605 """; | |
| 606 | |
| 607 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ | |
| 608 final String _EMPTY_WRAP_CALL1_FUNCTION = @""" | |
| 609 function $wrap_call$1(fn) { return fn; }; | |
| 610 """; | |
| 611 | |
| 612 /** Snippet for `$wrap_call$2`, in case it was not necessary. */ | |
| 613 final String _EMPTY_WRAP_CALL2_FUNCTION = @""" | |
| 614 function $wrap_call$2(fn) { return fn; }; | |
| 615 """; | |
| 616 | |
| 617 /** Snippet that initializes Function.prototype.bind. */ | |
| 618 final String _BIND_CODE = @""" | |
| 619 Function.prototype.bind = Function.prototype.bind || | |
| 620 function(thisObj) { | |
| 621 var func = this; | |
| 622 var funcLength = func.$length || func.length; | |
| 623 var argsLength = arguments.length; | |
| 624 if (argsLength > 1) { | |
| 625 var boundArgs = Array.prototype.slice.call(arguments, 1); | |
| 626 var bound = function() { | |
| 627 // Prepend the bound arguments to the current arguments. | |
| 628 var newArgs = Array.prototype.slice.call(arguments); | |
| 629 Array.prototype.unshift.apply(newArgs, boundArgs); | |
| 630 return func.apply(thisObj, newArgs); | |
| 631 }; | |
| 632 bound.$length = Math.max(0, funcLength - (argsLength - 1)); | |
| 633 return bound; | |
| 634 } else { | |
| 635 var bound = function() { | |
| 636 return func.apply(thisObj, arguments); | |
| 637 }; | |
| 638 bound.$length = funcLength; | |
| 639 return bound; | |
| 640 } | |
| 641 }; | |
| 642 """; | |
| OLD | NEW |