| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2013, 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 library _js_helper; |  | 
| 6 |  | 
| 7 import 'dart:_async_await_error_codes' as async_error_codes; |  | 
| 8 |  | 
| 9 import 'dart:_js_embedded_names' show |  | 
| 10     DEFERRED_LIBRARY_URIS, |  | 
| 11     DEFERRED_LIBRARY_HASHES, |  | 
| 12     GET_TYPE_FROM_NAME, |  | 
| 13     GET_ISOLATE_TAG, |  | 
| 14     INITIALIZE_LOADED_HUNK, |  | 
| 15     INTERCEPTED_NAMES, |  | 
| 16     INTERCEPTORS_BY_TAG, |  | 
| 17     IS_HUNK_LOADED, |  | 
| 18     IS_HUNK_INITIALIZED, |  | 
| 19     JsBuiltin, |  | 
| 20     JsGetName, |  | 
| 21     LEAF_TAGS, |  | 
| 22     NATIVE_SUPERCLASS_TAG_NAME; |  | 
| 23 |  | 
| 24 import 'dart:collection'; |  | 
| 25 |  | 
| 26 import 'dart:_isolate_helper' show |  | 
| 27     IsolateNatives, |  | 
| 28     enterJsAsync, |  | 
| 29     isWorker, |  | 
| 30     leaveJsAsync; |  | 
| 31 |  | 
| 32 import 'dart:async' show |  | 
| 33     Completer, |  | 
| 34     DeferredLoadException, |  | 
| 35     Future, |  | 
| 36     StreamController, |  | 
| 37     Stream, |  | 
| 38     StreamSubscription, |  | 
| 39     scheduleMicrotask; |  | 
| 40 |  | 
| 41 import 'dart:_foreign_helper' show |  | 
| 42     DART_CLOSURE_TO_JS, |  | 
| 43     JS, |  | 
| 44     JS_BUILTIN, |  | 
| 45     JS_CALL_IN_ISOLATE, |  | 
| 46     JS_CONST, |  | 
| 47     JS_CURRENT_ISOLATE, |  | 
| 48     JS_CURRENT_ISOLATE_CONTEXT, |  | 
| 49     JS_EFFECT, |  | 
| 50     JS_EMBEDDED_GLOBAL, |  | 
| 51     JS_GET_FLAG, |  | 
| 52     JS_GET_NAME, |  | 
| 53     JS_HAS_EQUALS, |  | 
| 54     JS_STRING_CONCAT, |  | 
| 55     RAW_DART_FUNCTION_REF; |  | 
| 56 |  | 
| 57 import 'dart:_interceptors'; |  | 
| 58 import 'dart:_internal' as _symbol_dev; |  | 
| 59 import 'dart:_internal' show EfficientLength, MappedIterable; |  | 
| 60 |  | 
| 61 import 'dart:_native_typed_data'; |  | 
| 62 |  | 
| 63 import 'dart:_js_names' show |  | 
| 64     extractKeys, |  | 
| 65     mangledNames, |  | 
| 66     unmangleGlobalNameIfPreservedAnyways, |  | 
| 67     unmangleAllIdentifiersIfPreservedAnyways; |  | 
| 68 |  | 
| 69 part 'annotations.dart'; |  | 
| 70 part 'constant_map.dart'; |  | 
| 71 part 'native_helper.dart'; |  | 
| 72 part 'regexp_helper.dart'; |  | 
| 73 part 'string_helper.dart'; |  | 
| 74 part 'js_rti.dart'; |  | 
| 75 part 'linked_hash_map.dart'; |  | 
| 76 |  | 
| 77 /// Marks the internal map in dart2js, so that internal libraries can is-check |  | 
| 78 /// them. |  | 
| 79 abstract class InternalMap { |  | 
| 80 } |  | 
| 81 |  | 
| 82 /// Extracts the JavaScript-constructor name from the given isCheckProperty. |  | 
| 83 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 84 @ForceInline() |  | 
| 85 String isCheckPropertyToJsConstructorName(String isCheckProperty) { |  | 
| 86   return JS_BUILTIN('returns:String;depends:none;effects:none', |  | 
| 87                     JsBuiltin.isCheckPropertyToJsConstructorName, |  | 
| 88                     isCheckProperty); |  | 
| 89 } |  | 
| 90 |  | 
| 91 /// Returns true if the given [type] is a function type object. |  | 
| 92 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 93 @ForceInline() |  | 
| 94 bool isDartFunctionType(Object type) { |  | 
| 95   return JS_BUILTIN('returns:bool;effects:none;depends:none', |  | 
| 96                     JsBuiltin.isFunctionType, type); |  | 
| 97 } |  | 
| 98 |  | 
| 99 |  | 
| 100 /// Creates a function type object. |  | 
| 101 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 102 @ForceInline() |  | 
| 103 createDartFunctionTypeRti() { |  | 
| 104   return JS_BUILTIN('returns:=Object;effects:none;depends:none', |  | 
| 105                     JsBuiltin.createFunctionTypeRti); |  | 
| 106 } |  | 
| 107 |  | 
| 108 /// Retrieves the class name from type information stored on the constructor of |  | 
| 109 /// [type]. |  | 
| 110 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 111 @ForceInline() |  | 
| 112 String rawRtiToJsConstructorName(Object rti) { |  | 
| 113   return JS_BUILTIN('String', JsBuiltin.rawRtiToJsConstructorName, rti); |  | 
| 114 } |  | 
| 115 |  | 
| 116 /// Returns the rti from the given [constructorName]. |  | 
| 117 // TODO(floitsch): make this a builtin. |  | 
| 118 jsConstructorNameToRti(String constructorName) { |  | 
| 119   var getTypeFromName = JS_EMBEDDED_GLOBAL('', GET_TYPE_FROM_NAME); |  | 
| 120   return JS('', '#(#)', getTypeFromName, constructorName); |  | 
| 121 } |  | 
| 122 |  | 
| 123 /// Returns the raw runtime type of the given object [o]. |  | 
| 124 /// |  | 
| 125 /// The argument [o] must be the interceptor for primitive types. If |  | 
| 126 /// necessary run it through [getInterceptor] first. |  | 
| 127 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 128 // TODO(floitsch): we should call getInterceptor ourselves, but currently |  | 
| 129 //    getInterceptor is not GVNed. |  | 
| 130 @ForceInline() |  | 
| 131 Object getRawRuntimeType(Object o) { |  | 
| 132   return JS_BUILTIN('', JsBuiltin.rawRuntimeType, o); |  | 
| 133 } |  | 
| 134 |  | 
| 135 /// Returns whether the given [type] is a subtype of [other]. |  | 
| 136 /// |  | 
| 137 /// The argument [other] is the name of the other type, as computed by |  | 
| 138 /// [runtimeTypeToString]. |  | 
| 139 @ForceInline() |  | 
| 140 bool builtinIsSubtype(type, String other) { |  | 
| 141   return JS_BUILTIN('returns:bool;effects:none;depends:none', |  | 
| 142                     JsBuiltin.isSubtype, other, type); |  | 
| 143 } |  | 
| 144 |  | 
| 145 /// Returns true if the given [type] is _the_ `Function` type. |  | 
| 146 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 147 @ForceInline() |  | 
| 148 bool isDartFunctionTypeRti(Object type) { |  | 
| 149   return JS_BUILTIN('returns:bool;effects:none;depends:none', |  | 
| 150                     JsBuiltin.isGivenTypeRti, |  | 
| 151                     type, |  | 
| 152                     JS_GET_NAME(JsGetName.FUNCTION_CLASS_TYPE_NAME)); |  | 
| 153 } |  | 
| 154 |  | 
| 155 /// Returns whether the given type is _the_ Dart Object type. |  | 
| 156 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 157 @ForceInline() |  | 
| 158 bool isDartObjectTypeRti(type) { |  | 
| 159   return JS_BUILTIN('returns:bool;effects:none;depends:none', |  | 
| 160                     JsBuiltin.isGivenTypeRti, |  | 
| 161                     type, |  | 
| 162                     JS_GET_NAME(JsGetName.OBJECT_CLASS_TYPE_NAME)); |  | 
| 163 } |  | 
| 164 |  | 
| 165 /// Returns whether the given type is _the_ null type. |  | 
| 166 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 167 @ForceInline() |  | 
| 168 bool isNullTypeRti(type) { |  | 
| 169   return JS_BUILTIN('returns:bool;effects:none;depends:none', |  | 
| 170                     JsBuiltin.isGivenTypeRti, |  | 
| 171                     type, |  | 
| 172                     JS_GET_NAME(JsGetName.NULL_CLASS_TYPE_NAME)); |  | 
| 173 } |  | 
| 174 |  | 
| 175 /// Returns the metadata of the given [index]. |  | 
| 176 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 177 @ForceInline() |  | 
| 178 getMetadata(int index) { |  | 
| 179   return JS_BUILTIN('returns:var;effects:none;depends:none', |  | 
| 180                     JsBuiltin.getMetadata, index); |  | 
| 181 } |  | 
| 182 |  | 
| 183 /// Returns the type of the given [index]. |  | 
| 184 // TODO(floitsch): move this to foreign_helper.dart or similar. |  | 
| 185 @ForceInline() |  | 
| 186 getType(int index) { |  | 
| 187   return JS_BUILTIN('returns:var;effects:none;depends:none', |  | 
| 188                     JsBuiltin.getType, index); |  | 
| 189 } |  | 
| 190 |  | 
| 191 /// No-op method that is called to inform the compiler that preambles might |  | 
| 192 /// be needed when executing the resulting JS file in a command-line |  | 
| 193 /// JS engine. |  | 
| 194 requiresPreamble() {} |  | 
| 195 |  | 
| 196 bool isJsIndexable(var object, var record) { |  | 
| 197   if (record != null) { |  | 
| 198     var result = dispatchRecordIndexability(record); |  | 
| 199     if (result != null) return result; |  | 
| 200   } |  | 
| 201   return object is JavaScriptIndexingBehavior; |  | 
| 202 } |  | 
| 203 |  | 
| 204 String S(value) { |  | 
| 205   if (value is String) return value; |  | 
| 206   if (value is num) { |  | 
| 207     if (value != 0) { |  | 
| 208       // ""+x is faster than String(x) for integers on most browsers. |  | 
| 209       return JS('String', r'"" + (#)', value); |  | 
| 210     } |  | 
| 211   } else if (true == value) { |  | 
| 212     return 'true'; |  | 
| 213   } else if (false == value) { |  | 
| 214     return 'false'; |  | 
| 215   } else if (value == null) { |  | 
| 216     return 'null'; |  | 
| 217   } |  | 
| 218   var res = value.toString(); |  | 
| 219   if (res is !String) throw argumentErrorValue(value); |  | 
| 220   return res; |  | 
| 221 } |  | 
| 222 |  | 
| 223 createInvocationMirror(String name, internalName, kind, arguments, |  | 
| 224                        argumentNames) { |  | 
| 225   return new JSInvocationMirror(name, |  | 
| 226                                 internalName, |  | 
| 227                                 kind, |  | 
| 228                                 arguments, |  | 
| 229                                 argumentNames); |  | 
| 230 } |  | 
| 231 |  | 
| 232 createUnmangledInvocationMirror(Symbol symbol, internalName, kind, arguments, |  | 
| 233                                 argumentNames) { |  | 
| 234   return new JSInvocationMirror(symbol, |  | 
| 235                                 internalName, |  | 
| 236                                 kind, |  | 
| 237                                 arguments, |  | 
| 238                                 argumentNames); |  | 
| 239 } |  | 
| 240 |  | 
| 241 void throwInvalidReflectionError(String memberName) { |  | 
| 242   throw new UnsupportedError("Can't use '$memberName' in reflection " |  | 
| 243       "because it is not included in a @MirrorsUsed annotation."); |  | 
| 244 } |  | 
| 245 |  | 
| 246 /// Helper to print the given method information to the console the first |  | 
| 247 /// time it is called with it. |  | 
| 248 @NoInline() |  | 
| 249 void traceHelper(String method) { |  | 
| 250   if (JS('bool', '!this.cache')) { |  | 
| 251     JS('', 'this.cache = Object.create(null)'); |  | 
| 252   } |  | 
| 253   if (JS('bool', '!this.cache[#]', method)) { |  | 
| 254     JS('', 'console.log(#)', method); |  | 
| 255     JS('', 'this.cache[#] = true', method); |  | 
| 256   } |  | 
| 257 } |  | 
| 258 |  | 
| 259 class JSInvocationMirror implements Invocation { |  | 
| 260   static const METHOD = 0; |  | 
| 261   static const GETTER = 1; |  | 
| 262   static const SETTER = 2; |  | 
| 263 |  | 
| 264   /// When [_memberName] is a String, it holds the mangled name of this |  | 
| 265   /// invocation.  When it is a Symbol, it holds the unmangled name. |  | 
| 266   var /* String or Symbol */ _memberName; |  | 
| 267   final String _internalName; |  | 
| 268   final int _kind; |  | 
| 269   final List _arguments; |  | 
| 270   final List _namedArgumentNames; |  | 
| 271   /** Map from argument name to index in _arguments. */ |  | 
| 272   Map<String, dynamic> _namedIndices = null; |  | 
| 273 |  | 
| 274   JSInvocationMirror(this._memberName, |  | 
| 275                      this._internalName, |  | 
| 276                      this._kind, |  | 
| 277                      this._arguments, |  | 
| 278                      this._namedArgumentNames); |  | 
| 279 |  | 
| 280   Symbol get memberName { |  | 
| 281     if (_memberName is Symbol) return _memberName; |  | 
| 282     String name = _memberName; |  | 
| 283     String unmangledName = mangledNames[name]; |  | 
| 284     if (unmangledName != null) { |  | 
| 285       name = unmangledName.split(':')[0]; |  | 
| 286     } else { |  | 
| 287       if (mangledNames[_internalName] == null) { |  | 
| 288         print("Warning: '$name' is used reflectively but not in MirrorsUsed. " |  | 
| 289               "This will break minified code."); |  | 
| 290       } |  | 
| 291     } |  | 
| 292     _memberName = new _symbol_dev.Symbol.unvalidated(name); |  | 
| 293     return _memberName; |  | 
| 294   } |  | 
| 295 |  | 
| 296   bool get isMethod => _kind == METHOD; |  | 
| 297   bool get isGetter => _kind == GETTER; |  | 
| 298   bool get isSetter => _kind == SETTER; |  | 
| 299   bool get isAccessor => _kind != METHOD; |  | 
| 300 |  | 
| 301   List get positionalArguments { |  | 
| 302     if (isGetter) return const []; |  | 
| 303     var argumentCount = _arguments.length - _namedArgumentNames.length; |  | 
| 304     if (argumentCount == 0) return const []; |  | 
| 305     var list = []; |  | 
| 306     for (var index = 0 ; index < argumentCount ; index++) { |  | 
| 307       list.add(_arguments[index]); |  | 
| 308     } |  | 
| 309     return JSArray.markUnmodifiableList(list); |  | 
| 310   } |  | 
| 311 |  | 
| 312   Map<Symbol, dynamic> get namedArguments { |  | 
| 313     if (isAccessor) return const <Symbol, dynamic>{}; |  | 
| 314     int namedArgumentCount = _namedArgumentNames.length; |  | 
| 315     int namedArgumentsStartIndex = _arguments.length - namedArgumentCount; |  | 
| 316     if (namedArgumentCount == 0) return const <Symbol, dynamic>{}; |  | 
| 317     var map = new Map<Symbol, dynamic>(); |  | 
| 318     for (int i = 0; i < namedArgumentCount; i++) { |  | 
| 319       map[new _symbol_dev.Symbol.unvalidated(_namedArgumentNames[i])] = |  | 
| 320           _arguments[namedArgumentsStartIndex + i]; |  | 
| 321     } |  | 
| 322     return new ConstantMapView<Symbol, dynamic>(map); |  | 
| 323   } |  | 
| 324 |  | 
| 325   _getCachedInvocation(Object object) { |  | 
| 326     var interceptor = getInterceptor(object); |  | 
| 327     var receiver = object; |  | 
| 328     var name = _internalName; |  | 
| 329     var arguments = _arguments; |  | 
| 330     var interceptedNames = JS_EMBEDDED_GLOBAL('', INTERCEPTED_NAMES); |  | 
| 331     bool isIntercepted = |  | 
| 332         JS("bool", 'Object.prototype.hasOwnProperty.call(#, #)', |  | 
| 333             interceptedNames, name); |  | 
| 334     if (isIntercepted) { |  | 
| 335       receiver = interceptor; |  | 
| 336       if (JS('bool', '# === #', object, interceptor)) { |  | 
| 337         interceptor = null; |  | 
| 338       } |  | 
| 339     } else { |  | 
| 340       interceptor = null; |  | 
| 341     } |  | 
| 342     bool isCatchAll = false; |  | 
| 343     var method = JS('var', '#[#]', receiver, name); |  | 
| 344     if (JS('bool', 'typeof # != "function"', method) ) { |  | 
| 345       String baseName = _symbol_dev.Symbol.getName(memberName); |  | 
| 346       method = JS('', '#[# + "*"]', receiver, baseName); |  | 
| 347       if (method == null) { |  | 
| 348         interceptor = getInterceptor(object); |  | 
| 349         method = JS('', '#[# + "*"]', interceptor, baseName); |  | 
| 350         if (method != null) { |  | 
| 351           isIntercepted = true; |  | 
| 352           receiver = interceptor; |  | 
| 353         } else { |  | 
| 354           interceptor = null; |  | 
| 355         } |  | 
| 356       } |  | 
| 357       isCatchAll = true; |  | 
| 358     } |  | 
| 359     if (JS('bool', 'typeof # == "function"', method)) { |  | 
| 360       if (isCatchAll) { |  | 
| 361         return new CachedCatchAllInvocation( |  | 
| 362             name, method, isIntercepted, interceptor); |  | 
| 363       } else { |  | 
| 364         return new CachedInvocation(name, method, isIntercepted, interceptor); |  | 
| 365       } |  | 
| 366     } else { |  | 
| 367       // In this case, receiver doesn't implement name.  So we should |  | 
| 368       // invoke noSuchMethod instead (which will often throw a |  | 
| 369       // NoSuchMethodError). |  | 
| 370       return new CachedNoSuchMethodInvocation(interceptor); |  | 
| 371     } |  | 
| 372   } |  | 
| 373 |  | 
| 374   /// This method is called by [InstanceMirror.delegate]. |  | 
| 375   static invokeFromMirror(JSInvocationMirror invocation, Object victim) { |  | 
| 376     var cached = invocation._getCachedInvocation(victim); |  | 
| 377     if (cached.isNoSuchMethod) { |  | 
| 378       return cached.invokeOn(victim, invocation); |  | 
| 379     } else { |  | 
| 380       return cached.invokeOn(victim, invocation._arguments); |  | 
| 381     } |  | 
| 382   } |  | 
| 383 |  | 
| 384   static getCachedInvocation(JSInvocationMirror invocation, Object victim) { |  | 
| 385     return invocation._getCachedInvocation(victim); |  | 
| 386   } |  | 
| 387 } |  | 
| 388 |  | 
| 389 class CachedInvocation { |  | 
| 390   // The mangled name of this invocation. |  | 
| 391   String mangledName; |  | 
| 392 |  | 
| 393   /// The JS function to call. |  | 
| 394   var jsFunction; |  | 
| 395 |  | 
| 396   /// True if this is an intercepted call. |  | 
| 397   bool isIntercepted; |  | 
| 398 |  | 
| 399   /// Non-null interceptor if this is an intercepted call through an |  | 
| 400   /// [Interceptor]. |  | 
| 401   Interceptor cachedInterceptor; |  | 
| 402 |  | 
| 403   CachedInvocation(this.mangledName, |  | 
| 404                    this.jsFunction, |  | 
| 405                    this.isIntercepted, |  | 
| 406                    this.cachedInterceptor); |  | 
| 407 |  | 
| 408   bool get isNoSuchMethod => false; |  | 
| 409   bool get isGetterStub => JS("bool", "!!#.\$getterStub", jsFunction); |  | 
| 410 |  | 
| 411   /// Applies [jsFunction] to [victim] with [arguments]. |  | 
| 412   /// Users of this class must take care to check the arguments first. |  | 
| 413   invokeOn(Object victim, List arguments) { |  | 
| 414     var receiver = victim; |  | 
| 415     if (!isIntercepted) { |  | 
| 416       if (arguments is! JSArray) arguments = new List.from(arguments); |  | 
| 417     } else { |  | 
| 418       arguments = [victim]..addAll(arguments); |  | 
| 419       if (cachedInterceptor != null) receiver = cachedInterceptor; |  | 
| 420     } |  | 
| 421     return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments); |  | 
| 422   } |  | 
| 423 } |  | 
| 424 |  | 
| 425 class CachedCatchAllInvocation extends CachedInvocation { |  | 
| 426   final ReflectionInfo info; |  | 
| 427 |  | 
| 428   CachedCatchAllInvocation(String name, |  | 
| 429                            jsFunction, |  | 
| 430                            bool isIntercepted, |  | 
| 431                            Interceptor cachedInterceptor) |  | 
| 432       : info = new ReflectionInfo(jsFunction), |  | 
| 433         super(name, jsFunction, isIntercepted, cachedInterceptor); |  | 
| 434 |  | 
| 435   bool get isGetterStub => false; |  | 
| 436 |  | 
| 437   invokeOn(Object victim, List arguments) { |  | 
| 438     var receiver = victim; |  | 
| 439     int providedArgumentCount; |  | 
| 440     int fullParameterCount = |  | 
| 441         info.requiredParameterCount + info.optionalParameterCount; |  | 
| 442     if (!isIntercepted) { |  | 
| 443       if (arguments is JSArray) { |  | 
| 444         providedArgumentCount = arguments.length; |  | 
| 445         // If we need to add extra arguments before calling, we have |  | 
| 446         // to copy the arguments array. |  | 
| 447         if (providedArgumentCount < fullParameterCount) { |  | 
| 448           arguments = new List.from(arguments); |  | 
| 449         } |  | 
| 450       } else { |  | 
| 451         arguments = new List.from(arguments); |  | 
| 452         providedArgumentCount = arguments.length; |  | 
| 453       } |  | 
| 454     } else { |  | 
| 455       arguments = [victim]..addAll(arguments); |  | 
| 456       if (cachedInterceptor != null) receiver = cachedInterceptor; |  | 
| 457       providedArgumentCount = arguments.length - 1; |  | 
| 458     } |  | 
| 459     if (info.areOptionalParametersNamed && |  | 
| 460         (providedArgumentCount > info.requiredParameterCount)) { |  | 
| 461       throw new UnimplementedNoSuchMethodError( |  | 
| 462           "Invocation of unstubbed method '${info.reflectionName}'" |  | 
| 463           " with ${arguments.length} arguments."); |  | 
| 464     } else if (providedArgumentCount < info.requiredParameterCount) { |  | 
| 465       throw new UnimplementedNoSuchMethodError( |  | 
| 466           "Invocation of unstubbed method '${info.reflectionName}'" |  | 
| 467           " with $providedArgumentCount arguments (too few)."); |  | 
| 468     } else if (providedArgumentCount > fullParameterCount) { |  | 
| 469       throw new UnimplementedNoSuchMethodError( |  | 
| 470           "Invocation of unstubbed method '${info.reflectionName}'" |  | 
| 471           " with $providedArgumentCount arguments (too many)."); |  | 
| 472     } |  | 
| 473     for (int i = providedArgumentCount; i < fullParameterCount; i++) { |  | 
| 474       arguments.add(getMetadata(info.defaultValue(i))); |  | 
| 475     } |  | 
| 476     return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments); |  | 
| 477   } |  | 
| 478 } |  | 
| 479 |  | 
| 480 class CachedNoSuchMethodInvocation { |  | 
| 481   /// Non-null interceptor if this is an intercepted call through an |  | 
| 482   /// [Interceptor]. |  | 
| 483   var interceptor; |  | 
| 484 |  | 
| 485   CachedNoSuchMethodInvocation(this.interceptor); |  | 
| 486 |  | 
| 487   bool get isNoSuchMethod => true; |  | 
| 488   bool get isGetterStub => false; |  | 
| 489 |  | 
| 490   invokeOn(Object victim, Invocation invocation) { |  | 
| 491     var receiver = (interceptor == null) ? victim : interceptor; |  | 
| 492     return receiver.noSuchMethod(invocation); |  | 
| 493   } |  | 
| 494 } |  | 
| 495 |  | 
| 496 class ReflectionInfo { |  | 
| 497   static const int REQUIRED_PARAMETERS_INFO = 0; |  | 
| 498   static const int OPTIONAL_PARAMETERS_INFO = 1; |  | 
| 499   static const int FUNCTION_TYPE_INDEX = 2; |  | 
| 500   static const int FIRST_DEFAULT_ARGUMENT = 3; |  | 
| 501 |  | 
| 502   /// A JavaScript function object. |  | 
| 503   final jsFunction; |  | 
| 504 |  | 
| 505   /// Raw reflection information. |  | 
| 506   final List data; |  | 
| 507 |  | 
| 508   /// Is this a getter or a setter. |  | 
| 509   final bool isAccessor; |  | 
| 510 |  | 
| 511   /// Number of required parameters. |  | 
| 512   final int requiredParameterCount; |  | 
| 513 |  | 
| 514   /// Number of optional parameters. |  | 
| 515   final int optionalParameterCount; |  | 
| 516 |  | 
| 517   /// Are optional parameters named. |  | 
| 518   final bool areOptionalParametersNamed; |  | 
| 519 |  | 
| 520   /// Either an index to the function type in the embedded `metadata` global or |  | 
| 521   /// a JavaScript function object which can compute such a type (presumably |  | 
| 522   /// due to free type variables). |  | 
| 523   final functionType; |  | 
| 524 |  | 
| 525   List cachedSortedIndices; |  | 
| 526 |  | 
| 527   ReflectionInfo.internal(this.jsFunction, |  | 
| 528                           this.data, |  | 
| 529                           this.isAccessor, |  | 
| 530                           this.requiredParameterCount, |  | 
| 531                           this.optionalParameterCount, |  | 
| 532                           this.areOptionalParametersNamed, |  | 
| 533                           this.functionType); |  | 
| 534 |  | 
| 535   factory ReflectionInfo(jsFunction) { |  | 
| 536     List data = JS('JSExtendableArray|Null', r'#.$reflectionInfo', jsFunction); |  | 
| 537     if (data == null) return null; |  | 
| 538     data = JSArray.markFixedList(data); |  | 
| 539 |  | 
| 540     int requiredParametersInfo = |  | 
| 541         JS('int', '#[#]', data, REQUIRED_PARAMETERS_INFO); |  | 
| 542     int requiredParameterCount = JS('int', '# >> 1', requiredParametersInfo); |  | 
| 543     bool isAccessor = (requiredParametersInfo & 1) == 1; |  | 
| 544 |  | 
| 545     int optionalParametersInfo = |  | 
| 546         JS('int', '#[#]', data, OPTIONAL_PARAMETERS_INFO); |  | 
| 547     int optionalParameterCount = JS('int', '# >> 1', optionalParametersInfo); |  | 
| 548     bool areOptionalParametersNamed = (optionalParametersInfo & 1) == 1; |  | 
| 549 |  | 
| 550     var functionType = JS('', '#[#]', data, FUNCTION_TYPE_INDEX); |  | 
| 551     return new ReflectionInfo.internal( |  | 
| 552         jsFunction, data, isAccessor, requiredParameterCount, |  | 
| 553         optionalParameterCount, areOptionalParametersNamed, functionType); |  | 
| 554   } |  | 
| 555 |  | 
| 556   String parameterName(int parameter) { |  | 
| 557     int metadataIndex; |  | 
| 558     if (JS_GET_FLAG('MUST_RETAIN_METADATA')) { |  | 
| 559       metadataIndex = JS('int', '#[2 * # + # + #]', data, |  | 
| 560           parameter, optionalParameterCount, FIRST_DEFAULT_ARGUMENT); |  | 
| 561     } else { |  | 
| 562       metadataIndex = JS('int', '#[# + # + #]', data, |  | 
| 563           parameter, optionalParameterCount, FIRST_DEFAULT_ARGUMENT); |  | 
| 564     } |  | 
| 565     var name = getMetadata(metadataIndex); |  | 
| 566     return JS('String', '#', name); |  | 
| 567   } |  | 
| 568 |  | 
| 569   List<int> parameterMetadataAnnotations(int parameter) { |  | 
| 570     if (!JS_GET_FLAG('MUST_RETAIN_METADATA')) { |  | 
| 571       throw new StateError('metadata has not been preserved'); |  | 
| 572     } else { |  | 
| 573       return JS('', '#[2 * # + # + # + 1]', data, parameter, |  | 
| 574           optionalParameterCount, FIRST_DEFAULT_ARGUMENT); |  | 
| 575     } |  | 
| 576   } |  | 
| 577 |  | 
| 578   int defaultValue(int parameter) { |  | 
| 579     if (parameter < requiredParameterCount) return null; |  | 
| 580     return JS('int', '#[# + # - #]', data, |  | 
| 581               FIRST_DEFAULT_ARGUMENT, parameter, requiredParameterCount); |  | 
| 582   } |  | 
| 583 |  | 
| 584   /// Returns the default value of the [parameter]th entry of the list of |  | 
| 585   /// parameters sorted by name. |  | 
| 586   int defaultValueInOrder(int parameter) { |  | 
| 587     if (parameter < requiredParameterCount) return null; |  | 
| 588 |  | 
| 589     if (!areOptionalParametersNamed || optionalParameterCount == 1) { |  | 
| 590       return defaultValue(parameter); |  | 
| 591     } |  | 
| 592 |  | 
| 593     int index = sortedIndex(parameter - requiredParameterCount); |  | 
| 594     return defaultValue(index); |  | 
| 595   } |  | 
| 596 |  | 
| 597   /// Returns the default value of the [parameter]th entry of the list of |  | 
| 598   /// parameters sorted by name. |  | 
| 599   String parameterNameInOrder(int parameter) { |  | 
| 600     if (parameter < requiredParameterCount) return null; |  | 
| 601 |  | 
| 602     if (!areOptionalParametersNamed || |  | 
| 603         optionalParameterCount == 1) { |  | 
| 604       return parameterName(parameter); |  | 
| 605     } |  | 
| 606 |  | 
| 607     int index = sortedIndex(parameter - requiredParameterCount); |  | 
| 608     return parameterName(index); |  | 
| 609   } |  | 
| 610 |  | 
| 611   /// Computes the index of the parameter in the list of named parameters sorted |  | 
| 612   /// by their name. |  | 
| 613   int sortedIndex(int unsortedIndex) { |  | 
| 614     if (cachedSortedIndices == null) { |  | 
| 615       // TODO(karlklose): cache this between [ReflectionInfo] instances or cache |  | 
| 616       // [ReflectionInfo] instances by [jsFunction]. |  | 
| 617       cachedSortedIndices = new List(optionalParameterCount); |  | 
| 618       Map<String, int> positions = <String, int>{}; |  | 
| 619       for (int i = 0; i < optionalParameterCount; i++) { |  | 
| 620         int index = requiredParameterCount + i; |  | 
| 621         positions[parameterName(index)] = index; |  | 
| 622       } |  | 
| 623       int index = 0; |  | 
| 624       (positions.keys.toList()..sort()).forEach((String name) { |  | 
| 625         cachedSortedIndices[index++] = positions[name]; |  | 
| 626       }); |  | 
| 627     } |  | 
| 628     return cachedSortedIndices[unsortedIndex]; |  | 
| 629   } |  | 
| 630 |  | 
| 631   @NoInline() |  | 
| 632   computeFunctionRti(jsConstructor) { |  | 
| 633     if (JS('bool', 'typeof # == "number"', functionType)) { |  | 
| 634       return getType(functionType); |  | 
| 635     } else if (JS('bool', 'typeof # == "function"', functionType)) { |  | 
| 636       var fakeInstance = JS('', 'new #()', jsConstructor); |  | 
| 637       setRuntimeTypeInfo( |  | 
| 638           fakeInstance, JS('JSExtendableArray', '#["<>"]', fakeInstance)); |  | 
| 639       return JS('=Object|Null', r'#.apply({$receiver:#})', |  | 
| 640                 functionType, fakeInstance); |  | 
| 641     } else { |  | 
| 642       throw new RuntimeError('Unexpected function type'); |  | 
| 643     } |  | 
| 644   } |  | 
| 645 |  | 
| 646   String get reflectionName => JS('String', r'#.$reflectionName', jsFunction); |  | 
| 647 } |  | 
| 648 |  | 
| 649 class Primitives { |  | 
| 650   /// Isolate-unique ID for caching [JsClosureMirror.function]. |  | 
| 651   /// Note the initial value is used by the first isolate (or if there are no |  | 
| 652   /// isolates), new isolates will update this value to avoid conflicts by |  | 
| 653   /// calling [initializeStatics]. |  | 
| 654   static String mirrorFunctionCacheName = '\$cachedFunction'; |  | 
| 655 |  | 
| 656   /// Isolate-unique ID for caching [JsInstanceMirror._invoke]. |  | 
| 657   static String mirrorInvokeCacheName = '\$cachedInvocation'; |  | 
| 658 |  | 
| 659   /// Called when creating a new isolate (see _IsolateContext constructor in |  | 
| 660   /// isolate_helper.dart). |  | 
| 661   /// Please don't add complicated code to this method, as it will impact |  | 
| 662   /// start-up performance. |  | 
| 663   static void initializeStatics(int id) { |  | 
| 664     // Benchmarking shows significant performance improvements if this is a |  | 
| 665     // fixed value. |  | 
| 666     mirrorFunctionCacheName += '_$id'; |  | 
| 667     mirrorInvokeCacheName += '_$id'; |  | 
| 668   } |  | 
| 669 |  | 
| 670   static int objectHashCode(object) { |  | 
| 671     int hash = JS('int|Null', r'#.$identityHash', object); |  | 
| 672     if (hash == null) { |  | 
| 673       hash = JS('int', '(Math.random() * 0x3fffffff) | 0'); |  | 
| 674       JS('void', r'#.$identityHash = #', object, hash); |  | 
| 675     } |  | 
| 676     return JS('int', '#', hash); |  | 
| 677   } |  | 
| 678 |  | 
| 679   @NoInline() |  | 
| 680   static int _parseIntError(String source, int handleError(String source)) { |  | 
| 681     if (handleError == null) throw new FormatException(source); |  | 
| 682     return handleError(source); |  | 
| 683   } |  | 
| 684 |  | 
| 685   static int parseInt(String source, |  | 
| 686                       int radix, |  | 
| 687                       int handleError(String source)) { |  | 
| 688     checkString(source); |  | 
| 689     var re = JS('', r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i'); |  | 
| 690     var match = JS('JSExtendableArray|Null', '#.exec(#)', re, source); |  | 
| 691     int digitsIndex = 1; |  | 
| 692     int hexIndex = 2; |  | 
| 693     int decimalIndex = 3; |  | 
| 694     int nonDecimalHexIndex = 4; |  | 
| 695     if (match == null) { |  | 
| 696       // TODO(sra): It might be that the match failed due to unrecognized U+0085 |  | 
| 697       // spaces.  We could replace them with U+0020 spaces and try matching |  | 
| 698       // again. |  | 
| 699       return _parseIntError(source, handleError); |  | 
| 700     } |  | 
| 701     String decimalMatch = match[decimalIndex]; |  | 
| 702     if (radix == null) { |  | 
| 703       if (decimalMatch != null) { |  | 
| 704         // Cannot fail because we know that the digits are all decimal. |  | 
| 705         return JS('int', r'parseInt(#, 10)', source); |  | 
| 706       } |  | 
| 707       if (match[hexIndex] != null) { |  | 
| 708         // Cannot fail because we know that the digits are all hex. |  | 
| 709         return JS('int', r'parseInt(#, 16)', source); |  | 
| 710       } |  | 
| 711       return _parseIntError(source, handleError); |  | 
| 712     } |  | 
| 713 |  | 
| 714     if (radix is! int) { |  | 
| 715       throw new ArgumentError.value(radix, 'radix', 'is not an integer'); |  | 
| 716     } |  | 
| 717     if (radix < 2 || radix > 36) { |  | 
| 718       throw new RangeError.range(radix, 2, 36, 'radix'); |  | 
| 719     } |  | 
| 720     if (radix == 10 && decimalMatch != null) { |  | 
| 721       // Cannot fail because we know that the digits are all decimal. |  | 
| 722       return JS('int', r'parseInt(#, 10)', source); |  | 
| 723     } |  | 
| 724     // If radix >= 10 and we have only decimal digits the string is safe. |  | 
| 725     // Otherwise we need to check the digits. |  | 
| 726     if (radix < 10 || decimalMatch == null) { |  | 
| 727       // We know that the characters must be ASCII as otherwise the |  | 
| 728       // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus |  | 
| 729       // guaranteed to be a safe operation, since it preserves digits |  | 
| 730       // and lower-cases ASCII letters. |  | 
| 731       int maxCharCode; |  | 
| 732       if (radix <= 10) { |  | 
| 733         // Allow all digits less than the radix. For example 0, 1, 2 for |  | 
| 734         // radix 3. |  | 
| 735         // "0".codeUnitAt(0) + radix - 1; |  | 
| 736         maxCharCode = (0x30 - 1) + radix; |  | 
| 737       } else { |  | 
| 738         // Letters are located after the digits in ASCII. Therefore we |  | 
| 739         // only check for the character code. The regexp above made already |  | 
| 740         // sure that the string does not contain anything but digits or |  | 
| 741         // letters. |  | 
| 742         // "a".codeUnitAt(0) + (radix - 10) - 1; |  | 
| 743         maxCharCode = (0x61 - 10 - 1) + radix; |  | 
| 744       } |  | 
| 745       assert(match[digitsIndex] is String); |  | 
| 746       String digitsPart = JS('String', '#[#]', match, digitsIndex); |  | 
| 747       for (int i = 0; i < digitsPart.length; i++) { |  | 
| 748         int characterCode = digitsPart.codeUnitAt(i) | 0x20; |  | 
| 749         if (characterCode > maxCharCode) { |  | 
| 750           return _parseIntError(source, handleError); |  | 
| 751         } |  | 
| 752       } |  | 
| 753     } |  | 
| 754     // The above matching and checks ensures the source has at least one digits |  | 
| 755     // and all digits are suitable for the radix, so parseInt cannot return NaN. |  | 
| 756     return JS('int', r'parseInt(#, #)', source, radix); |  | 
| 757   } |  | 
| 758 |  | 
| 759   @NoInline() |  | 
| 760   static double _parseDoubleError(String source, |  | 
| 761                                   double handleError(String source)) { |  | 
| 762     if (handleError == null) { |  | 
| 763       throw new FormatException('Invalid double', source); |  | 
| 764     } |  | 
| 765     return handleError(source); |  | 
| 766   } |  | 
| 767 |  | 
| 768   static double parseDouble(String source, double handleError(String source)) { |  | 
| 769     checkString(source); |  | 
| 770     // Notice that JS parseFloat accepts garbage at the end of the string. |  | 
| 771     // Accept only: |  | 
| 772     // - [+/-]NaN |  | 
| 773     // - [+/-]Infinity |  | 
| 774     // - a Dart double literal |  | 
| 775     // We do allow leading or trailing whitespace. |  | 
| 776     if (!JS('bool', |  | 
| 777             r'/^\s*[+-]?(?:Infinity|NaN|' |  | 
| 778                 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', |  | 
| 779             source)) { |  | 
| 780       return _parseDoubleError(source, handleError); |  | 
| 781     } |  | 
| 782     var result = JS('num', r'parseFloat(#)', source); |  | 
| 783     if (result.isNaN) { |  | 
| 784       var trimmed = source.trim(); |  | 
| 785       if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { |  | 
| 786         return result; |  | 
| 787       } |  | 
| 788       return _parseDoubleError(source, handleError); |  | 
| 789     } |  | 
| 790     return result; |  | 
| 791   } |  | 
| 792 |  | 
| 793   /** [: r"$".codeUnitAt(0) :] */ |  | 
| 794   static const int DOLLAR_CHAR_VALUE = 36; |  | 
| 795 |  | 
| 796   /// Creates a string containing the complete type for the class [className] |  | 
| 797   /// with the given type arguments. |  | 
| 798   /// |  | 
| 799   /// In minified mode, uses the unminified names if available. |  | 
| 800   /// |  | 
| 801   /// The given [className] string generally contains the name of the JavaScript |  | 
| 802   /// constructor of the given class. |  | 
| 803   static String formatType(String className, List typeArguments) { |  | 
| 804     return unmangleAllIdentifiersIfPreservedAnyways |  | 
| 805         ('$className${joinArguments(typeArguments, 0)}'); |  | 
| 806   } |  | 
| 807 |  | 
| 808   /// Returns the type of [object] as a string (including type arguments). |  | 
| 809   /// |  | 
| 810   /// In minified mode, uses the unminified names if available. |  | 
| 811   static String objectTypeName(Object object) { |  | 
| 812     String name = constructorNameFallback(getInterceptor(object)); |  | 
| 813     if (name == 'Object') { |  | 
| 814       // Try to decompile the constructor by turning it into a string and get |  | 
| 815       // the name out of that. If the decompiled name is a string containing an |  | 
| 816       // identifier, we use that instead of the very generic 'Object'. |  | 
| 817       var decompiled = |  | 
| 818           JS('var', r'#.match(/^\s*function\s*([\w$]*)\s*\(/)[1]', |  | 
| 819               JS('var', r'String(#.constructor)', object)); |  | 
| 820       if (decompiled is String) |  | 
| 821         if (JS('bool', r'/^\w+$/.test(#)', decompiled)) |  | 
| 822           name = decompiled; |  | 
| 823     } |  | 
| 824     // TODO(kasperl): If the namer gave us a fresh global name, we may |  | 
| 825     // want to remove the numeric suffix that makes it unique too. |  | 
| 826     if (name.length > 1 && identical(name.codeUnitAt(0), DOLLAR_CHAR_VALUE)) { |  | 
| 827       name = name.substring(1); |  | 
| 828     } |  | 
| 829     return formatType(name, getRuntimeTypeInfo(object)); |  | 
| 830   } |  | 
| 831 |  | 
| 832   /// In minified mode, uses the unminified names if available. |  | 
| 833   static String objectToHumanReadableString(Object object) { |  | 
| 834     String name = objectTypeName(object); |  | 
| 835     return "Instance of '$name'"; |  | 
| 836   } |  | 
| 837 |  | 
| 838   static num dateNow() => JS('int', r'Date.now()'); |  | 
| 839 |  | 
| 840   static void initTicker() { |  | 
| 841     if (timerFrequency != null) return; |  | 
| 842     // Start with low-resolution. We overwrite the fields if we find better. |  | 
| 843     timerFrequency = 1000; |  | 
| 844     timerTicks = dateNow; |  | 
| 845     if (JS('bool', 'typeof window == "undefined"')) return; |  | 
| 846     var window = JS('var', 'window'); |  | 
| 847     if (window == null) return; |  | 
| 848     var performance = JS('var', '#.performance', window); |  | 
| 849     if (performance == null) return; |  | 
| 850     if (JS('bool', 'typeof #.now != "function"', performance)) return; |  | 
| 851     timerFrequency = 1000000; |  | 
| 852     timerTicks = () => (1000 * JS('num', '#.now()', performance)).floor(); |  | 
| 853   } |  | 
| 854 |  | 
| 855   static int timerFrequency; |  | 
| 856   static Function timerTicks; |  | 
| 857 |  | 
| 858   static String currentUri() { |  | 
| 859     requiresPreamble(); |  | 
| 860     // In a browser return self.location.href. |  | 
| 861     if (JS('bool', '!!self.location')) { |  | 
| 862       return JS('String', 'self.location.href'); |  | 
| 863     } |  | 
| 864 |  | 
| 865     return null; |  | 
| 866   } |  | 
| 867 |  | 
| 868   // This is to avoid stack overflows due to very large argument arrays in |  | 
| 869   // apply().  It fixes http://dartbug.com/6919 |  | 
| 870   static String _fromCharCodeApply(List<int> array) { |  | 
| 871     const kMaxApply = 500; |  | 
| 872     int end = array.length; |  | 
| 873     if (end <= kMaxApply) { |  | 
| 874       return JS('String', r'String.fromCharCode.apply(null, #)', array); |  | 
| 875     } |  | 
| 876     String result = ''; |  | 
| 877     for (int i = 0; i < end; i += kMaxApply) { |  | 
| 878       int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; |  | 
| 879       result = JS('String', |  | 
| 880           r'# + String.fromCharCode.apply(null, #.slice(#, #))', |  | 
| 881           result, array, i, chunkEnd); |  | 
| 882     } |  | 
| 883     return result; |  | 
| 884   } |  | 
| 885 |  | 
| 886   static String stringFromCodePoints(codePoints) { |  | 
| 887     List<int> a = <int>[]; |  | 
| 888     for (var i in codePoints) { |  | 
| 889       if (i is !int) throw argumentErrorValue(i); |  | 
| 890       if (i <= 0xffff) { |  | 
| 891         a.add(i); |  | 
| 892       } else if (i <= 0x10ffff) { |  | 
| 893         a.add(0xd800 + ((((i - 0x10000) >> 10) & 0x3ff))); |  | 
| 894         a.add(0xdc00 + (i & 0x3ff)); |  | 
| 895       } else { |  | 
| 896         throw argumentErrorValue(i); |  | 
| 897       } |  | 
| 898     } |  | 
| 899     return _fromCharCodeApply(a); |  | 
| 900   } |  | 
| 901 |  | 
| 902   static String stringFromCharCodes(charCodes) { |  | 
| 903     for (var i in charCodes) { |  | 
| 904       if (i is !int) throw argumentErrorValue(i); |  | 
| 905       if (i < 0) throw argumentErrorValue(i); |  | 
| 906       if (i > 0xffff) return stringFromCodePoints(charCodes); |  | 
| 907     } |  | 
| 908     return _fromCharCodeApply(charCodes); |  | 
| 909   } |  | 
| 910 |  | 
| 911   // [start] and [end] are validated. |  | 
| 912   static String stringFromNativeUint8List( |  | 
| 913       NativeUint8List charCodes, int start, int end) { |  | 
| 914     const kMaxApply = 500; |  | 
| 915     if (end <= kMaxApply && start == 0 && end == charCodes.length) { |  | 
| 916       return JS('String', r'String.fromCharCode.apply(null, #)', charCodes); |  | 
| 917     } |  | 
| 918     String result = ''; |  | 
| 919     for (int i = start; i < end; i += kMaxApply) { |  | 
| 920       int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; |  | 
| 921       result = JS('String', |  | 
| 922           r'# + String.fromCharCode.apply(null, #.subarray(#, #))', |  | 
| 923           result, charCodes, i, chunkEnd); |  | 
| 924     } |  | 
| 925     return result; |  | 
| 926   } |  | 
| 927 |  | 
| 928 |  | 
| 929   static String stringFromCharCode(charCode) { |  | 
| 930     if (0 <= charCode) { |  | 
| 931       if (charCode <= 0xffff) { |  | 
| 932         return JS('String', 'String.fromCharCode(#)', charCode); |  | 
| 933       } |  | 
| 934       if (charCode <= 0x10ffff) { |  | 
| 935         var bits = charCode - 0x10000; |  | 
| 936         var low = 0xDC00 | (bits & 0x3ff); |  | 
| 937         var high = 0xD800 | (bits >> 10); |  | 
| 938         return  JS('String', 'String.fromCharCode(#, #)', high, low); |  | 
| 939       } |  | 
| 940     } |  | 
| 941     throw new RangeError.range(charCode, 0, 0x10ffff); |  | 
| 942   } |  | 
| 943 |  | 
| 944   static String stringConcatUnchecked(String string1, String string2) { |  | 
| 945     return JS_STRING_CONCAT(string1, string2); |  | 
| 946   } |  | 
| 947 |  | 
| 948   static String flattenString(String str) { |  | 
| 949     return JS('String', "#.charCodeAt(0) == 0 ? # : #", str, str, str); |  | 
| 950   } |  | 
| 951 |  | 
| 952   static String getTimeZoneName(receiver) { |  | 
| 953     // Firefox and Chrome emit the timezone in parenthesis. |  | 
| 954     // Example: "Wed May 16 2012 21:13:00 GMT+0200 (CEST)". |  | 
| 955     // We extract this name using a regexp. |  | 
| 956     var d = lazyAsJsDate(receiver); |  | 
| 957     List match = JS('JSArray|Null', r'/\((.*)\)/.exec(#.toString())', d); |  | 
| 958     if (match != null) return match[1]; |  | 
| 959 |  | 
| 960     // Internet Explorer 10+ emits the zone name without parenthesis: |  | 
| 961     // Example: Thu Oct 31 14:07:44 PDT 2013 |  | 
| 962     match = JS('JSArray|Null', |  | 
| 963                 // Thu followed by a space. |  | 
| 964                 r'/^[A-Z,a-z]{3}\s' |  | 
| 965                 // Oct 31 followed by space. |  | 
| 966                 r'[A-Z,a-z]{3}\s\d+\s' |  | 
| 967                 // Time followed by a space. |  | 
| 968                 r'\d{2}:\d{2}:\d{2}\s' |  | 
| 969                 // The time zone name followed by a space. |  | 
| 970                 r'([A-Z]{3,5})\s' |  | 
| 971                 // The year. |  | 
| 972                 r'\d{4}$/' |  | 
| 973                 '.exec(#.toString())', |  | 
| 974                 d); |  | 
| 975     if (match != null) return match[1]; |  | 
| 976 |  | 
| 977     // IE 9 and Opera don't provide the zone name. We fall back to emitting the |  | 
| 978     // UTC/GMT offset. |  | 
| 979     // Example (IE9): Wed Nov 20 09:51:00 UTC+0100 2013 |  | 
| 980     //       (Opera): Wed Nov 20 2013 11:03:38 GMT+0100 |  | 
| 981     match = JS('JSArray|Null', r'/(?:GMT|UTC)[+-]\d{4}/.exec(#.toString())', d); |  | 
| 982     if (match != null) return match[0]; |  | 
| 983     return ""; |  | 
| 984   } |  | 
| 985 |  | 
| 986   static int getTimeZoneOffsetInMinutes(receiver) { |  | 
| 987     // Note that JS and Dart disagree on the sign of the offset. |  | 
| 988     return -JS('int', r'#.getTimezoneOffset()', lazyAsJsDate(receiver)); |  | 
| 989   } |  | 
| 990 |  | 
| 991   static valueFromDecomposedDate(years, month, day, hours, minutes, seconds, |  | 
| 992                                  milliseconds, isUtc) { |  | 
| 993     final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; |  | 
| 994     checkInt(years); |  | 
| 995     checkInt(month); |  | 
| 996     checkInt(day); |  | 
| 997     checkInt(hours); |  | 
| 998     checkInt(minutes); |  | 
| 999     checkInt(seconds); |  | 
| 1000     checkInt(milliseconds); |  | 
| 1001     checkBool(isUtc); |  | 
| 1002     var jsMonth = month - 1; |  | 
| 1003     var value; |  | 
| 1004     if (isUtc) { |  | 
| 1005       value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', |  | 
| 1006                  years, jsMonth, day, hours, minutes, seconds, milliseconds); |  | 
| 1007     } else { |  | 
| 1008       value = JS('num', r'new Date(#, #, #, #, #, #, #).valueOf()', |  | 
| 1009                  years, jsMonth, day, hours, minutes, seconds, milliseconds); |  | 
| 1010     } |  | 
| 1011     if (value.isNaN || |  | 
| 1012         value < -MAX_MILLISECONDS_SINCE_EPOCH || |  | 
| 1013         value > MAX_MILLISECONDS_SINCE_EPOCH) { |  | 
| 1014       return null; |  | 
| 1015     } |  | 
| 1016     if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc); |  | 
| 1017     return value; |  | 
| 1018   } |  | 
| 1019 |  | 
| 1020   static patchUpY2K(value, years, isUtc) { |  | 
| 1021     var date = JS('', r'new Date(#)', value); |  | 
| 1022     if (isUtc) { |  | 
| 1023       JS('num', r'#.setUTCFullYear(#)', date, years); |  | 
| 1024     } else { |  | 
| 1025       JS('num', r'#.setFullYear(#)', date, years); |  | 
| 1026     } |  | 
| 1027     return JS('num', r'#.valueOf()', date); |  | 
| 1028   } |  | 
| 1029 |  | 
| 1030   // Lazily keep a JS Date stored in the JS object. |  | 
| 1031   static lazyAsJsDate(receiver) { |  | 
| 1032     if (JS('bool', r'#.date === (void 0)', receiver)) { |  | 
| 1033       JS('void', r'#.date = new Date(#)', receiver, |  | 
| 1034          receiver.millisecondsSinceEpoch); |  | 
| 1035     } |  | 
| 1036     return JS('var', r'#.date', receiver); |  | 
| 1037   } |  | 
| 1038 |  | 
| 1039   // The getters for date and time parts below add a positive integer to ensure |  | 
| 1040   // that the result is really an integer, because the JavaScript implementation |  | 
| 1041   // may return -0.0 instead of 0. |  | 
| 1042 |  | 
| 1043   static getYear(receiver) { |  | 
| 1044     return (receiver.isUtc) |  | 
| 1045       ? JS('int', r'(#.getUTCFullYear() + 0)', lazyAsJsDate(receiver)) |  | 
| 1046       : JS('int', r'(#.getFullYear() + 0)', lazyAsJsDate(receiver)); |  | 
| 1047   } |  | 
| 1048 |  | 
| 1049   static getMonth(receiver) { |  | 
| 1050     return (receiver.isUtc) |  | 
| 1051       ? JS('JSUInt31', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver)) |  | 
| 1052       : JS('JSUInt31', r'#.getMonth() + 1', lazyAsJsDate(receiver)); |  | 
| 1053   } |  | 
| 1054 |  | 
| 1055   static getDay(receiver) { |  | 
| 1056     return (receiver.isUtc) |  | 
| 1057       ? JS('JSUInt31', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver)) |  | 
| 1058       : JS('JSUInt31', r'(#.getDate() + 0)', lazyAsJsDate(receiver)); |  | 
| 1059   } |  | 
| 1060 |  | 
| 1061   static getHours(receiver) { |  | 
| 1062     return (receiver.isUtc) |  | 
| 1063       ? JS('JSUInt31', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver)) |  | 
| 1064       : JS('JSUInt31', r'(#.getHours() + 0)', lazyAsJsDate(receiver)); |  | 
| 1065   } |  | 
| 1066 |  | 
| 1067   static getMinutes(receiver) { |  | 
| 1068     return (receiver.isUtc) |  | 
| 1069       ? JS('JSUInt31', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver)) |  | 
| 1070       : JS('JSUInt31', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver)); |  | 
| 1071   } |  | 
| 1072 |  | 
| 1073   static getSeconds(receiver) { |  | 
| 1074     return (receiver.isUtc) |  | 
| 1075       ? JS('JSUInt31', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver)) |  | 
| 1076       : JS('JSUInt31', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver)); |  | 
| 1077   } |  | 
| 1078 |  | 
| 1079   static getMilliseconds(receiver) { |  | 
| 1080     return (receiver.isUtc) |  | 
| 1081       ? JS('JSUInt31', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver)) |  | 
| 1082       : JS('JSUInt31', r'(#.getMilliseconds() + 0)', lazyAsJsDate(receiver)); |  | 
| 1083   } |  | 
| 1084 |  | 
| 1085   static getWeekday(receiver) { |  | 
| 1086     int weekday = (receiver.isUtc) |  | 
| 1087       ? JS('int', r'#.getUTCDay() + 0', lazyAsJsDate(receiver)) |  | 
| 1088       : JS('int', r'#.getDay() + 0', lazyAsJsDate(receiver)); |  | 
| 1089     // Adjust by one because JS weeks start on Sunday. |  | 
| 1090     return (weekday + 6) % 7 + 1; |  | 
| 1091   } |  | 
| 1092 |  | 
| 1093   static valueFromDateString(str) { |  | 
| 1094     if (str is !String) throw argumentErrorValue(str); |  | 
| 1095     var value = JS('num', r'Date.parse(#)', str); |  | 
| 1096     if (value.isNaN) throw argumentErrorValue(str); |  | 
| 1097     return value; |  | 
| 1098   } |  | 
| 1099 |  | 
| 1100   static getProperty(object, key) { |  | 
| 1101     if (object == null || object is bool || object is num || object is String) { |  | 
| 1102       throw argumentErrorValue(object); |  | 
| 1103     } |  | 
| 1104     return JS('var', '#[#]', object, key); |  | 
| 1105   } |  | 
| 1106 |  | 
| 1107   static void setProperty(object, key, value) { |  | 
| 1108     if (object == null || object is bool || object is num || object is String) { |  | 
| 1109       throw argumentErrorValue(object); |  | 
| 1110     } |  | 
| 1111     JS('void', '#[#] = #', object, key, value); |  | 
| 1112   } |  | 
| 1113 |  | 
| 1114   static functionNoSuchMethod(function, |  | 
| 1115                               List positionalArguments, |  | 
| 1116                               Map<String, dynamic> namedArguments) { |  | 
| 1117     int argumentCount = 0; |  | 
| 1118     List arguments = []; |  | 
| 1119     List namedArgumentList = []; |  | 
| 1120 |  | 
| 1121     if (positionalArguments != null) { |  | 
| 1122       argumentCount += positionalArguments.length; |  | 
| 1123       arguments.addAll(positionalArguments); |  | 
| 1124     } |  | 
| 1125 |  | 
| 1126     String names = ''; |  | 
| 1127     if (namedArguments != null && !namedArguments.isEmpty) { |  | 
| 1128       namedArguments.forEach((String name, argument) { |  | 
| 1129         names = '$names\$$name'; |  | 
| 1130         namedArgumentList.add(name); |  | 
| 1131         arguments.add(argument); |  | 
| 1132         argumentCount++; |  | 
| 1133       }); |  | 
| 1134     } |  | 
| 1135 |  | 
| 1136     String selectorName = |  | 
| 1137       '${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$$argumentCount$names'; |  | 
| 1138 |  | 
| 1139     return function.noSuchMethod( |  | 
| 1140         createUnmangledInvocationMirror( |  | 
| 1141             #call, |  | 
| 1142             selectorName, |  | 
| 1143             JSInvocationMirror.METHOD, |  | 
| 1144             arguments, |  | 
| 1145             namedArgumentList)); |  | 
| 1146   } |  | 
| 1147 |  | 
| 1148   static applyFunctionNewEmitter(Function function, |  | 
| 1149                                  List positionalArguments, |  | 
| 1150                                  Map<String, dynamic> namedArguments) { |  | 
| 1151     if (namedArguments == null) { |  | 
| 1152       int requiredParameterCount = JS('int', r'#[#]', function, |  | 
| 1153           JS_GET_NAME(JsGetName.REQUIRED_PARAMETER_PROPERTY)); |  | 
| 1154       int argumentCount = positionalArguments.length; |  | 
| 1155       if (argumentCount < requiredParameterCount) { |  | 
| 1156         return functionNoSuchMethod(function, positionalArguments, null); |  | 
| 1157       } |  | 
| 1158       String selectorName = |  | 
| 1159           '${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$$argumentCount'; |  | 
| 1160       var jsStub = JS('var', r'#[#]', function, selectorName); |  | 
| 1161       if (jsStub == null) { |  | 
| 1162         // Do a dynamic call. |  | 
| 1163         var interceptor = getInterceptor(function); |  | 
| 1164         var jsFunction = JS('', '#[#]', interceptor, |  | 
| 1165             JS_GET_NAME(JsGetName.CALL_CATCH_ALL)); |  | 
| 1166         var defaultValues = JS('var', r'#[#]', function, |  | 
| 1167             JS_GET_NAME(JsGetName.DEFAULT_VALUES_PROPERTY)); |  | 
| 1168         if (!JS('bool', '# instanceof Array', defaultValues)) { |  | 
| 1169           // The function expects named arguments! |  | 
| 1170           return functionNoSuchMethod(function, positionalArguments, null); |  | 
| 1171         } |  | 
| 1172         int defaultsLength = JS('int', "#.length", defaultValues); |  | 
| 1173         int maxArguments = requiredParameterCount + defaultsLength; |  | 
| 1174         if (argumentCount > maxArguments) { |  | 
| 1175           // The function expects less arguments! |  | 
| 1176           return functionNoSuchMethod(function, positionalArguments, null); |  | 
| 1177         } |  | 
| 1178         List arguments = new List.from(positionalArguments); |  | 
| 1179         List missingDefaults = JS('JSArray', '#.slice(#)', defaultValues, |  | 
| 1180             argumentCount - requiredParameterCount); |  | 
| 1181         arguments.addAll(missingDefaults); |  | 
| 1182         return JS('var', '#.apply(#, #)', jsFunction, function, arguments); |  | 
| 1183       } |  | 
| 1184       return JS('var', '#.apply(#, #)', jsStub, function, positionalArguments); |  | 
| 1185     } else { |  | 
| 1186       var interceptor = getInterceptor(function); |  | 
| 1187       var jsFunction = JS('', '#[#]', interceptor, |  | 
| 1188           JS_GET_NAME(JsGetName.CALL_CATCH_ALL)); |  | 
| 1189       var defaultValues = JS('JSArray', r'#[#]', function, |  | 
| 1190           JS_GET_NAME(JsGetName.DEFAULT_VALUES_PROPERTY)); |  | 
| 1191       List keys = JS('JSArray', r'Object.keys(#)', defaultValues); |  | 
| 1192       List arguments = new List.from(positionalArguments); |  | 
| 1193       int used = 0; |  | 
| 1194       for (String key in keys) { |  | 
| 1195         var value = namedArguments[key]; |  | 
| 1196         if (value != null) { |  | 
| 1197           used++; |  | 
| 1198           arguments.add(value); |  | 
| 1199         } else { |  | 
| 1200           arguments.add(JS('var', r'#[#]', defaultValues, key)); |  | 
| 1201         } |  | 
| 1202       } |  | 
| 1203       if (used != namedArguments.length) { |  | 
| 1204         return functionNoSuchMethod(function, positionalArguments, |  | 
| 1205             namedArguments); |  | 
| 1206       } |  | 
| 1207       return JS('var', r'#.apply(#, #)', jsFunction, function, arguments); |  | 
| 1208     } |  | 
| 1209   } |  | 
| 1210 |  | 
| 1211   static applyFunction(Function function, |  | 
| 1212                        List positionalArguments, |  | 
| 1213                        Map<String, dynamic> namedArguments) { |  | 
| 1214     // Dispatch on presence of named arguments to improve tree-shaking. |  | 
| 1215     // |  | 
| 1216     // This dispatch is as simple as possible to help the compiler detect the |  | 
| 1217     // common case of `null` namedArguments, either via inlining or |  | 
| 1218     // specialization. |  | 
| 1219     return namedArguments == null |  | 
| 1220         ? applyFunctionWithPositionalArguments( |  | 
| 1221             function, positionalArguments) |  | 
| 1222         : applyFunctionWithNamedArguments( |  | 
| 1223             function, positionalArguments, namedArguments); |  | 
| 1224   } |  | 
| 1225 |  | 
| 1226   static applyFunctionWithPositionalArguments(Function function, |  | 
| 1227                                               List positionalArguments) { |  | 
| 1228     List arguments; |  | 
| 1229 |  | 
| 1230     if (positionalArguments != null) { |  | 
| 1231       if (JS('bool', '# instanceof Array', positionalArguments)) { |  | 
| 1232         arguments = JS('JSArray', '#', positionalArguments); |  | 
| 1233       } else { |  | 
| 1234         arguments = new List.from(positionalArguments); |  | 
| 1235       } |  | 
| 1236     } else { |  | 
| 1237       arguments = []; |  | 
| 1238     } |  | 
| 1239 |  | 
| 1240     if (arguments.length == 0) { |  | 
| 1241       String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX0); |  | 
| 1242       if (JS('bool', '!!#[#]', function, selectorName)) { |  | 
| 1243         return JS('', '#[#]()', function, selectorName); |  | 
| 1244       } |  | 
| 1245     } else if (arguments.length == 1) { |  | 
| 1246       String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX1); |  | 
| 1247       if (JS('bool', '!!#[#]', function, selectorName)) { |  | 
| 1248         return JS('', '#[#](#[0])', function, selectorName, arguments); |  | 
| 1249       } |  | 
| 1250     } else if (arguments.length == 2) { |  | 
| 1251       String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX2); |  | 
| 1252       if (JS('bool', '!!#[#]', function, selectorName)) { |  | 
| 1253         return JS('', '#[#](#[0],#[1])', function, selectorName, |  | 
| 1254             arguments, arguments); |  | 
| 1255       } |  | 
| 1256     } else if (arguments.length == 3) { |  | 
| 1257       String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX3); |  | 
| 1258       if (JS('bool', '!!#[#]', function, selectorName)) { |  | 
| 1259         return JS('', '#[#](#[0],#[1],#[2])', function, selectorName, |  | 
| 1260             arguments, arguments, arguments); |  | 
| 1261       } |  | 
| 1262     } |  | 
| 1263     return _genericApplyFunctionWithPositionalArguments(function, arguments); |  | 
| 1264   } |  | 
| 1265 |  | 
| 1266   static _genericApplyFunctionWithPositionalArguments(Function function, |  | 
| 1267                                                       List arguments) { |  | 
| 1268     int argumentCount = arguments.length; |  | 
| 1269     String selectorName = |  | 
| 1270         '${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$$argumentCount'; |  | 
| 1271     var jsFunction = JS('var', '#[#]', function, selectorName); |  | 
| 1272     if (jsFunction == null) { |  | 
| 1273       var interceptor = getInterceptor(function); |  | 
| 1274       jsFunction = JS('', '#["call*"]', interceptor); |  | 
| 1275 |  | 
| 1276       if (jsFunction == null) { |  | 
| 1277         return functionNoSuchMethod(function, arguments, null); |  | 
| 1278       } |  | 
| 1279       ReflectionInfo info = new ReflectionInfo(jsFunction); |  | 
| 1280       int requiredArgumentCount = info.requiredParameterCount; |  | 
| 1281       int maxArgumentCount = requiredArgumentCount + |  | 
| 1282           info.optionalParameterCount; |  | 
| 1283       if (info.areOptionalParametersNamed || |  | 
| 1284           requiredArgumentCount > argumentCount || |  | 
| 1285           maxArgumentCount < argumentCount) { |  | 
| 1286         return functionNoSuchMethod(function, arguments, null); |  | 
| 1287       } |  | 
| 1288       arguments = new List.from(arguments); |  | 
| 1289       for (int pos = argumentCount; pos < maxArgumentCount; pos++) { |  | 
| 1290         arguments.add(getMetadata(info.defaultValue(pos))); |  | 
| 1291       } |  | 
| 1292     } |  | 
| 1293     // We bound 'this' to [function] because of how we compile |  | 
| 1294     // closures: escaped local variables are stored and accessed through |  | 
| 1295     // [function]. |  | 
| 1296     return JS('var', '#.apply(#, #)', jsFunction, function, arguments); |  | 
| 1297   } |  | 
| 1298 |  | 
| 1299   static applyFunctionWithNamedArguments(Function function, |  | 
| 1300                                          List positionalArguments, |  | 
| 1301                                          Map<String, dynamic> namedArguments) { |  | 
| 1302     if (namedArguments.isEmpty) { |  | 
| 1303       return applyFunctionWithPositionalArguments( |  | 
| 1304           function, positionalArguments); |  | 
| 1305     } |  | 
| 1306     // TODO(ahe): The following code can be shared with |  | 
| 1307     // JsInstanceMirror.invoke. |  | 
| 1308     var interceptor = getInterceptor(function); |  | 
| 1309     var jsFunction = JS('', '#[#]', interceptor, |  | 
| 1310         JS_GET_NAME(JsGetName.CALL_CATCH_ALL)); |  | 
| 1311 |  | 
| 1312     if (jsFunction == null) { |  | 
| 1313       return functionNoSuchMethod( |  | 
| 1314           function, positionalArguments, namedArguments); |  | 
| 1315     } |  | 
| 1316     ReflectionInfo info = new ReflectionInfo(jsFunction); |  | 
| 1317     if (info == null || !info.areOptionalParametersNamed) { |  | 
| 1318       return functionNoSuchMethod( |  | 
| 1319           function, positionalArguments, namedArguments); |  | 
| 1320     } |  | 
| 1321 |  | 
| 1322     if (positionalArguments != null) { |  | 
| 1323       positionalArguments = new List.from(positionalArguments); |  | 
| 1324     } else { |  | 
| 1325       positionalArguments = []; |  | 
| 1326     } |  | 
| 1327     // Check the number of positional arguments is valid. |  | 
| 1328     if (info.requiredParameterCount != positionalArguments.length) { |  | 
| 1329       return functionNoSuchMethod( |  | 
| 1330           function, positionalArguments, namedArguments); |  | 
| 1331     } |  | 
| 1332     var defaultArguments = new Map(); |  | 
| 1333     for (int i = 0; i < info.optionalParameterCount; i++) { |  | 
| 1334       int index = i + info.requiredParameterCount; |  | 
| 1335       var parameterName = info.parameterNameInOrder(index); |  | 
| 1336       var value = info.defaultValueInOrder(index); |  | 
| 1337       var defaultValue = getMetadata(value); |  | 
| 1338       defaultArguments[parameterName] = defaultValue; |  | 
| 1339     } |  | 
| 1340     bool bad = false; |  | 
| 1341     namedArguments.forEach((String parameter, value) { |  | 
| 1342       if (defaultArguments.containsKey(parameter)) { |  | 
| 1343         defaultArguments[parameter] = value; |  | 
| 1344       } else { |  | 
| 1345         // Extraneous named argument. |  | 
| 1346         bad = true; |  | 
| 1347       } |  | 
| 1348     }); |  | 
| 1349     if (bad) { |  | 
| 1350       return functionNoSuchMethod( |  | 
| 1351           function, positionalArguments, namedArguments); |  | 
| 1352     } |  | 
| 1353     positionalArguments.addAll(defaultArguments.values); |  | 
| 1354     return JS('', '#.apply(#, #)', jsFunction, function, positionalArguments); |  | 
| 1355   } |  | 
| 1356 |  | 
| 1357   static bool identicalImplementation(a, b) { |  | 
| 1358     return JS('bool', '# == null', a) |  | 
| 1359       ? JS('bool', '# == null', b) |  | 
| 1360       : JS('bool', '# === #', a, b); |  | 
| 1361   } |  | 
| 1362 |  | 
| 1363   static StackTrace extractStackTrace(Error error) { |  | 
| 1364     return getTraceFromException(JS('', r'#.$thrownJsError', error)); |  | 
| 1365   } |  | 
| 1366 } |  | 
| 1367 |  | 
| 1368 /// Helper class for allocating and using JS object literals as caches. |  | 
| 1369 class JsCache { |  | 
| 1370   /// Returns a JavaScript object suitable for use as a cache. |  | 
| 1371   static allocate() { |  | 
| 1372     var result = JS('=Object', 'Object.create(null)'); |  | 
| 1373     // Deleting a property makes V8 assume that it shouldn't create a hidden |  | 
| 1374     // class for [result] and map transitions. Although these map transitions |  | 
| 1375     // pay off if there are many cache hits for the same keys, it becomes |  | 
| 1376     // really slow when there aren't many repeated hits. |  | 
| 1377     JS('void', '#.x=0', result); |  | 
| 1378     JS('void', 'delete #.x', result); |  | 
| 1379     return result; |  | 
| 1380   } |  | 
| 1381 |  | 
| 1382   static fetch(cache, String key) { |  | 
| 1383     return JS('', '#[#]', cache, key); |  | 
| 1384   } |  | 
| 1385 |  | 
| 1386   static void update(cache, String key, value) { |  | 
| 1387     JS('void', '#[#] = #', cache, key, value); |  | 
| 1388   } |  | 
| 1389 } |  | 
| 1390 |  | 
| 1391 /** |  | 
| 1392  * Called by generated code to throw an illegal-argument exception, |  | 
| 1393  * for example, if a non-integer index is given to an optimized |  | 
| 1394  * indexed access. |  | 
| 1395  */ |  | 
| 1396 @NoInline() |  | 
| 1397 iae(argument) { |  | 
| 1398   throw argumentErrorValue(argument); |  | 
| 1399 } |  | 
| 1400 |  | 
| 1401 /** |  | 
| 1402  * Called by generated code to throw an index-out-of-range exception, for |  | 
| 1403  * example, if a bounds check fails in an optimized indexed access.  This may |  | 
| 1404  * also be called when the index is not an integer, in which case it throws an |  | 
| 1405  * illegal-argument exception instead, like [iae], or when the receiver is null. |  | 
| 1406  */ |  | 
| 1407 @NoInline() |  | 
| 1408 ioore(receiver, index) { |  | 
| 1409   if (receiver == null) receiver.length; // Force a NoSuchMethodError. |  | 
| 1410   throw diagnoseIndexError(receiver, index); |  | 
| 1411 } |  | 
| 1412 |  | 
| 1413 /** |  | 
| 1414  * Diagnoses an indexing error. Returns the ArgumentError or RangeError that |  | 
| 1415  * describes the problem. |  | 
| 1416  */ |  | 
| 1417 @NoInline() |  | 
| 1418 Error diagnoseIndexError(indexable, index) { |  | 
| 1419   if (index is !int) return new ArgumentError.value(index, 'index'); |  | 
| 1420   int length = indexable.length; |  | 
| 1421   // The following returns the same error that would be thrown by calling |  | 
| 1422   // [RangeError.checkValidIndex] with no optional parameters provided. |  | 
| 1423   if (index < 0 || index >= length) { |  | 
| 1424     return new RangeError.index(index, indexable, 'index', null, length); |  | 
| 1425   } |  | 
| 1426   // The above should always match, but if it does not, use the following. |  | 
| 1427   return new RangeError.value(index, 'index'); |  | 
| 1428 } |  | 
| 1429 |  | 
| 1430 |  | 
| 1431 stringLastIndexOfUnchecked(receiver, element, start) |  | 
| 1432   => JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); |  | 
| 1433 |  | 
| 1434 |  | 
| 1435 /// 'factory' for constructing ArgumentError.value to keep the call sites small. |  | 
| 1436 @NoInline() |  | 
| 1437 ArgumentError argumentErrorValue(object) { |  | 
| 1438   return new ArgumentError.value(object); |  | 
| 1439 } |  | 
| 1440 |  | 
| 1441 checkNull(object) { |  | 
| 1442   if (object == null) throw argumentErrorValue(object); |  | 
| 1443   return object; |  | 
| 1444 } |  | 
| 1445 |  | 
| 1446 checkNum(value) { |  | 
| 1447   if (value is !num) throw argumentErrorValue(value); |  | 
| 1448   return value; |  | 
| 1449 } |  | 
| 1450 |  | 
| 1451 checkInt(value) { |  | 
| 1452   if (value is !int) throw argumentErrorValue(value); |  | 
| 1453   return value; |  | 
| 1454 } |  | 
| 1455 |  | 
| 1456 checkBool(value) { |  | 
| 1457   if (value is !bool) throw argumentErrorValue(value); |  | 
| 1458   return value; |  | 
| 1459 } |  | 
| 1460 |  | 
| 1461 checkString(value) { |  | 
| 1462   if (value is !String) throw argumentErrorValue(value); |  | 
| 1463   return value; |  | 
| 1464 } |  | 
| 1465 |  | 
| 1466 /** |  | 
| 1467  * Wrap the given Dart object and record a stack trace. |  | 
| 1468  * |  | 
| 1469  * The code in [unwrapException] deals with getting the original Dart |  | 
| 1470  * object out of the wrapper again. |  | 
| 1471  */ |  | 
| 1472 @NoInline() |  | 
| 1473 wrapException(ex) { |  | 
| 1474   if (ex == null) ex = new NullThrownError(); |  | 
| 1475   var wrapper = JS('', 'new Error()'); |  | 
| 1476   // [unwrapException] looks for the property 'dartException'. |  | 
| 1477   JS('void', '#.dartException = #', wrapper, ex); |  | 
| 1478 |  | 
| 1479   if (JS('bool', '"defineProperty" in Object')) { |  | 
| 1480     // Define a JavaScript getter for 'message'. This is to work around V8 bug |  | 
| 1481     // (https://code.google.com/p/v8/issues/detail?id=2519).  The default |  | 
| 1482     // toString on Error returns the value of 'message' if 'name' is |  | 
| 1483     // empty. Setting toString directly doesn't work, see the bug. |  | 
| 1484     JS('void', 'Object.defineProperty(#, "message", { get: # })', |  | 
| 1485        wrapper, DART_CLOSURE_TO_JS(toStringWrapper)); |  | 
| 1486     JS('void', '#.name = ""', wrapper); |  | 
| 1487   } else { |  | 
| 1488     // In the unlikely event the browser doesn't support Object.defineProperty, |  | 
| 1489     // hope that it just calls toString. |  | 
| 1490     JS('void', '#.toString = #', wrapper, DART_CLOSURE_TO_JS(toStringWrapper)); |  | 
| 1491   } |  | 
| 1492 |  | 
| 1493   return wrapper; |  | 
| 1494 } |  | 
| 1495 |  | 
| 1496 /// Do not call directly. |  | 
| 1497 toStringWrapper() { |  | 
| 1498   // This method gets installed as toString on a JavaScript object. Due to the |  | 
| 1499   // weird scope rules of JavaScript, JS 'this' will refer to that object. |  | 
| 1500   return JS('', r'this.dartException').toString(); |  | 
| 1501 } |  | 
| 1502 |  | 
| 1503 /** |  | 
| 1504  * This wraps the exception and does the throw.  It is possible to call this in |  | 
| 1505  * a JS expression context, where the throw statement is not allowed.  Helpers |  | 
| 1506  * are never inlined, so we don't risk inlining the throw statement into an |  | 
| 1507  * expression context. |  | 
| 1508  */ |  | 
| 1509 throwExpression(ex) { |  | 
| 1510   JS('void', 'throw #', wrapException(ex)); |  | 
| 1511 } |  | 
| 1512 |  | 
| 1513 throwRuntimeError(message) { |  | 
| 1514   throw new RuntimeError(message); |  | 
| 1515 } |  | 
| 1516 |  | 
| 1517 throwAbstractClassInstantiationError(className) { |  | 
| 1518   throw new AbstractClassInstantiationError(className); |  | 
| 1519 } |  | 
| 1520 |  | 
| 1521 // This is used in open coded for-in loops on arrays. |  | 
| 1522 // |  | 
| 1523 //     checkConcurrentModificationError(a.length == startLength, a) |  | 
| 1524 // |  | 
| 1525 // is replaced in codegen by: |  | 
| 1526 // |  | 
| 1527 //     a.length == startLength || throwConcurrentModificationError(a) |  | 
| 1528 // |  | 
| 1529 // TODO(sra): We would like to annotate this as @NoSideEffects() so that loops |  | 
| 1530 // with no other effects can recognize that the array length does not |  | 
| 1531 // change. However, in the usual case where the loop does have other effects, |  | 
| 1532 // that causes the length in the loop condition to be phi(startLength,a.length), |  | 
| 1533 // which causes confusion in range analysis and the insertion of a bounds check. |  | 
| 1534 @NoInline() |  | 
| 1535 checkConcurrentModificationError(sameLength, collection) { |  | 
| 1536   if (true != sameLength) { |  | 
| 1537     throwConcurrentModificationError(collection); |  | 
| 1538   } |  | 
| 1539 } |  | 
| 1540 |  | 
| 1541 @NoInline() |  | 
| 1542 throwConcurrentModificationError(collection) { |  | 
| 1543   throw new ConcurrentModificationError(collection); |  | 
| 1544 } |  | 
| 1545 |  | 
| 1546 /** |  | 
| 1547  * Helper class for building patterns recognizing native type errors. |  | 
| 1548  */ |  | 
| 1549 class TypeErrorDecoder { |  | 
| 1550   // Field names are private to help tree-shaking. |  | 
| 1551 |  | 
| 1552   /// A regular expression which matches is matched against an error message. |  | 
| 1553   final String _pattern; |  | 
| 1554 |  | 
| 1555   /// The group index of "arguments" in [_pattern], or -1 if _pattern has no |  | 
| 1556   /// match for "arguments". |  | 
| 1557   final int _arguments; |  | 
| 1558 |  | 
| 1559   /// The group index of "argumentsExpr" in [_pattern], or -1 if _pattern has |  | 
| 1560   /// no match for "argumentsExpr". |  | 
| 1561   final int _argumentsExpr; |  | 
| 1562 |  | 
| 1563   /// The group index of "expr" in [_pattern], or -1 if _pattern has no match |  | 
| 1564   /// for "expr". |  | 
| 1565   final int _expr; |  | 
| 1566 |  | 
| 1567   /// The group index of "method" in [_pattern], or -1 if _pattern has no match |  | 
| 1568   /// for "method". |  | 
| 1569   final int _method; |  | 
| 1570 |  | 
| 1571   /// The group index of "receiver" in [_pattern], or -1 if _pattern has no |  | 
| 1572   /// match for "receiver". |  | 
| 1573   final int _receiver; |  | 
| 1574 |  | 
| 1575   /// Pattern used to recognize a NoSuchMethodError error (and |  | 
| 1576   /// possibly extract the method name). |  | 
| 1577   static final TypeErrorDecoder noSuchMethodPattern = |  | 
| 1578       extractPattern(provokeCallErrorOn(buildJavaScriptObject())); |  | 
| 1579 |  | 
| 1580   /// Pattern used to recognize an "object not a closure" error (and |  | 
| 1581   /// possibly extract the method name). |  | 
| 1582   static final TypeErrorDecoder notClosurePattern = |  | 
| 1583       extractPattern(provokeCallErrorOn(buildJavaScriptObjectWithNonClosure())); |  | 
| 1584 |  | 
| 1585   /// Pattern used to recognize a NoSuchMethodError on JavaScript null |  | 
| 1586   /// call. |  | 
| 1587   static final TypeErrorDecoder nullCallPattern = |  | 
| 1588       extractPattern(provokeCallErrorOn(JS('', 'null'))); |  | 
| 1589 |  | 
| 1590   /// Pattern used to recognize a NoSuchMethodError on JavaScript literal null |  | 
| 1591   /// call. |  | 
| 1592   static final TypeErrorDecoder nullLiteralCallPattern = |  | 
| 1593       extractPattern(provokeCallErrorOnNull()); |  | 
| 1594 |  | 
| 1595   /// Pattern used to recognize a NoSuchMethodError on JavaScript |  | 
| 1596   /// undefined call. |  | 
| 1597   static final TypeErrorDecoder undefinedCallPattern = |  | 
| 1598       extractPattern(provokeCallErrorOn(JS('', 'void 0'))); |  | 
| 1599 |  | 
| 1600   /// Pattern used to recognize a NoSuchMethodError on JavaScript literal |  | 
| 1601   /// undefined call. |  | 
| 1602   static final TypeErrorDecoder undefinedLiteralCallPattern = |  | 
| 1603       extractPattern(provokeCallErrorOnUndefined()); |  | 
| 1604 |  | 
| 1605   /// Pattern used to recognize a NoSuchMethodError on JavaScript null |  | 
| 1606   /// property access. |  | 
| 1607   static final TypeErrorDecoder nullPropertyPattern = |  | 
| 1608       extractPattern(provokePropertyErrorOn(JS('', 'null'))); |  | 
| 1609 |  | 
| 1610   /// Pattern used to recognize a NoSuchMethodError on JavaScript literal null |  | 
| 1611   /// property access. |  | 
| 1612   static final TypeErrorDecoder nullLiteralPropertyPattern = |  | 
| 1613       extractPattern(provokePropertyErrorOnNull()); |  | 
| 1614 |  | 
| 1615   /// Pattern used to recognize a NoSuchMethodError on JavaScript |  | 
| 1616   /// undefined property access. |  | 
| 1617   static final TypeErrorDecoder undefinedPropertyPattern = |  | 
| 1618       extractPattern(provokePropertyErrorOn(JS('', 'void 0'))); |  | 
| 1619 |  | 
| 1620   /// Pattern used to recognize a NoSuchMethodError on JavaScript literal |  | 
| 1621   /// undefined property access. |  | 
| 1622   static final TypeErrorDecoder undefinedLiteralPropertyPattern = |  | 
| 1623       extractPattern(provokePropertyErrorOnUndefined()); |  | 
| 1624 |  | 
| 1625   TypeErrorDecoder(this._arguments, |  | 
| 1626                    this._argumentsExpr, |  | 
| 1627                    this._expr, |  | 
| 1628                    this._method, |  | 
| 1629                    this._receiver, |  | 
| 1630                    this._pattern); |  | 
| 1631 |  | 
| 1632   /// Returns a JavaScript object literal (map) with at most the |  | 
| 1633   /// following keys: |  | 
| 1634   /// |  | 
| 1635   /// * arguments: The arguments as formatted by the JavaScript |  | 
| 1636   ///   engine. No browsers are known to provide this information. |  | 
| 1637   /// |  | 
| 1638   /// * argumentsExpr: The syntax of the arguments (JavaScript source |  | 
| 1639   ///   code). No browsers are known to provide this information. |  | 
| 1640   /// |  | 
| 1641   /// * expr: The syntax of the receiver expression (JavaScript source |  | 
| 1642   ///   code). Firefox provides this information, for example: "$expr$.$method$ |  | 
| 1643   ///   is not a function". |  | 
| 1644   /// |  | 
| 1645   /// * method: The name of the called method (mangled name). At least Firefox |  | 
| 1646   ///   and Chrome/V8 provides this information, for example, "Object [object |  | 
| 1647   ///   Object] has no method '$method$'". |  | 
| 1648   /// |  | 
| 1649   /// * receiver: The string representation of the receiver. Chrome/V8 |  | 
| 1650   ///   used to provide this information (by calling user-defined |  | 
| 1651   ///   JavaScript toString on receiver), but it has degenerated into |  | 
| 1652   ///   "[object Object]" in recent versions. |  | 
| 1653   matchTypeError(message) { |  | 
| 1654     var match = JS('JSExtendableArray|Null', |  | 
| 1655         'new RegExp(#).exec(#)', _pattern, message); |  | 
| 1656     if (match == null) return null; |  | 
| 1657     var result = JS('', 'Object.create(null)'); |  | 
| 1658     if (_arguments != -1) { |  | 
| 1659       JS('', '#.arguments = #[# + 1]', result, match, _arguments); |  | 
| 1660     } |  | 
| 1661     if (_argumentsExpr != -1) { |  | 
| 1662       JS('', '#.argumentsExpr = #[# + 1]', result, match, _argumentsExpr); |  | 
| 1663     } |  | 
| 1664     if (_expr != -1) { |  | 
| 1665       JS('', '#.expr = #[# + 1]', result, match, _expr); |  | 
| 1666     } |  | 
| 1667     if (_method != -1) { |  | 
| 1668       JS('', '#.method = #[# + 1]', result, match, _method); |  | 
| 1669     } |  | 
| 1670     if (_receiver != -1) { |  | 
| 1671       JS('', '#.receiver = #[# + 1]', result, match, _receiver); |  | 
| 1672     } |  | 
| 1673 |  | 
| 1674     return result; |  | 
| 1675   } |  | 
| 1676 |  | 
| 1677   /// Builds a JavaScript Object with a toString method saying |  | 
| 1678   /// r"$receiver$". |  | 
| 1679   static buildJavaScriptObject() { |  | 
| 1680     return JS('', r'{ toString: function() { return "$receiver$"; } }'); |  | 
| 1681   } |  | 
| 1682 |  | 
| 1683   /// Builds a JavaScript Object with a toString method saying |  | 
| 1684   /// r"$receiver$". The property "$method" is defined, but is not a function. |  | 
| 1685   static buildJavaScriptObjectWithNonClosure() { |  | 
| 1686     return JS('', r'{ $method$: null, ' |  | 
| 1687               r'toString: function() { return "$receiver$"; } }'); |  | 
| 1688   } |  | 
| 1689 |  | 
| 1690   /// Extract a pattern from a JavaScript TypeError message. |  | 
| 1691   /// |  | 
| 1692   /// The patterns are extracted by forcing TypeErrors on known |  | 
| 1693   /// objects thus forcing known strings into the error message. The |  | 
| 1694   /// known strings are then replaced with wildcards which in theory |  | 
| 1695   /// makes it possible to recognize the desired information even if |  | 
| 1696   /// the error messages are reworded or translated. |  | 
| 1697   static extractPattern(String message) { |  | 
| 1698     // Some JavaScript implementations (V8 at least) include a |  | 
| 1699     // representation of the receiver in the error message, however, |  | 
| 1700     // this representation is not always [: receiver.toString() :], |  | 
| 1701     // sometimes it is [: Object.prototype.toString(receiver) :], and |  | 
| 1702     // sometimes it is an implementation specific method (but that |  | 
| 1703     // doesn't seem to happen for object literals). So sometimes we |  | 
| 1704     // get the text "[object Object]". The shortest way to get that |  | 
| 1705     // string is using "String({})". |  | 
| 1706     // See: http://code.google.com/p/v8/issues/detail?id=2519. |  | 
| 1707     message = JS('String', r"#.replace(String({}), '$receiver$')", message); |  | 
| 1708 |  | 
| 1709     // Since we want to create a new regular expression from an unknown string, |  | 
| 1710     // we must escape all regular expression syntax. |  | 
| 1711     message = JS('String', r"#.replace(new RegExp(#, 'g'), '\\$&')", |  | 
| 1712                  message, ESCAPE_REGEXP); |  | 
| 1713 |  | 
| 1714     // Look for the special pattern \$camelCase\$ (all the $ symbols |  | 
| 1715     // have been escaped already), as we will soon be inserting |  | 
| 1716     // regular expression syntax that we want interpreted by RegExp. |  | 
| 1717     List<String> match = |  | 
| 1718         JS('JSExtendableArray|Null', r"#.match(/\\\$[a-zA-Z]+\\\$/g)", message); |  | 
| 1719     if (match == null) match = []; |  | 
| 1720 |  | 
| 1721     // Find the positions within the substring matches of the error message |  | 
| 1722     // components.  This will help us extract information later, such as the |  | 
| 1723     // method name. |  | 
| 1724     int arguments = JS('int', '#.indexOf(#)', match, r'\$arguments\$'); |  | 
| 1725     int argumentsExpr = JS('int', '#.indexOf(#)', match, r'\$argumentsExpr\$'); |  | 
| 1726     int expr = JS('int', '#.indexOf(#)', match, r'\$expr\$'); |  | 
| 1727     int method = JS('int', '#.indexOf(#)', match, r'\$method\$'); |  | 
| 1728     int receiver = JS('int', '#.indexOf(#)', match, r'\$receiver\$'); |  | 
| 1729 |  | 
| 1730     // Replace the patterns with a regular expression wildcard. |  | 
| 1731     // Note: in a perfect world, one would use "(.*)", but not in |  | 
| 1732     // JavaScript, "." does not match newlines. |  | 
| 1733     String pattern = JS('String', |  | 
| 1734                         r"#.replace('\\$arguments\\$', '((?:x|[^x])*)')" |  | 
| 1735                         r".replace('\\$argumentsExpr\\$',  '((?:x|[^x])*)')" |  | 
| 1736                         r".replace('\\$expr\\$',  '((?:x|[^x])*)')" |  | 
| 1737                         r".replace('\\$method\\$',  '((?:x|[^x])*)')" |  | 
| 1738                         r".replace('\\$receiver\\$',  '((?:x|[^x])*)')", |  | 
| 1739                         message); |  | 
| 1740 |  | 
| 1741     return new TypeErrorDecoder(arguments, |  | 
| 1742                                 argumentsExpr, |  | 
| 1743                                 expr, |  | 
| 1744                                 method, |  | 
| 1745                                 receiver, |  | 
| 1746                                 pattern); |  | 
| 1747   } |  | 
| 1748 |  | 
| 1749   /// Provokes a TypeError and returns its message. |  | 
| 1750   /// |  | 
| 1751   /// The error is provoked so all known variable content can be recognized and |  | 
| 1752   /// a pattern can be inferred. |  | 
| 1753   static String provokeCallErrorOn(expression) { |  | 
| 1754     // This function is carefully created to maximize the possibility |  | 
| 1755     // of decoding the TypeError message and turning it into a general |  | 
| 1756     // pattern. |  | 
| 1757     // |  | 
| 1758     // The idea is to inject something known into something unknown.  The |  | 
| 1759     // unknown entity is the error message that the browser provides with a |  | 
| 1760     // TypeError.  It is a human readable message, possibly localized in a |  | 
| 1761     // language no dart2js engineer understand.  We assume that $name$ would |  | 
| 1762     // never naturally occur in a human readable error message, yet it is easy |  | 
| 1763     // to decode. |  | 
| 1764     // |  | 
| 1765     // For example, evaluate this in V8 version 3.13.7.6: |  | 
| 1766     // |  | 
| 1767     // var $expr$ = null; $expr$.$method$() |  | 
| 1768     // |  | 
| 1769     // The VM throws an instance of TypeError whose message property contains |  | 
| 1770     // "Cannot call method '$method$' of null".  We can then reasonably assume |  | 
| 1771     // that if the string contains $method$, that's where the method name will |  | 
| 1772     // be in general.  Call this automatically reverse engineering the error |  | 
| 1773     // format string in V8. |  | 
| 1774     // |  | 
| 1775     // So the error message from V8 is turned into this regular expression: |  | 
| 1776     // |  | 
| 1777     // "Cannot call method '(.*)' of null" |  | 
| 1778     // |  | 
| 1779     // Similarly, if we evaluate: |  | 
| 1780     // |  | 
| 1781     // var $expr$ = {toString: function() { return '$receiver$'; }}; |  | 
| 1782     // $expr$.$method$() |  | 
| 1783     // |  | 
| 1784     // We get this message: "Object $receiver$ has no method '$method$'" |  | 
| 1785     // |  | 
| 1786     // Which is turned into this regular expression: |  | 
| 1787     // |  | 
| 1788     // "Object (.*) has no method '(.*)'" |  | 
| 1789     // |  | 
| 1790     // Firefox/jsshell is slightly different, it tries to include the source |  | 
| 1791     // code that caused the exception, so we get this message: "$expr$.$method$ |  | 
| 1792     // is not a function" which is turned into this regular expression: |  | 
| 1793     // |  | 
| 1794     // "(.*)\\.(.*) is not a function" |  | 
| 1795 |  | 
| 1796     var function = JS('', r"""function($expr$) { |  | 
| 1797   var $argumentsExpr$ = '$arguments$'; |  | 
| 1798   try { |  | 
| 1799     $expr$.$method$($argumentsExpr$); |  | 
| 1800   } catch (e) { |  | 
| 1801     return e.message; |  | 
| 1802   } |  | 
| 1803 }"""); |  | 
| 1804     return JS('String', '(#)(#)', function, expression); |  | 
| 1805   } |  | 
| 1806 |  | 
| 1807   /// Similar to [provokeCallErrorOn], but provokes an error directly on |  | 
| 1808   /// literal "null" expression. |  | 
| 1809   static String provokeCallErrorOnNull() { |  | 
| 1810     // See [provokeCallErrorOn] for a detailed explanation. |  | 
| 1811     var function = JS('', r"""function() { |  | 
| 1812   var $argumentsExpr$ = '$arguments$'; |  | 
| 1813   try { |  | 
| 1814     null.$method$($argumentsExpr$); |  | 
| 1815   } catch (e) { |  | 
| 1816     return e.message; |  | 
| 1817   } |  | 
| 1818 }"""); |  | 
| 1819     return JS('String', '(#)()', function); |  | 
| 1820   } |  | 
| 1821 |  | 
| 1822   /// Similar to [provokeCallErrorOnNull], but provokes an error directly on |  | 
| 1823   /// (void 0), that is, "undefined". |  | 
| 1824   static String provokeCallErrorOnUndefined() { |  | 
| 1825     // See [provokeCallErrorOn] for a detailed explanation. |  | 
| 1826     var function = JS('', r"""function() { |  | 
| 1827   var $argumentsExpr$ = '$arguments$'; |  | 
| 1828   try { |  | 
| 1829     (void 0).$method$($argumentsExpr$); |  | 
| 1830   } catch (e) { |  | 
| 1831     return e.message; |  | 
| 1832   } |  | 
| 1833 }"""); |  | 
| 1834     return JS('String', '(#)()', function); |  | 
| 1835   } |  | 
| 1836 |  | 
| 1837   /// Similar to [provokeCallErrorOn], but provokes a property access |  | 
| 1838   /// error. |  | 
| 1839   static String provokePropertyErrorOn(expression) { |  | 
| 1840     // See [provokeCallErrorOn] for a detailed explanation. |  | 
| 1841     var function = JS('', r"""function($expr$) { |  | 
| 1842   try { |  | 
| 1843     $expr$.$method$; |  | 
| 1844   } catch (e) { |  | 
| 1845     return e.message; |  | 
| 1846   } |  | 
| 1847 }"""); |  | 
| 1848     return JS('String', '(#)(#)', function, expression); |  | 
| 1849   } |  | 
| 1850 |  | 
| 1851   /// Similar to [provokePropertyErrorOn], but provokes an property access |  | 
| 1852   /// error directly on literal "null" expression. |  | 
| 1853   static String provokePropertyErrorOnNull() { |  | 
| 1854     // See [provokeCallErrorOn] for a detailed explanation. |  | 
| 1855     var function = JS('', r"""function() { |  | 
| 1856   try { |  | 
| 1857     null.$method$; |  | 
| 1858   } catch (e) { |  | 
| 1859     return e.message; |  | 
| 1860   } |  | 
| 1861 }"""); |  | 
| 1862     return JS('String', '(#)()', function); |  | 
| 1863   } |  | 
| 1864 |  | 
| 1865   /// Similar to [provokePropertyErrorOnNull], but provokes an property access |  | 
| 1866   /// error directly on (void 0), that is, "undefined". |  | 
| 1867   static String provokePropertyErrorOnUndefined() { |  | 
| 1868     // See [provokeCallErrorOn] for a detailed explanation. |  | 
| 1869     var function = JS('', r"""function() { |  | 
| 1870   try { |  | 
| 1871     (void 0).$method$; |  | 
| 1872   } catch (e) { |  | 
| 1873     return e.message; |  | 
| 1874   } |  | 
| 1875 }"""); |  | 
| 1876     return JS('String', '(#)()', function); |  | 
| 1877   } |  | 
| 1878 } |  | 
| 1879 |  | 
| 1880 class NullError extends Error implements NoSuchMethodError { |  | 
| 1881   final String _message; |  | 
| 1882   final String _method; |  | 
| 1883 |  | 
| 1884   NullError(this._message, match) |  | 
| 1885       : _method = match == null ? null : JS('', '#.method', match); |  | 
| 1886 |  | 
| 1887   String toString() { |  | 
| 1888     if (_method == null) return 'NullError: $_message'; |  | 
| 1889     return "NullError: method not found: '$_method' on null"; |  | 
| 1890   } |  | 
| 1891 } |  | 
| 1892 |  | 
| 1893 class JsNoSuchMethodError extends Error implements NoSuchMethodError { |  | 
| 1894   final String _message; |  | 
| 1895   final String _method; |  | 
| 1896   final String _receiver; |  | 
| 1897 |  | 
| 1898   JsNoSuchMethodError(this._message, match) |  | 
| 1899       : _method = match == null ? null : JS('String|Null', '#.method', match), |  | 
| 1900         _receiver = |  | 
| 1901             match == null ? null : JS('String|Null', '#.receiver', match); |  | 
| 1902 |  | 
| 1903   String toString() { |  | 
| 1904     if (_method == null) return 'NoSuchMethodError: $_message'; |  | 
| 1905     if (_receiver == null) { |  | 
| 1906       return "NoSuchMethodError: method not found: '$_method' ($_message)"; |  | 
| 1907     } |  | 
| 1908     return "NoSuchMethodError: " |  | 
| 1909         "method not found: '$_method' on '$_receiver' ($_message)"; |  | 
| 1910   } |  | 
| 1911 } |  | 
| 1912 |  | 
| 1913 class UnknownJsTypeError extends Error { |  | 
| 1914   final String _message; |  | 
| 1915 |  | 
| 1916   UnknownJsTypeError(this._message); |  | 
| 1917 |  | 
| 1918   String toString() => _message.isEmpty ? 'Error' : 'Error: $_message'; |  | 
| 1919 } |  | 
| 1920 |  | 
| 1921 /** |  | 
| 1922  * Called from catch blocks in generated code to extract the Dart |  | 
| 1923  * exception from the thrown value. The thrown value may have been |  | 
| 1924  * created by [wrapException] or it may be a 'native' JS exception. |  | 
| 1925  * |  | 
| 1926  * Some native exceptions are mapped to new Dart instances, others are |  | 
| 1927  * returned unmodified. |  | 
| 1928  */ |  | 
| 1929 unwrapException(ex) { |  | 
| 1930   /// If error implements Error, save [ex] in [error.$thrownJsError]. |  | 
| 1931   /// Otherwise, do nothing. Later, the stack trace can then be extraced from |  | 
| 1932   /// [ex]. |  | 
| 1933   saveStackTrace(error) { |  | 
| 1934     if (error is Error) { |  | 
| 1935       var thrownStackTrace = JS('', r'#.$thrownJsError', error); |  | 
| 1936       if (thrownStackTrace == null) { |  | 
| 1937         JS('void', r'#.$thrownJsError = #', error, ex); |  | 
| 1938       } |  | 
| 1939     } |  | 
| 1940     return error; |  | 
| 1941   } |  | 
| 1942 |  | 
| 1943   // Note that we are checking if the object has the property. If it |  | 
| 1944   // has, it could be set to null if the thrown value is null. |  | 
| 1945   if (ex == null) return null; |  | 
| 1946   if (ex is ExceptionAndStackTrace) { |  | 
| 1947     return saveStackTrace(ex.dartException); |  | 
| 1948   } |  | 
| 1949   if (JS('bool', 'typeof # !== "object"', ex)) return ex; |  | 
| 1950 |  | 
| 1951   if (JS('bool', r'"dartException" in #', ex)) { |  | 
| 1952     return saveStackTrace(JS('', r'#.dartException', ex)); |  | 
| 1953   } else if (!JS('bool', r'"message" in #', ex)) { |  | 
| 1954     return ex; |  | 
| 1955   } |  | 
| 1956 |  | 
| 1957   // Grab hold of the exception message. This field is available on |  | 
| 1958   // all supported browsers. |  | 
| 1959   var message = JS('var', r'#.message', ex); |  | 
| 1960 |  | 
| 1961   // Internet Explorer has an error number.  This is the most reliable way to |  | 
| 1962   // detect specific errors, so check for this first. |  | 
| 1963   if (JS('bool', '"number" in #', ex) |  | 
| 1964       && JS('bool', 'typeof #.number == "number"', ex)) { |  | 
| 1965     int number = JS('int', '#.number', ex); |  | 
| 1966 |  | 
| 1967     // From http://msdn.microsoft.com/en-us/library/ie/hc53e755(v=vs.94).aspx |  | 
| 1968     // "number" is a 32-bit word. The error code is the low 16 bits, and the |  | 
| 1969     // facility code is the upper 16 bits. |  | 
| 1970     var ieErrorCode = number & 0xffff; |  | 
| 1971     var ieFacilityNumber = (number >> 16) & 0x1fff; |  | 
| 1972 |  | 
| 1973     // http://msdn.microsoft.com/en-us/library/aa264975(v=vs.60).aspx |  | 
| 1974     // http://msdn.microsoft.com/en-us/library/ie/1dk3k160(v=vs.94).aspx |  | 
| 1975     if (ieFacilityNumber == 10) { |  | 
| 1976       switch (ieErrorCode) { |  | 
| 1977       case 438: |  | 
| 1978         return saveStackTrace( |  | 
| 1979             new JsNoSuchMethodError('$message (Error $ieErrorCode)', null)); |  | 
| 1980       case 445: |  | 
| 1981       case 5007: |  | 
| 1982         return saveStackTrace( |  | 
| 1983             new NullError('$message (Error $ieErrorCode)', null)); |  | 
| 1984       } |  | 
| 1985     } |  | 
| 1986   } |  | 
| 1987 |  | 
| 1988   if (JS('bool', r'# instanceof TypeError', ex)) { |  | 
| 1989     var match; |  | 
| 1990     // Using JS to give type hints to the compiler to help tree-shaking. |  | 
| 1991     // TODO(ahe): That should be unnecessary due to type inference. |  | 
| 1992     var nsme = TypeErrorDecoder.noSuchMethodPattern; |  | 
| 1993     var notClosure = TypeErrorDecoder.notClosurePattern; |  | 
| 1994     var nullCall = TypeErrorDecoder.nullCallPattern; |  | 
| 1995     var nullLiteralCall = TypeErrorDecoder.nullLiteralCallPattern; |  | 
| 1996     var undefCall = TypeErrorDecoder.undefinedCallPattern; |  | 
| 1997     var undefLiteralCall = TypeErrorDecoder.undefinedLiteralCallPattern; |  | 
| 1998     var nullProperty = TypeErrorDecoder.nullPropertyPattern; |  | 
| 1999     var nullLiteralProperty = TypeErrorDecoder.nullLiteralPropertyPattern; |  | 
| 2000     var undefProperty = TypeErrorDecoder.undefinedPropertyPattern; |  | 
| 2001     var undefLiteralProperty = |  | 
| 2002         TypeErrorDecoder.undefinedLiteralPropertyPattern; |  | 
| 2003     if ((match = nsme.matchTypeError(message)) != null) { |  | 
| 2004       return saveStackTrace(new JsNoSuchMethodError(message, match)); |  | 
| 2005     } else if ((match = notClosure.matchTypeError(message)) != null) { |  | 
| 2006       // notClosure may match "({c:null}).c()" or "({c:1}).c()", so we |  | 
| 2007       // cannot tell if this an attempt to invoke call on null or a |  | 
| 2008       // non-function object. |  | 
| 2009       // But we do know the method name is "call". |  | 
| 2010       JS('', '#.method = "call"', match); |  | 
| 2011       return saveStackTrace(new JsNoSuchMethodError(message, match)); |  | 
| 2012     } else if ((match = nullCall.matchTypeError(message)) != null || |  | 
| 2013                (match = nullLiteralCall.matchTypeError(message)) != null || |  | 
| 2014                (match = undefCall.matchTypeError(message)) != null || |  | 
| 2015                (match = undefLiteralCall.matchTypeError(message)) != null || |  | 
| 2016                (match = nullProperty.matchTypeError(message)) != null || |  | 
| 2017                (match = nullLiteralCall.matchTypeError(message)) != null || |  | 
| 2018                (match = undefProperty.matchTypeError(message)) != null || |  | 
| 2019                (match = undefLiteralProperty.matchTypeError(message)) != null) { |  | 
| 2020       return saveStackTrace(new NullError(message, match)); |  | 
| 2021     } |  | 
| 2022 |  | 
| 2023     // If we cannot determine what kind of error this is, we fall back |  | 
| 2024     // to reporting this as a generic error. It's probably better than |  | 
| 2025     // nothing. |  | 
| 2026     return saveStackTrace( |  | 
| 2027         new UnknownJsTypeError(message is String ? message : '')); |  | 
| 2028   } |  | 
| 2029 |  | 
| 2030   if (JS('bool', r'# instanceof RangeError', ex)) { |  | 
| 2031     if (message is String && contains(message, 'call stack')) { |  | 
| 2032       return new StackOverflowError(); |  | 
| 2033     } |  | 
| 2034 |  | 
| 2035     // In general, a RangeError is thrown when trying to pass a number as an |  | 
| 2036     // argument to a function that does not allow a range that includes that |  | 
| 2037     // number. Translate to a Dart ArgumentError with the same message. |  | 
| 2038     // TODO(sra): Translate to RangeError. |  | 
| 2039     String message = tryStringifyException(ex); |  | 
| 2040     if (message is String) { |  | 
| 2041       message = JS('String', r'#.replace(/^RangeError:\s*/, "")', message); |  | 
| 2042     } |  | 
| 2043     return saveStackTrace(new ArgumentError(message)); |  | 
| 2044   } |  | 
| 2045 |  | 
| 2046   // Check for the Firefox specific stack overflow signal. |  | 
| 2047   if (JS('bool', |  | 
| 2048          r'typeof InternalError == "function" && # instanceof InternalError', |  | 
| 2049          ex)) { |  | 
| 2050     if (message is String && message == 'too much recursion') { |  | 
| 2051       return new StackOverflowError(); |  | 
| 2052     } |  | 
| 2053   } |  | 
| 2054 |  | 
| 2055   // Just return the exception. We should not wrap it because in case |  | 
| 2056   // the exception comes from the DOM, it is a JavaScript |  | 
| 2057   // object backed by a native Dart class. |  | 
| 2058   return ex; |  | 
| 2059 } |  | 
| 2060 |  | 
| 2061 String tryStringifyException(ex) { |  | 
| 2062   // Since this function is called from [unwrapException] which is called from |  | 
| 2063   // code injected into a catch-clause, use JavaScript try-catch to avoid a |  | 
| 2064   // potential loop if stringifying crashes. |  | 
| 2065   return JS('String|Null', r''' |  | 
| 2066     (function(ex) { |  | 
| 2067       try { |  | 
| 2068         return String(ex); |  | 
| 2069       } catch (e) {} |  | 
| 2070       return null; |  | 
| 2071     })(#) |  | 
| 2072     ''', ex); |  | 
| 2073 } |  | 
| 2074 |  | 
| 2075 /** |  | 
| 2076  * Called by generated code to fetch the stack trace from an |  | 
| 2077  * exception. Should never return null. |  | 
| 2078  */ |  | 
| 2079 StackTrace getTraceFromException(exception) { |  | 
| 2080   if (exception is ExceptionAndStackTrace) { |  | 
| 2081     return exception.stackTrace; |  | 
| 2082   } |  | 
| 2083   if (exception == null) return new _StackTrace(exception); |  | 
| 2084   _StackTrace trace = JS('_StackTrace|Null', r'#.$cachedTrace', exception); |  | 
| 2085   if (trace != null) return trace; |  | 
| 2086   trace = new _StackTrace(exception); |  | 
| 2087   return JS('_StackTrace', r'#.$cachedTrace = #', exception, trace); |  | 
| 2088 } |  | 
| 2089 |  | 
| 2090 class _StackTrace implements StackTrace { |  | 
| 2091   var _exception; |  | 
| 2092   String _trace; |  | 
| 2093   _StackTrace(this._exception); |  | 
| 2094 |  | 
| 2095   String toString() { |  | 
| 2096     if (_trace != null) return JS('String', '#', _trace); |  | 
| 2097 |  | 
| 2098     String trace; |  | 
| 2099     if (JS('bool', '# !== null', _exception) && |  | 
| 2100         JS('bool', 'typeof # === "object"', _exception)) { |  | 
| 2101       trace = JS("String|Null", r"#.stack", _exception); |  | 
| 2102     } |  | 
| 2103     return _trace = (trace == null) ? '' : trace; |  | 
| 2104   } |  | 
| 2105 } |  | 
| 2106 |  | 
| 2107 int objectHashCode(var object) { |  | 
| 2108   if (object == null || JS('bool', "typeof # != 'object'", object)) { |  | 
| 2109     return object.hashCode; |  | 
| 2110   } else { |  | 
| 2111     return Primitives.objectHashCode(object); |  | 
| 2112   } |  | 
| 2113 } |  | 
| 2114 |  | 
| 2115 /** |  | 
| 2116  * Called by generated code to build a map literal. [keyValuePairs] is |  | 
| 2117  * a list of key, value, key, value, ..., etc. |  | 
| 2118  */ |  | 
| 2119 fillLiteralMap(keyValuePairs, Map result) { |  | 
| 2120   // TODO(johnniwinther): Use JSArray to optimize this code instead of calling |  | 
| 2121   // [getLength] and [getIndex]. |  | 
| 2122   int index = 0; |  | 
| 2123   int length = getLength(keyValuePairs); |  | 
| 2124   while (index < length) { |  | 
| 2125     var key = getIndex(keyValuePairs, index++); |  | 
| 2126     var value = getIndex(keyValuePairs, index++); |  | 
| 2127     result[key] = value; |  | 
| 2128   } |  | 
| 2129   return result; |  | 
| 2130 } |  | 
| 2131 |  | 
| 2132 invokeClosure(Function closure, |  | 
| 2133               var isolate, |  | 
| 2134               int numberOfArguments, |  | 
| 2135               var arg1, |  | 
| 2136               var arg2, |  | 
| 2137               var arg3, |  | 
| 2138               var arg4) { |  | 
| 2139   if (numberOfArguments == 0) { |  | 
| 2140     return JS_CALL_IN_ISOLATE(isolate, () => closure()); |  | 
| 2141   } else if (numberOfArguments == 1) { |  | 
| 2142     return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1)); |  | 
| 2143   } else if (numberOfArguments == 2) { |  | 
| 2144     return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2)); |  | 
| 2145   } else if (numberOfArguments == 3) { |  | 
| 2146     return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2, arg3)); |  | 
| 2147   } else if (numberOfArguments == 4) { |  | 
| 2148     return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2, arg3, arg4)); |  | 
| 2149   } else { |  | 
| 2150     throw new Exception( |  | 
| 2151         'Unsupported number of arguments for wrapped closure'); |  | 
| 2152   } |  | 
| 2153 } |  | 
| 2154 |  | 
| 2155 /** |  | 
| 2156  * Called by generated code to convert a Dart closure to a JS |  | 
| 2157  * closure when the Dart closure is passed to the DOM. |  | 
| 2158  */ |  | 
| 2159 convertDartClosureToJS(closure, int arity) { |  | 
| 2160   if (closure == null) return null; |  | 
| 2161   var function = JS('var', r'#.$identity', closure); |  | 
| 2162   if (JS('bool', r'!!#', function)) return function; |  | 
| 2163 |  | 
| 2164   // We use $0 and $1 to not clash with variable names used by the |  | 
| 2165   // compiler and/or minifier. |  | 
| 2166   function = JS('var', |  | 
| 2167                 '(function(closure, arity, context, invoke) {' |  | 
| 2168                 '  return function(a1, a2, a3, a4) {' |  | 
| 2169                 '     return invoke(closure, context, arity, a1, a2, a3, a4);' |  | 
| 2170                 '  };' |  | 
| 2171                 '})(#,#,#,#)', |  | 
| 2172                 closure, |  | 
| 2173                 arity, |  | 
| 2174                 // Capture the current isolate now.  Remember that "#" |  | 
| 2175                 // in JS is simply textual substitution of compiled |  | 
| 2176                 // expressions. |  | 
| 2177                 JS_CURRENT_ISOLATE_CONTEXT(), |  | 
| 2178                 DART_CLOSURE_TO_JS(invokeClosure)); |  | 
| 2179 |  | 
| 2180   JS('void', r'#.$identity = #', closure, function); |  | 
| 2181   return function; |  | 
| 2182 } |  | 
| 2183 |  | 
| 2184 /** |  | 
| 2185  * Super class for Dart closures. |  | 
| 2186  */ |  | 
| 2187 abstract class Closure implements Function { |  | 
| 2188   // TODO(ahe): These constants must be in sync with |  | 
| 2189   // reflection_data_parser.dart. |  | 
| 2190   static const FUNCTION_INDEX = 0; |  | 
| 2191   static const NAME_INDEX = 1; |  | 
| 2192   static const CALL_NAME_INDEX = 2; |  | 
| 2193   static const REQUIRED_PARAMETER_INDEX = 3; |  | 
| 2194   static const OPTIONAL_PARAMETER_INDEX = 4; |  | 
| 2195   static const DEFAULT_ARGUMENTS_INDEX = 5; |  | 
| 2196 |  | 
| 2197   /** |  | 
| 2198    * Global counter to prevent reusing function code objects. |  | 
| 2199    * |  | 
| 2200    * V8 will share the underlying function code objects when the same string is |  | 
| 2201    * passed to "new Function".  Shared function code objects can lead to |  | 
| 2202    * sub-optimal performance due to polymorhism, and can be prevented by |  | 
| 2203    * ensuring the strings are different. |  | 
| 2204    */ |  | 
| 2205   static int functionCounter = 0; |  | 
| 2206 |  | 
| 2207   Closure(); |  | 
| 2208 |  | 
| 2209   /** |  | 
| 2210    * Creates a new closure class for use by implicit getters associated with a |  | 
| 2211    * method. |  | 
| 2212    * |  | 
| 2213    * In other words, creates a tear-off closure. |  | 
| 2214    * |  | 
| 2215    * Called from [closureFromTearOff] as well as from reflection when tearing |  | 
| 2216    * of a method via [:getField:]. |  | 
| 2217    * |  | 
| 2218    * This method assumes that [functions] was created by the JavaScript function |  | 
| 2219    * `addStubs` in `reflection_data_parser.dart`. That is, a list of JavaScript |  | 
| 2220    * function objects with properties `$stubName` and `$callName`. |  | 
| 2221    * |  | 
| 2222    * Further assumes that [reflectionInfo] is the end of the array created by |  | 
| 2223    * [dart2js.js_emitter.ContainerBuilder.addMemberMethod] starting with |  | 
| 2224    * required parameter count or, in case of the new emitter, the runtime |  | 
| 2225    * representation of the function's type. |  | 
| 2226    * |  | 
| 2227    * Caution: this function may be called when building constants. |  | 
| 2228    * TODO(ahe): Don't call this function when building constants. |  | 
| 2229    */ |  | 
| 2230   static fromTearOff(receiver, |  | 
| 2231                      List functions, |  | 
| 2232                      var reflectionInfo, |  | 
| 2233                      bool isStatic, |  | 
| 2234                      jsArguments, |  | 
| 2235                      String propertyName) { |  | 
| 2236     JS_EFFECT(() { |  | 
| 2237       BoundClosure.receiverOf(JS('BoundClosure', 'void 0')); |  | 
| 2238       BoundClosure.selfOf(JS('BoundClosure', 'void 0')); |  | 
| 2239     }); |  | 
| 2240     // TODO(ahe): All the place below using \$ should be rewritten to go |  | 
| 2241     // through the namer. |  | 
| 2242     var function = JS('', '#[#]', functions, 0); |  | 
| 2243     String name = JS('String|Null', '#.\$stubName', function); |  | 
| 2244     String callName = JS('String|Null', '#[#]', function, |  | 
| 2245         JS_GET_NAME(JsGetName.CALL_NAME_PROPERTY)); |  | 
| 2246 |  | 
| 2247     // This variable holds either an index into the types-table, or a function |  | 
| 2248     // that can compute a function-rti. (The latter is necessary if the type |  | 
| 2249     // is dependent on generic arguments). |  | 
| 2250     var functionType; |  | 
| 2251     if (reflectionInfo is List) { |  | 
| 2252       JS('', '#.\$reflectionInfo = #', function, reflectionInfo); |  | 
| 2253       ReflectionInfo info = new ReflectionInfo(function); |  | 
| 2254       functionType = info.functionType; |  | 
| 2255     } else { |  | 
| 2256       functionType = reflectionInfo; |  | 
| 2257     } |  | 
| 2258 |  | 
| 2259 |  | 
| 2260     // function tmp() {}; |  | 
| 2261     // tmp.prototype = BC.prototype; |  | 
| 2262     // var proto = new tmp; |  | 
| 2263     // for each computed prototype property: |  | 
| 2264     //   proto[property] = ...; |  | 
| 2265     // proto._init = BC; |  | 
| 2266     // var dynClosureConstructor = |  | 
| 2267     //     new Function('self', 'target', 'receiver', 'name', |  | 
| 2268     //                  'this._init(self, target, receiver, name)'); |  | 
| 2269     // proto.constructor = dynClosureConstructor; |  | 
| 2270     // dynClosureConstructor.prototype = proto; |  | 
| 2271     // return dynClosureConstructor; |  | 
| 2272 |  | 
| 2273     // We need to create a new subclass of TearOffClosure, one of StaticClosure |  | 
| 2274     // or BoundClosure.  For this, we need to create an object whose prototype |  | 
| 2275     // is the prototype is either StaticClosure.prototype or |  | 
| 2276     // BoundClosure.prototype, respectively in pseudo JavaScript code. The |  | 
| 2277     // simplest way to access the JavaScript construction function of a Dart |  | 
| 2278     // class is to create an instance and access its constructor property. |  | 
| 2279     // Creating an instance ensures that any lazy class initialization has taken |  | 
| 2280     // place. The newly created instance could in theory be used directly as the |  | 
| 2281     // prototype, but it might include additional fields that we don't need.  So |  | 
| 2282     // we only use the new instance to access the constructor property and use |  | 
| 2283     // Object.create to create the desired prototype. |  | 
| 2284     // |  | 
| 2285     // TODO(sra): Perhaps cache the prototype to avoid the allocation. |  | 
| 2286     var prototype = isStatic |  | 
| 2287         ? JS('StaticClosure', 'Object.create(#.constructor.prototype)', |  | 
| 2288              new StaticClosure()) |  | 
| 2289         : JS('BoundClosure', 'Object.create(#.constructor.prototype)', |  | 
| 2290              new BoundClosure(null, null, null, null)); |  | 
| 2291 |  | 
| 2292     JS('', '#.\$initialize = #', prototype, JS('', '#.constructor', prototype)); |  | 
| 2293     var constructor = isStatic |  | 
| 2294         ? JS('', 'function(){this.\$initialize()}') |  | 
| 2295         : isCsp |  | 
| 2296             ? JS('', 'function(a,b,c,d) {this.\$initialize(a,b,c,d)}') |  | 
| 2297             : JS('', |  | 
| 2298                  'new Function("a,b,c,d", "this.\$initialize(a,b,c,d);" + #)', |  | 
| 2299                  functionCounter++); |  | 
| 2300 |  | 
| 2301     // It is necessary to set the constructor property, otherwise it will be |  | 
| 2302     // "Object". |  | 
| 2303     JS('', '#.constructor = #', prototype, constructor); |  | 
| 2304 |  | 
| 2305     JS('', '#.prototype = #', constructor, prototype); |  | 
| 2306 |  | 
| 2307     // Create a closure and "monkey" patch it with call stubs. |  | 
| 2308     var trampoline = function; |  | 
| 2309     var isIntercepted = false; |  | 
| 2310     if (!isStatic) { |  | 
| 2311       if (JS('bool', '#.length == 1', jsArguments)) { |  | 
| 2312         // Intercepted call. |  | 
| 2313         isIntercepted = true; |  | 
| 2314       } |  | 
| 2315       trampoline = forwardCallTo(receiver, function, isIntercepted); |  | 
| 2316       JS('', '#.\$reflectionInfo = #', trampoline, reflectionInfo); |  | 
| 2317     } else { |  | 
| 2318       JS('', '#.\$name = #', prototype, propertyName); |  | 
| 2319     } |  | 
| 2320 |  | 
| 2321     var signatureFunction; |  | 
| 2322     if (JS('bool', 'typeof # == "number"', functionType)) { |  | 
| 2323       // We cannot call [getType] here, since the types-metadata might not be |  | 
| 2324       // set yet. This is, because fromTearOff might be called for constants |  | 
| 2325       // when the program isn't completely set up yet. |  | 
| 2326       // |  | 
| 2327       // Note that we cannot just textually inline the call |  | 
| 2328       // `getType(functionType)` since we cannot guarantee that the (then) |  | 
| 2329       // captured variable `functionType` isn't reused. |  | 
| 2330       signatureFunction = |  | 
| 2331           JS('', |  | 
| 2332              '''(function(t) { |  | 
| 2333                     return function(){ return #(t); }; |  | 
| 2334                 })(#)''', |  | 
| 2335              RAW_DART_FUNCTION_REF(getType), |  | 
| 2336              functionType); |  | 
| 2337     } else if (!isStatic |  | 
| 2338                && JS('bool', 'typeof # == "function"', functionType)) { |  | 
| 2339       var getReceiver = isIntercepted |  | 
| 2340           ? RAW_DART_FUNCTION_REF(BoundClosure.receiverOf) |  | 
| 2341           : RAW_DART_FUNCTION_REF(BoundClosure.selfOf); |  | 
| 2342       signatureFunction = JS( |  | 
| 2343         '', |  | 
| 2344         'function(f,r){' |  | 
| 2345           'return function(){' |  | 
| 2346             'return f.apply({\$receiver:r(this)},arguments)' |  | 
| 2347           '}' |  | 
| 2348         '}(#,#)', functionType, getReceiver); |  | 
| 2349     } else { |  | 
| 2350       throw 'Error in reflectionInfo.'; |  | 
| 2351     } |  | 
| 2352 |  | 
| 2353     JS('', '#[#] = #', prototype, JS_GET_NAME(JsGetName.SIGNATURE_NAME), |  | 
| 2354         signatureFunction); |  | 
| 2355 |  | 
| 2356     JS('', '#[#] = #', prototype, callName, trampoline); |  | 
| 2357     for (int i = 1; i < functions.length; i++) { |  | 
| 2358       var stub = functions[i]; |  | 
| 2359       var stubCallName = JS('String|Null', '#[#]', stub, |  | 
| 2360           JS_GET_NAME(JsGetName.CALL_NAME_PROPERTY)); |  | 
| 2361       if (stubCallName != null) { |  | 
| 2362         JS('', '#[#] = #', prototype, stubCallName, |  | 
| 2363            isStatic ? stub : forwardCallTo(receiver, stub, isIntercepted)); |  | 
| 2364       } |  | 
| 2365     } |  | 
| 2366 |  | 
| 2367     JS('', '#[#] = #', prototype, JS_GET_NAME(JsGetName.CALL_CATCH_ALL), |  | 
| 2368         trampoline); |  | 
| 2369     String reqArgProperty = JS_GET_NAME(JsGetName.REQUIRED_PARAMETER_PROPERTY); |  | 
| 2370     String defValProperty = JS_GET_NAME(JsGetName.DEFAULT_VALUES_PROPERTY); |  | 
| 2371     JS('', '#.# = #.#', prototype, reqArgProperty, function, reqArgProperty); |  | 
| 2372     JS('', '#.# = #.#', prototype, defValProperty, function, defValProperty); |  | 
| 2373 |  | 
| 2374     return constructor; |  | 
| 2375   } |  | 
| 2376 |  | 
| 2377   static cspForwardCall(int arity, bool isSuperCall, String stubName, |  | 
| 2378                         function) { |  | 
| 2379     var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf); |  | 
| 2380     // Handle intercepted stub-names with the default slow case. |  | 
| 2381     if (isSuperCall) arity = -1; |  | 
| 2382     switch (arity) { |  | 
| 2383     case 0: |  | 
| 2384       return JS( |  | 
| 2385           '', |  | 
| 2386           'function(n,S){' |  | 
| 2387             'return function(){' |  | 
| 2388               'return S(this)[n]()' |  | 
| 2389             '}' |  | 
| 2390           '}(#,#)', stubName, getSelf); |  | 
| 2391     case 1: |  | 
| 2392       return JS( |  | 
| 2393           '', |  | 
| 2394           'function(n,S){' |  | 
| 2395             'return function(a){' |  | 
| 2396               'return S(this)[n](a)' |  | 
| 2397             '}' |  | 
| 2398           '}(#,#)', stubName, getSelf); |  | 
| 2399     case 2: |  | 
| 2400       return JS( |  | 
| 2401           '', |  | 
| 2402           'function(n,S){' |  | 
| 2403             'return function(a,b){' |  | 
| 2404               'return S(this)[n](a,b)' |  | 
| 2405             '}' |  | 
| 2406           '}(#,#)', stubName, getSelf); |  | 
| 2407     case 3: |  | 
| 2408       return JS( |  | 
| 2409           '', |  | 
| 2410           'function(n,S){' |  | 
| 2411             'return function(a,b,c){' |  | 
| 2412               'return S(this)[n](a,b,c)' |  | 
| 2413             '}' |  | 
| 2414           '}(#,#)', stubName, getSelf); |  | 
| 2415     case 4: |  | 
| 2416       return JS( |  | 
| 2417           '', |  | 
| 2418           'function(n,S){' |  | 
| 2419             'return function(a,b,c,d){' |  | 
| 2420               'return S(this)[n](a,b,c,d)' |  | 
| 2421             '}' |  | 
| 2422           '}(#,#)', stubName, getSelf); |  | 
| 2423     case 5: |  | 
| 2424       return JS( |  | 
| 2425           '', |  | 
| 2426           'function(n,S){' |  | 
| 2427             'return function(a,b,c,d,e){' |  | 
| 2428               'return S(this)[n](a,b,c,d,e)' |  | 
| 2429             '}' |  | 
| 2430           '}(#,#)', stubName, getSelf); |  | 
| 2431     default: |  | 
| 2432       return JS( |  | 
| 2433           '', |  | 
| 2434           'function(f,s){' |  | 
| 2435             'return function(){' |  | 
| 2436               'return f.apply(s(this),arguments)' |  | 
| 2437             '}' |  | 
| 2438           '}(#,#)', function, getSelf); |  | 
| 2439     } |  | 
| 2440   } |  | 
| 2441 |  | 
| 2442   static bool get isCsp => JS_GET_FLAG("USE_CONTENT_SECURITY_POLICY"); |  | 
| 2443 |  | 
| 2444   static forwardCallTo(receiver, function, bool isIntercepted) { |  | 
| 2445     if (isIntercepted) return forwardInterceptedCallTo(receiver, function); |  | 
| 2446     String stubName = JS('String|Null', '#.\$stubName', function); |  | 
| 2447     int arity = JS('int', '#.length', function); |  | 
| 2448     var lookedUpFunction = JS("", "#[#]", receiver, stubName); |  | 
| 2449     // The receiver[stubName] may not be equal to the function if we try to |  | 
| 2450     // forward to a super-method. Especially when we create a bound closure |  | 
| 2451     // of a super-call we need to make sure that we don't forward back to the |  | 
| 2452     // dynamically looked up function. |  | 
| 2453     bool isSuperCall = !identical(function, lookedUpFunction); |  | 
| 2454 |  | 
| 2455     if (isCsp || isSuperCall || arity >= 27) { |  | 
| 2456       return cspForwardCall(arity, isSuperCall, stubName, function); |  | 
| 2457     } |  | 
| 2458 |  | 
| 2459     if (arity == 0) { |  | 
| 2460       return JS( |  | 
| 2461           '', |  | 
| 2462           '(new Function(#))()', |  | 
| 2463           'return function(){' |  | 
| 2464             'return this.${BoundClosure.selfFieldName()}.$stubName();' |  | 
| 2465             '${functionCounter++}' |  | 
| 2466           '}'); |  | 
| 2467     } |  | 
| 2468     assert (1 <= arity && arity < 27); |  | 
| 2469     String arguments = JS( |  | 
| 2470         'String', |  | 
| 2471         '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")', |  | 
| 2472         arity); |  | 
| 2473     return JS( |  | 
| 2474         '', |  | 
| 2475         '(new Function(#))()', |  | 
| 2476         'return function($arguments){' |  | 
| 2477           'return this.${BoundClosure.selfFieldName()}.$stubName($arguments);' |  | 
| 2478           '${functionCounter++}' |  | 
| 2479         '}'); |  | 
| 2480   } |  | 
| 2481 |  | 
| 2482   static cspForwardInterceptedCall(int arity, bool isSuperCall, |  | 
| 2483                                    String name, function) { |  | 
| 2484     var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf); |  | 
| 2485     var getReceiver = RAW_DART_FUNCTION_REF(BoundClosure.receiverOf); |  | 
| 2486     // Handle intercepted stub-names with the default slow case. |  | 
| 2487     if (isSuperCall) arity = -1; |  | 
| 2488     switch (arity) { |  | 
| 2489     case 0: |  | 
| 2490       // Intercepted functions always takes at least one argument (the |  | 
| 2491       // receiver). |  | 
| 2492       throw new RuntimeError('Intercepted function with no arguments.'); |  | 
| 2493     case 1: |  | 
| 2494       return JS( |  | 
| 2495           '', |  | 
| 2496           'function(n,s,r){' |  | 
| 2497             'return function(){' |  | 
| 2498               'return s(this)[n](r(this))' |  | 
| 2499             '}' |  | 
| 2500           '}(#,#,#)', name, getSelf, getReceiver); |  | 
| 2501     case 2: |  | 
| 2502       return JS( |  | 
| 2503           '', |  | 
| 2504           'function(n,s,r){' |  | 
| 2505             'return function(a){' |  | 
| 2506               'return s(this)[n](r(this),a)' |  | 
| 2507             '}' |  | 
| 2508           '}(#,#,#)', name, getSelf, getReceiver); |  | 
| 2509     case 3: |  | 
| 2510       return JS( |  | 
| 2511           '', |  | 
| 2512           'function(n,s,r){' |  | 
| 2513             'return function(a,b){' |  | 
| 2514               'return s(this)[n](r(this),a,b)' |  | 
| 2515             '}' |  | 
| 2516           '}(#,#,#)', name, getSelf, getReceiver); |  | 
| 2517     case 4: |  | 
| 2518       return JS( |  | 
| 2519           '', |  | 
| 2520           'function(n,s,r){' |  | 
| 2521             'return function(a,b,c){' |  | 
| 2522               'return s(this)[n](r(this),a,b,c)' |  | 
| 2523             '}' |  | 
| 2524           '}(#,#,#)', name, getSelf, getReceiver); |  | 
| 2525     case 5: |  | 
| 2526       return JS( |  | 
| 2527           '', |  | 
| 2528           'function(n,s,r){' |  | 
| 2529             'return function(a,b,c,d){' |  | 
| 2530               'return s(this)[n](r(this),a,b,c,d)' |  | 
| 2531             '}' |  | 
| 2532           '}(#,#,#)', name, getSelf, getReceiver); |  | 
| 2533     case 6: |  | 
| 2534       return JS( |  | 
| 2535           '', |  | 
| 2536           'function(n,s,r){' |  | 
| 2537             'return function(a,b,c,d,e){' |  | 
| 2538               'return s(this)[n](r(this),a,b,c,d,e)' |  | 
| 2539             '}' |  | 
| 2540           '}(#,#,#)', name, getSelf, getReceiver); |  | 
| 2541     default: |  | 
| 2542       return JS( |  | 
| 2543           '', |  | 
| 2544           'function(f,s,r,a){' |  | 
| 2545             'return function(){' |  | 
| 2546               'a=[r(this)];' |  | 
| 2547               'Array.prototype.push.apply(a,arguments);' |  | 
| 2548               'return f.apply(s(this),a)' |  | 
| 2549             '}' |  | 
| 2550           '}(#,#,#)', function, getSelf, getReceiver); |  | 
| 2551     } |  | 
| 2552   } |  | 
| 2553 |  | 
| 2554   static forwardInterceptedCallTo(receiver, function) { |  | 
| 2555     String selfField = BoundClosure.selfFieldName(); |  | 
| 2556     String receiverField = BoundClosure.receiverFieldName(); |  | 
| 2557     String stubName = JS('String|Null', '#.\$stubName', function); |  | 
| 2558     int arity = JS('int', '#.length', function); |  | 
| 2559     bool isCsp = JS_GET_FLAG("USE_CONTENT_SECURITY_POLICY"); |  | 
| 2560     var lookedUpFunction = JS("", "#[#]", receiver, stubName); |  | 
| 2561     // The receiver[stubName] may not be equal to the function if we try to |  | 
| 2562     // forward to a super-method. Especially when we create a bound closure |  | 
| 2563     // of a super-call we need to make sure that we don't forward back to the |  | 
| 2564     // dynamically looked up function. |  | 
| 2565     bool isSuperCall = !identical(function, lookedUpFunction); |  | 
| 2566 |  | 
| 2567     if (isCsp || isSuperCall || arity >= 28) { |  | 
| 2568       return cspForwardInterceptedCall(arity, isSuperCall, stubName, |  | 
| 2569                                        function); |  | 
| 2570     } |  | 
| 2571     if (arity == 1) { |  | 
| 2572       return JS( |  | 
| 2573           '', |  | 
| 2574           '(new Function(#))()', |  | 
| 2575           'return function(){' |  | 
| 2576             'return this.$selfField.$stubName(this.$receiverField);' |  | 
| 2577             '${functionCounter++}' |  | 
| 2578           '}'); |  | 
| 2579     } |  | 
| 2580     assert(1 < arity && arity < 28); |  | 
| 2581     String arguments = JS( |  | 
| 2582         'String', |  | 
| 2583         '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")', |  | 
| 2584         arity - 1); |  | 
| 2585     return JS( |  | 
| 2586         '', |  | 
| 2587         '(new Function(#))()', |  | 
| 2588         'return function($arguments){' |  | 
| 2589           'return this.$selfField.$stubName(this.$receiverField, $arguments);' |  | 
| 2590           '${functionCounter++}' |  | 
| 2591         '}'); |  | 
| 2592   } |  | 
| 2593 |  | 
| 2594   // The backend adds a special getter of the form |  | 
| 2595   // |  | 
| 2596   // Closure get call => this; |  | 
| 2597   // |  | 
| 2598   // to allow tearing off a closure from itself. We do this magically in the |  | 
| 2599   // backend rather than simply adding it here, as we do not want this getter |  | 
| 2600   // to be visible to resolution and the generation of extra stubs. |  | 
| 2601 |  | 
| 2602   String toString() { |  | 
| 2603     String name = Primitives.objectTypeName(this); |  | 
| 2604     return "Closure '$name'"; |  | 
| 2605   } |  | 
| 2606 } |  | 
| 2607 |  | 
| 2608 /// Called from implicit method getter (aka tear-off). |  | 
| 2609 closureFromTearOff(receiver, |  | 
| 2610                    functions, |  | 
| 2611                    reflectionInfo, |  | 
| 2612                    isStatic, |  | 
| 2613                    jsArguments, |  | 
| 2614                    name) { |  | 
| 2615   return Closure.fromTearOff( |  | 
| 2616       receiver, |  | 
| 2617       JSArray.markFixedList(functions), |  | 
| 2618       reflectionInfo is List ? JSArray.markFixedList(reflectionInfo) |  | 
| 2619                              : reflectionInfo, |  | 
| 2620       JS('bool', '!!#', isStatic), |  | 
| 2621       jsArguments, |  | 
| 2622       JS('String', '#', name)); |  | 
| 2623 } |  | 
| 2624 |  | 
| 2625 /// Represents an implicit closure of a function. |  | 
| 2626 abstract class TearOffClosure extends Closure { |  | 
| 2627 } |  | 
| 2628 |  | 
| 2629 class StaticClosure extends TearOffClosure { |  | 
| 2630   String toString() { |  | 
| 2631     String name = JS('String|Null', '#.\$name', this); |  | 
| 2632     if (name == null) return "Closure of unknown static method"; |  | 
| 2633     return "Closure '$name'"; |  | 
| 2634   } |  | 
| 2635 } |  | 
| 2636 |  | 
| 2637 /// Represents a 'tear-off' or property extraction closure of an instance |  | 
| 2638 /// method, that is an instance method bound to a specific receiver (instance). |  | 
| 2639 class BoundClosure extends TearOffClosure { |  | 
| 2640   /// The receiver or interceptor. |  | 
| 2641   // TODO(ahe): This could just be the interceptor, we always know if |  | 
| 2642   // we need the interceptor when generating the call method. |  | 
| 2643   final _self; |  | 
| 2644 |  | 
| 2645   /// The method. |  | 
| 2646   final _target; |  | 
| 2647 |  | 
| 2648   /// The receiver. Null if [_self] is not an interceptor. |  | 
| 2649   final _receiver; |  | 
| 2650 |  | 
| 2651   /// The name of the function. Only used by the mirror system. |  | 
| 2652   final String _name; |  | 
| 2653 |  | 
| 2654   BoundClosure(this._self, this._target, this._receiver, this._name); |  | 
| 2655 |  | 
| 2656   bool operator==(other) { |  | 
| 2657     if (identical(this, other)) return true; |  | 
| 2658     if (other is! BoundClosure) return false; |  | 
| 2659     return JS('bool', '# === # && # === # && # === #', |  | 
| 2660         _self, other._self, |  | 
| 2661         _target, other._target, |  | 
| 2662         _receiver, other._receiver); |  | 
| 2663   } |  | 
| 2664 |  | 
| 2665   int get hashCode { |  | 
| 2666     int receiverHashCode; |  | 
| 2667     if (_receiver == null) { |  | 
| 2668       // A bound closure on a regular Dart object, just use the |  | 
| 2669       // identity hash code. |  | 
| 2670       receiverHashCode = Primitives.objectHashCode(_self); |  | 
| 2671     } else if (JS('String', 'typeof #', _receiver) != 'object') { |  | 
| 2672       // A bound closure on a primitive JavaScript type. We |  | 
| 2673       // use the hashCode method we define for those primitive types. |  | 
| 2674       receiverHashCode = _receiver.hashCode; |  | 
| 2675     } else { |  | 
| 2676       // A bound closure on an intercepted native class, just use the |  | 
| 2677       // identity hash code. |  | 
| 2678       receiverHashCode = Primitives.objectHashCode(_receiver); |  | 
| 2679     } |  | 
| 2680     return receiverHashCode ^ Primitives.objectHashCode(_target); |  | 
| 2681   } |  | 
| 2682 |  | 
| 2683   toString() { |  | 
| 2684     var receiver = _receiver == null ? _self : _receiver; |  | 
| 2685     return "Closure '$_name' of ${Primitives.objectToHumanReadableString(receive
      r)}"; |  | 
| 2686   } |  | 
| 2687 |  | 
| 2688   @NoInline() |  | 
| 2689   static selfOf(BoundClosure closure) => closure._self; |  | 
| 2690 |  | 
| 2691   static targetOf(BoundClosure closure) => closure._target; |  | 
| 2692 |  | 
| 2693   @NoInline() |  | 
| 2694   static receiverOf(BoundClosure closure) => closure._receiver; |  | 
| 2695 |  | 
| 2696   static nameOf(BoundClosure closure) => closure._name; |  | 
| 2697 |  | 
| 2698   static String selfFieldNameCache; |  | 
| 2699 |  | 
| 2700   static String selfFieldName() { |  | 
| 2701     if (selfFieldNameCache == null) { |  | 
| 2702       selfFieldNameCache = computeFieldNamed('self'); |  | 
| 2703     } |  | 
| 2704     return selfFieldNameCache; |  | 
| 2705   } |  | 
| 2706 |  | 
| 2707   static String receiverFieldNameCache; |  | 
| 2708 |  | 
| 2709   static String receiverFieldName() { |  | 
| 2710     if (receiverFieldNameCache == null) { |  | 
| 2711       receiverFieldNameCache = computeFieldNamed('receiver'); |  | 
| 2712     } |  | 
| 2713     return receiverFieldNameCache; |  | 
| 2714   } |  | 
| 2715 |  | 
| 2716   @NoInline() @NoSideEffects() |  | 
| 2717   static String computeFieldNamed(String fieldName) { |  | 
| 2718     var template = new BoundClosure('self', 'target', 'receiver', 'name'); |  | 
| 2719     var names = JSArray.markFixedList( |  | 
| 2720         JS('', 'Object.getOwnPropertyNames(#)', template)); |  | 
| 2721     for (int i = 0; i < names.length; i++) { |  | 
| 2722       var name = names[i]; |  | 
| 2723       if (JS('bool', '#[#] === #', template, name, fieldName)) { |  | 
| 2724         return JS('String', '#', name); |  | 
| 2725       } |  | 
| 2726     } |  | 
| 2727   } |  | 
| 2728 } |  | 
| 2729 |  | 
| 2730 bool jsHasOwnProperty(var jsObject, String property) { |  | 
| 2731   return JS('bool', r'#.hasOwnProperty(#)', jsObject, property); |  | 
| 2732 } |  | 
| 2733 |  | 
| 2734 jsPropertyAccess(var jsObject, String property) { |  | 
| 2735   return JS('var', r'#[#]', jsObject, property); |  | 
| 2736 } |  | 
| 2737 |  | 
| 2738 /** |  | 
| 2739  * Called at the end of unaborted switch cases to get the singleton |  | 
| 2740  * FallThroughError exception that will be thrown. |  | 
| 2741  */ |  | 
| 2742 getFallThroughError() => new FallThroughErrorImplementation(); |  | 
| 2743 |  | 
| 2744 /** |  | 
| 2745  * A metadata annotation describing the types instantiated by a native element. |  | 
| 2746  * |  | 
| 2747  * The annotation is valid on a native method and a field of a native class. |  | 
| 2748  * |  | 
| 2749  * By default, a field of a native class is seen as an instantiation point for |  | 
| 2750  * all native classes that are a subtype of the field's type, and a native |  | 
| 2751  * method is seen as an instantiation point fo all native classes that are a |  | 
| 2752  * subtype of the method's return type, or the argument types of the declared |  | 
| 2753  * type of the method's callback parameter. |  | 
| 2754  * |  | 
| 2755  * An @[Creates] annotation overrides the default set of instantiated types.  If |  | 
| 2756  * one or more @[Creates] annotations are present, the type of the native |  | 
| 2757  * element is ignored, and the union of @[Creates] annotations is used instead. |  | 
| 2758  * The names in the strings are resolved and the program will fail to compile |  | 
| 2759  * with dart2js if they do not name types. |  | 
| 2760  * |  | 
| 2761  * The argument to [Creates] is a string.  The string is parsed as the names of |  | 
| 2762  * one or more types, separated by vertical bars `|`.  There are some special |  | 
| 2763  * names: |  | 
| 2764  * |  | 
| 2765  * * `=Object`. This means 'exactly Object', which is a plain JavaScript object |  | 
| 2766  *   with properties and none of the subtypes of Object. |  | 
| 2767  * |  | 
| 2768  * Example: we may know that a method always returns a specific implementation: |  | 
| 2769  * |  | 
| 2770  *     @Creates('_NodeList') |  | 
| 2771  *     List<Node> getElementsByTagName(String tag) native; |  | 
| 2772  * |  | 
| 2773  * Useful trick: A method can be marked as not instantiating any native classes |  | 
| 2774  * with the annotation `@Creates('Null')`.  This is useful for fields on native |  | 
| 2775  * classes that are used only in Dart code. |  | 
| 2776  * |  | 
| 2777  *     @Creates('Null') |  | 
| 2778  *     var _cachedFoo; |  | 
| 2779  */ |  | 
| 2780 class Creates { |  | 
| 2781   final String types; |  | 
| 2782   const Creates(this.types); |  | 
| 2783 } |  | 
| 2784 |  | 
| 2785 /** |  | 
| 2786  * A metadata annotation describing the types returned or yielded by a native |  | 
| 2787  * element. |  | 
| 2788  * |  | 
| 2789  * The annotation is valid on a native method and a field of a native class. |  | 
| 2790  * |  | 
| 2791  * By default, a native method or field is seen as returning or yielding all |  | 
| 2792  * subtypes if the method return type or field type.  This annotation allows a |  | 
| 2793  * more precise set of types to be specified. |  | 
| 2794  * |  | 
| 2795  * See [Creates] for the syntax of the argument. |  | 
| 2796  * |  | 
| 2797  * Example: IndexedDB keys are numbers, strings and JavaScript Arrays of keys. |  | 
| 2798  * |  | 
| 2799  *     @Returns('String|num|JSExtendableArray') |  | 
| 2800  *     dynamic key; |  | 
| 2801  * |  | 
| 2802  *     // Equivalent: |  | 
| 2803  *     @Returns('String') @Returns('num') @Returns('JSExtendableArray') |  | 
| 2804  *     dynamic key; |  | 
| 2805  */ |  | 
| 2806 class Returns { |  | 
| 2807   final String types; |  | 
| 2808   const Returns(this.types); |  | 
| 2809 } |  | 
| 2810 |  | 
| 2811 /** |  | 
| 2812  * A metadata annotation placed on native methods and fields of native classes |  | 
| 2813  * to specify the JavaScript name. |  | 
| 2814  * |  | 
| 2815  * This example declares a Dart field + getter + setter called `$dom_title` that |  | 
| 2816  * corresponds to the JavaScript property `title`. |  | 
| 2817  * |  | 
| 2818  *     class Docmument native "*Foo" { |  | 
| 2819  *       @JSName('title') |  | 
| 2820  *       String $dom_title; |  | 
| 2821  *     } |  | 
| 2822  */ |  | 
| 2823 class JSName { |  | 
| 2824   final String name; |  | 
| 2825   const JSName(this.name); |  | 
| 2826 } |  | 
| 2827 |  | 
| 2828 /** |  | 
| 2829  * The following methods are called by the runtime to implement |  | 
| 2830  * checked mode and casts. We specialize each primitive type (eg int, bool), and |  | 
| 2831  * use the compiler's convention to do is-checks on regular objects. |  | 
| 2832  */ |  | 
| 2833 boolConversionCheck(value) { |  | 
| 2834   if (value is bool) return value; |  | 
| 2835   // One of the following checks will always fail. |  | 
| 2836   boolTypeCheck(value); |  | 
| 2837   assert(value != null); |  | 
| 2838   return false; |  | 
| 2839 } |  | 
| 2840 |  | 
| 2841 stringTypeCheck(value) { |  | 
| 2842   if (value == null) return value; |  | 
| 2843   if (value is String) return value; |  | 
| 2844   throw new TypeErrorImplementation(value, 'String'); |  | 
| 2845 } |  | 
| 2846 |  | 
| 2847 stringTypeCast(value) { |  | 
| 2848   if (value is String || value == null) return value; |  | 
| 2849   // TODO(lrn): When reified types are available, pass value.class and String. |  | 
| 2850   throw new CastErrorImplementation( |  | 
| 2851       Primitives.objectTypeName(value), 'String'); |  | 
| 2852 } |  | 
| 2853 |  | 
| 2854 doubleTypeCheck(value) { |  | 
| 2855   if (value == null) return value; |  | 
| 2856   if (value is double) return value; |  | 
| 2857   throw new TypeErrorImplementation(value, 'double'); |  | 
| 2858 } |  | 
| 2859 |  | 
| 2860 doubleTypeCast(value) { |  | 
| 2861   if (value is double || value == null) return value; |  | 
| 2862   throw new CastErrorImplementation( |  | 
| 2863       Primitives.objectTypeName(value), 'double'); |  | 
| 2864 } |  | 
| 2865 |  | 
| 2866 numTypeCheck(value) { |  | 
| 2867   if (value == null) return value; |  | 
| 2868   if (value is num) return value; |  | 
| 2869   throw new TypeErrorImplementation(value, 'num'); |  | 
| 2870 } |  | 
| 2871 |  | 
| 2872 numTypeCast(value) { |  | 
| 2873   if (value is num || value == null) return value; |  | 
| 2874   throw new CastErrorImplementation( |  | 
| 2875       Primitives.objectTypeName(value), 'num'); |  | 
| 2876 } |  | 
| 2877 |  | 
| 2878 boolTypeCheck(value) { |  | 
| 2879   if (value == null) return value; |  | 
| 2880   if (value is bool) return value; |  | 
| 2881   throw new TypeErrorImplementation(value, 'bool'); |  | 
| 2882 } |  | 
| 2883 |  | 
| 2884 boolTypeCast(value) { |  | 
| 2885   if (value is bool || value == null) return value; |  | 
| 2886   throw new CastErrorImplementation( |  | 
| 2887       Primitives.objectTypeName(value), 'bool'); |  | 
| 2888 } |  | 
| 2889 |  | 
| 2890 intTypeCheck(value) { |  | 
| 2891   if (value == null) return value; |  | 
| 2892   if (value is int) return value; |  | 
| 2893   throw new TypeErrorImplementation(value, 'int'); |  | 
| 2894 } |  | 
| 2895 |  | 
| 2896 intTypeCast(value) { |  | 
| 2897   if (value is int || value == null) return value; |  | 
| 2898   throw new CastErrorImplementation( |  | 
| 2899       Primitives.objectTypeName(value), 'int'); |  | 
| 2900 } |  | 
| 2901 |  | 
| 2902 void propertyTypeError(value, property) { |  | 
| 2903   String name = isCheckPropertyToJsConstructorName(property); |  | 
| 2904   throw new TypeErrorImplementation(value, name); |  | 
| 2905 } |  | 
| 2906 |  | 
| 2907 void propertyTypeCastError(value, property) { |  | 
| 2908   // Cuts the property name to the class name. |  | 
| 2909   String actualType = Primitives.objectTypeName(value); |  | 
| 2910   String expectedType = property.substring(3, property.length); |  | 
| 2911   throw new CastErrorImplementation(actualType, expectedType); |  | 
| 2912 } |  | 
| 2913 |  | 
| 2914 /** |  | 
| 2915  * For types that are not supertypes of native (eg DOM) types, |  | 
| 2916  * we emit a simple property check to check that an object implements |  | 
| 2917  * that type. |  | 
| 2918  */ |  | 
| 2919 propertyTypeCheck(value, property) { |  | 
| 2920   if (value == null) return value; |  | 
| 2921   if (JS('bool', '!!#[#]', value, property)) return value; |  | 
| 2922   propertyTypeError(value, property); |  | 
| 2923 } |  | 
| 2924 |  | 
| 2925 /** |  | 
| 2926  * For types that are not supertypes of native (eg DOM) types, |  | 
| 2927  * we emit a simple property check to check that an object implements |  | 
| 2928  * that type. |  | 
| 2929  */ |  | 
| 2930 propertyTypeCast(value, property) { |  | 
| 2931   if (value == null || JS('bool', '!!#[#]', value, property)) return value; |  | 
| 2932   propertyTypeCastError(value, property); |  | 
| 2933 } |  | 
| 2934 |  | 
| 2935 /** |  | 
| 2936  * For types that are supertypes of native (eg DOM) types, we use the |  | 
| 2937  * interceptor for the class because we cannot add a JS property to the |  | 
| 2938  * prototype at load time. |  | 
| 2939  */ |  | 
| 2940 interceptedTypeCheck(value, property) { |  | 
| 2941   if (value == null) return value; |  | 
| 2942   if ((identical(JS('String', 'typeof #', value), 'object')) |  | 
| 2943       && JS('bool', '#[#]', getInterceptor(value), property)) { |  | 
| 2944     return value; |  | 
| 2945   } |  | 
| 2946   propertyTypeError(value, property); |  | 
| 2947 } |  | 
| 2948 |  | 
| 2949 /** |  | 
| 2950  * For types that are supertypes of native (eg DOM) types, we use the |  | 
| 2951  * interceptor for the class because we cannot add a JS property to the |  | 
| 2952  * prototype at load time. |  | 
| 2953  */ |  | 
| 2954 interceptedTypeCast(value, property) { |  | 
| 2955   if (value == null |  | 
| 2956       || ((JS('bool', 'typeof # === "object"', value)) |  | 
| 2957           && JS('bool', '#[#]', getInterceptor(value), property))) { |  | 
| 2958     return value; |  | 
| 2959   } |  | 
| 2960   propertyTypeCastError(value, property); |  | 
| 2961 } |  | 
| 2962 |  | 
| 2963 /** |  | 
| 2964  * Specialization of the type check for num and String and their |  | 
| 2965  * supertype since [value] can be a JS primitive. |  | 
| 2966  */ |  | 
| 2967 numberOrStringSuperTypeCheck(value, property) { |  | 
| 2968   if (value == null) return value; |  | 
| 2969   if (value is String) return value; |  | 
| 2970   if (value is num) return value; |  | 
| 2971   if (JS('bool', '!!#[#]', value, property)) return value; |  | 
| 2972   propertyTypeError(value, property); |  | 
| 2973 } |  | 
| 2974 |  | 
| 2975 numberOrStringSuperTypeCast(value, property) { |  | 
| 2976   if (value is String) return value; |  | 
| 2977   if (value is num) return value; |  | 
| 2978   return propertyTypeCast(value, property); |  | 
| 2979 } |  | 
| 2980 |  | 
| 2981 numberOrStringSuperNativeTypeCheck(value, property) { |  | 
| 2982   if (value == null) return value; |  | 
| 2983   if (value is String) return value; |  | 
| 2984   if (value is num) return value; |  | 
| 2985   if (JS('bool', '#[#]', getInterceptor(value), property)) return value; |  | 
| 2986   propertyTypeError(value, property); |  | 
| 2987 } |  | 
| 2988 |  | 
| 2989 numberOrStringSuperNativeTypeCast(value, property) { |  | 
| 2990   if (value == null) return value; |  | 
| 2991   if (value is String) return value; |  | 
| 2992   if (value is num) return value; |  | 
| 2993   if (JS('bool', '#[#]', getInterceptor(value), property)) return value; |  | 
| 2994   propertyTypeCastError(value, property); |  | 
| 2995 } |  | 
| 2996 |  | 
| 2997 /** |  | 
| 2998  * Specialization of the type check for String and its supertype |  | 
| 2999  * since [value] can be a JS primitive. |  | 
| 3000  */ |  | 
| 3001 stringSuperTypeCheck(value, property) { |  | 
| 3002   if (value == null) return value; |  | 
| 3003   if (value is String) return value; |  | 
| 3004   if (JS('bool', '!!#[#]', value, property)) return value; |  | 
| 3005   propertyTypeError(value, property); |  | 
| 3006 } |  | 
| 3007 |  | 
| 3008 stringSuperTypeCast(value, property) { |  | 
| 3009   if (value is String) return value; |  | 
| 3010   return propertyTypeCast(value, property); |  | 
| 3011 } |  | 
| 3012 |  | 
| 3013 stringSuperNativeTypeCheck(value, property) { |  | 
| 3014   if (value == null) return value; |  | 
| 3015   if (value is String) return value; |  | 
| 3016   if (JS('bool', '#[#]', getInterceptor(value), property)) return value; |  | 
| 3017   propertyTypeError(value, property); |  | 
| 3018 } |  | 
| 3019 |  | 
| 3020 stringSuperNativeTypeCast(value, property) { |  | 
| 3021   if (value is String || value == null) return value; |  | 
| 3022   if (JS('bool', '#[#]', getInterceptor(value), property)) return value; |  | 
| 3023   propertyTypeCastError(value, property); |  | 
| 3024 } |  | 
| 3025 |  | 
| 3026 /** |  | 
| 3027  * Specialization of the type check for List and its supertypes, |  | 
| 3028  * since [value] can be a JS array. |  | 
| 3029  */ |  | 
| 3030 listTypeCheck(value) { |  | 
| 3031   if (value == null) return value; |  | 
| 3032   if (value is List) return value; |  | 
| 3033   throw new TypeErrorImplementation(value, 'List'); |  | 
| 3034 } |  | 
| 3035 |  | 
| 3036 listTypeCast(value) { |  | 
| 3037   if (value is List || value == null) return value; |  | 
| 3038   throw new CastErrorImplementation( |  | 
| 3039       Primitives.objectTypeName(value), 'List'); |  | 
| 3040 } |  | 
| 3041 |  | 
| 3042 listSuperTypeCheck(value, property) { |  | 
| 3043   if (value == null) return value; |  | 
| 3044   if (value is List) return value; |  | 
| 3045   if (JS('bool', '!!#[#]', value, property)) return value; |  | 
| 3046   propertyTypeError(value, property); |  | 
| 3047 } |  | 
| 3048 |  | 
| 3049 listSuperTypeCast(value, property) { |  | 
| 3050   if (value is List) return value; |  | 
| 3051   return propertyTypeCast(value, property); |  | 
| 3052 } |  | 
| 3053 |  | 
| 3054 listSuperNativeTypeCheck(value, property) { |  | 
| 3055   if (value == null) return value; |  | 
| 3056   if (value is List) return value; |  | 
| 3057   if (JS('bool', '#[#]', getInterceptor(value), property)) return value; |  | 
| 3058   propertyTypeError(value, property); |  | 
| 3059 } |  | 
| 3060 |  | 
| 3061 listSuperNativeTypeCast(value, property) { |  | 
| 3062   if (value is List || value == null) return value; |  | 
| 3063   if (JS('bool', '#[#]', getInterceptor(value), property)) return value; |  | 
| 3064   propertyTypeCastError(value, property); |  | 
| 3065 } |  | 
| 3066 |  | 
| 3067 voidTypeCheck(value) { |  | 
| 3068   if (value == null) return value; |  | 
| 3069   throw new TypeErrorImplementation(value, 'void'); |  | 
| 3070 } |  | 
| 3071 |  | 
| 3072 checkMalformedType(value, message) { |  | 
| 3073   if (value == null) return value; |  | 
| 3074   throw new TypeErrorImplementation.fromMessage(message); |  | 
| 3075 } |  | 
| 3076 |  | 
| 3077 @NoInline() |  | 
| 3078 void checkDeferredIsLoaded(String loadId, String uri) { |  | 
| 3079   if (!_loadedLibraries.contains(loadId)) { |  | 
| 3080     throw new DeferredNotLoadedError(uri); |  | 
| 3081   } |  | 
| 3082 } |  | 
| 3083 |  | 
| 3084 /** |  | 
| 3085  * Special interface recognized by the compiler and implemented by DOM |  | 
| 3086  * objects that support integer indexing. This interface is not |  | 
| 3087  * visible to anyone, and is only injected into special libraries. |  | 
| 3088  */ |  | 
| 3089 abstract class JavaScriptIndexingBehavior extends JSMutableIndexable { |  | 
| 3090 } |  | 
| 3091 |  | 
| 3092 // TODO(lrn): These exceptions should be implemented in core. |  | 
| 3093 // When they are, remove the 'Implementation' here. |  | 
| 3094 |  | 
| 3095 /** Thrown by type assertions that fail. */ |  | 
| 3096 class TypeErrorImplementation extends Error implements TypeError { |  | 
| 3097   final String message; |  | 
| 3098 |  | 
| 3099   /** |  | 
| 3100    * Normal type error caused by a failed subtype test. |  | 
| 3101    */ |  | 
| 3102   TypeErrorImplementation(Object value, String type) |  | 
| 3103       : message = "type '${Primitives.objectTypeName(value)}' is not a subtype " |  | 
| 3104                   "of type '$type'"; |  | 
| 3105 |  | 
| 3106   TypeErrorImplementation.fromMessage(String this.message); |  | 
| 3107 |  | 
| 3108   String toString() => message; |  | 
| 3109 } |  | 
| 3110 |  | 
| 3111 /** Thrown by the 'as' operator if the cast isn't valid. */ |  | 
| 3112 class CastErrorImplementation extends Error implements CastError { |  | 
| 3113   // TODO(lrn): Rename to CastError (and move implementation into core). |  | 
| 3114   final String message; |  | 
| 3115 |  | 
| 3116   /** |  | 
| 3117    * Normal cast error caused by a failed type cast. |  | 
| 3118    */ |  | 
| 3119   CastErrorImplementation(Object actualType, Object expectedType) |  | 
| 3120       : message = "CastError: Casting value of type $actualType to" |  | 
| 3121                   " incompatible type $expectedType"; |  | 
| 3122 |  | 
| 3123   String toString() => message; |  | 
| 3124 } |  | 
| 3125 |  | 
| 3126 class FallThroughErrorImplementation extends FallThroughError { |  | 
| 3127   FallThroughErrorImplementation(); |  | 
| 3128   String toString() => "Switch case fall-through."; |  | 
| 3129 } |  | 
| 3130 |  | 
| 3131 /** |  | 
| 3132  * Helper function for implementing asserts. The compiler treats this specially. |  | 
| 3133  */ |  | 
| 3134 void assertHelper(condition) { |  | 
| 3135   // Do a bool check first because it is common and faster than 'is Function'. |  | 
| 3136   if (condition is !bool) { |  | 
| 3137     if (condition is Function) condition = condition(); |  | 
| 3138     if (condition is !bool) { |  | 
| 3139       throw new TypeErrorImplementation(condition, 'bool'); |  | 
| 3140     } |  | 
| 3141   } |  | 
| 3142   // Compare to true to avoid boolean conversion check in checked |  | 
| 3143   // mode. |  | 
| 3144   if (true != condition) throw new AssertionError(); |  | 
| 3145 } |  | 
| 3146 |  | 
| 3147 /** |  | 
| 3148  * Called by generated code when a method that must be statically |  | 
| 3149  * resolved cannot be found. |  | 
| 3150  */ |  | 
| 3151 void throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) { |  | 
| 3152   Symbol memberName = new _symbol_dev.Symbol.unvalidated(name); |  | 
| 3153   throw new NoSuchMethodError(obj, memberName, arguments, |  | 
| 3154                               new Map<Symbol, dynamic>(), |  | 
| 3155                               expectedArgumentNames); |  | 
| 3156 } |  | 
| 3157 |  | 
| 3158 /** |  | 
| 3159  * Called by generated code when a static field's initializer references the |  | 
| 3160  * field that is currently being initialized. |  | 
| 3161  */ |  | 
| 3162 void throwCyclicInit(String staticName) { |  | 
| 3163   throw new CyclicInitializationError( |  | 
| 3164       "Cyclic initialization for static $staticName"); |  | 
| 3165 } |  | 
| 3166 |  | 
| 3167 /** |  | 
| 3168  * Error thrown when a runtime error occurs. |  | 
| 3169  */ |  | 
| 3170 class RuntimeError extends Error { |  | 
| 3171   final message; |  | 
| 3172   RuntimeError(this.message); |  | 
| 3173   String toString() => "RuntimeError: $message"; |  | 
| 3174 } |  | 
| 3175 |  | 
| 3176 class DeferredNotLoadedError extends Error implements NoSuchMethodError { |  | 
| 3177   String libraryName; |  | 
| 3178 |  | 
| 3179   DeferredNotLoadedError(this.libraryName); |  | 
| 3180 |  | 
| 3181   String toString() { |  | 
| 3182     return "Deferred library $libraryName was not loaded."; |  | 
| 3183   } |  | 
| 3184 } |  | 
| 3185 |  | 
| 3186 abstract class RuntimeType { |  | 
| 3187   const RuntimeType(); |  | 
| 3188 |  | 
| 3189   toRti(); |  | 
| 3190 } |  | 
| 3191 |  | 
| 3192 class RuntimeFunctionType extends RuntimeType { |  | 
| 3193   final RuntimeType returnType; |  | 
| 3194   final List<RuntimeType> parameterTypes; |  | 
| 3195   final List<RuntimeType> optionalParameterTypes; |  | 
| 3196   final namedParameters; |  | 
| 3197 |  | 
| 3198   static var /* bool */ inAssert = false; |  | 
| 3199 |  | 
| 3200   RuntimeFunctionType(this.returnType, |  | 
| 3201                       this.parameterTypes, |  | 
| 3202                       this.optionalParameterTypes, |  | 
| 3203                       this.namedParameters); |  | 
| 3204 |  | 
| 3205   bool get isVoid => returnType is VoidRuntimeType; |  | 
| 3206 |  | 
| 3207   /// Called from generated code. [expression] is a Dart object and this method |  | 
| 3208   /// returns true if [this] is a supertype of [expression]. |  | 
| 3209   @NoInline() @NoSideEffects() |  | 
| 3210   bool _isTest(expression) { |  | 
| 3211     var functionTypeObject = _extractFunctionTypeObjectFrom(expression); |  | 
| 3212     return functionTypeObject == null |  | 
| 3213         ? false |  | 
| 3214         : isFunctionSubtype(functionTypeObject, toRti()); |  | 
| 3215   } |  | 
| 3216 |  | 
| 3217   @NoInline() @NoSideEffects() |  | 
| 3218   _asCheck(expression) { |  | 
| 3219     // Type inferrer doesn't think this is called with dynamic arguments. |  | 
| 3220     return _check(JS('', '#', expression), true); |  | 
| 3221   } |  | 
| 3222 |  | 
| 3223   @NoInline() @NoSideEffects() |  | 
| 3224   _assertCheck(expression) { |  | 
| 3225     if (inAssert) return null; |  | 
| 3226     inAssert = true; // Don't try to check this library itself. |  | 
| 3227     try { |  | 
| 3228       // Type inferrer don't think this is called with dynamic arguments. |  | 
| 3229       return _check(JS('', '#', expression), false); |  | 
| 3230     } finally { |  | 
| 3231       inAssert = false; |  | 
| 3232     } |  | 
| 3233   } |  | 
| 3234 |  | 
| 3235   _check(expression, bool isCast) { |  | 
| 3236     if (expression == null) return null; |  | 
| 3237     if (_isTest(expression)) return expression; |  | 
| 3238 |  | 
| 3239     var self = new FunctionTypeInfoDecoderRing(toRti()).toString(); |  | 
| 3240     if (isCast) { |  | 
| 3241       var functionTypeObject = _extractFunctionTypeObjectFrom(expression); |  | 
| 3242       var pretty; |  | 
| 3243       if (functionTypeObject != null) { |  | 
| 3244         pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString(); |  | 
| 3245       } else { |  | 
| 3246         pretty = Primitives.objectTypeName(expression); |  | 
| 3247       } |  | 
| 3248       throw new CastErrorImplementation(pretty, self); |  | 
| 3249     } else { |  | 
| 3250       // TODO(ahe): Pass "pretty" function-type to TypeErrorImplementation? |  | 
| 3251       throw new TypeErrorImplementation(expression, self); |  | 
| 3252     } |  | 
| 3253   } |  | 
| 3254 |  | 
| 3255   _extractFunctionTypeObjectFrom(o) { |  | 
| 3256     var interceptor = getInterceptor(o); |  | 
| 3257     var signatureName = JS_GET_NAME(JsGetName.SIGNATURE_NAME); |  | 
| 3258     return JS('bool', '# in #', signatureName, interceptor) |  | 
| 3259         ? JS('', '#[#]()', interceptor, JS_GET_NAME(JsGetName.SIGNATURE_NAME)) |  | 
| 3260         : null; |  | 
| 3261   } |  | 
| 3262 |  | 
| 3263   toRti() { |  | 
| 3264     var result = createDartFunctionTypeRti(); |  | 
| 3265     if (isVoid) { |  | 
| 3266       JS('', '#[#] = true', result, |  | 
| 3267           JS_GET_NAME(JsGetName.FUNCTION_TYPE_VOID_RETURN_TAG)); |  | 
| 3268     } else { |  | 
| 3269       if (returnType is! DynamicRuntimeType) { |  | 
| 3270         JS('', '#[#] = #', result, |  | 
| 3271            JS_GET_NAME(JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG), |  | 
| 3272            returnType.toRti()); |  | 
| 3273       } |  | 
| 3274     } |  | 
| 3275     if (parameterTypes != null && !parameterTypes.isEmpty) { |  | 
| 3276       JS('', '#[#] = #', result, |  | 
| 3277          JS_GET_NAME(JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG), |  | 
| 3278          listToRti(parameterTypes)); |  | 
| 3279     } |  | 
| 3280 |  | 
| 3281     if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { |  | 
| 3282       JS('', '#[#] = #', result, |  | 
| 3283          JS_GET_NAME(JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG), |  | 
| 3284          listToRti(optionalParameterTypes)); |  | 
| 3285     } |  | 
| 3286 |  | 
| 3287     if (namedParameters != null) { |  | 
| 3288       var namedRti = JS('=Object', 'Object.create(null)'); |  | 
| 3289       var keys = extractKeys(namedParameters); |  | 
| 3290       for (var i = 0; i < keys.length; i++) { |  | 
| 3291         var name = keys[i]; |  | 
| 3292         var rti = JS('', '#[#]', namedParameters, name).toRti(); |  | 
| 3293         JS('', '#[#] = #', namedRti, name, rti); |  | 
| 3294       } |  | 
| 3295       JS('', '#[#] = #', result, |  | 
| 3296          JS_GET_NAME(JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG), |  | 
| 3297          namedRti); |  | 
| 3298     } |  | 
| 3299 |  | 
| 3300     return result; |  | 
| 3301   } |  | 
| 3302 |  | 
| 3303   static listToRti(list) { |  | 
| 3304     list = JS('JSFixedArray', '#', list); |  | 
| 3305     var result = JS('JSExtendableArray', '[]'); |  | 
| 3306     for (var i = 0; i < list.length; i++) { |  | 
| 3307       JS('', '#.push(#)', result, list[i].toRti()); |  | 
| 3308     } |  | 
| 3309     return result; |  | 
| 3310   } |  | 
| 3311 |  | 
| 3312   String toString() { |  | 
| 3313     String result = '('; |  | 
| 3314     bool needsComma = false; |  | 
| 3315     if (parameterTypes != null) { |  | 
| 3316       for (var i = 0; i < parameterTypes.length; i++) { |  | 
| 3317         RuntimeType type = parameterTypes[i]; |  | 
| 3318         if (needsComma) result += ', '; |  | 
| 3319         result += '$type'; |  | 
| 3320         needsComma = true; |  | 
| 3321       } |  | 
| 3322     } |  | 
| 3323     if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { |  | 
| 3324       if (needsComma) result += ', '; |  | 
| 3325       needsComma = false; |  | 
| 3326       result += '['; |  | 
| 3327       for (var i = 0; i < optionalParameterTypes.length; i++) { |  | 
| 3328         RuntimeType type = optionalParameterTypes[i]; |  | 
| 3329         if (needsComma) result += ', '; |  | 
| 3330         result += '$type'; |  | 
| 3331         needsComma = true; |  | 
| 3332       } |  | 
| 3333       result += ']'; |  | 
| 3334     } else if (namedParameters != null) { |  | 
| 3335       if (needsComma) result += ', '; |  | 
| 3336       needsComma = false; |  | 
| 3337       result += '{'; |  | 
| 3338       var keys = extractKeys(namedParameters); |  | 
| 3339       for (var i = 0; i < keys.length; i++) { |  | 
| 3340         var name = keys[i]; |  | 
| 3341         if (needsComma) result += ', '; |  | 
| 3342         var rti = JS('', '#[#]', namedParameters, name).toRti(); |  | 
| 3343         result += '$rti ${JS("String", "#", name)}'; |  | 
| 3344         needsComma = true; |  | 
| 3345       } |  | 
| 3346       result += '}'; |  | 
| 3347     } |  | 
| 3348 |  | 
| 3349     result += ') -> $returnType'; |  | 
| 3350     return result; |  | 
| 3351   } |  | 
| 3352 } |  | 
| 3353 |  | 
| 3354 RuntimeFunctionType buildFunctionType(returnType, |  | 
| 3355                                       parameterTypes, |  | 
| 3356                                       optionalParameterTypes) { |  | 
| 3357   return new RuntimeFunctionType( |  | 
| 3358       returnType, |  | 
| 3359       parameterTypes, |  | 
| 3360       optionalParameterTypes, |  | 
| 3361       null); |  | 
| 3362 } |  | 
| 3363 |  | 
| 3364 RuntimeFunctionType buildNamedFunctionType(returnType, |  | 
| 3365                                            parameterTypes, |  | 
| 3366                                            namedParameters) { |  | 
| 3367   return new RuntimeFunctionType( |  | 
| 3368       returnType, |  | 
| 3369       parameterTypes, |  | 
| 3370       null, |  | 
| 3371       namedParameters); |  | 
| 3372 } |  | 
| 3373 |  | 
| 3374 RuntimeType buildInterfaceType(rti, typeArguments) { |  | 
| 3375   String jsConstructorName = rawRtiToJsConstructorName(rti); |  | 
| 3376   if (typeArguments == null || typeArguments.isEmpty) { |  | 
| 3377     return new RuntimeTypePlain(jsConstructorName); |  | 
| 3378   } |  | 
| 3379   return new RuntimeTypeGeneric(jsConstructorName, typeArguments, null); |  | 
| 3380 } |  | 
| 3381 |  | 
| 3382 class DynamicRuntimeType extends RuntimeType { |  | 
| 3383   const DynamicRuntimeType(); |  | 
| 3384 |  | 
| 3385   String toString() => 'dynamic'; |  | 
| 3386 |  | 
| 3387   toRti() => null; |  | 
| 3388 } |  | 
| 3389 |  | 
| 3390 RuntimeType getDynamicRuntimeType() => const DynamicRuntimeType(); |  | 
| 3391 |  | 
| 3392 class VoidRuntimeType extends RuntimeType { |  | 
| 3393   const VoidRuntimeType(); |  | 
| 3394 |  | 
| 3395   String toString() => 'void'; |  | 
| 3396 |  | 
| 3397   toRti() => throw 'internal error'; |  | 
| 3398 } |  | 
| 3399 |  | 
| 3400 RuntimeType getVoidRuntimeType() => const VoidRuntimeType(); |  | 
| 3401 |  | 
| 3402 /** |  | 
| 3403  * Meta helper for function type tests. |  | 
| 3404  * |  | 
| 3405  * A "meta helper" is a helper function that is never called but simulates how |  | 
| 3406  * generated code behaves as far as resolution and type inference is concerned. |  | 
| 3407  */ |  | 
| 3408 functionTypeTestMetaHelper() { |  | 
| 3409   var dyn = JS('', 'x'); |  | 
| 3410   var dyn2 = JS('', 'x'); |  | 
| 3411   List fixedListOrNull = JS('JSFixedArray|Null', 'x'); |  | 
| 3412   List fixedListOrNull2 = JS('JSFixedArray|Null', 'x'); |  | 
| 3413   List fixedList = JS('JSFixedArray', 'x'); |  | 
| 3414   // TODO(ahe): Can we use [UnknownJavaScriptObject] below? |  | 
| 3415   var /* UnknownJavaScriptObject */ jsObject = JS('=Object', 'x'); |  | 
| 3416 |  | 
| 3417   buildFunctionType(dyn, fixedListOrNull, fixedListOrNull2); |  | 
| 3418   buildNamedFunctionType(dyn, fixedList, jsObject); |  | 
| 3419   buildInterfaceType(dyn, fixedListOrNull); |  | 
| 3420   getDynamicRuntimeType(); |  | 
| 3421   getVoidRuntimeType(); |  | 
| 3422   convertRtiToRuntimeType(dyn); |  | 
| 3423   dyn._isTest(dyn2); |  | 
| 3424   dyn._asCheck(dyn2); |  | 
| 3425   dyn._assertCheck(dyn2); |  | 
| 3426 } |  | 
| 3427 |  | 
| 3428 RuntimeType convertRtiToRuntimeType(rti) { |  | 
| 3429   if (rti == null) { |  | 
| 3430     return getDynamicRuntimeType(); |  | 
| 3431   } else if (JS('bool', 'typeof # == "function"', rti)) { |  | 
| 3432     return new RuntimeTypePlain(JS('String', r'#.name', rti)); |  | 
| 3433   } else if (JS('bool', '#.constructor == Array', rti)) { |  | 
| 3434     List list = JS('JSFixedArray', '#', rti); |  | 
| 3435     String name = JS('String', r'#.name', list[0]); |  | 
| 3436     List arguments = []; |  | 
| 3437     for (int i = 1; i < list.length; i++) { |  | 
| 3438       arguments.add(convertRtiToRuntimeType(list[i])); |  | 
| 3439     } |  | 
| 3440     return new RuntimeTypeGeneric(name, arguments, rti); |  | 
| 3441   } else if (JS('bool', '"func" in #', rti)) { |  | 
| 3442     return new FunctionTypeInfoDecoderRing(rti).toRuntimeType(); |  | 
| 3443   } else { |  | 
| 3444     throw new RuntimeError( |  | 
| 3445         "Cannot convert " |  | 
| 3446         "'${JS('String', 'JSON.stringify(#)', rti)}' to RuntimeType."); |  | 
| 3447   } |  | 
| 3448 } |  | 
| 3449 |  | 
| 3450 class RuntimeTypePlain extends RuntimeType { |  | 
| 3451   /// The constructor name of this raw type. |  | 
| 3452   final String _jsConstructorName; |  | 
| 3453 |  | 
| 3454   RuntimeTypePlain(this._jsConstructorName); |  | 
| 3455 |  | 
| 3456   toRti() { |  | 
| 3457     var rti = jsConstructorNameToRti(_jsConstructorName); |  | 
| 3458     if (rti == null) throw "no type for '$_jsConstructorName'"; |  | 
| 3459     return rti; |  | 
| 3460   } |  | 
| 3461 |  | 
| 3462   String toString() => _jsConstructorName; |  | 
| 3463 } |  | 
| 3464 |  | 
| 3465 class RuntimeTypeGeneric extends RuntimeType { |  | 
| 3466   /// The constructor name of the raw type for this generic type. |  | 
| 3467   final String _jsConstructorName; |  | 
| 3468   final List<RuntimeType> arguments; |  | 
| 3469   var rti; |  | 
| 3470 |  | 
| 3471   RuntimeTypeGeneric(this._jsConstructorName, this.arguments, this.rti); |  | 
| 3472 |  | 
| 3473   toRti() { |  | 
| 3474     if (rti != null) return rti; |  | 
| 3475     var result = [jsConstructorNameToRti(_jsConstructorName)]; |  | 
| 3476     if (result[0] == null) { |  | 
| 3477       throw "no type for '$_jsConstructorName<...>'"; |  | 
| 3478     } |  | 
| 3479     for (RuntimeType argument in arguments) { |  | 
| 3480       result.add(argument.toRti()); |  | 
| 3481     } |  | 
| 3482     return rti = result; |  | 
| 3483   } |  | 
| 3484 |  | 
| 3485   String toString() => '$_jsConstructorName<${arguments.join(", ")}>'; |  | 
| 3486 } |  | 
| 3487 |  | 
| 3488 class FunctionTypeInfoDecoderRing { |  | 
| 3489   final _typeData; |  | 
| 3490   String _cachedToString; |  | 
| 3491 |  | 
| 3492   FunctionTypeInfoDecoderRing(this._typeData); |  | 
| 3493 |  | 
| 3494   bool get _hasReturnType => JS('bool', '"ret" in #', _typeData); |  | 
| 3495   get _returnType => JS('', '#.ret', _typeData); |  | 
| 3496 |  | 
| 3497   bool get _isVoid => JS('bool', '!!#.void', _typeData); |  | 
| 3498 |  | 
| 3499   bool get _hasArguments => JS('bool', '"args" in #', _typeData); |  | 
| 3500   List get _arguments => JS('JSExtendableArray', '#.args', _typeData); |  | 
| 3501 |  | 
| 3502   bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData); |  | 
| 3503   List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData); |  | 
| 3504 |  | 
| 3505   bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData); |  | 
| 3506   get _namedArguments => JS('=Object', '#.named', _typeData); |  | 
| 3507 |  | 
| 3508   RuntimeType toRuntimeType() { |  | 
| 3509     // TODO(ahe): Implement this (and update return type). |  | 
| 3510     return const DynamicRuntimeType(); |  | 
| 3511   } |  | 
| 3512 |  | 
| 3513   String _convert(type) { |  | 
| 3514     String result = runtimeTypeToString(type); |  | 
| 3515     if (result != null) return result; |  | 
| 3516     // Currently the [runtimeTypeToString] method doesn't handle function rtis. |  | 
| 3517     if (JS('bool', '"func" in #', type)) { |  | 
| 3518       return new FunctionTypeInfoDecoderRing(type).toString(); |  | 
| 3519     } else { |  | 
| 3520       throw 'bad type'; |  | 
| 3521     } |  | 
| 3522   } |  | 
| 3523 |  | 
| 3524   String toString() { |  | 
| 3525     if (_cachedToString != null) return _cachedToString; |  | 
| 3526     var s = "("; |  | 
| 3527     var sep = ''; |  | 
| 3528     if (_hasArguments) { |  | 
| 3529       for (var argument in _arguments) { |  | 
| 3530         s += sep; |  | 
| 3531         s += _convert(argument); |  | 
| 3532         sep = ', '; |  | 
| 3533       } |  | 
| 3534     } |  | 
| 3535     if (_hasOptionalArguments) { |  | 
| 3536       s += '$sep['; |  | 
| 3537       sep = ''; |  | 
| 3538       for (var argument in _optionalArguments) { |  | 
| 3539         s += sep; |  | 
| 3540         s += _convert(argument); |  | 
| 3541         sep = ', '; |  | 
| 3542       } |  | 
| 3543       s += ']'; |  | 
| 3544     } |  | 
| 3545     if (_hasNamedArguments) { |  | 
| 3546       s += '$sep{'; |  | 
| 3547       sep = ''; |  | 
| 3548       for (var name in extractKeys(_namedArguments)) { |  | 
| 3549         s += sep; |  | 
| 3550         s += '$name: '; |  | 
| 3551         s += _convert(JS('', '#[#]', _namedArguments, name)); |  | 
| 3552         sep = ', '; |  | 
| 3553       } |  | 
| 3554       s += '}'; |  | 
| 3555     } |  | 
| 3556     s += ') -> '; |  | 
| 3557     if (_isVoid) { |  | 
| 3558       s += 'void'; |  | 
| 3559     } else if (_hasReturnType) { |  | 
| 3560       s += _convert(_returnType); |  | 
| 3561     } else { |  | 
| 3562       s += 'dynamic'; |  | 
| 3563     } |  | 
| 3564     return _cachedToString = "$s"; |  | 
| 3565   } |  | 
| 3566 } |  | 
| 3567 |  | 
| 3568 // TODO(ahe): Remove this class and call noSuchMethod instead. |  | 
| 3569 class UnimplementedNoSuchMethodError extends Error |  | 
| 3570     implements NoSuchMethodError { |  | 
| 3571   final String _message; |  | 
| 3572 |  | 
| 3573   UnimplementedNoSuchMethodError(this._message); |  | 
| 3574 |  | 
| 3575   String toString() => "Unsupported operation: $_message"; |  | 
| 3576 } |  | 
| 3577 |  | 
| 3578 /** |  | 
| 3579  * Creates a random number with 64 bits of randomness. |  | 
| 3580  * |  | 
| 3581  * This will be truncated to the 53 bits available in a double. |  | 
| 3582  */ |  | 
| 3583 int random64() { |  | 
| 3584   // TODO(lrn): Use a secure random source. |  | 
| 3585   int int32a = JS("int", "(Math.random() * 0x100000000) >>> 0"); |  | 
| 3586   int int32b = JS("int", "(Math.random() * 0x100000000) >>> 0"); |  | 
| 3587   return int32a + int32b * 0x100000000; |  | 
| 3588 } |  | 
| 3589 |  | 
| 3590 String jsonEncodeNative(String string) { |  | 
| 3591   return JS("String", "JSON.stringify(#)", string); |  | 
| 3592 } |  | 
| 3593 |  | 
| 3594 /** |  | 
| 3595  * Returns a property name for placing data on JavaScript objects shared between |  | 
| 3596  * DOM isolates.  This happens when multiple programs are loaded in the same |  | 
| 3597  * JavaScript context (i.e. page).  The name is based on [name] but with an |  | 
| 3598  * additional part that is unique for each isolate. |  | 
| 3599  * |  | 
| 3600  * The form of the name is '___dart_$name_$id'. |  | 
| 3601  */ |  | 
| 3602 String getIsolateAffinityTag(String name) { |  | 
| 3603   var isolateTagGetter = |  | 
| 3604       JS_EMBEDDED_GLOBAL('', GET_ISOLATE_TAG); |  | 
| 3605   return JS('String', '#(#)', isolateTagGetter, name); |  | 
| 3606 } |  | 
| 3607 |  | 
| 3608 typedef Future<Null> LoadLibraryFunctionType(); |  | 
| 3609 |  | 
| 3610 LoadLibraryFunctionType _loadLibraryWrapper(String loadId) { |  | 
| 3611   return () => loadDeferredLibrary(loadId); |  | 
| 3612 } |  | 
| 3613 |  | 
| 3614 final Map<String, Future<Null>> _loadingLibraries = <String, Future<Null>>{}; |  | 
| 3615 final Set<String> _loadedLibraries = new Set<String>(); |  | 
| 3616 |  | 
| 3617 typedef void DeferredLoadCallback(); |  | 
| 3618 |  | 
| 3619 // Function that will be called every time a new deferred import is loaded. |  | 
| 3620 DeferredLoadCallback deferredLoadHook; |  | 
| 3621 |  | 
| 3622 Future<Null> loadDeferredLibrary(String loadId) { |  | 
| 3623   // For each loadId there is a list of hunk-uris to load, and a corresponding |  | 
| 3624   // list of hashes. These are stored in the app-global scope. |  | 
| 3625   var urisMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_URIS); |  | 
| 3626   List<String> uris = JS('JSExtendableArray|Null', '#[#]', urisMap, loadId); |  | 
| 3627   var hashesMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_HASHES); |  | 
| 3628   List<String> hashes = JS('JSExtendableArray|Null', '#[#]', hashesMap, loadId); |  | 
| 3629   if (uris == null) return new Future.value(null); |  | 
| 3630   // The indices into `uris` and `hashes` that we want to load. |  | 
| 3631   List<int> indices = new List.generate(uris.length, (i) => i); |  | 
| 3632   var isHunkLoaded = JS_EMBEDDED_GLOBAL('', IS_HUNK_LOADED); |  | 
| 3633   var isHunkInitialized = JS_EMBEDDED_GLOBAL('', IS_HUNK_INITIALIZED); |  | 
| 3634   // Filter away indices for hunks that have already been loaded. |  | 
| 3635   List<int> indicesToLoad = indices |  | 
| 3636       .where((int i) => !JS('bool','#(#)', isHunkLoaded, hashes[i])) |  | 
| 3637       .toList(); |  | 
| 3638   return Future.wait(indicesToLoad |  | 
| 3639       .map((int i) => _loadHunk(uris[i]))).then((_) { |  | 
| 3640     // Now all hunks have been loaded, we run the needed initializers. |  | 
| 3641     List<int> indicesToInitialize = indices |  | 
| 3642         .where((int i) => !JS('bool','#(#)', isHunkInitialized, hashes[i])) |  | 
| 3643         .toList();  // Load the needed hunks. |  | 
| 3644     for (int i in indicesToInitialize) { |  | 
| 3645       var initializer = JS_EMBEDDED_GLOBAL('', INITIALIZE_LOADED_HUNK); |  | 
| 3646       JS('void', '#(#)', initializer, hashes[i]); |  | 
| 3647     } |  | 
| 3648     bool updated = _loadedLibraries.add(loadId); |  | 
| 3649     if (updated && deferredLoadHook != null) { |  | 
| 3650       deferredLoadHook(); |  | 
| 3651     } |  | 
| 3652   }); |  | 
| 3653 } |  | 
| 3654 |  | 
| 3655 Future<Null> _loadHunk(String hunkName) { |  | 
| 3656   Future<Null> future = _loadingLibraries[hunkName]; |  | 
| 3657   if (future != null) { |  | 
| 3658     return future.then((_) => null); |  | 
| 3659   } |  | 
| 3660 |  | 
| 3661   String uri = IsolateNatives.thisScript; |  | 
| 3662 |  | 
| 3663   int index = uri.lastIndexOf('/'); |  | 
| 3664   uri = '${uri.substring(0, index + 1)}$hunkName'; |  | 
| 3665 |  | 
| 3666   var deferredLibraryLoader = JS('', 'self.dartDeferredLibraryLoader'); |  | 
| 3667   Completer<Null> completer = new Completer<Null>(); |  | 
| 3668 |  | 
| 3669   void success() { |  | 
| 3670     completer.complete(null); |  | 
| 3671   } |  | 
| 3672 |  | 
| 3673   void failure([error, StackTrace stackTrace]) { |  | 
| 3674     _loadingLibraries[hunkName] = null; |  | 
| 3675     completer.completeError( |  | 
| 3676         new DeferredLoadException("Loading $uri failed: $error"), |  | 
| 3677         stackTrace); |  | 
| 3678   } |  | 
| 3679 |  | 
| 3680   var jsSuccess = convertDartClosureToJS(success, 0); |  | 
| 3681   var jsFailure = convertDartClosureToJS((error) { |  | 
| 3682     failure(unwrapException(error), getTraceFromException(error)); |  | 
| 3683   }, 1); |  | 
| 3684 |  | 
| 3685   if (JS('bool', 'typeof # === "function"', deferredLibraryLoader)) { |  | 
| 3686     try { |  | 
| 3687       JS('void', '#(#, #, #)', deferredLibraryLoader, uri, |  | 
| 3688           jsSuccess, jsFailure); |  | 
| 3689     } catch (error, stackTrace) { |  | 
| 3690       failure(error, stackTrace); |  | 
| 3691     } |  | 
| 3692   } else if (isWorker()) { |  | 
| 3693     // We are in a web worker. Load the code with an XMLHttpRequest. |  | 
| 3694     enterJsAsync(); |  | 
| 3695     Future<Null> leavingFuture = completer.future.whenComplete(() { |  | 
| 3696       leaveJsAsync(); |  | 
| 3697     }); |  | 
| 3698 |  | 
| 3699     int index = uri.lastIndexOf('/'); |  | 
| 3700     uri = '${uri.substring(0, index + 1)}$hunkName'; |  | 
| 3701     var xhr = JS('var', 'new XMLHttpRequest()'); |  | 
| 3702     JS('void', '#.open("GET", #)', xhr, uri); |  | 
| 3703     JS('void', '#.addEventListener("load", #, false)', |  | 
| 3704        xhr, convertDartClosureToJS((event) { |  | 
| 3705       if (JS('int', '#.status', xhr) != 200) { |  | 
| 3706         failure(""); |  | 
| 3707       } |  | 
| 3708       String code = JS('String', '#.responseText', xhr); |  | 
| 3709       try { |  | 
| 3710         // Create a new function to avoid getting access to current function |  | 
| 3711         // context. |  | 
| 3712         JS('void', '(new Function(#))()', code); |  | 
| 3713         success(); |  | 
| 3714       } catch (error, stackTrace) { |  | 
| 3715         failure(error, stackTrace); |  | 
| 3716       } |  | 
| 3717     }, 1)); |  | 
| 3718 |  | 
| 3719     JS('void', '#.addEventListener("error", #, false)', xhr, failure); |  | 
| 3720     JS('void', '#.addEventListener("abort", #, false)', xhr, failure); |  | 
| 3721     JS('void', '#.send()', xhr); |  | 
| 3722   } else { |  | 
| 3723     // We are in a dom-context. |  | 
| 3724     // Inject a script tag. |  | 
| 3725     var script = JS('', 'document.createElement("script")'); |  | 
| 3726     JS('', '#.type = "text/javascript"', script); |  | 
| 3727     JS('', '#.src = #', script, uri); |  | 
| 3728     JS('', '#.addEventListener("load", #, false)', script, jsSuccess); |  | 
| 3729     JS('', '#.addEventListener("error", #, false)', script, jsFailure); |  | 
| 3730     JS('', 'document.body.appendChild(#)', script); |  | 
| 3731   } |  | 
| 3732   _loadingLibraries[hunkName] = completer.future; |  | 
| 3733   return completer.future; |  | 
| 3734 } |  | 
| 3735 |  | 
| 3736 class MainError extends Error implements NoSuchMethodError { |  | 
| 3737   final String _message; |  | 
| 3738 |  | 
| 3739   MainError(this._message); |  | 
| 3740 |  | 
| 3741   String toString() => 'NoSuchMethodError: $_message'; |  | 
| 3742 } |  | 
| 3743 |  | 
| 3744 void missingMain() { |  | 
| 3745   throw new MainError("No top-level function named 'main'."); |  | 
| 3746 } |  | 
| 3747 |  | 
| 3748 void badMain() { |  | 
| 3749   throw new MainError("'main' is not a function."); |  | 
| 3750 } |  | 
| 3751 |  | 
| 3752 void mainHasTooManyParameters() { |  | 
| 3753   throw new MainError("'main' expects too many parameters."); |  | 
| 3754 } |  | 
| 3755 |  | 
| 3756 /// A wrapper around an exception, much like the one created by [wrapException] |  | 
| 3757 /// but with a pre-given stack-trace. |  | 
| 3758 class ExceptionAndStackTrace { |  | 
| 3759   dynamic dartException; |  | 
| 3760   StackTrace stackTrace; |  | 
| 3761 |  | 
| 3762   ExceptionAndStackTrace(this.dartException, this.stackTrace); |  | 
| 3763 } |  | 
| 3764 |  | 
| 3765 /// Runtime support for async-await transformation. |  | 
| 3766 /// |  | 
| 3767 /// This function is called by a transformed function on each await and return |  | 
| 3768 /// in the untransformed function, and before starting. |  | 
| 3769 /// |  | 
| 3770 /// If [object] is not a future it will be wrapped in a `new Future.value`. |  | 
| 3771 /// |  | 
| 3772 /// If [asyncBody] is [async_error_codes.SUCCESS]/[async_error_codes.ERROR] it |  | 
| 3773 /// indicates a return or throw from the async function, and |  | 
| 3774 /// complete/completeError is called on [completer] with [object]. |  | 
| 3775 /// |  | 
| 3776 /// Otherwise [asyncBody] is set up to be called when the future is completed |  | 
| 3777 /// with a code [async_error_codes.SUCCESS]/[async_error_codes.ERROR] depending |  | 
| 3778 /// on the success of the future. |  | 
| 3779 /// |  | 
| 3780 /// Returns the future of the completer for convenience of the first call. |  | 
| 3781 dynamic asyncHelper(dynamic object, |  | 
| 3782                     dynamic /* js function */ bodyFunctionOrErrorCode, |  | 
| 3783                     Completer completer) { |  | 
| 3784   if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |  | 
| 3785     completer.complete(object); |  | 
| 3786     return; |  | 
| 3787   } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |  | 
| 3788     // The error is a js-error. |  | 
| 3789     completer.completeError(unwrapException(object), |  | 
| 3790                             getTraceFromException(object)); |  | 
| 3791     return; |  | 
| 3792   } |  | 
| 3793   Future future = object is Future ? object : new Future.value(object); |  | 
| 3794   future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |  | 
| 3795                                       async_error_codes.SUCCESS), |  | 
| 3796       onError: (dynamic error, StackTrace stackTrace) { |  | 
| 3797         ExceptionAndStackTrace wrappedException = |  | 
| 3798             new ExceptionAndStackTrace(error, stackTrace); |  | 
| 3799         Function wrapped =_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |  | 
| 3800             async_error_codes.ERROR); |  | 
| 3801         wrapped(wrappedException); |  | 
| 3802       }); |  | 
| 3803   return completer.future; |  | 
| 3804 } |  | 
| 3805 |  | 
| 3806 Function _wrapJsFunctionForAsync(dynamic /* js function */ function, |  | 
| 3807                                  int errorCode) { |  | 
| 3808   var protected = JS('', """ |  | 
| 3809     // Invokes [function] with [errorCode] and [result]. |  | 
| 3810     // |  | 
| 3811     // If (and as long as) the invocation throws, calls [function] again, |  | 
| 3812     // with an error-code. |  | 
| 3813     function(errorCode, result) { |  | 
| 3814       while (true) { |  | 
| 3815         try { |  | 
| 3816           #(errorCode, result); |  | 
| 3817           break; |  | 
| 3818         } catch (error) { |  | 
| 3819           result = error; |  | 
| 3820           errorCode = #; |  | 
| 3821         } |  | 
| 3822       } |  | 
| 3823     }""", function, async_error_codes.ERROR); |  | 
| 3824   return (result) { |  | 
| 3825     JS('', '#(#, #)', protected, errorCode, result); |  | 
| 3826   }; |  | 
| 3827 } |  | 
| 3828 |  | 
| 3829 /// Implements the runtime support for async* functions. |  | 
| 3830 /// |  | 
| 3831 /// Called by the transformed function for each original return, await, yield, |  | 
| 3832 /// yield* and before starting the function. |  | 
| 3833 /// |  | 
| 3834 /// When the async* function wants to return it calls this function with |  | 
| 3835 /// [asyncBody] == [async_error_codes.SUCCESS], the asyncStarHelper takes this |  | 
| 3836 /// as signal to close the stream. |  | 
| 3837 /// |  | 
| 3838 /// When the async* function wants to signal that an uncaught error was thrown, |  | 
| 3839 /// it calls this function with [asyncBody] == [async_error_codes.ERROR], |  | 
| 3840 /// the streamHelper takes this as signal to addError [object] to the |  | 
| 3841 /// [controller] and close it. |  | 
| 3842 /// |  | 
| 3843 /// If the async* function wants to do a yield or yield*, it calls this function |  | 
| 3844 /// with [object] being an [IterationMarker]. |  | 
| 3845 /// |  | 
| 3846 /// In the case of a yield or yield*, if the stream subscription has been |  | 
| 3847 /// canceled, schedules [asyncBody] to be called with |  | 
| 3848 /// [async_error_codes.STREAM_WAS_CANCELED]. |  | 
| 3849 /// |  | 
| 3850 /// If [object] is a single-yield [IterationMarker], adds the value of the |  | 
| 3851 /// [IterationMarker] to the stream. If the stream subscription has been |  | 
| 3852 /// paused, return early. Otherwise schedule the helper function to be |  | 
| 3853 /// executed again. |  | 
| 3854 /// |  | 
| 3855 /// If [object] is a yield-star [IterationMarker], starts listening to the |  | 
| 3856 /// yielded stream, and adds all events and errors to our own controller (taking |  | 
| 3857 /// care if the subscription has been paused or canceled) - when the sub-stream |  | 
| 3858 /// is done, schedules [asyncBody] again. |  | 
| 3859 /// |  | 
| 3860 /// If the async* function wants to do an await it calls this function with |  | 
| 3861 /// [object] not and [IterationMarker]. |  | 
| 3862 /// |  | 
| 3863 /// If [object] is not a [Future], it is wrapped in a `Future.value`. |  | 
| 3864 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. |  | 
| 3865 void asyncStarHelper(dynamic object, |  | 
| 3866                      dynamic /* int | js function */ bodyFunctionOrErrorCode, |  | 
| 3867                      AsyncStarStreamController controller) { |  | 
| 3868   if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |  | 
| 3869     // This happens on return from the async* function. |  | 
| 3870     if (controller.isCanceled) { |  | 
| 3871       controller.cancelationCompleter.complete(); |  | 
| 3872     } else { |  | 
| 3873       controller.close(); |  | 
| 3874     } |  | 
| 3875     return; |  | 
| 3876   } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |  | 
| 3877     // The error is a js-error. |  | 
| 3878     if (controller.isCanceled) { |  | 
| 3879       controller.cancelationCompleter.completeError( |  | 
| 3880           unwrapException(object), |  | 
| 3881           getTraceFromException(object)); |  | 
| 3882     } else { |  | 
| 3883       controller.addError(unwrapException(object), |  | 
| 3884                           getTraceFromException(object)); |  | 
| 3885       controller.close(); |  | 
| 3886     } |  | 
| 3887     return; |  | 
| 3888   } |  | 
| 3889 |  | 
| 3890   if (object is IterationMarker) { |  | 
| 3891     if (controller.isCanceled) { |  | 
| 3892       Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |  | 
| 3893           async_error_codes.STREAM_WAS_CANCELED); |  | 
| 3894       wrapped(null); |  | 
| 3895       return; |  | 
| 3896     } |  | 
| 3897     if (object.state == IterationMarker.YIELD_SINGLE) { |  | 
| 3898       controller.add(object.value); |  | 
| 3899 |  | 
| 3900       scheduleMicrotask(() { |  | 
| 3901         if (controller.isPaused) { |  | 
| 3902           // We only suspend the thread inside the microtask in order to allow |  | 
| 3903           // listeners on the output stream to pause in response to the just |  | 
| 3904           // output value, and have the stream immediately stop producing. |  | 
| 3905           controller.isSuspended = true; |  | 
| 3906           return; |  | 
| 3907         } |  | 
| 3908         Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |  | 
| 3909             async_error_codes.SUCCESS); |  | 
| 3910         wrapped(null); |  | 
| 3911       }); |  | 
| 3912       return; |  | 
| 3913     } else if (object.state == IterationMarker.YIELD_STAR) { |  | 
| 3914       Stream stream = object.value; |  | 
| 3915       // Errors of [stream] are passed though to the main stream. (see |  | 
| 3916       // [AsyncStreamController.addStream]). |  | 
| 3917       // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |  | 
| 3918       controller.addStream(stream).then((_) { |  | 
| 3919         // No check for isPaused here because the spec 17.16.2 only |  | 
| 3920         // demands checks *before* each element in [stream] not after the last |  | 
| 3921         // one. On the other hand we check for isCanceled, as that check happens |  | 
| 3922         // after insertion of each element. |  | 
| 3923         int errorCode = controller.isCanceled |  | 
| 3924             ? async_error_codes.STREAM_WAS_CANCELED |  | 
| 3925             : async_error_codes.SUCCESS; |  | 
| 3926         Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |  | 
| 3927                                 errorCode); |  | 
| 3928         wrapped(null); |  | 
| 3929       }); |  | 
| 3930       return; |  | 
| 3931     } |  | 
| 3932   } |  | 
| 3933 |  | 
| 3934   Future future = object is Future ? object : new Future.value(object); |  | 
| 3935   future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |  | 
| 3936                                       async_error_codes.SUCCESS), |  | 
| 3937               onError: (error, StackTrace stackTrace) { |  | 
| 3938                 ExceptionAndStackTrace wrappedException = |  | 
| 3939                     new ExceptionAndStackTrace(error, stackTrace); |  | 
| 3940                 Function wrapped = _wrapJsFunctionForAsync( |  | 
| 3941                     bodyFunctionOrErrorCode, async_error_codes.ERROR); |  | 
| 3942                 return wrapped(wrappedException); |  | 
| 3943               }); |  | 
| 3944 } |  | 
| 3945 |  | 
| 3946 Stream streamOfController(AsyncStarStreamController controller) { |  | 
| 3947   return controller.stream; |  | 
| 3948 } |  | 
| 3949 |  | 
| 3950 /// A wrapper around a [StreamController] that keeps track of the state of |  | 
| 3951 /// the execution of an async* function. |  | 
| 3952 /// It can be in 1 of 3 states: |  | 
| 3953 /// |  | 
| 3954 /// - running/scheduled |  | 
| 3955 /// - suspended |  | 
| 3956 /// - canceled |  | 
| 3957 /// |  | 
| 3958 /// If yielding while the subscription is paused it will become suspended. And |  | 
| 3959 /// only resume after the subscription is resumed or canceled. |  | 
| 3960 class AsyncStarStreamController { |  | 
| 3961   StreamController controller; |  | 
| 3962   Stream get stream => controller.stream; |  | 
| 3963 |  | 
| 3964   /// True when the async* function has yielded while being paused. |  | 
| 3965   /// When true execution will only resume after a `onResume` or `onCancel` |  | 
| 3966   /// event. |  | 
| 3967   bool isSuspended = false; |  | 
| 3968 |  | 
| 3969   bool get isPaused => controller.isPaused; |  | 
| 3970 |  | 
| 3971   Completer cancelationCompleter = null; |  | 
| 3972 |  | 
| 3973   /// True after the StreamSubscription has been cancelled. |  | 
| 3974   /// When this is true, errors thrown from the async* body should go to the |  | 
| 3975   /// [cancelationCompleter] instead of adding them to [controller], and |  | 
| 3976   /// returning from the async function should complete [cancelationCompleter]. |  | 
| 3977   bool get isCanceled => cancelationCompleter != null; |  | 
| 3978 |  | 
| 3979   add(event) => controller.add(event); |  | 
| 3980 |  | 
| 3981   addStream(Stream stream) { |  | 
| 3982     return controller.addStream(stream, cancelOnError: false); |  | 
| 3983   } |  | 
| 3984 |  | 
| 3985   addError(error, stackTrace) => controller.addError(error, stackTrace); |  | 
| 3986 |  | 
| 3987   close() => controller.close(); |  | 
| 3988 |  | 
| 3989   AsyncStarStreamController(body) { |  | 
| 3990 |  | 
| 3991     _resumeBody() { |  | 
| 3992       scheduleMicrotask(() { |  | 
| 3993         Function wrapped = |  | 
| 3994             _wrapJsFunctionForAsync(body, async_error_codes.SUCCESS); |  | 
| 3995         wrapped(null); |  | 
| 3996       }); |  | 
| 3997     } |  | 
| 3998 |  | 
| 3999     controller = new StreamController( |  | 
| 4000       onListen: () { |  | 
| 4001         _resumeBody(); |  | 
| 4002       }, onResume: () { |  | 
| 4003         // Only schedule again if the async* function actually is suspended. |  | 
| 4004         // Resume directly instead of scheduling, so that the sequence |  | 
| 4005         // `pause-resume-pause` will result in one extra event produced. |  | 
| 4006         if (isSuspended) { |  | 
| 4007           isSuspended = false; |  | 
| 4008           _resumeBody(); |  | 
| 4009         } |  | 
| 4010       }, onCancel: () { |  | 
| 4011         // If the async* is finished we ignore cancel events. |  | 
| 4012         if (!controller.isClosed) { |  | 
| 4013           cancelationCompleter = new Completer(); |  | 
| 4014           if (isSuspended) { |  | 
| 4015             // Resume the suspended async* function to run finalizers. |  | 
| 4016             isSuspended = false; |  | 
| 4017             scheduleMicrotask(() { |  | 
| 4018               Function wrapped =_wrapJsFunctionForAsync(body, |  | 
| 4019                   async_error_codes.STREAM_WAS_CANCELED); |  | 
| 4020               wrapped(null); |  | 
| 4021             }); |  | 
| 4022           } |  | 
| 4023           return cancelationCompleter.future; |  | 
| 4024         } |  | 
| 4025       }); |  | 
| 4026   } |  | 
| 4027 } |  | 
| 4028 |  | 
| 4029 makeAsyncStarController(body) { |  | 
| 4030   return new AsyncStarStreamController(body); |  | 
| 4031 } |  | 
| 4032 |  | 
| 4033 class IterationMarker { |  | 
| 4034   static const YIELD_SINGLE = 0; |  | 
| 4035   static const YIELD_STAR = 1; |  | 
| 4036   static const ITERATION_ENDED = 2; |  | 
| 4037   static const UNCAUGHT_ERROR = 3; |  | 
| 4038 |  | 
| 4039   final value; |  | 
| 4040   final int state; |  | 
| 4041 |  | 
| 4042   IterationMarker._(this.state, this.value); |  | 
| 4043 |  | 
| 4044   static yieldStar(dynamic /* Iterable or Stream */ values) { |  | 
| 4045     return new IterationMarker._(YIELD_STAR, values); |  | 
| 4046   } |  | 
| 4047 |  | 
| 4048   static endOfIteration() { |  | 
| 4049     return new IterationMarker._(ITERATION_ENDED, null); |  | 
| 4050   } |  | 
| 4051 |  | 
| 4052   static yieldSingle(dynamic value) { |  | 
| 4053     return new IterationMarker._(YIELD_SINGLE, value); |  | 
| 4054   } |  | 
| 4055 |  | 
| 4056   static uncaughtError(dynamic error) { |  | 
| 4057     return new IterationMarker._(UNCAUGHT_ERROR, error); |  | 
| 4058   } |  | 
| 4059 |  | 
| 4060   toString() => "IterationMarker($state, $value)"; |  | 
| 4061 } |  | 
| 4062 |  | 
| 4063 class SyncStarIterator implements Iterator { |  | 
| 4064   final dynamic _body; |  | 
| 4065 |  | 
| 4066   // If [runningNested] this is the nested iterator, otherwise it is the |  | 
| 4067   // current value. |  | 
| 4068   dynamic _current = null; |  | 
| 4069   bool _runningNested = false; |  | 
| 4070 |  | 
| 4071   get current => _runningNested ? _current.current : _current; |  | 
| 4072 |  | 
| 4073   SyncStarIterator(this._body); |  | 
| 4074 |  | 
| 4075   _runBody() { |  | 
| 4076     return JS('', ''' |  | 
| 4077       // Invokes [body] with [errorCode] and [result]. |  | 
| 4078       // |  | 
| 4079       // If (and as long as) the invocation throws, calls [function] again, |  | 
| 4080       // with an error-code. |  | 
| 4081       (function(body) { |  | 
| 4082         var errorValue, errorCode = #; |  | 
| 4083         while (true) { |  | 
| 4084           try { |  | 
| 4085             return body(errorCode, errorValue); |  | 
| 4086           } catch (error) { |  | 
| 4087             errorValue = error; |  | 
| 4088             errorCode = # |  | 
| 4089           } |  | 
| 4090         } |  | 
| 4091       })(#)''', async_error_codes.SUCCESS, async_error_codes.ERROR, _body); |  | 
| 4092   } |  | 
| 4093 |  | 
| 4094 |  | 
| 4095   bool moveNext() { |  | 
| 4096     if (_runningNested) { |  | 
| 4097       if (_current.moveNext()) { |  | 
| 4098         return true; |  | 
| 4099       } else { |  | 
| 4100         _runningNested = false; |  | 
| 4101       } |  | 
| 4102     } |  | 
| 4103     _current = _runBody(); |  | 
| 4104     if (_current is IterationMarker) { |  | 
| 4105       if (_current.state == IterationMarker.ITERATION_ENDED) { |  | 
| 4106         _current = null; |  | 
| 4107         // Rely on [_body] to repeatedly return `ITERATION_ENDED`. |  | 
| 4108         return false; |  | 
| 4109       } else if (_current.state == IterationMarker.UNCAUGHT_ERROR) { |  | 
| 4110         // Rely on [_body] to repeatedly return `UNCAUGHT_ERROR`. |  | 
| 4111         // This is a wrapped exception, so we use JavaScript throw to throw it. |  | 
| 4112         JS('', 'throw #', _current.value); |  | 
| 4113       } else { |  | 
| 4114         assert(_current.state == IterationMarker.YIELD_STAR); |  | 
| 4115         _current = _current.value.iterator; |  | 
| 4116         _runningNested = true; |  | 
| 4117         return moveNext(); |  | 
| 4118       } |  | 
| 4119     } |  | 
| 4120     return true; |  | 
| 4121   } |  | 
| 4122 } |  | 
| 4123 |  | 
| 4124 /// An Iterable corresponding to a sync* method. |  | 
| 4125 /// |  | 
| 4126 /// Each invocation of a sync* method will return a new instance of this class. |  | 
| 4127 class SyncStarIterable extends IterableBase { |  | 
| 4128   // This is a function that will return a helper function that does the |  | 
| 4129   // iteration of the sync*. |  | 
| 4130   // |  | 
| 4131   // Each invocation should give a body with fresh state. |  | 
| 4132   final dynamic /* js function */ _outerHelper; |  | 
| 4133 |  | 
| 4134   SyncStarIterable(this._outerHelper); |  | 
| 4135 |  | 
| 4136   Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |  | 
| 4137 } |  | 
| OLD | NEW | 
|---|