| OLD | NEW | 
|     1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file |     1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
|     2 // for details. All rights reserved. Use of this source code is governed by a |     2 // for details. All rights reserved. Use of this source code is governed by a | 
|     3 // BSD-style license that can be found in the LICENSE file. |     3 // BSD-style license that can be found in the LICENSE file. | 
|     4  |     4  | 
|     5 // Patch file for the dart:isolate library. |     5 // Patch file for the dart:isolate library. | 
|     6  |     6  | 
|     7 import 'dart:uri'; |  | 
|     8  |  | 
|     9 /** |  | 
|    10  * Called by the compiler to support switching |  | 
|    11  * between isolates when we get a callback from the DOM. |  | 
|    12  */ |  | 
|    13 void _callInIsolate(_IsolateContext isolate, Function function) { |  | 
|    14   isolate.eval(function); |  | 
|    15   _globalState.topEventLoop.run(); |  | 
|    16 } |  | 
|    17  |  | 
|    18 /** |  | 
|    19  * Called by the compiler to fetch the current isolate context. |  | 
|    20  */ |  | 
|    21 _IsolateContext _currentIsolate() => _globalState.currentContext; |  | 
|    22  |  | 
|    23 /******************************************************** |  | 
|    24   Inserted from lib/isolate/dart2js/compiler_hooks.dart |  | 
|    25  ********************************************************/ |  | 
|    26  |  | 
|    27 /** |  | 
|    28  * Wrapper that takes the dart entry point and runs it within an isolate. The |  | 
|    29  * dart2js compiler will inject a call of the form |  | 
|    30  * [: startRootIsolate(main); :] when it determines that this wrapping |  | 
|    31  * is needed. For single-isolate applications (e.g. hello world), this |  | 
|    32  * call is not emitted. |  | 
|    33  */ |  | 
|    34 void startRootIsolate(entry) { |  | 
|    35   _globalState = new _Manager(); |  | 
|    36  |  | 
|    37   // Don't start the main loop again, if we are in a worker. |  | 
|    38   if (_globalState.isWorker) return; |  | 
|    39   final rootContext = new _IsolateContext(); |  | 
|    40   _globalState.rootContext = rootContext; |  | 
|    41   _fillStatics(rootContext); |  | 
|    42  |  | 
|    43   // BUG(5151491): Setting currentContext should not be necessary, but |  | 
|    44   // because closures passed to the DOM as event handlers do not bind their |  | 
|    45   // isolate automatically we try to give them a reasonable context to live in |  | 
|    46   // by having a "default" isolate (the first one created). |  | 
|    47   _globalState.currentContext = rootContext; |  | 
|    48  |  | 
|    49   rootContext.eval(entry); |  | 
|    50   _globalState.topEventLoop.run(); |  | 
|    51 } |  | 
|    52  |  | 
|    53 /******************************************************** |  | 
|    54   Inserted from lib/isolate/dart2js/isolateimpl.dart |  | 
|    55  ********************************************************/ |  | 
|    56  |  | 
|    57 /** |  | 
|    58  * Concepts used here: |  | 
|    59  * |  | 
|    60  * "manager" - A manager contains one or more isolates, schedules their |  | 
|    61  * execution, and performs other plumbing on their behalf.  The isolate |  | 
|    62  * present at the creation of the manager is designated as its "root isolate". |  | 
|    63  * A manager may, for example, be implemented on a web Worker. |  | 
|    64  * |  | 
|    65  * [_Manager] - State present within a manager (exactly once, as a global). |  | 
|    66  * |  | 
|    67  * [_ManagerStub] - A handle held within one manager that allows interaction |  | 
|    68  * with another manager.  A target manager may be addressed by zero or more |  | 
|    69  * [_ManagerStub]s. |  | 
|    70  * |  | 
|    71  */ |  | 
|    72  |  | 
|    73 /** |  | 
|    74  * A native object that is shared across isolates. This object is visible to all |  | 
|    75  * isolates running under the same manager (either UI or background web worker). |  | 
|    76  * |  | 
|    77  * This is code that is intended to 'escape' the isolate boundaries in order to |  | 
|    78  * implement the semantics of isolates in JavaScript. Without this we would have |  | 
|    79  * been forced to implement more code (including the top-level event loop) in |  | 
|    80  * JavaScript itself. |  | 
|    81  */ |  | 
|    82 // TODO(eub, sigmund): move the "manager" to be entirely in JS. |  | 
|    83 // Running any Dart code outside the context of an isolate gives it |  | 
|    84 // the change to break the isolate abstraction. |  | 
|    85 _Manager get _globalState => JS("_Manager", r"$globalState"); |  | 
|    86 set _globalState(_Manager val) { |  | 
|    87   JS("void", r"$globalState = #", val); |  | 
|    88 } |  | 
|    89  |  | 
|    90 void _fillStatics(context) { |  | 
|    91   JS("void", r"$globals = #.isolateStatics", context); |  | 
|    92   JS("void", r"$static_init()"); |  | 
|    93 } |  | 
|    94  |  | 
|    95 ReceivePort _lazyPort; |  | 
|    96 patch ReceivePort get port { |     7 patch ReceivePort get port { | 
|    97   if (_lazyPort == null) { |     8   if (lazyPort == null) { | 
|    98     _lazyPort = new ReceivePort(); |     9     lazyPort = new ReceivePort(); | 
|    99   } |    10   } | 
|   100   return _lazyPort; |    11   return lazyPort; | 
|   101 } |    12 } | 
|   102  |    13  | 
|   103 patch SendPort spawnFunction(void topLevelFunction(), |    14 patch SendPort spawnFunction(void topLevelFunction(), | 
|   104     [bool UnhandledExceptionCallback(IsolateUnhandledException e)]) { |    15     [bool UnhandledExceptionCallback(IsolateUnhandledException e)]) { | 
|   105   final name = _IsolateNatives._getJSFunctionName(topLevelFunction); |    16   return IsolateNatives.spawnFunction(topLevelFunction); | 
|   106   if (name == null) { |  | 
|   107     throw new UnsupportedError( |  | 
|   108         "only top-level functions can be spawned."); |  | 
|   109   } |  | 
|   110   return _IsolateNatives._spawn(name, null, false); |  | 
|   111 } |    17 } | 
|   112  |    18  | 
|   113 patch SendPort spawnUri(String uri) { |    19 patch SendPort spawnUri(String uri) { | 
|   114   return _IsolateNatives._spawn(null, uri, false); |    20   return IsolateNatives.spawn(null, uri, false); | 
|   115 } |    21 } | 
|   116  |    22  | 
|   117 /** State associated with the current manager. See [globalState]. */ |  | 
|   118 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? |  | 
|   119 class _Manager { |  | 
|   120  |  | 
|   121   /** Next available isolate id within this [_Manager]. */ |  | 
|   122   int nextIsolateId = 0; |  | 
|   123  |  | 
|   124   /** id assigned to this [_Manager]. */ |  | 
|   125   int currentManagerId = 0; |  | 
|   126  |  | 
|   127   /** |  | 
|   128    * Next available manager id. Only used by the main manager to assign a unique |  | 
|   129    * id to each manager created by it. |  | 
|   130    */ |  | 
|   131   int nextManagerId = 1; |  | 
|   132  |  | 
|   133   /** Context for the currently running [Isolate]. */ |  | 
|   134   _IsolateContext currentContext = null; |  | 
|   135  |  | 
|   136   /** Context for the root [Isolate] that first run in this [_Manager]. */ |  | 
|   137   _IsolateContext rootContext = null; |  | 
|   138  |  | 
|   139   /** The top-level event loop. */ |  | 
|   140   _EventLoop topEventLoop; |  | 
|   141  |  | 
|   142   /** Whether this program is running from the command line. */ |  | 
|   143   bool fromCommandLine; |  | 
|   144  |  | 
|   145   /** Whether this [_Manager] is running as a web worker. */ |  | 
|   146   bool isWorker; |  | 
|   147  |  | 
|   148   /** Whether we support spawning web workers. */ |  | 
|   149   bool supportsWorkers; |  | 
|   150  |  | 
|   151   /** |  | 
|   152    * Whether to use web workers when implementing isolates. Set to false for |  | 
|   153    * debugging/testing. |  | 
|   154    */ |  | 
|   155   bool get useWorkers => supportsWorkers; |  | 
|   156  |  | 
|   157   /** |  | 
|   158    * Whether to use the web-worker JSON-based message serialization protocol. By |  | 
|   159    * default this is only used with web workers. For debugging, you can force |  | 
|   160    * using this protocol by changing this field value to [true]. |  | 
|   161    */ |  | 
|   162   bool get needSerialization => useWorkers; |  | 
|   163  |  | 
|   164   /** |  | 
|   165    * Registry of isolates. Isolates must be registered if, and only if, receive |  | 
|   166    * ports are alive.  Normally no open receive-ports means that the isolate is |  | 
|   167    * dead, but DOM callbacks could resurrect it. |  | 
|   168    */ |  | 
|   169   Map<int, _IsolateContext> isolates; |  | 
|   170  |  | 
|   171   /** Reference to the main [_Manager].  Null in the main [_Manager] itself. */ |  | 
|   172   _ManagerStub mainManager; |  | 
|   173  |  | 
|   174   /** Registry of active [_ManagerStub]s.  Only used in the main [_Manager]. */ |  | 
|   175   Map<int, _ManagerStub> managers; |  | 
|   176  |  | 
|   177   _Manager() { |  | 
|   178     _nativeDetectEnvironment(); |  | 
|   179     topEventLoop = new _EventLoop(); |  | 
|   180     isolates = new Map<int, _IsolateContext>(); |  | 
|   181     managers = new Map<int, _ManagerStub>(); |  | 
|   182     if (isWorker) {  // "if we are not the main manager ourself" is the intent. |  | 
|   183       mainManager = new _MainManagerStub(); |  | 
|   184       _nativeInitWorkerMessageHandler(); |  | 
|   185     } |  | 
|   186   } |  | 
|   187  |  | 
|   188   void _nativeDetectEnvironment() { |  | 
|   189     isWorker = JS("bool", r"$isWorker"); |  | 
|   190     supportsWorkers = JS("bool", r"$supportsWorkers"); |  | 
|   191     fromCommandLine = JS("bool", r"typeof(window) == 'undefined'"); |  | 
|   192   } |  | 
|   193  |  | 
|   194   void _nativeInitWorkerMessageHandler() { |  | 
|   195     JS("void", r""" |  | 
|   196 $globalThis.onmessage = function (e) { |  | 
|   197   _IsolateNatives._processWorkerMessage(this.mainManager, e); |  | 
|   198 }"""); |  | 
|   199   } |  | 
|   200   /*: TODO: check that _processWorkerMessage is not discarded while treeshaking. |  | 
|   201   """ { |  | 
|   202     _IsolateNatives._processWorkerMessage(null, null); |  | 
|   203   } |  | 
|   204   */ |  | 
|   205  |  | 
|   206  |  | 
|   207   /** Close the worker running this code if all isolates are done. */ |  | 
|   208   void maybeCloseWorker() { |  | 
|   209     if (isolates.isEmpty) { |  | 
|   210       mainManager.postMessage(_serializeMessage({'command': 'close'})); |  | 
|   211     } |  | 
|   212   } |  | 
|   213 } |  | 
|   214  |  | 
|   215 /** Context information tracked for each isolate. */ |  | 
|   216 class _IsolateContext { |  | 
|   217   /** Current isolate id. */ |  | 
|   218   int id; |  | 
|   219  |  | 
|   220   /** Registry of receive ports currently active on this isolate. */ |  | 
|   221   Map<int, ReceivePort> ports; |  | 
|   222  |  | 
|   223   /** Holds isolate globals (statics and top-level properties). */ |  | 
|   224   var isolateStatics; // native object containing all globals of an isolate. |  | 
|   225  |  | 
|   226   _IsolateContext() { |  | 
|   227     id = _globalState.nextIsolateId++; |  | 
|   228     ports = new Map<int, ReceivePort>(); |  | 
|   229     initGlobals(); |  | 
|   230   } |  | 
|   231  |  | 
|   232   // these are filled lazily the first time the isolate starts running. |  | 
|   233   void initGlobals() { JS("void", r'$initGlobals(#)', this); } |  | 
|   234  |  | 
|   235   /** |  | 
|   236    * Run [code] in the context of the isolate represented by [this]. Note this |  | 
|   237    * is called from JavaScript (see $wrap_call in corejs.dart). |  | 
|   238    */ |  | 
|   239   dynamic eval(Function code) { |  | 
|   240     var old = _globalState.currentContext; |  | 
|   241     _globalState.currentContext = this; |  | 
|   242     this._setGlobals(); |  | 
|   243     var result = null; |  | 
|   244     try { |  | 
|   245       result = code(); |  | 
|   246     } finally { |  | 
|   247       _globalState.currentContext = old; |  | 
|   248       if (old != null) old._setGlobals(); |  | 
|   249     } |  | 
|   250     return result; |  | 
|   251   } |  | 
|   252  |  | 
|   253   void _setGlobals() { JS("void", r'$setGlobals(#)', this); } |  | 
|   254  |  | 
|   255   /** Lookup a port registered for this isolate. */ |  | 
|   256   ReceivePort lookup(int portId) => ports[portId]; |  | 
|   257  |  | 
|   258   /** Register a port on this isolate. */ |  | 
|   259   void register(int portId, ReceivePort port)  { |  | 
|   260     if (ports.containsKey(portId)) { |  | 
|   261       throw new Exception("Registry: ports must be registered only once."); |  | 
|   262     } |  | 
|   263     ports[portId] = port; |  | 
|   264     _globalState.isolates[id] = this; // indicate this isolate is active |  | 
|   265   } |  | 
|   266  |  | 
|   267   /** Unregister a port on this isolate. */ |  | 
|   268   void unregister(int portId) { |  | 
|   269     ports.remove(portId); |  | 
|   270     if (ports.isEmpty) { |  | 
|   271       _globalState.isolates.remove(id); // indicate this isolate is not active |  | 
|   272     } |  | 
|   273   } |  | 
|   274 } |  | 
|   275  |  | 
|   276 /** Represent the event loop on a javascript thread (DOM or worker). */ |  | 
|   277 class _EventLoop { |  | 
|   278   Queue<_IsolateEvent> events; |  | 
|   279  |  | 
|   280   _EventLoop() : events = new Queue<_IsolateEvent>(); |  | 
|   281  |  | 
|   282   void enqueue(isolate, fn, msg) { |  | 
|   283     events.addLast(new _IsolateEvent(isolate, fn, msg)); |  | 
|   284   } |  | 
|   285  |  | 
|   286   _IsolateEvent dequeue() { |  | 
|   287     if (events.isEmpty) return null; |  | 
|   288     return events.removeFirst(); |  | 
|   289   } |  | 
|   290  |  | 
|   291   /** Process a single event, if any. */ |  | 
|   292   bool runIteration() { |  | 
|   293     final event = dequeue(); |  | 
|   294     if (event == null) { |  | 
|   295       if (_globalState.isWorker) { |  | 
|   296         _globalState.maybeCloseWorker(); |  | 
|   297       } else if (_globalState.rootContext != null && |  | 
|   298                  _globalState.isolates.containsKey( |  | 
|   299                      _globalState.rootContext.id) && |  | 
|   300                  _globalState.fromCommandLine && |  | 
|   301                  _globalState.rootContext.ports.isEmpty) { |  | 
|   302         // We want to reach here only on the main [_Manager] and only |  | 
|   303         // on the command-line.  In the browser the isolate might |  | 
|   304         // still be alive due to DOM callbacks, but the presumption is |  | 
|   305         // that on the command-line, no future events can be injected |  | 
|   306         // into the event queue once it's empty.  Node has setTimeout |  | 
|   307         // so this presumption is incorrect there.  We think(?) that |  | 
|   308         // in d8 this assumption is valid. |  | 
|   309         throw new Exception("Program exited with open ReceivePorts."); |  | 
|   310       } |  | 
|   311       return false; |  | 
|   312     } |  | 
|   313     event.process(); |  | 
|   314     return true; |  | 
|   315   } |  | 
|   316  |  | 
|   317   /** |  | 
|   318    * Runs multiple iterations of the run-loop. If possible, each iteration is |  | 
|   319    * run asynchronously. |  | 
|   320    */ |  | 
|   321   void _runHelper() { |  | 
|   322     // [_window] is defined in timer_provider.dart. |  | 
|   323     if (_window != null) { |  | 
|   324       // Run each iteration from the browser's top event loop. |  | 
|   325       void next() { |  | 
|   326         if (!runIteration()) return; |  | 
|   327         _window.setTimeout(next, 0); |  | 
|   328       } |  | 
|   329       next(); |  | 
|   330     } else { |  | 
|   331       // Run synchronously until no more iterations are available. |  | 
|   332       while (runIteration()) {} |  | 
|   333     } |  | 
|   334   } |  | 
|   335  |  | 
|   336   /** |  | 
|   337    * Call [_runHelper] but ensure that worker exceptions are propragated. Note |  | 
|   338    * this is called from JavaScript (see $wrap_call in corejs.dart). |  | 
|   339    */ |  | 
|   340   void run() { |  | 
|   341     if (!_globalState.isWorker) { |  | 
|   342       _runHelper(); |  | 
|   343     } else { |  | 
|   344       try { |  | 
|   345         _runHelper(); |  | 
|   346       } catch (e, trace) { |  | 
|   347         _globalState.mainManager.postMessage(_serializeMessage( |  | 
|   348             {'command': 'error', 'msg': '$e\n$trace' })); |  | 
|   349       } |  | 
|   350     } |  | 
|   351   } |  | 
|   352 } |  | 
|   353  |  | 
|   354 /** An event in the top-level event queue. */ |  | 
|   355 class _IsolateEvent { |  | 
|   356   _IsolateContext isolate; |  | 
|   357   Function fn; |  | 
|   358   String message; |  | 
|   359  |  | 
|   360   _IsolateEvent(this.isolate, this.fn, this.message); |  | 
|   361  |  | 
|   362   void process() { |  | 
|   363     isolate.eval(fn); |  | 
|   364   } |  | 
|   365 } |  | 
|   366  |  | 
|   367 /** An interface for a stub used to interact with a manager. */ |  | 
|   368 abstract class _ManagerStub { |  | 
|   369   get id; |  | 
|   370   void set id(int i); |  | 
|   371   void set onmessage(Function f); |  | 
|   372   void postMessage(msg); |  | 
|   373   void terminate(); |  | 
|   374 } |  | 
|   375  |  | 
|   376 /** A stub for interacting with the main manager. */ |  | 
|   377 class _MainManagerStub implements _ManagerStub { |  | 
|   378   get id => 0; |  | 
|   379   void set id(int i) { throw new UnimplementedError(); } |  | 
|   380   void set onmessage(f) { |  | 
|   381     throw new Exception("onmessage should not be set on MainManagerStub"); |  | 
|   382   } |  | 
|   383   void postMessage(msg) { JS("void", r"$globalThis.postMessage(#)", msg); } |  | 
|   384   void terminate() {}  // Nothing useful to do here. |  | 
|   385 } |  | 
|   386  |  | 
|   387 /** |  | 
|   388  * A stub for interacting with a manager built on a web worker. This |  | 
|   389  * definition uses a 'hidden' type (* prefix on the native name) to |  | 
|   390  * enforce that the type is defined dynamically only when web workers |  | 
|   391  * are actually available. |  | 
|   392  */ |  | 
|   393 class _WorkerStub implements _ManagerStub native "*Worker" { |  | 
|   394   get id => JS("var", "#.id", this); |  | 
|   395   void set id(i) { JS("void", "#.id = #", this, i); } |  | 
|   396   void set onmessage(f) { JS("void", "#.onmessage = #", this, f); } |  | 
|   397   void postMessage(msg) => JS("void", "#.postMessage(#)", this, msg); |  | 
|   398   // terminate() is implemented by Worker. |  | 
|   399   void terminate(); |  | 
|   400 } |  | 
|   401  |  | 
|   402 const String _SPAWNED_SIGNAL = "spawned"; |  | 
|   403  |  | 
|   404 class _IsolateNatives { |  | 
|   405  |  | 
|   406   /** |  | 
|   407    * The src url for the script tag that loaded this code. Used to create |  | 
|   408    * JavaScript workers. |  | 
|   409    */ |  | 
|   410   static String get _thisScript => JS("String", r"$thisScriptUrl"); |  | 
|   411  |  | 
|   412   /** Starts a new worker with the given URL. */ |  | 
|   413   static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url)
      ; |  | 
|   414  |  | 
|   415   /** |  | 
|   416    * Assume that [e] is a browser message event and extract its message data. |  | 
|   417    * We don't import the dom explicitly so, when workers are disabled, this |  | 
|   418    * library can also run on top of nodejs. |  | 
|   419    */ |  | 
|   420   //static _getEventData(e) => JS("Object", "#.data", e); |  | 
|   421   static _getEventData(e) => JS("", "#.data", e); |  | 
|   422  |  | 
|   423   /** |  | 
|   424    * Process messages on a worker, either to control the worker instance or to |  | 
|   425    * pass messages along to the isolate running in the worker. |  | 
|   426    */ |  | 
|   427   static void _processWorkerMessage(sender, e) { |  | 
|   428     var msg = _deserializeMessage(_getEventData(e)); |  | 
|   429     switch (msg['command']) { |  | 
|   430       case 'start': |  | 
|   431         _globalState.currentManagerId = msg['id']; |  | 
|   432         Function entryPoint = _getJSFunctionFromName(msg['functionName']); |  | 
|   433         var replyTo = _deserializeMessage(msg['replyTo']); |  | 
|   434         _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { |  | 
|   435           _startIsolate(entryPoint, replyTo); |  | 
|   436         }, 'worker-start'); |  | 
|   437         _globalState.topEventLoop.run(); |  | 
|   438         break; |  | 
|   439       case 'spawn-worker': |  | 
|   440         _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']); |  | 
|   441         break; |  | 
|   442       case 'message': |  | 
|   443         msg['port'].send(msg['msg'], msg['replyTo']); |  | 
|   444         _globalState.topEventLoop.run(); |  | 
|   445         break; |  | 
|   446       case 'close': |  | 
|   447         _log("Closing Worker"); |  | 
|   448         _globalState.managers.remove(sender.id); |  | 
|   449         sender.terminate(); |  | 
|   450         _globalState.topEventLoop.run(); |  | 
|   451         break; |  | 
|   452       case 'log': |  | 
|   453         _log(msg['msg']); |  | 
|   454         break; |  | 
|   455       case 'print': |  | 
|   456         if (_globalState.isWorker) { |  | 
|   457           _globalState.mainManager.postMessage( |  | 
|   458               _serializeMessage({'command': 'print', 'msg': msg})); |  | 
|   459         } else { |  | 
|   460           print(msg['msg']); |  | 
|   461         } |  | 
|   462         break; |  | 
|   463       case 'error': |  | 
|   464         throw msg['msg']; |  | 
|   465     } |  | 
|   466   } |  | 
|   467  |  | 
|   468   /** Log a message, forwarding to the main [_Manager] if appropriate. */ |  | 
|   469   static _log(msg) { |  | 
|   470     if (_globalState.isWorker) { |  | 
|   471       _globalState.mainManager.postMessage( |  | 
|   472           _serializeMessage({'command': 'log', 'msg': msg })); |  | 
|   473     } else { |  | 
|   474       try { |  | 
|   475         _consoleLog(msg); |  | 
|   476       } catch (e, trace) { |  | 
|   477         throw new Exception(trace); |  | 
|   478       } |  | 
|   479     } |  | 
|   480   } |  | 
|   481  |  | 
|   482   static void _consoleLog(msg) { |  | 
|   483     JS("void", r"$globalThis.console.log(#)", msg); |  | 
|   484   } |  | 
|   485  |  | 
|   486   /** |  | 
|   487    * Extract the constructor of runnable, so it can be allocated in another |  | 
|   488    * isolate. |  | 
|   489    */ |  | 
|   490   static dynamic _getJSConstructor(Isolate runnable) { |  | 
|   491     return JS("Object", "#.constructor", runnable); |  | 
|   492   } |  | 
|   493  |  | 
|   494   /** Extract the constructor name of a runnable */ |  | 
|   495   // TODO(sigmund): find a browser-generic way to support this. |  | 
|   496   // TODO(floitsch): is this function still used? If yes, should we use |  | 
|   497   // Primitives.objectTypeName instead? |  | 
|   498   static dynamic _getJSConstructorName(Isolate runnable) { |  | 
|   499     return JS("Object", "#.constructor.name", runnable); |  | 
|   500   } |  | 
|   501  |  | 
|   502   /** Find a constructor given its name. */ |  | 
|   503   static dynamic _getJSConstructorFromName(String factoryName) { |  | 
|   504     return JS("Object", r"$globalThis[#]", factoryName); |  | 
|   505   } |  | 
|   506  |  | 
|   507   static dynamic _getJSFunctionFromName(String functionName) { |  | 
|   508     return JS("Object", r"$globalThis[#]", functionName); |  | 
|   509   } |  | 
|   510  |  | 
|   511   /** |  | 
|   512    * Get a string name for the function, if possible.  The result for |  | 
|   513    * anonymous functions is browser-dependent -- it may be "" or "anonymous" |  | 
|   514    * but you should probably not count on this. |  | 
|   515    */ |  | 
|   516   static String _getJSFunctionName(Function f) { |  | 
|   517     return JS("Object", r"(#.$name || #)", f, null); |  | 
|   518   } |  | 
|   519  |  | 
|   520   /** Create a new JavaScript object instance given its constructor. */ |  | 
|   521   static dynamic _allocate(var ctor) { |  | 
|   522     return JS("Object", "new #()", ctor); |  | 
|   523   } |  | 
|   524  |  | 
|   525   // TODO(sigmund): clean up above, after we make the new API the default: |  | 
|   526  |  | 
|   527   static _spawn(String functionName, String uri, bool isLight) { |  | 
|   528     Completer<SendPort> completer = new Completer<SendPort>(); |  | 
|   529     ReceivePort port = new ReceivePort(); |  | 
|   530     port.receive((msg, SendPort replyPort) { |  | 
|   531       port.close(); |  | 
|   532       assert(msg == _SPAWNED_SIGNAL); |  | 
|   533       completer.complete(replyPort); |  | 
|   534     }); |  | 
|   535  |  | 
|   536     SendPort signalReply = port.toSendPort(); |  | 
|   537  |  | 
|   538     if (_globalState.useWorkers && !isLight) { |  | 
|   539       _startWorker(functionName, uri, signalReply); |  | 
|   540     } else { |  | 
|   541       _startNonWorker(functionName, uri, signalReply); |  | 
|   542     } |  | 
|   543     return new _BufferingSendPort( |  | 
|   544         _globalState.currentContext.id, completer.future); |  | 
|   545   } |  | 
|   546  |  | 
|   547   static SendPort _startWorker( |  | 
|   548       String functionName, String uri, SendPort replyPort) { |  | 
|   549     if (_globalState.isWorker) { |  | 
|   550       _globalState.mainManager.postMessage(_serializeMessage({ |  | 
|   551           'command': 'spawn-worker', |  | 
|   552           'functionName': functionName, |  | 
|   553           'uri': uri, |  | 
|   554           'replyPort': replyPort})); |  | 
|   555     } else { |  | 
|   556       _spawnWorker(functionName, uri, replyPort); |  | 
|   557     } |  | 
|   558   } |  | 
|   559  |  | 
|   560   static SendPort _startNonWorker( |  | 
|   561       String functionName, String uri, SendPort replyPort) { |  | 
|   562     // TODO(eub): support IE9 using an iframe -- Dart issue 1702. |  | 
|   563     if (uri != null) throw new UnsupportedError( |  | 
|   564             "Currently spawnUri is not supported without web workers."); |  | 
|   565     _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { |  | 
|   566       final func = _getJSFunctionFromName(functionName); |  | 
|   567       _startIsolate(func, replyPort); |  | 
|   568     }, 'nonworker start'); |  | 
|   569   } |  | 
|   570  |  | 
|   571   static void _startIsolate(Function topLevel, SendPort replyTo) { |  | 
|   572     _fillStatics(_globalState.currentContext); |  | 
|   573     _lazyPort = new ReceivePort(); |  | 
|   574     replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |  | 
|   575  |  | 
|   576     topLevel(); |  | 
|   577   } |  | 
|   578  |  | 
|   579   /** |  | 
|   580    * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |  | 
|   581    * name for the isolate entry point class. |  | 
|   582    */ |  | 
|   583   static void _spawnWorker(functionName, uri, replyPort) { |  | 
|   584     if (functionName == null) functionName = 'main'; |  | 
|   585     if (uri == null) uri = _thisScript; |  | 
|   586     if (!(new Uri.fromString(uri).isAbsolute())) { |  | 
|   587       // The constructor of dom workers requires an absolute URL. If we use a |  | 
|   588       // relative path we will get a DOM exception. |  | 
|   589       String prefix = _thisScript.substring(0, _thisScript.lastIndexOf('/')); |  | 
|   590       uri = "$prefix/$uri"; |  | 
|   591     } |  | 
|   592     final worker = _newWorker(uri); |  | 
|   593     worker.onmessage = (e) { _processWorkerMessage(worker, e); }; |  | 
|   594     var workerId = _globalState.nextManagerId++; |  | 
|   595     // We also store the id on the worker itself so that we can unregister it. |  | 
|   596     worker.id = workerId; |  | 
|   597     _globalState.managers[workerId] = worker; |  | 
|   598     worker.postMessage(_serializeMessage({ |  | 
|   599       'command': 'start', |  | 
|   600       'id': workerId, |  | 
|   601       // Note: we serialize replyPort twice because the child worker needs to |  | 
|   602       // first deserialize the worker id, before it can correctly deserialize |  | 
|   603       // the port (port deserialization is sensitive to what is the current |  | 
|   604       // workerId). |  | 
|   605       'replyTo': _serializeMessage(replyPort), |  | 
|   606       'functionName': functionName })); |  | 
|   607   } |  | 
|   608 } |  | 
|   609  |  | 
|   610 /******************************************************** |  | 
|   611   Inserted from lib/isolate/dart2js/ports.dart |  | 
|   612  ********************************************************/ |  | 
|   613  |  | 
|   614 /** Common functionality to all send ports. */ |  | 
|   615 class _BaseSendPort implements SendPort { |  | 
|   616   /** Id for the destination isolate. */ |  | 
|   617   final int _isolateId; |  | 
|   618  |  | 
|   619   const _BaseSendPort(this._isolateId); |  | 
|   620  |  | 
|   621   void _checkReplyTo(SendPort replyTo) { |  | 
|   622     if (replyTo != null |  | 
|   623         && replyTo is! _NativeJsSendPort |  | 
|   624         && replyTo is! _WorkerSendPort |  | 
|   625         && replyTo is! _BufferingSendPort) { |  | 
|   626       throw new Exception("SendPort.send: Illegal replyTo port type"); |  | 
|   627     } |  | 
|   628   } |  | 
|   629  |  | 
|   630   Future call(var message) { |  | 
|   631     final completer = new Completer(); |  | 
|   632     final port = new _ReceivePortImpl(); |  | 
|   633     send(message, port.toSendPort()); |  | 
|   634     port.receive((value, ignoreReplyTo) { |  | 
|   635       port.close(); |  | 
|   636       if (value is Exception) { |  | 
|   637         completer.completeException(value); |  | 
|   638       } else { |  | 
|   639         completer.complete(value); |  | 
|   640       } |  | 
|   641     }); |  | 
|   642     return completer.future; |  | 
|   643   } |  | 
|   644  |  | 
|   645   void send(var message, [SendPort replyTo]); |  | 
|   646   bool operator ==(var other); |  | 
|   647   int get hashCode; |  | 
|   648 } |  | 
|   649  |  | 
|   650 /** A send port that delivers messages in-memory via native JavaScript calls. */ |  | 
|   651 class _NativeJsSendPort extends _BaseSendPort implements SendPort { |  | 
|   652   final _ReceivePortImpl _receivePort; |  | 
|   653  |  | 
|   654   const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId); |  | 
|   655  |  | 
|   656   void send(var message, [SendPort replyTo = null]) { |  | 
|   657     _waitForPendingPorts([message, replyTo], () { |  | 
|   658       _checkReplyTo(replyTo); |  | 
|   659       // Check that the isolate still runs and the port is still open |  | 
|   660       final isolate = _globalState.isolates[_isolateId]; |  | 
|   661       if (isolate == null) return; |  | 
|   662       if (_receivePort._callback == null) return; |  | 
|   663  |  | 
|   664       // We force serialization/deserialization as a simple way to ensure |  | 
|   665       // isolate communication restrictions are respected between isolates that |  | 
|   666       // live in the same worker. [_NativeJsSendPort] delivers both messages |  | 
|   667       // from the same worker and messages from other workers. In particular, |  | 
|   668       // messages sent from a worker via a [_WorkerSendPort] are received at |  | 
|   669       // [_processWorkerMessage] and forwarded to a native port. In such cases, |  | 
|   670       // here we'll see [_globalState.currentContext == null]. |  | 
|   671       final shouldSerialize = _globalState.currentContext != null |  | 
|   672           && _globalState.currentContext.id != _isolateId; |  | 
|   673       var msg = message; |  | 
|   674       var reply = replyTo; |  | 
|   675       if (shouldSerialize) { |  | 
|   676         msg = _serializeMessage(msg); |  | 
|   677         reply = _serializeMessage(reply); |  | 
|   678       } |  | 
|   679       _globalState.topEventLoop.enqueue(isolate, () { |  | 
|   680         if (_receivePort._callback != null) { |  | 
|   681           if (shouldSerialize) { |  | 
|   682             msg = _deserializeMessage(msg); |  | 
|   683             reply = _deserializeMessage(reply); |  | 
|   684           } |  | 
|   685           _receivePort._callback(msg, reply); |  | 
|   686         } |  | 
|   687       }, 'receive $message'); |  | 
|   688     }); |  | 
|   689   } |  | 
|   690  |  | 
|   691   bool operator ==(var other) => (other is _NativeJsSendPort) && |  | 
|   692       (_receivePort == other._receivePort); |  | 
|   693  |  | 
|   694   int get hashCode => _receivePort._id; |  | 
|   695 } |  | 
|   696  |  | 
|   697 /** A send port that delivers messages via worker.postMessage. */ |  | 
|   698 // TODO(eub): abstract this for iframes. |  | 
|   699 class _WorkerSendPort extends _BaseSendPort implements SendPort { |  | 
|   700   final int _workerId; |  | 
|   701   final int _receivePortId; |  | 
|   702  |  | 
|   703   const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId) |  | 
|   704       : super(isolateId); |  | 
|   705  |  | 
|   706   void send(var message, [SendPort replyTo = null]) { |  | 
|   707     _waitForPendingPorts([message, replyTo], () { |  | 
|   708       _checkReplyTo(replyTo); |  | 
|   709       final workerMessage = _serializeMessage({ |  | 
|   710           'command': 'message', |  | 
|   711           'port': this, |  | 
|   712           'msg': message, |  | 
|   713           'replyTo': replyTo}); |  | 
|   714  |  | 
|   715       if (_globalState.isWorker) { |  | 
|   716         // communication from one worker to another go through the main worker: |  | 
|   717         _globalState.mainManager.postMessage(workerMessage); |  | 
|   718       } else { |  | 
|   719         _globalState.managers[_workerId].postMessage(workerMessage); |  | 
|   720       } |  | 
|   721     }); |  | 
|   722   } |  | 
|   723  |  | 
|   724   bool operator ==(var other) { |  | 
|   725     return (other is _WorkerSendPort) && |  | 
|   726         (_workerId == other._workerId) && |  | 
|   727         (_isolateId == other._isolateId) && |  | 
|   728         (_receivePortId == other._receivePortId); |  | 
|   729   } |  | 
|   730  |  | 
|   731   int get hashCode { |  | 
|   732     // TODO(sigmund): use a standard hash when we get one available in corelib. |  | 
|   733     return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; |  | 
|   734   } |  | 
|   735 } |  | 
|   736  |  | 
|   737 /** A port that buffers messages until an underlying port gets resolved. */ |  | 
|   738 class _BufferingSendPort extends _BaseSendPort implements SendPort { |  | 
|   739   /** Internal counter to assign unique ids to each port. */ |  | 
|   740   static int _idCount = 0; |  | 
|   741  |  | 
|   742   /** For implementing equals and hashcode. */ |  | 
|   743   final int _id; |  | 
|   744  |  | 
|   745   /** Underlying port, when resolved. */ |  | 
|   746   SendPort _port; |  | 
|   747  |  | 
|   748   /** |  | 
|   749    * Future of the underlying port, so that we can detect when this port can be |  | 
|   750    * sent on messages. |  | 
|   751    */ |  | 
|   752   Future<SendPort> _futurePort; |  | 
|   753  |  | 
|   754   /** Pending messages (and reply ports). */ |  | 
|   755   List pending; |  | 
|   756  |  | 
|   757   _BufferingSendPort(isolateId, this._futurePort) |  | 
|   758       : super(isolateId), _id = _idCount, pending = [] { |  | 
|   759     _idCount++; |  | 
|   760     _futurePort.then((p) { |  | 
|   761       _port = p; |  | 
|   762       for (final item in pending) { |  | 
|   763         p.send(item['message'], item['replyTo']); |  | 
|   764       } |  | 
|   765       pending = null; |  | 
|   766     }); |  | 
|   767   } |  | 
|   768  |  | 
|   769   _BufferingSendPort.fromPort(isolateId, this._port) |  | 
|   770       : super(isolateId), _id = _idCount { |  | 
|   771     _idCount++; |  | 
|   772   } |  | 
|   773  |  | 
|   774   void send(var message, [SendPort replyTo]) { |  | 
|   775     if (_port != null) { |  | 
|   776       _port.send(message, replyTo); |  | 
|   777     } else { |  | 
|   778       pending.add({'message': message, 'replyTo': replyTo}); |  | 
|   779     } |  | 
|   780   } |  | 
|   781  |  | 
|   782   bool operator ==(var other) => |  | 
|   783       other is _BufferingSendPort && _id == other._id; |  | 
|   784   int get hashCode => _id; |  | 
|   785 } |  | 
|   786  |    23  | 
|   787 /** Default factory for receive ports. */ |    24 /** Default factory for receive ports. */ | 
|   788 patch class ReceivePort { |    25 patch class ReceivePort { | 
|   789   patch factory ReceivePort() { |    26   patch factory ReceivePort() { | 
|   790     return new _ReceivePortImpl(); |    27     return new ReceivePortImpl(); | 
|   791   } |  | 
|   792  |  | 
|   793 } |  | 
|   794  |  | 
|   795 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ |  | 
|   796 class _ReceivePortImpl implements ReceivePort { |  | 
|   797   int _id; |  | 
|   798   Function _callback; |  | 
|   799   static int _nextFreeId = 1; |  | 
|   800  |  | 
|   801   _ReceivePortImpl() |  | 
|   802       : _id = _nextFreeId++ { |  | 
|   803     _globalState.currentContext.register(_id, this); |  | 
|   804   } |  | 
|   805  |  | 
|   806   void receive(void onMessage(var message, SendPort replyTo)) { |  | 
|   807     _callback = onMessage; |  | 
|   808   } |  | 
|   809  |  | 
|   810   void close() { |  | 
|   811     _callback = null; |  | 
|   812     _globalState.currentContext.unregister(_id); |  | 
|   813   } |  | 
|   814  |  | 
|   815   SendPort toSendPort() { |  | 
|   816     return new _NativeJsSendPort(this, _globalState.currentContext.id); |  | 
|   817   } |  | 
|   818 } |  | 
|   819  |  | 
|   820 /** Wait until all ports in a message are resolved. */ |  | 
|   821 _waitForPendingPorts(var message, void callback()) { |  | 
|   822   final finder = new _PendingSendPortFinder(); |  | 
|   823   finder.traverse(message); |  | 
|   824   Futures.wait(finder.ports).then((_) => callback()); |  | 
|   825 } |  | 
|   826  |  | 
|   827  |  | 
|   828 /** Visitor that finds all unresolved [SendPort]s in a message. */ |  | 
|   829 class _PendingSendPortFinder extends _MessageTraverser { |  | 
|   830   List<Future<SendPort>> ports; |  | 
|   831   _PendingSendPortFinder() : super(), ports = [] { |  | 
|   832     _visited = new _JsVisitedMap(); |  | 
|   833   } |  | 
|   834  |  | 
|   835   visitPrimitive(x) {} |  | 
|   836  |  | 
|   837   visitList(List list) { |  | 
|   838     final seen = _visited[list]; |  | 
|   839     if (seen != null) return; |  | 
|   840     _visited[list] = true; |  | 
|   841     // TODO(sigmund): replace with the following: (bug #1660) |  | 
|   842     // list.forEach(_dispatch); |  | 
|   843     list.forEach((e) => _dispatch(e)); |  | 
|   844   } |  | 
|   845  |  | 
|   846   visitMap(Map map) { |  | 
|   847     final seen = _visited[map]; |  | 
|   848     if (seen != null) return; |  | 
|   849  |  | 
|   850     _visited[map] = true; |  | 
|   851     // TODO(sigmund): replace with the following: (bug #1660) |  | 
|   852     // map.values.forEach(_dispatch); |  | 
|   853     map.values.forEach((e) => _dispatch(e)); |  | 
|   854   } |  | 
|   855  |  | 
|   856   visitSendPort(SendPort port) { |  | 
|   857     if (port is _BufferingSendPort && port._port == null) { |  | 
|   858       ports.add(port._futurePort); |  | 
|   859     } |  | 
|   860   } |  | 
|   861 } |  | 
|   862  |  | 
|   863 /******************************************************** |  | 
|   864   Inserted from lib/isolate/dart2js/messages.dart |  | 
|   865  ********************************************************/ |  | 
|   866  |  | 
|   867 // Defines message visitors, serialization, and deserialization. |  | 
|   868  |  | 
|   869 /** Serialize [message] (or simulate serialization). */ |  | 
|   870 _serializeMessage(message) { |  | 
|   871   if (_globalState.needSerialization) { |  | 
|   872     return new _JsSerializer().traverse(message); |  | 
|   873   } else { |  | 
|   874     return new _JsCopier().traverse(message); |  | 
|   875   } |  | 
|   876 } |  | 
|   877  |  | 
|   878 /** Deserialize [message] (or simulate deserialization). */ |  | 
|   879 _deserializeMessage(message) { |  | 
|   880   if (_globalState.needSerialization) { |  | 
|   881     return new _JsDeserializer().deserialize(message); |  | 
|   882   } else { |  | 
|   883     // Nothing more to do. |  | 
|   884     return message; |  | 
|   885   } |  | 
|   886 } |  | 
|   887  |  | 
|   888 class _JsSerializer extends _Serializer { |  | 
|   889  |  | 
|   890   _JsSerializer() : super() { _visited = new _JsVisitedMap(); } |  | 
|   891  |  | 
|   892   visitSendPort(SendPort x) { |  | 
|   893     if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); |  | 
|   894     if (x is _WorkerSendPort) return visitWorkerSendPort(x); |  | 
|   895     if (x is _BufferingSendPort) return visitBufferingSendPort(x); |  | 
|   896     throw "Illegal underlying port $x"; |  | 
|   897   } |  | 
|   898  |  | 
|   899   visitNativeJsSendPort(_NativeJsSendPort port) { |  | 
|   900     return ['sendport', _globalState.currentManagerId, |  | 
|   901         port._isolateId, port._receivePort._id]; |  | 
|   902   } |  | 
|   903  |  | 
|   904   visitWorkerSendPort(_WorkerSendPort port) { |  | 
|   905     return ['sendport', port._workerId, port._isolateId, port._receivePortId]; |  | 
|   906   } |  | 
|   907  |  | 
|   908   visitBufferingSendPort(_BufferingSendPort port) { |  | 
|   909     if (port._port != null) { |  | 
|   910       return visitSendPort(port._port); |  | 
|   911     } else { |  | 
|   912       // TODO(floitsch): Use real exception (which one?). |  | 
|   913       throw |  | 
|   914           "internal error: must call _waitForPendingPorts to ensure all" |  | 
|   915           " ports are resolved at this point."; |  | 
|   916     } |  | 
|   917   } |  | 
|   918  |  | 
|   919 } |  | 
|   920  |  | 
|   921  |  | 
|   922 class _JsCopier extends _Copier { |  | 
|   923  |  | 
|   924   _JsCopier() : super() { _visited = new _JsVisitedMap(); } |  | 
|   925  |  | 
|   926   visitSendPort(SendPort x) { |  | 
|   927     if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); |  | 
|   928     if (x is _WorkerSendPort) return visitWorkerSendPort(x); |  | 
|   929     if (x is _BufferingSendPort) return visitBufferingSendPort(x); |  | 
|   930     throw "Illegal underlying port $p"; |  | 
|   931   } |  | 
|   932  |  | 
|   933   SendPort visitNativeJsSendPort(_NativeJsSendPort port) { |  | 
|   934     return new _NativeJsSendPort(port._receivePort, port._isolateId); |  | 
|   935   } |  | 
|   936  |  | 
|   937   SendPort visitWorkerSendPort(_WorkerSendPort port) { |  | 
|   938     return new _WorkerSendPort( |  | 
|   939         port._workerId, port._isolateId, port._receivePortId); |  | 
|   940   } |  | 
|   941  |  | 
|   942   SendPort visitBufferingSendPort(_BufferingSendPort port) { |  | 
|   943     if (port._port != null) { |  | 
|   944       return visitSendPort(port._port); |  | 
|   945     } else { |  | 
|   946       // TODO(floitsch): Use real exception (which one?). |  | 
|   947       throw |  | 
|   948           "internal error: must call _waitForPendingPorts to ensure all" |  | 
|   949           " ports are resolved at this point."; |  | 
|   950     } |  | 
|   951   } |  | 
|   952  |  | 
|   953 } |  | 
|   954  |  | 
|   955 class _JsDeserializer extends _Deserializer { |  | 
|   956  |  | 
|   957   SendPort deserializeSendPort(List x) { |  | 
|   958     int managerId = x[1]; |  | 
|   959     int isolateId = x[2]; |  | 
|   960     int receivePortId = x[3]; |  | 
|   961     // If two isolates are in the same manager, we use NativeJsSendPorts to |  | 
|   962     // deliver messages directly without using postMessage. |  | 
|   963     if (managerId == _globalState.currentManagerId) { |  | 
|   964       var isolate = _globalState.isolates[isolateId]; |  | 
|   965       if (isolate == null) return null; // Isolate has been closed. |  | 
|   966       var receivePort = isolate.lookup(receivePortId); |  | 
|   967       return new _NativeJsSendPort(receivePort, isolateId); |  | 
|   968     } else { |  | 
|   969       return new _WorkerSendPort(managerId, isolateId, receivePortId); |  | 
|   970     } |  | 
|   971   } |  | 
|   972  |  | 
|   973 } |  | 
|   974  |  | 
|   975 class _JsVisitedMap implements _MessageTraverserVisitedMap { |  | 
|   976   List tagged; |  | 
|   977  |  | 
|   978   /** Retrieves any information stored in the native object [object]. */ |  | 
|   979   operator[](var object) { |  | 
|   980     return _getAttachedInfo(object); |  | 
|   981   } |  | 
|   982  |  | 
|   983   /** Injects some information into the native [object]. */ |  | 
|   984   void operator[]=(var object, var info) { |  | 
|   985     tagged.add(object); |  | 
|   986     _setAttachedInfo(object, info); |  | 
|   987   } |  | 
|   988  |  | 
|   989   /** Get ready to rumble. */ |  | 
|   990   void reset() { |  | 
|   991     assert(tagged == null); |  | 
|   992     tagged = new List(); |  | 
|   993   } |  | 
|   994  |  | 
|   995   /** Remove all information injected in the native objects. */ |  | 
|   996   void cleanup() { |  | 
|   997     for (int i = 0, length = tagged.length; i < length; i++) { |  | 
|   998       _clearAttachedInfo(tagged[i]); |  | 
|   999     } |  | 
|  1000     tagged = null; |  | 
|  1001   } |  | 
|  1002  |  | 
|  1003   void _clearAttachedInfo(var o) { |  | 
|  1004     JS("void", "#['__MessageTraverser__attached_info__'] = #", o, null); |  | 
|  1005   } |  | 
|  1006  |  | 
|  1007   void _setAttachedInfo(var o, var info) { |  | 
|  1008     JS("void", "#['__MessageTraverser__attached_info__'] = #", o, info); |  | 
|  1009   } |  | 
|  1010  |  | 
|  1011   _getAttachedInfo(var o) { |  | 
|  1012     return JS("", "#['__MessageTraverser__attached_info__']", o); |  | 
|  1013   } |  | 
|  1014 } |  | 
|  1015  |  | 
|  1016 // only visible for testing purposes |  | 
|  1017 // TODO(sigmund): remove once we can disable privacy for testing (bug #1882) |  | 
|  1018 class TestingOnly { |  | 
|  1019   static copy(x) { |  | 
|  1020     return new _JsCopier().traverse(x); |  | 
|  1021   } |  | 
|  1022  |  | 
|  1023   // only visible for testing purposes |  | 
|  1024   static serialize(x) { |  | 
|  1025     _Serializer serializer = new _JsSerializer(); |  | 
|  1026     _Deserializer deserializer = new _JsDeserializer(); |  | 
|  1027     return deserializer.deserialize(serializer.traverse(x)); |  | 
|  1028   } |  | 
|  1029 } |  | 
|  1030  |  | 
|  1031 /******************************************************** |  | 
|  1032   Inserted from lib/isolate/serialization.dart |  | 
|  1033  ********************************************************/ |  | 
|  1034  |  | 
|  1035 class _MessageTraverserVisitedMap { |  | 
|  1036  |  | 
|  1037   operator[](var object) => null; |  | 
|  1038   void operator[]=(var object, var info) { } |  | 
|  1039  |  | 
|  1040   void reset() { } |  | 
|  1041   void cleanup() { } |  | 
|  1042  |  | 
|  1043 } |  | 
|  1044  |  | 
|  1045 /** Abstract visitor for dart objects that can be sent as isolate messages. */ |  | 
|  1046 class _MessageTraverser { |  | 
|  1047  |  | 
|  1048   _MessageTraverserVisitedMap _visited; |  | 
|  1049   _MessageTraverser() : _visited = new _MessageTraverserVisitedMap(); |  | 
|  1050  |  | 
|  1051   /** Visitor's entry point. */ |  | 
|  1052   traverse(var x) { |  | 
|  1053     if (isPrimitive(x)) return visitPrimitive(x); |  | 
|  1054     _visited.reset(); |  | 
|  1055     var result; |  | 
|  1056     try { |  | 
|  1057       result = _dispatch(x); |  | 
|  1058     } finally { |  | 
|  1059       _visited.cleanup(); |  | 
|  1060     } |  | 
|  1061     return result; |  | 
|  1062   } |  | 
|  1063  |  | 
|  1064   _dispatch(var x) { |  | 
|  1065     if (isPrimitive(x)) return visitPrimitive(x); |  | 
|  1066     if (x is List) return visitList(x); |  | 
|  1067     if (x is Map) return visitMap(x); |  | 
|  1068     if (x is SendPort) return visitSendPort(x); |  | 
|  1069     if (x is SendPortSync) return visitSendPortSync(x); |  | 
|  1070  |  | 
|  1071     // Overridable fallback. |  | 
|  1072     return visitObject(x); |  | 
|  1073   } |  | 
|  1074  |  | 
|  1075   visitPrimitive(x); |  | 
|  1076   visitList(List x); |  | 
|  1077   visitMap(Map x); |  | 
|  1078   visitSendPort(SendPort x); |  | 
|  1079   visitSendPortSync(SendPortSync x); |  | 
|  1080  |  | 
|  1081   visitObject(Object x) { |  | 
|  1082     // TODO(floitsch): make this a real exception. (which one)? |  | 
|  1083     throw "Message serialization: Illegal value $x passed"; |  | 
|  1084   } |  | 
|  1085  |  | 
|  1086   static bool isPrimitive(x) { |  | 
|  1087     return (x == null) || (x is String) || (x is num) || (x is bool); |  | 
|  1088   } |  | 
|  1089 } |  | 
|  1090  |  | 
|  1091  |  | 
|  1092 /** A visitor that recursively copies a message. */ |  | 
|  1093 class _Copier extends _MessageTraverser { |  | 
|  1094  |  | 
|  1095   visitPrimitive(x) => x; |  | 
|  1096  |  | 
|  1097   List visitList(List list) { |  | 
|  1098     List copy = _visited[list]; |  | 
|  1099     if (copy != null) return copy; |  | 
|  1100  |  | 
|  1101     int len = list.length; |  | 
|  1102  |  | 
|  1103     // TODO(floitsch): we loose the generic type of the List. |  | 
|  1104     copy = new List(len); |  | 
|  1105     _visited[list] = copy; |  | 
|  1106     for (int i = 0; i < len; i++) { |  | 
|  1107       copy[i] = _dispatch(list[i]); |  | 
|  1108     } |  | 
|  1109     return copy; |  | 
|  1110   } |  | 
|  1111  |  | 
|  1112   Map visitMap(Map map) { |  | 
|  1113     Map copy = _visited[map]; |  | 
|  1114     if (copy != null) return copy; |  | 
|  1115  |  | 
|  1116     // TODO(floitsch): we loose the generic type of the map. |  | 
|  1117     copy = new Map(); |  | 
|  1118     _visited[map] = copy; |  | 
|  1119     map.forEach((key, val) { |  | 
|  1120       copy[_dispatch(key)] = _dispatch(val); |  | 
|  1121     }); |  | 
|  1122     return copy; |  | 
|  1123   } |  | 
|  1124  |  | 
|  1125 } |  | 
|  1126  |  | 
|  1127 /** Visitor that serializes a message as a JSON array. */ |  | 
|  1128 class _Serializer extends _MessageTraverser { |  | 
|  1129   int _nextFreeRefId = 0; |  | 
|  1130  |  | 
|  1131   visitPrimitive(x) => x; |  | 
|  1132  |  | 
|  1133   visitList(List list) { |  | 
|  1134     int copyId = _visited[list]; |  | 
|  1135     if (copyId != null) return ['ref', copyId]; |  | 
|  1136  |  | 
|  1137     int id = _nextFreeRefId++; |  | 
|  1138     _visited[list] = id; |  | 
|  1139     var jsArray = _serializeList(list); |  | 
|  1140     // TODO(floitsch): we are losing the generic type. |  | 
|  1141     return ['list', id, jsArray]; |  | 
|  1142   } |  | 
|  1143  |  | 
|  1144   visitMap(Map map) { |  | 
|  1145     int copyId = _visited[map]; |  | 
|  1146     if (copyId != null) return ['ref', copyId]; |  | 
|  1147  |  | 
|  1148     int id = _nextFreeRefId++; |  | 
|  1149     _visited[map] = id; |  | 
|  1150     var keys = _serializeList(map.keys); |  | 
|  1151     var values = _serializeList(map.values); |  | 
|  1152     // TODO(floitsch): we are losing the generic type. |  | 
|  1153     return ['map', id, keys, values]; |  | 
|  1154   } |  | 
|  1155  |  | 
|  1156   _serializeList(List list) { |  | 
|  1157     int len = list.length; |  | 
|  1158     var result = new List(len); |  | 
|  1159     for (int i = 0; i < len; i++) { |  | 
|  1160       result[i] = _dispatch(list[i]); |  | 
|  1161     } |  | 
|  1162     return result; |  | 
|  1163   } |  | 
|  1164 } |  | 
|  1165  |  | 
|  1166 /** Deserializes arrays created with [_Serializer]. */ |  | 
|  1167 class _Deserializer { |  | 
|  1168   Map<int, dynamic> _deserialized; |  | 
|  1169  |  | 
|  1170   _Deserializer(); |  | 
|  1171  |  | 
|  1172   static bool isPrimitive(x) { |  | 
|  1173     return (x == null) || (x is String) || (x is num) || (x is bool); |  | 
|  1174   } |  | 
|  1175  |  | 
|  1176   deserialize(x) { |  | 
|  1177     if (isPrimitive(x)) return x; |  | 
|  1178     // TODO(floitsch): this should be new HashMap<int, var|Dynamic>() |  | 
|  1179     _deserialized = new HashMap(); |  | 
|  1180     return _deserializeHelper(x); |  | 
|  1181   } |  | 
|  1182  |  | 
|  1183   _deserializeHelper(x) { |  | 
|  1184     if (isPrimitive(x)) return x; |  | 
|  1185     assert(x is List); |  | 
|  1186     switch (x[0]) { |  | 
|  1187       case 'ref': return _deserializeRef(x); |  | 
|  1188       case 'list': return _deserializeList(x); |  | 
|  1189       case 'map': return _deserializeMap(x); |  | 
|  1190       case 'sendport': return deserializeSendPort(x); |  | 
|  1191       default: return deserializeObject(x); |  | 
|  1192     } |  | 
|  1193   } |  | 
|  1194  |  | 
|  1195   _deserializeRef(List x) { |  | 
|  1196     int id = x[1]; |  | 
|  1197     var result = _deserialized[id]; |  | 
|  1198     assert(result != null); |  | 
|  1199     return result; |  | 
|  1200   } |  | 
|  1201  |  | 
|  1202   List _deserializeList(List x) { |  | 
|  1203     int id = x[1]; |  | 
|  1204     // We rely on the fact that Dart-lists are directly mapped to Js-arrays. |  | 
|  1205     List dartList = x[2]; |  | 
|  1206     _deserialized[id] = dartList; |  | 
|  1207     int len = dartList.length; |  | 
|  1208     for (int i = 0; i < len; i++) { |  | 
|  1209       dartList[i] = _deserializeHelper(dartList[i]); |  | 
|  1210     } |  | 
|  1211     return dartList; |  | 
|  1212   } |  | 
|  1213  |  | 
|  1214   Map _deserializeMap(List x) { |  | 
|  1215     Map result = new Map(); |  | 
|  1216     int id = x[1]; |  | 
|  1217     _deserialized[id] = result; |  | 
|  1218     List keys = x[2]; |  | 
|  1219     List values = x[3]; |  | 
|  1220     int len = keys.length; |  | 
|  1221     assert(len == values.length); |  | 
|  1222     for (int i = 0; i < len; i++) { |  | 
|  1223       var key = _deserializeHelper(keys[i]); |  | 
|  1224       var value = _deserializeHelper(values[i]); |  | 
|  1225       result[key] = value; |  | 
|  1226     } |  | 
|  1227     return result; |  | 
|  1228   } |  | 
|  1229  |  | 
|  1230   deserializeSendPort(List x); |  | 
|  1231  |  | 
|  1232   deserializeObject(List x) { |  | 
|  1233     // TODO(floitsch): Use real exception (which one?). |  | 
|  1234     throw "Unexpected serialized object"; |  | 
|  1235   } |  | 
|  1236 } |  | 
|  1237  |  | 
|  1238 /******************************************************** |  | 
|  1239   Inserted from lib/isolate/dart2js/timer_provider.dart |  | 
|  1240  ********************************************************/ |  | 
|  1241  |  | 
|  1242 // We don't want to import the DOM library just because of window.setTimeout, |  | 
|  1243 // so we reconstruct the Window class here. The only conflict that could happen |  | 
|  1244 // with the other DOMWindow class would be because of subclasses. |  | 
|  1245 // Currently, none of the two Dart classes have subclasses. |  | 
|  1246 typedef void _TimeoutHandler(); |  | 
|  1247  |  | 
|  1248 class _Window native "@*DOMWindow" { |  | 
|  1249   int setTimeout(_TimeoutHandler handler, int timeout) native; |  | 
|  1250   int setInterval(_TimeoutHandler handler, int timeout) native; |  | 
|  1251   void clearTimeout(int handle) native; |  | 
|  1252   void clearInterval(int handle) native; |  | 
|  1253 } |  | 
|  1254  |  | 
|  1255 _Window get _window => |  | 
|  1256   JS('bool', 'typeof window != "undefined"') ? JS('_Window', 'window') : null; |  | 
|  1257  |  | 
|  1258 class _Timer implements Timer { |  | 
|  1259   final bool _once; |  | 
|  1260   int _handle; |  | 
|  1261  |  | 
|  1262   _Timer(int milliSeconds, void callback(Timer timer)) |  | 
|  1263       : _once = true { |  | 
|  1264     _handle = _window.setTimeout(() => callback(this), milliSeconds); |  | 
|  1265   } |  | 
|  1266  |  | 
|  1267   _Timer.repeating(int milliSeconds, void callback(Timer timer)) |  | 
|  1268       : _once = false { |  | 
|  1269     _handle = _window.setInterval(() => callback(this), milliSeconds); |  | 
|  1270   } |  | 
|  1271  |  | 
|  1272   void cancel() { |  | 
|  1273     if (_once) { |  | 
|  1274       _window.clearTimeout(_handle); |  | 
|  1275     } else { |  | 
|  1276       _window.clearInterval(_handle); |  | 
|  1277     } |  | 
|  1278   } |    28   } | 
|  1279 } |    29 } | 
|  1280  |    30  | 
|  1281 patch class Timer { |    31 patch class Timer { | 
|  1282   patch factory Timer(int milliSeconds, void callback(Timer timer)) { |    32   patch factory Timer(int milliseconds, void callback(Timer timer)) { | 
|  1283     if (_window == null) { |    33     if (!hasTimer()) { | 
|  1284       throw new UnsupportedError("Timer interface not supported."); |    34       throw new UnsupportedError("Timer interface not supported."); | 
|  1285     } |    35     } | 
|  1286     return new _Timer(milliSeconds, callback); |    36     return new TimerImpl(milliseconds, callback); | 
|  1287   } |    37   } | 
|  1288  |    38  | 
|  1289   /** |    39   /** | 
|  1290    * Creates a new repeating timer. The [callback] is invoked every |    40    * Creates a new repeating timer. The [callback] is invoked every | 
|  1291    * [milliSeconds] millisecond until cancelled. |    41    * [milliseconds] millisecond until cancelled. | 
|  1292    */ |    42    */ | 
|  1293   patch factory Timer.repeating(int milliSeconds, void callback(Timer timer)) { |    43   patch factory Timer.repeating(int milliseconds, void callback(Timer timer)) { | 
|  1294     if (_window == null) { |    44     if (!hasTimer()) { | 
|  1295       throw new UnsupportedError("Timer interface not supported."); |    45       throw new UnsupportedError("Timer interface not supported."); | 
|  1296     } |    46     } | 
|  1297     return new _Timer.repeating(milliSeconds, callback); |    47     return new TimerImpl.repeating(milliseconds, callback); | 
|  1298   } |    48   } | 
|  1299 } |    49 } | 
| OLD | NEW |