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

Side by Side Diff: sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart

Issue 200323008: Load the transformer isolate code from pub's asset directory. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: extraneous field Created 6 years, 9 months 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 library pub.load_transformers; 5 // Explicitly avoid using a library tag because pub will add additional imports
Bob Nystrom 2014/03/17 21:16:07 Wording this as an imperative sentence seems a bit
nweiz 2014/03/17 22:20:40 Done.
6 // at the top of the file.
6 7
7 import 'dart:async'; 8 import 'dart:async';
8 import 'dart:convert';
9 import 'dart:isolate';
10
11 import 'package:barback/barback.dart';
12 // TODO(nweiz): don't import from "src" once issue 14966 is fixed.
13 import 'package:barback/src/internal_asset.dart';
14 import 'package:source_maps/source_maps.dart';
15 import 'package:stack_trace/stack_trace.dart';
16
17 import '../barback.dart';
18 import '../dart.dart' as dart;
19 import '../log.dart' as log;
20 import '../utils.dart';
21 import 'build_environment.dart';
22 import 'excluding_transformer.dart';
23 import 'server.dart';
24
25 /// A Dart script to run in an isolate.
26 ///
27 /// This script serializes one or more transformers defined in a Dart library
28 /// and marshals calls to and from them with the host isolate.
29 const _TRANSFORMER_ISOLATE = """
30 import 'dart:async';
31 import 'dart:isolate'; 9 import 'dart:isolate';
32 import 'dart:convert'; 10 import 'dart:convert';
33 import 'dart:mirrors'; 11 import 'dart:mirrors';
34 12
35 import '<<URL_BASE>>/packages/source_maps/span.dart'; 13 import '<<URL_BASE>>/packages/source_maps/span.dart';
36 import '<<URL_BASE>>/packages/stack_trace/stack_trace.dart'; 14 import '<<URL_BASE>>/packages/stack_trace/stack_trace.dart';
37 import '<<URL_BASE>>/packages/barback/barback.dart'; 15 import '<<URL_BASE>>/packages/barback/barback.dart';
38 // TODO(nweiz): don't import from "src" once issue 14966 is fixed. 16 // TODO(nweiz): don't import from "src" once issue 14966 is fixed.
39 import '<<URL_BASE>>/packages/barback/src/internal_asset.dart'; 17 import '<<URL_BASE>>/packages/barback/src/internal_asset.dart';
Bob Nystrom 2014/03/17 21:16:07 How does the analyzer handle these?
nweiz 2014/03/17 22:20:40 Poorly. That's why this CL adds a skip to the anal
40 18
41 /// Sets up the initial communication with the host isolate. 19 /// Sets up the initial communication with the host isolate.
42 void main(_, SendPort replyTo) { 20 void main(_, SendPort replyTo) {
43 var port = new ReceivePort(); 21 var port = new ReceivePort();
44 replyTo.send(port.sendPort); 22 replyTo.send(port.sendPort);
45 port.first.then((wrappedMessage) { 23 port.first.then((wrappedMessage) {
46 _respond(wrappedMessage, (message) { 24 _respond(wrappedMessage, (message) {
47 var library = Uri.parse(message['library']); 25 var library = Uri.parse(message['library']);
48 var configuration = JSON.decode(message['configuration']); 26 var configuration = JSON.decode(message['configuration']);
49 var mode = new BarbackMode(message['mode']); 27 var mode = new BarbackMode(message['mode']);
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 'output': serializeAsset(output) 115 'output': serializeAsset(output)
138 }); 116 });
139 } 117 }
140 } 118 }
141 119
142 /// Returns the mirror for the root Object type. 120 /// Returns the mirror for the root Object type.
143 ClassMirror get objectMirror => reflectClass(Object); 121 ClassMirror get objectMirror => reflectClass(Object);
144 122
145 // TODO(nweiz): clean this up when issue 13248 is fixed. 123 // TODO(nweiz): clean this up when issue 13248 is fixed.
146 MethodMirror getConstructor(ClassMirror classMirror, String constructor) { 124 MethodMirror getConstructor(ClassMirror classMirror, String constructor) {
147 var name = new Symbol("\${MirrorSystem.getName(classMirror.simpleName)}" 125 var name = new Symbol("${MirrorSystem.getName(classMirror.simpleName)}"
148 ".\$constructor"); 126 ".$constructor");
149 var candidate = classMirror.declarations[name]; 127 var candidate = classMirror.declarations[name];
150 if (candidate is MethodMirror && candidate.isConstructor) return candidate; 128 if (candidate is MethodMirror && candidate.isConstructor) return candidate;
151 return null; 129 return null;
152 } 130 }
153 131
154 // TODO(nweiz): get rid of this when issue 12439 is fixed. 132 // TODO(nweiz): get rid of this when issue 12439 is fixed.
155 /// Returns whether or not [mirror] is a subtype of [superclass]. 133 /// Returns whether or not [mirror] is a subtype of [superclass].
156 /// 134 ///
157 /// This includes [superclass] being mixed in to or implemented by [mirror]. 135 /// This includes [superclass] being mixed in to or implemented by [mirror].
158 bool classIsA(ClassMirror mirror, ClassMirror superclass) { 136 bool classIsA(ClassMirror mirror, ClassMirror superclass) {
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 /// boundaries. 285 /// boundaries.
308 static Map serialize(error, [StackTrace stack]) { 286 static Map serialize(error, [StackTrace stack]) {
309 if (stack == null && error is Error) stack = error.stackTrace; 287 if (stack == null && error is Error) stack = error.stackTrace;
310 return { 288 return {
311 'type': error.runtimeType.toString(), 289 'type': error.runtimeType.toString(),
312 'message': getErrorMessage(error), 290 'message': getErrorMessage(error),
313 'stack': stack == null ? null : new Chain.forTrace(stack).toString() 291 'stack': stack == null ? null : new Chain.forTrace(stack).toString()
314 }; 292 };
315 } 293 }
316 294
317 String toString() => "\$message\\n\$stackTrace"; 295 String toString() => "$message\n$stackTrace";
318 } 296 }
319 297
320 /// An [AssetNotFoundException] that was originally raised in another isolate. 298 /// An [AssetNotFoundException] that was originally raised in another isolate.
321 class _CrossIsolateAssetNotFoundException extends CrossIsolateException 299 class _CrossIsolateAssetNotFoundException extends CrossIsolateException
322 implements AssetNotFoundException { 300 implements AssetNotFoundException {
323 final TransformInfo transform;
324 final AssetId id; 301 final AssetId id;
325 302
326 String get message => "Could not find asset \$id."; 303 String get message => "Could not find asset $id.";
327 304
328 /// Loads a [_CrossIsolateAssetNotFoundException] from a serialized 305 /// Loads a [_CrossIsolateAssetNotFoundException] from a serialized
329 /// representation. 306 /// representation.
330 /// 307 ///
331 /// [error] should be the result of 308 /// [error] should be the result of
332 /// [_CrossIsolateAssetNotFoundException.serialize]. 309 /// [_CrossIsolateAssetNotFoundException.serialize].
333 _CrossIsolateAssetNotFoundException.deserialize(Map error) 310 _CrossIsolateAssetNotFoundException.deserialize(Map error)
334 : id = new AssetId(error['package'], error['path']), 311 : id = new AssetId(error['package'], error['path']),
335 super.deserialize(error); 312 super.deserialize(error);
336 313
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 subscription = callback().listen(controller.add, 381 subscription = callback().listen(controller.add,
405 onError: controller.addError, 382 onError: controller.addError,
406 onDone: controller.close); 383 onDone: controller.close);
407 }, 384 },
408 onCancel: () => subscription.cancel(), 385 onCancel: () => subscription.cancel(),
409 onPause: () => subscription.pause(), 386 onPause: () => subscription.pause(),
410 onResume: () => subscription.resume(), 387 onResume: () => subscription.resume(),
411 sync: true); 388 sync: true);
412 return controller.stream; 389 return controller.stream;
413 } 390 }
414 """;
415
416 /// Load and return all transformers and groups from the library identified by
417 /// [id].
418 Future<Set> loadTransformers(BuildEnvironment environment,
419 BarbackServer transformerServer, TransformerId id) {
420 return id.getAssetId(environment.barback).then((assetId) {
421 var path = assetId.path.replaceFirst('lib/', '');
422 // TODO(nweiz): load from a "package:" URI when issue 12474 is fixed.
423
424 var baseUrl = transformerServer.url;
425 var uri = '$baseUrl/packages/${id.package}/$path';
426 var code = 'import "$uri";\n' +
427 _TRANSFORMER_ISOLATE.replaceAll('<<URL_BASE>>', baseUrl);
428 log.fine("Loading transformers from $assetId");
429
430 var port = new ReceivePort();
431 return dart.runInIsolate(code, port.sendPort)
432 .then((_) => port.first)
433 .then((sendPort) {
434 return _call(sendPort, {
435 'library': uri,
436 'mode': environment.mode.name,
437 // TODO(nweiz): support non-JSON-encodable configuration maps.
438 'configuration': JSON.encode(id.configuration)
439 }).then((transformers) {
440 transformers = transformers.map(
441 (transformer) => _deserializeTransformerOrGroup(transformer, id))
442 .toSet();
443 log.fine("Transformers from $assetId: $transformers");
444 return transformers;
445 });
446 }).catchError((error, stackTrace) {
447 if (error is! dart.CrossIsolateException) throw error;
448 if (error.type != 'IsolateSpawnException') throw error;
449 // TODO(nweiz): don't parse this as a string once issues 12617 and 12689
450 // are fixed.
451 if (!error.message.split('\n')[1].startsWith('import "$uri";')) {
452 throw error;
453 }
454
455 // If there was an IsolateSpawnException and the import that actually
456 // failed was the one we were loading transformers from, throw an
457 // application exception with a more user-friendly message.
458 fail('Transformer library "package:${id.package}/$path" not found.',
459 error, stackTrace);
460 });
461 });
462 }
463
464 /// A wrapper for a transformer that's in a different isolate.
465 class _ForeignTransformer extends Transformer {
466 /// The port with which we communicate with the child isolate.
467 ///
468 /// This port and all messages sent across it are specific to this
469 /// transformer.
470 final SendPort _port;
471
472 /// The result of calling [toString] on the transformer in the isolate.
473 final String _toString;
474
475 _ForeignTransformer(Map map)
476 : _port = map['port'],
477 _toString = map['toString'];
478
479 Future<bool> isPrimary(Asset asset) {
480 return _call(_port, {
481 'type': 'isPrimary',
482 'asset': serializeAsset(asset)
483 });
484 }
485
486 Future apply(Transform transform) {
487 return _call(_port, {
488 'type': 'apply',
489 'transform': _serializeTransform(transform)
490 });
491 }
492
493 String toString() => _toString;
494 }
495
496 /// A wrapper for a transformer group that's in a different isolate.
497 class _ForeignGroup implements TransformerGroup {
498 final Iterable<Iterable> phases;
499
500 /// The result of calling [toString] on the transformer group in the isolate.
501 final String _toString;
502
503 _ForeignGroup(TransformerId id, Map map)
504 : phases = map['phases'].map((phase) {
505 return phase.map((transformer) => _deserializeTransformerOrGroup(
506 transformer, id)).toList();
507 }).toList(),
508 _toString = map['toString'];
509
510 String toString() => _toString;
511 }
512
513 /// Converts a serializable map into a [Transformer] or a [TransformerGroup].
514 _deserializeTransformerOrGroup(Map map, TransformerId id) {
515 if (map['type'] == 'Transformer') {
516 var transformer = new _ForeignTransformer(map);
517 return ExcludingTransformer.wrap(transformer, id.includes, id.excludes);
518 }
519
520 assert(map['type'] == 'TransformerGroup');
521 return new _ForeignGroup(id, map);
522 }
523
524 /// Converts [transform] into a serializable map.
525 Map _serializeTransform(Transform transform) {
526 var receivePort = new ReceivePort();
527 receivePort.listen((wrappedMessage) {
528 _respond(wrappedMessage, (message) {
529 if (message['type'] == 'getInput') {
530 return transform.getInput(_deserializeId(message['id']))
531 .then((asset) => serializeAsset(asset));
532 }
533
534 if (message['type'] == 'addOutput') {
535 transform.addOutput(deserializeAsset(message['output']));
536 return null;
537 }
538
539 assert(message['type'] == 'log');
540 var method;
541 if (message['level'] == 'Info') {
542 method = transform.logger.info;
543 } else if (message['level'] == 'Fine') {
544 method = transform.logger.fine;
545 } else if (message['level'] == 'Warning') {
546 method = transform.logger.warning;
547 } else {
548 assert(message['level'] == 'Error');
549 method = transform.logger.error;
550 }
551
552 var assetId = message['assetId'] == null ? null :
553 _deserializeId(message['assetId']);
554 var span = message['span'] == null ? null :
555 _deserializeSpan(message['span']);
556 method(message['message'], asset: assetId, span: span);
557 });
558 });
559
560 return {
561 'port': receivePort.sendPort,
562 'primaryInput': serializeAsset(transform.primaryInput)
563 };
564 }
565
566 /// Converts a serializable map into an [AssetId].
567 AssetId _deserializeId(Map id) => new AssetId(id['package'], id['path']);
568
569 /// Converts a serializable map into a [Span].
570 Span _deserializeSpan(Map span) {
571 assert(span['type'] == 'fixed');
572 var location = _deserializeLocation(span['start']);
573 return new FixedSpan(span['sourceUrl'], location.offset, location.line,
574 location.column, text: span['text'], isIdentifier: span['isIdentifier']);
575 }
576
577 /// Converts a serializable map into a [Location].
578 Location _deserializeLocation(Map location) {
579 assert(location['type'] == 'fixed');
580 return new FixedLocation(location['offset'], location['sourceUrl'],
581 location['line'], location['column']);
582 }
583
584 /// Converts [id] into a serializable map.
585 Map _serializeId(AssetId id) => {'package': id.package, 'path': id.path};
586
587 /// Responds to a message sent by [_call].
588 ///
589 /// [wrappedMessage] is the raw message sent by [_call]. This unwraps it and
590 /// passes the contents of the message to [callback], then sends the return
591 /// value of [callback] back to [_call]. If [callback] returns a Future or
592 /// throws an error, that will also be sent.
593 void _respond(wrappedMessage, callback(message)) {
594 var replyTo = wrappedMessage['replyTo'];
595 syncFuture(() => callback(wrappedMessage['message']))
596 .then((result) => replyTo.send({'type': 'success', 'value': result}))
597 .catchError((error, stackTrace) {
598 replyTo.send({
599 'type': 'error',
600 'error': _serializeException(error, stackTrace)
601 });
602 });
603 }
604
605 /// Wraps [message] and sends it across [port], then waits for a response which
606 /// should be sent using [_respond].
607 ///
608 /// The returned Future will complete to the value or error returned by
609 /// [_respond].
610 Future _call(SendPort port, message) {
611 var receivePort = new ReceivePort();
612 port.send({
613 'message': message,
614 'replyTo': receivePort.sendPort
615 });
616
617 return Chain.track(receivePort.first).then((response) {
618 if (response['type'] == 'success') return response['value'];
619 assert(response['type'] == 'error');
620 var exception = _deserializeException(response['error']);
621 return new Future.error(exception, exception.stackTrace);
622 });
623 }
624
625 /// An [AssetNotFoundException] that was originally raised in another isolate.
626 class _CrossIsolateAssetNotFoundException extends dart.CrossIsolateException
627 implements AssetNotFoundException {
628 final AssetId id;
629
630 String get message => "Could not find asset $id.";
631
632 /// Loads a [_CrossIsolateAssetNotFoundException] from a serialized
633 /// representation.
634 ///
635 /// [error] should be the result of
636 /// [_CrossIsolateAssetNotFoundException.serialize].
637 _CrossIsolateAssetNotFoundException.deserialize(Map error)
638 : id = new AssetId(error['package'], error['path']),
639 super.deserialize(error);
640
641 /// Serializes [error] to an object that can safely be passed across isolate
642 /// boundaries.
643 static Map serialize(AssetNotFoundException error, [StackTrace stack]) {
644 var map = dart.CrossIsolateException.serialize(error);
645 map['package'] = error.id.package;
646 map['path'] = error.id.path;
647 return map;
648 }
649 }
650
651 /// Serializes [error] to an object that can safely be passed across isolate
652 /// boundaries.
653 ///
654 /// This handles [AssetNotFoundException]s specially, ensuring that their
655 /// metadata is preserved.
656 Map _serializeException(error, [StackTrace stack]) {
657 if (error is AssetNotFoundException) {
658 return _CrossIsolateAssetNotFoundException.serialize(error, stack);
659 } else {
660 return dart.CrossIsolateException.serialize(error, stack);
661 }
662 }
663
664 /// Loads an exception from a serialized representation.
665 ///
666 /// This handles [AssetNotFoundException]s specially, ensuring that their
667 /// metadata is preserved.
668 dart.CrossIsolateException _deserializeException(Map error) {
669 if (error['type'] == 'AssetNotFoundException') {
670 return new _CrossIsolateAssetNotFoundException.deserialize(error);
671 } else {
672 return new dart.CrossIsolateException.deserialize(error);
673 }
674 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698