| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library pub.transformer_isolate; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:convert'; | |
| 9 import 'dart:isolate'; | |
| 10 | |
| 11 import 'package:barback/barback.dart'; | |
| 12 import 'package:source_span/source_span.dart'; | |
| 13 import 'package:stack_trace/stack_trace.dart'; | |
| 14 | |
| 15 import '../../../asset/dart/serialize.dart'; | |
| 16 import '../barback.dart'; | |
| 17 import '../exceptions.dart'; | |
| 18 import '../dart.dart' as dart; | |
| 19 import '../log.dart' as log; | |
| 20 import '../utils.dart'; | |
| 21 import 'asset_environment.dart'; | |
| 22 import 'barback_server.dart'; | |
| 23 import 'foreign_transformer.dart'; | |
| 24 import 'transformer_config.dart'; | |
| 25 import 'transformer_id.dart'; | |
| 26 | |
| 27 /// A wrapper for an isolate from which transformer plugins can be instantiated. | |
| 28 class TransformerIsolate { | |
| 29 /// The port used to communicate with the wrapped isolate. | |
| 30 final SendPort _port; | |
| 31 | |
| 32 /// A map indicating the barback server URLs for each [TransformerId] that's | |
| 33 /// loaded in the wrapped isolate. | |
| 34 /// | |
| 35 /// A barback server URL is the URL for the library that the given id | |
| 36 /// identifies. For example, the URL for "polymer/src/mirrors_remover" might | |
| 37 /// be "http://localhost:56234/packages/polymer/src/mirrors_remover.dart". | |
| 38 final Map<TransformerId, Uri> _idsToUrls; | |
| 39 | |
| 40 /// The barback mode for this run of pub. | |
| 41 final BarbackMode _mode; | |
| 42 | |
| 43 /// Spawns an isolate that loads all transformer libraries defined by [ids]. | |
| 44 /// | |
| 45 /// This doesn't actually instantiate any transformers, since a | |
| 46 /// [TransformerId] doesn't define the transformers' configuration. The | |
| 47 /// transformers can be constructed using [create]. | |
| 48 /// | |
| 49 /// If [snapshot] is passed, the isolate will be loaded from that path if it | |
| 50 /// exists. Otherwise, a snapshot of the isolate's code will be saved to that | |
| 51 /// path once the isolate is loaded. | |
| 52 static Future<TransformerIsolate> spawn(AssetEnvironment environment, | |
| 53 BarbackServer transformerServer, List<TransformerId> ids, {String snapshot
}) { | |
| 54 return mapFromIterableAsync(ids, value: (id) { | |
| 55 return id.getAssetId(environment.barback); | |
| 56 }).then((idsToAssetIds) { | |
| 57 var baseUrl = transformerServer.url; | |
| 58 var idsToUrls = mapMap(idsToAssetIds, value: (id, assetId) { | |
| 59 var path = assetId.path.replaceFirst('lib/', ''); | |
| 60 return Uri.parse('package:${id.package}/$path'); | |
| 61 }); | |
| 62 | |
| 63 var code = new StringBuffer(); | |
| 64 code.writeln("import 'dart:isolate';"); | |
| 65 | |
| 66 for (var url in idsToUrls.values) { | |
| 67 code.writeln("import '$url';"); | |
| 68 } | |
| 69 | |
| 70 code.writeln("import r'package:\$pub/transformer_isolate.dart';"); | |
| 71 code.writeln( | |
| 72 "void main(_, SendPort replyTo) => loadTransformers(replyTo);"); | |
| 73 | |
| 74 log.fine("Loading transformers from $ids"); | |
| 75 | |
| 76 var port = new ReceivePort(); | |
| 77 return dart.runInIsolate( | |
| 78 code.toString(), | |
| 79 port.sendPort, | |
| 80 packageRoot: baseUrl.resolve('packages'), | |
| 81 snapshot: snapshot).then((_) => port.first).then((sendPort) { | |
| 82 return new TransformerIsolate._(sendPort, environment.mode, idsToUrls); | |
| 83 }).catchError((error, stackTrace) { | |
| 84 if (error is! CrossIsolateException) throw error; | |
| 85 if (error.type != 'IsolateSpawnException') throw error; | |
| 86 | |
| 87 // TODO(nweiz): don't parse this as a string once issues 12617 and 12689 | |
| 88 // are fixed. | |
| 89 var firstErrorLine = error.message.split('\n')[1]; | |
| 90 | |
| 91 // The isolate error message contains the fully expanded path, not the | |
| 92 // "package:" URI, so we have to be liberal in what we look for in the | |
| 93 // error message. | |
| 94 var missingTransformer = idsToUrls.keys.firstWhere( | |
| 95 (id) => | |
| 96 firstErrorLine.startsWith("Uncaught Error: Load Error: Failure g
etting ") && | |
| 97 firstErrorLine.contains(idsToUrls[id].path), | |
| 98 orElse: () => throw error); | |
| 99 var packageUri = idToPackageUri(idsToAssetIds[missingTransformer]); | |
| 100 | |
| 101 // If there was an IsolateSpawnException and the import that actually | |
| 102 // failed was the one we were loading transformers from, throw an | |
| 103 // application exception with a more user-friendly message. | |
| 104 fail('Transformer library "$packageUri" not found.', error, stackTrace); | |
| 105 }); | |
| 106 }); | |
| 107 } | |
| 108 | |
| 109 TransformerIsolate._(this._port, this._mode, this._idsToUrls); | |
| 110 | |
| 111 /// Instantiate the transformers in the [config.id] with | |
| 112 /// [config.configuration]. | |
| 113 /// | |
| 114 /// If there are no transformers defined in the given library, this will | |
| 115 /// return an empty set. | |
| 116 Future<Set<Transformer>> create(TransformerConfig config) { | |
| 117 return call(_port, { | |
| 118 'library': _idsToUrls[config.id].toString(), | |
| 119 'mode': _mode.name, | |
| 120 'configuration': JSON.encode(config.configuration) | |
| 121 }).then((transformers) { | |
| 122 transformers = transformers.map( | |
| 123 (transformer) => deserializeTransformerLike(transformer, config)).toSe
t(); | |
| 124 log.fine("Transformers from $config: $transformers"); | |
| 125 return transformers; | |
| 126 }).catchError((error, stackTrace) { | |
| 127 throw new TransformerLoadError(error, config.span); | |
| 128 }); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 /// An error thrown when a transformer fails to load. | |
| 133 class TransformerLoadError extends SourceSpanException implements | |
| 134 WrappedException { | |
| 135 final CrossIsolateException innerError; | |
| 136 Chain get innerChain => innerError.stackTrace; | |
| 137 | |
| 138 TransformerLoadError(CrossIsolateException error, SourceSpan span) | |
| 139 : innerError = error, | |
| 140 super("Error loading transformer: ${error.message}", span); | |
| 141 } | |
| OLD | NEW |