Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(399)

Side by Side Diff: frog/lib/isolate.js

Issue 8467034: Isolates in frog - tweaks in existing js code to make things run (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // TODO(jmesserly): this whole file won't work in Frog yet.
jimhug 2011/11/08 15:39:01 Yay! Nice TODO to make go away.
6
7 var isolate$current = null; 5 var isolate$current = null;
8 var isolate$rootIsolate = null; // Will only be set in the main worker. 6 var isolate$rootIsolate = null; // Will only be set in the main worker.
9 var isolate$inits = []; 7 var isolate$inits = [];
10 var isolate$globalThis = this; 8 var isolate$globalThis = this;
11 9
12 // These declarations are needed to avoid errors from the Closure Compiler
13 // optimizer. They are defined in client/dom/generated/dart_dom_wrapping.js.
jimhug 2011/11/08 15:39:01 <smile>
14 var __dom_wrap;
15 var __dom_unwrap;
16
17 var isolate$inWorker = 10 var isolate$inWorker =
18 (typeof isolate$globalThis['importScripts']) != "undefined"; 11 (typeof isolate$globalThis['importScripts']) != "undefined";
19 var isolate$supportsWorkers = 12 var isolate$supportsWorkers =
20 isolate$inWorker || ((typeof isolate$globalThis['Worker']) != 'undefined'); 13 isolate$inWorker || ((typeof isolate$globalThis['Worker']) != 'undefined');
21 14
22 var isolate$MAIN_WORKER_ID = 0; 15 var isolate$MAIN_WORKER_ID = 0;
23 // Non-main workers will update the id variable. 16 // Non-main workers will update the id variable.
24 var isolate$thisWorkerId = isolate$MAIN_WORKER_ID; 17 var isolate$thisWorkerId = isolate$MAIN_WORKER_ID;
25 18
26 // Whether to use web workers when implementing isolates. 19 // Whether to use web workers when implementing isolates.
27 var isolate$useWorkers = isolate$supportsWorkers; 20 var isolate$useWorkers = isolate$supportsWorkers;
28 // Uncomment this to not use web workers even if they're available. 21 // Uncomment this to not use web workers even if they're available.
29 // isolate$useWorkers = false; 22 // isolate$useWorkers = false;
30 23
31 // Whether to use the web-worker JSON-based message serialization protocol, 24 // Whether to use the web-worker JSON-based message serialization protocol,
32 // even if not using web workers. 25 // even if not using web workers.
33 var isolate$useWorkerSerializationProtocol = false; 26 var isolate$useWorkerSerializationProtocol = false;
34 // Uncomment this to always use the web-worker JSON-based message 27 // Uncomment this to always use the web-worker JSON-based message
35 // serialization protocol, e.g. for testing purposes. 28 // serialization protocol, e.g. for testing purposes.
36 // isolate$useWorkerSerializationProtocol = true; 29 // isolate$useWorkerSerializationProtocol = true;
37 30
38 31
39 // ------- SendPort ------- 32 // ------- SendPort -------
40 function isolate$sendMessage(workerId, isolateId, receivePortId,
41 message, replyTo) {
42 // Both, the message and the replyTo are already serialized.
43 if (workerId == isolate$thisWorkerId) {
44 var isolate = isolate$isolateRegistry.get(isolateId);
45 if (!isolate) return; // Isolate has been closed.
46 var receivePort = isolate.getReceivePortForId(receivePortId);
47 if (!receivePort) return; // ReceivePort has been closed.
48 isolate$receiveMessage(receivePort, isolate, message, replyTo);
49 } else {
50 var worker;
51 if (isolate$inWorker) {
52 worker = isolate$mainWorker;
53 } else {
54 worker = isolate$workerRegistry.get(workerId);
55 }
56 worker.postMessage({ command: 'message',
57 workerId: workerId,
58 isolateId: isolateId,
59 portId: receivePortId,
60 msg: message,
61 replyTo: replyTo });
62 }
63 }
64 33
65 function isolate$receiveMessage(port, isolate, 34 function isolate$receiveMessage(port, isolate,
66 serializedMessage, serializedReplyTo) { 35 serializedMessage, serializedReplyTo) {
67 isolate$IsolateEvent.enqueue(isolate, function() { 36 isolate$IsolateEvent.enqueue(isolate, function() {
68 var message = isolate$deserializeMessage(serializedMessage); 37 var message = isolate$deserializeMessage(serializedMessage);
69 var replyTo = isolate$deserializeMessage(serializedReplyTo); 38 var replyTo = isolate$deserializeMessage(serializedReplyTo);
70 native_ReceivePortImpl__invokeCallback(port, message, replyTo); 39 port._callback(message, replyTo);
71 }); 40 });
72 } 41 }
73 42
74 // ------- ReceivePort -------
75
76 function native_ReceivePortImpl__register(id) {
77 isolate$current.registerReceivePort(id, this);
78 }
79
80 function native_ReceivePortImpl__unregister(id) {
81 isolate$current.unregisterReceivePort(id);
82 }
83
84 function native_ReceivePortImpl__currentWorkerId() {
85 return isolate$thisWorkerId;
86 }
87
88 function native_ReceivePortImpl__currentIsolateId() {
89 return isolate$current.id;
90 }
91
92 // -------- Registry --------- 43 // -------- Registry ---------
93 function isolate$Registry() { 44 function isolate$Registry() {
94 this.map = {}; 45 this.map = {};
95 this.count = 0; 46 this.count = 0;
96 } 47 }
97 48
98 isolate$Registry.prototype.register = function(id, val) { 49 isolate$Registry.prototype.register = function(id, val) {
99 if (this.map[id]) { 50 if (this.map[id]) {
100 throw Error("Registry: Elements must be registered only once."); 51 throw Error("Registry: Elements must be registered only once.");
101 } 52 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 } 105 }
155 } 106 }
156 107
157 // ------- Message handler ------- 108 // ------- Message handler -------
158 function isolate$processWorkerMessage(sender, e) { 109 function isolate$processWorkerMessage(sender, e) {
159 var msg = e.data; 110 var msg = e.data;
160 switch (msg.command) { 111 switch (msg.command) {
161 case 'start': 112 case 'start':
162 isolate$log("starting worker: " + msg.id + " " + msg.factoryName); 113 isolate$log("starting worker: " + msg.id + " " + msg.factoryName);
163 isolate$initializeWorker(msg.id); 114 isolate$initializeWorker(msg.id);
164 var runnerObject = (isolate$globalThis[msg.factoryName])(); 115 var runnerObject = new (isolate$globalThis[msg.factoryName])();
165 var serializedReplyTo = msg.replyTo; 116 var serializedReplyTo = msg.replyTo;
166 isolate$IsolateEvent.enqueue(new isolate$Isolate(), function() { 117 isolate$IsolateEvent.enqueue(new isolate$Isolate(), function() {
167 var replyTo = isolate$deserializeMessage(serializedReplyTo); 118 var replyTo = isolate$deserializeMessage(serializedReplyTo);
168 native__IsolateJsUtil__startIsolate(runnerObject, replyTo); 119 _IsolateJsUtil._startIsolate(runnerObject, replyTo);
169 }); 120 });
170 isolate$runEventLoop(); 121 isolate$runEventLoop();
171 break; 122 break;
172 case 'spawn-worker': 123 case 'spawn-worker':
173 isolate$spawnWorker(msg.factoryName, msg.replyPort); 124 isolate$spawnWorker(msg.factoryName, msg.replyPort);
174 break; 125 break;
175 case 'message': 126 case 'message':
176 isolate$sendMessage(msg.workerId, msg.isolateId, msg.portId, 127 IsolateNatives.sendMessage(
177 msg.msg, msg.replyTo); 128 msg.workerId, msg.isolateId, msg.portId, msg.msg, msg.replyTo);
178 isolate$runEventLoop(); 129 isolate$runEventLoop();
179 break; 130 break;
180 case 'close': 131 case 'close':
181 isolate$log("Closing Worker"); 132 isolate$log("Closing Worker");
182 isolate$workerRegistry.unregister(sender.id); 133 isolate$workerRegistry.unregister(sender.id);
183 sender.terminate(); 134 sender.terminate();
184 isolate$runEventLoop(); 135 isolate$runEventLoop();
185 break; 136 break;
186 case 'log': 137 case 'log':
187 isolate$log(msg.msg); 138 isolate$log(msg.msg);
188 break; 139 break;
189 case 'print': 140 case 'print':
190 native__IsolateJsUtil__print(msg.msg); 141 _IsolateJsUtil._print(msg.msg);
191 break; 142 break;
192 case 'error': 143 case 'error':
193 throw msg.msg; 144 throw msg.msg;
194 break; 145 break;
195 } 146 }
196 } 147 }
197 148
149
198 if (isolate$supportsWorkers) { 150 if (isolate$supportsWorkers) {
199 isolate$globalThis.onmessage = function(e) { 151 isolate$globalThis.onmessage = function(e) {
200 isolate$processWorkerMessage(isolate$mainWorker, e); 152 isolate$processWorkerMessage(isolate$mainWorker, e);
201 }; 153 };
202 } 154 }
203 155
204 // ------- Default Worker ------- 156 // ------- Default Worker -------
205 function isolate$MainWorker() { 157 function isolate$MainWorker() {
206 this.id = isolate$MAIN_WORKER_ID; 158 this.id = isolate$MAIN_WORKER_ID;
207 } 159 }
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 } 231 }
280 232
281 isolate$IsolateEvent.prototype.process = function() { 233 isolate$IsolateEvent.prototype.process = function() {
282 this.isolate.run(this.fn); 234 this.isolate.run(this.fn);
283 }; 235 };
284 236
285 isolate$IsolateEvent.enqueue = function(isolate, fn) { 237 isolate$IsolateEvent.enqueue = function(isolate, fn) {
286 isolate$events.push(new isolate$IsolateEvent(isolate, fn)); 238 isolate$events.push(new isolate$IsolateEvent(isolate, fn));
287 }; 239 };
288 240
241
289 isolate$IsolateEvent.dequeue = function() { 242 isolate$IsolateEvent.dequeue = function() {
290 if (isolate$events.length == 0) return $Dart$Null; 243 if (isolate$events.length == 0) return null;
291 var result = isolate$events[0]; 244 var result = isolate$events[0];
292 isolate$events.splice(0, 1); 245 isolate$events.splice(0, 1);
293 return result; 246 return result;
294 }; 247 };
295 248
296 function native_IsolateNatives__spawn(runnable, light, replyPort) { 249 function IsolateNatives() {}
250
251 IsolateNatives.sendMessage = function (workerId, isolateId, receivePortId,
252 message, replyTo) {
253 // Both, the message and the replyTo are already serialized.
Jennifer Messerly 2011/11/08 05:04:35 nit: extra comma
Siggi Cherem (dart-lang) 2011/11/08 17:56:42 copy/paste :) - fixed.
254 if (workerId == isolate$thisWorkerId) {
255 var isolate = isolate$isolateRegistry.get(isolateId);
256 if (!isolate) return; // Isolate has been closed.
Jennifer Messerly 2011/11/08 05:04:35 are either of these conditions errors?
257 var receivePort = isolate.getReceivePortForId(receivePortId);
258 if (!receivePort) return; // ReceivePort has been closed.
259 isolate$receiveMessage(receivePort, isolate, message, replyTo);
260 } else {
261 var worker;
262 if (isolate$inWorker) {
263 worker = isolate$mainWorker;
264 } else {
265 worker = isolate$workerRegistry.get(workerId);
266 }
267 worker.postMessage({ command: 'message',
268 workerId: workerId,
269 isolateId: isolateId,
270 portId: receivePortId,
271 msg: message,
272 replyTo: replyTo });
273 }
274 }
275
276 // Wrap a 0-arg dom-callback to bind it with the current isolate:
277 function $wrap_call$0(fn) { return fn && fn.wrap$call$0(); }
278 Function.prototype.wrap$call$0 = function() {
279 var isolate = isolate$current;
280 var self = this;
281 this.wrap$0 = function() {
282 isolate.run(function() {
283 self();
284 });
Jennifer Messerly 2011/11/08 05:04:35 can this just be isolate.run(self) ?
Siggi Cherem (dart-lang) 2011/11/08 17:56:42 good point. Done
285 isolate$runEventLoop();
286 };
287 this.wrap$call$0 = function() { return this.wrap$0; };
288 return this.wrap$0;
289 }
290
291 // Wrap a 1-arg dom-callback to bind it with the current isolate:
292 function $wrap_call$1(fn) { return fn && fn.wrap$call$1(); }
293 Function.prototype.wrap$call$1 = function() {
294 var isolate = isolate$current;
295 var self = this;
296 this.wrap$1 = function(arg) {
297 isolate.run(function() {
298 self(arg);
299 });
Jennifer Messerly 2011/11/08 05:04:35 nit: i'd put on one line: isolate.run(function()
Siggi Cherem (dart-lang) 2011/11/08 17:56:42 Done.
300 isolate$runEventLoop();
301 };
302 this.wrap$call$1 = function() { return this.wrap$1; };
303 return this.wrap$1;
304 }
305
306 IsolateNatives._spawn = function(runnable, light, replyPort) {
297 // TODO(floitsch): throw exception if runnable's class doesn't have a 307 // TODO(floitsch): throw exception if runnable's class doesn't have a
298 // default constructor. 308 // default constructor.
299 if (isolate$useWorkers && !light) { 309 if (isolate$useWorkers && !light) {
300 isolate$startWorker(runnable, replyPort); 310 isolate$startWorker(runnable, replyPort);
301 } else { 311 } else {
302 isolate$startNonWorker(runnable, replyPort); 312 isolate$startNonWorker(runnable, replyPort);
303 } 313 }
304 } 314 }
305 315
306 function native_IsolateNatives_bind(fn) { 316 IsolateNatives.get$shouldSerialize = function() {
307 var isolate = isolate$current; 317 return isolate$useWorkers || isolate$useWorkerSerializationProtocol;
308 return function() { 318 }
309 var self = this; 319
310 var args = arguments; 320 IsolateNatives.registerPort = function(id, port) {
311 isolate.run(function() { 321 isolate$current.registerReceivePort(id, port);
312 fn.apply(self, args); 322 }
313 }); 323
314 isolate$runEventLoop(); 324 IsolateNatives.unregisterPort = function(id) {
315 }; 325 isolate$current.unregisterReceivePort(id);
326 }
327
328 IsolateNatives._currentWorkerId = function() {
329 return isolate$thisWorkerId;
330 }
331
332 IsolateNatives._currentIsolateId = function() {
333 return isolate$current.id;
316 } 334 }
317 335
318 function isolate$startNonWorker(runnable, replyTo) { 336 function isolate$startNonWorker(runnable, replyTo) {
319 // Spawn a new isolate and create the receive port in it. 337 // Spawn a new isolate and create the receive port in it.
320 var spawned = new isolate$Isolate(); 338 var spawned = new isolate$Isolate();
321 339
322 // Instead of just running the provided runnable, we create a 340 // Instead of just running the provided runnable, we create a
323 // new cloned instance of it with a fresh state in the spawned 341 // new cloned instance of it with a fresh state in the spawned
324 // isolate. This way, we do not get cross-isolate references 342 // isolate. This way, we do not get cross-isolate references
325 // through the runnable. 343 // through the runnable.
326 var factory = runnable.getIsolateFactory(); 344 var ctor = runnable.constructor;
327 isolate$IsolateEvent.enqueue(spawned, function() { 345 isolate$IsolateEvent.enqueue(spawned, function() {
328 native__IsolateJsUtil__startIsolate(factory(), replyTo); 346 _IsolateJsUtil._startIsolate(new ctor(), replyTo);
329 }); 347 });
330 } 348 }
331 349
332 // This field is only used by the main worker. 350 // This field is only used by the main worker.
333 var isolate$nextFreeWorkerId = isolate$thisWorkerId + 1; 351 var isolate$nextFreeWorkerId = isolate$thisWorkerId + 1;
334 352
335 var isolate$thisScript = function() { 353 var isolate$thisScript = function() {
336 if (!isolate$supportsWorkers || isolate$inWorker) return null; 354 if (!isolate$supportsWorkers || isolate$inWorker) return null;
337 355
338 // TODO(5334778): Find a cross-platform non-brittle way of getting the 356 // TODO(5334778): Find a cross-platform non-brittle way of getting the
339 // currently running script. 357 // currently running script.
340 var scripts = document.getElementsByTagName('script'); 358 var scripts = document.getElementsByTagName('script');
341 // The scripts variable only contains the scripts that have already been 359 // The scripts variable only contains the scripts that have already been
342 // executed. The last one is the currently running script. 360 // executed. The last one is the currently running script.
343 var script = scripts[scripts.length - 1]; 361 var script = scripts[scripts.length - 1];
344 var src = script.src; 362 var src = script.src;
345 if (!src) { 363 if (!src) {
346 // TODO() 364 // TODO()
347 src = "FIXME:5407062" + "_" + Math.random().toString(); 365 src = "FIXME:5407062" + "_" + Math.random().toString();
348 script.src = src; 366 script.src = src;
349 } 367 }
350 return src; 368 return src;
351 }(); 369 }();
352 370
353 function isolate$startWorker(runnable, replyPort) { 371 function isolate$startWorker(runnable, replyPort) {
354 var factory = runnable.getIsolateFactory(); 372 // TODO(sigmund): make this browser independent
355 var factoryName = factory.name; 373 var factoryName = runnable.constructor.name;
356 var serializedReplyPort = isolate$serializeMessage(replyPort); 374 var serializedReplyPort = isolate$serializeMessage(replyPort);
357 if (isolate$inWorker) { 375 if (isolate$inWorker) {
358 isolate$mainWorker.postMessage({ command: 'spawn-worker', 376 isolate$mainWorker.postMessage({ command: 'spawn-worker',
359 factoryName: factoryName, 377 factoryName: factoryName,
360 replyPort: serializedReplyPort } ); 378 replyPort: serializedReplyPort } );
361 } else { 379 } else {
362 isolate$spawnWorker(factoryName, serializedReplyPort); 380 isolate$spawnWorker(factoryName, serializedReplyPort);
363 } 381 }
364 } 382 }
365 383
366 function isolate$spawnWorker(factoryName, serializedReplyPort) { 384 function isolate$spawnWorker(factoryName, serializedReplyPort) {
367 var worker = new Worker(isolate$thisScript); 385 var worker = new Worker(isolate$thisScript);
368 worker.onmessage = function(e) { 386 worker.onmessage = function(e) {
369 isolate$processWorkerMessage(worker, e); 387 isolate$processWorkerMessage(worker, e);
370 }; 388 };
371 var workerId = isolate$nextFreeWorkerId++; 389 var workerId = isolate$nextFreeWorkerId++;
372 // We also store the id on the worker itself so that we can unregister it. 390 // We also store the id on the worker itself so that we can unregister it.
373 worker.id = workerId; 391 worker.id = workerId;
374 isolate$workerRegistry.register(workerId, worker); 392 isolate$workerRegistry.register(workerId, worker);
375 worker.postMessage({ command: 'start', 393 worker.postMessage({ command: 'start',
376 id: workerId, 394 id: workerId,
377 replyTo: serializedReplyPort, 395 replyTo: serializedReplyPort,
378 factoryName: factoryName }); 396 factoryName: factoryName });
379 } 397 }
380 398
381 function native_SendPortImpl__sendNow(message, replyTo) {
382 if (replyTo !== $Dart$Null && !(replyTo instanceof SendPortImpl$Dart)) {
383 throw "SendPort::send: Illegal replyTo type.";
384 }
385 message = isolate$serializeMessage(message);
386 replyTo = isolate$serializeMessage(replyTo);
387 var workerId = native_SendPortImpl__getWorkerId(this);
388 var isolateId = native_SendPortImpl__getIsolateId(this);
389 var receivePortId = native_SendPortImpl__getReceivePortId(this);
390 isolate$sendMessage(workerId, isolateId, receivePortId, message, replyTo);
391 }
392
393 function isolate$closeWorkerIfNecessary() { 399 function isolate$closeWorkerIfNecessary() {
394 if (!isolate$isolateRegistry.isEmpty()) return; 400 if (!isolate$isolateRegistry.isEmpty()) return;
395 isolate$mainWorker.postMessage( { command: 'close' } ); 401 isolate$mainWorker.postMessage( { command: 'close' } );
396 } 402 }
397 403
398 function isolate$doOneEventLoopIteration() { 404 function isolate$doOneEventLoopIteration() {
399 var CONTINUE_LOOP = true; 405 var CONTINUE_LOOP = true;
400 var STOP_LOOP = false; 406 var STOP_LOOP = false;
401 var event = isolate$IsolateEvent.dequeue(); 407 var event = isolate$IsolateEvent.dequeue();
402 if (!event) { 408 if (!event) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 465
460 // BUG(5151491): This should not be necessary, but because closures 466 // BUG(5151491): This should not be necessary, but because closures
461 // passed to the DOM as event handlers do not bind their isolate 467 // passed to the DOM as event handlers do not bind their isolate
462 // automatically we try to give them a reasonable context to live in 468 // automatically we try to give them a reasonable context to live in
463 // by having a "default" isolate (the first one created). 469 // by having a "default" isolate (the first one created).
464 isolate$current = isolate; 470 isolate$current = isolate;
465 } 471 }
466 472
467 // ------- Message Serializing and Deserializing ------- 473 // ------- Message Serializing and Deserializing -------
468 474
469 function native_MessageTraverser__clearAttachedInfo(o) { 475 function isolate$serializeMessage(message) {
470 o['__MessageTraverser__attached_info__'] = (void 0); 476 if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) {
471 } 477 return _IsolateJsUtil._serializeObject(message);
472
473 function native_MessageTraverser__setAttachedInfo(o, info) {
474 o['__MessageTraverser__attached_info__'] = info;
475 }
476
477 function native_MessageTraverser__getAttachedInfo(o) {
478 return o['__MessageTraverser__attached_info__'];
479 }
480
481 function native_Serializer__newJsArray(len) {
482 return new Array(len);
483 }
484
485 function native_Serializer__jsArrayIndexSet(jsArray, index, val) {
486 jsArray[index] = val;
487 }
488
489 function native_Serializer__dartListToJsArrayNoCopy(list) {
490 if (list instanceof Array) {
491 RTT.removeTypeInfo(list);
492 return list;
493 } else { 478 } else {
494 var len = native__ListJsUtil__listLength(list); 479 return _IsolateJsUtil._copyObject(message);
495 var array = new Array(len);
496 for (var i = 0; i < len; i++) {
497 array[i] = INDEX$operator(list, i);
498 }
499 return array;
500 } 480 }
501 } 481 }
502 482
503 function native_Deserializer__isJsArray(x) { 483 function isolate$deserializeMessage(message_) {
504 return x instanceof Array;
505 }
506
507 function native_Deserializer__jsArrayIndex(x, index) {
508 return x[index];
509 }
510
511 function native_Deserializer__jsArrayLength(x) {
512 return x.length;
513 }
514
515 function isolate$serializeMessage(message) {
516 if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { 484 if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) {
517 return native__IsolateJsUtil__serializeObject(message); 485 return _IsolateJsUtil._deserializeMessage(message_);
518 } else { 486 } else {
519 return native__IsolateJsUtil__copyObject(message); 487 // Nothing more to do.
488 return message_;
520 } 489 }
521 } 490 }
522 491
523 function isolate$deserializeMessage(message) { 492 function _IsolateJsUtil() {}
524 if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) {
525 return native__IsolateJsUtil__deserializeMessage(message);
526 } else {
527 // Nothing more to do.
528 return message;
529 }
530 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698