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

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

Issue 203913003: Share code between the transformer isolate and pub proper. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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) 2014, 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.transformer_isolate; 5 library pub.asset.transformer_isolate;
6 6
7 import 'dart:async'; 7 import 'dart:convert';
8 import 'dart:isolate'; 8 import 'dart:isolate';
9 import 'dart:convert';
10 import 'dart:mirrors'; 9 import 'dart:mirrors';
11 10
12 import 'package:source_maps/span.dart';
13 import 'package:stack_trace/stack_trace.dart';
14 import 'package:barback/barback.dart'; 11 import 'package:barback/barback.dart';
15 // TODO(nweiz): don't import from "src" once issue 14966 is fixed. 12
16 import 'package:barback/src/internal_asset.dart'; 13 import 'serialize.dart';
17 14
18 /// Sets up the initial communication with the host isolate. 15 /// Sets up the initial communication with the host isolate.
19 void loadTransformers(SendPort replyTo) { 16 void loadTransformers(SendPort replyTo) {
20 var port = new ReceivePort(); 17 var port = new ReceivePort();
21 replyTo.send(port.sendPort); 18 replyTo.send(port.sendPort);
22 port.first.then((wrappedMessage) { 19 port.first.then((wrappedMessage) {
23 _respond(wrappedMessage, (message) { 20 respond(wrappedMessage, (message) {
24 var library = Uri.parse(message['library']); 21 var library = Uri.parse(message['library']);
25 var configuration = JSON.decode(message['configuration']); 22 var configuration = JSON.decode(message['configuration']);
26 var mode = new BarbackMode(message['mode']); 23 var mode = new BarbackMode(message['mode']);
27 return _initialize(library, configuration, mode). 24 return _initialize(library, configuration, mode).
28 map(_serializeTransformerOrGroup).toList(); 25 map(serializeTransformerOrGroup).toList();
29 }); 26 });
30 }); 27 });
31 } 28 }
32 29
33 /// Loads all the transformers and groups defined in [uri]. 30 /// Loads all the transformers and groups defined in [uri].
34 /// 31 ///
35 /// Loads the library, finds any Transformer or TransformerGroup subclasses in 32 /// Loads the library, finds any Transformer or TransformerGroup subclasses in
36 /// it, instantiates them with [configuration] and [mode], and returns them. 33 /// it, instantiates them with [configuration] and [mode], and returns them.
37 Iterable _initialize(Uri uri, Map configuration, BarbackMode mode) { 34 Iterable _initialize(Uri uri, Map configuration, BarbackMode mode) {
38 var mirrors = currentMirrorSystem(); 35 var mirrors = currentMirrorSystem();
39 var transformerClass = reflectClass(Transformer); 36 var transformerClass = reflectClass(Transformer);
40 var groupClass = reflectClass(TransformerGroup); 37 var groupClass = reflectClass(TransformerGroup);
41 38
42 // TODO(nweiz): if no valid transformers are found, throw an error message 39 // TODO(nweiz): if no valid transformers are found, throw an error message
43 // describing candidates and why they were rejected. 40 // describing candidates and why they were rejected.
44 return mirrors.libraries[uri].declarations.values.map((declaration) { 41 return mirrors.libraries[uri].declarations.values.map((declaration) {
45 if (declaration is! ClassMirror) return null; 42 if (declaration is! ClassMirror) return null;
46 var classMirror = declaration; 43 var classMirror = declaration;
47 if (classMirror.isPrivate) return null; 44 if (classMirror.isPrivate) return null;
48 if (classMirror.isAbstract) return null; 45 if (classMirror.isAbstract) return null;
49 if (!classIsA(classMirror, transformerClass) && 46 if (!classMirror.isSubtypeOf(transformerClass) &&
50 !classIsA(classMirror, groupClass)) { 47 !classMirror.isSubtypeOf(groupClass)) {
51 return null; 48 return null;
52 } 49 }
53 50
54 var constructor = getConstructor(classMirror, 'asPlugin'); 51 var constructor = _getConstructor(classMirror, 'asPlugin');
55 if (constructor == null) return null; 52 if (constructor == null) return null;
56 if (constructor.parameters.isEmpty) { 53 if (constructor.parameters.isEmpty) {
57 if (configuration.isNotEmpty) return null; 54 if (configuration.isNotEmpty) return null;
58 return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee; 55 return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee;
59 } 56 }
60 if (constructor.parameters.length != 1) return null; 57 if (constructor.parameters.length != 1) return null;
61 58
62 return classMirror.newInstance(const Symbol('asPlugin'), 59 return classMirror.newInstance(const Symbol('asPlugin'),
63 [new BarbackSettings(configuration, mode)]).reflectee; 60 [new BarbackSettings(configuration, mode)]).reflectee;
64 }).where((classMirror) => classMirror != null); 61 }).where((classMirror) => classMirror != null);
65 } 62 }
66 63
67 /// A wrapper for a [Transform] that's in the host isolate.
68 ///
69 /// This retrieves inputs from and sends outputs and logs to the host isolate.
70 class ForeignTransform implements Transform {
71 /// The port with which we communicate with the host isolate.
72 ///
73 /// This port and all messages sent across it are specific to this transform.
74 final SendPort _port;
75
76 final Asset primaryInput;
77
78 TransformLogger get logger => _logger;
79 TransformLogger _logger;
80
81 /// Creates a transform from a serializable map sent from the host isolate.
82 ForeignTransform(Map transform)
83 : _port = transform['port'],
84 primaryInput = deserializeAsset(transform['primaryInput']) {
85 _logger = new TransformLogger((assetId, level, message, span) {
86 _call(_port, {
87 'type': 'log',
88 'level': level.name,
89 'message': message,
90 'assetId': assetId == null ? null : _serializeId(assetId),
91 'span': span == null ? null : _serializeSpan(span)
92 });
93 });
94 }
95
96 Future<Asset> getInput(AssetId id) {
97 return _call(_port, {
98 'type': 'getInput',
99 'id': _serializeId(id)
100 }).then(deserializeAsset);
101 }
102
103 Future<String> readInputAsString(AssetId id, {Encoding encoding}) {
104 if (encoding == null) encoding = UTF8;
105 return getInput(id).then((input) => input.readAsString(encoding: encoding));
106 }
107
108 Stream<List<int>> readInput(AssetId id) =>
109 _futureStream(getInput(id).then((input) => input.read()));
110
111 Future<bool> hasInput(AssetId id) {
112 return getInput(id).then((_) => true).catchError((error) {
113 if (error is AssetNotFoundException && error.id == id) return false;
114 throw error;
115 });
116 }
117
118 void addOutput(Asset output) {
119 _call(_port, {
120 'type': 'addOutput',
121 'output': serializeAsset(output)
122 });
123 }
124
125 void consumePrimary() {
126 _call(_port, {'type': 'consumePrimary'});
127 }
128 }
129
130 /// Returns the mirror for the root Object type.
131 ClassMirror get objectMirror => reflectClass(Object);
132
133 // TODO(nweiz): clean this up when issue 13248 is fixed. 64 // TODO(nweiz): clean this up when issue 13248 is fixed.
134 MethodMirror getConstructor(ClassMirror classMirror, String constructor) { 65 MethodMirror _getConstructor(ClassMirror classMirror, String constructor) {
135 var name = new Symbol("${MirrorSystem.getName(classMirror.simpleName)}" 66 var name = new Symbol("${MirrorSystem.getName(classMirror.simpleName)}"
136 ".$constructor"); 67 ".$constructor");
137 var candidate = classMirror.declarations[name]; 68 var candidate = classMirror.declarations[name];
138 if (candidate is MethodMirror && candidate.isConstructor) return candidate; 69 if (candidate is MethodMirror && candidate.isConstructor) return candidate;
139 return null; 70 return null;
140 } 71 }
141
142 // TODO(nweiz): get rid of this when issue 12439 is fixed.
143 /// Returns whether or not [mirror] is a subtype of [superclass].
144 ///
145 /// This includes [superclass] being mixed in to or implemented by [mirror].
146 bool classIsA(ClassMirror mirror, ClassMirror superclass) {
147 if (mirror == superclass) return true;
148 if (mirror == objectMirror) return false;
149 return classIsA(mirror.superclass, superclass) ||
150 mirror.superinterfaces.any((int) => classIsA(int, superclass));
151 }
152
153 /// Converts [transformerOrGroup] into a serializable map.
154 Map _serializeTransformerOrGroup(transformerOrGroup) {
155 if (transformerOrGroup is Transformer) {
156 return _serializeTransformer(transformerOrGroup);
157 } else {
158 assert(transformerOrGroup is TransformerGroup);
159 return _serializeTransformerGroup(transformerOrGroup);
160 }
161 }
162
163 /// Converts [transformer] into a serializable map.
164 Map _serializeTransformer(Transformer transformer) {
165 var port = new ReceivePort();
166 port.listen((wrappedMessage) {
167 _respond(wrappedMessage, (message) {
168 if (message['type'] == 'isPrimary') {
169 return transformer.isPrimary(deserializeAsset(message['asset']));
170 } else {
171 assert(message['type'] == 'apply');
172
173 // Make sure we return null so that if the transformer's [apply] returns
174 // a non-serializable value it doesn't cause problems.
175 return transformer.apply(
176 new ForeignTransform(message['transform'])).then((_) => null);
177 }
178 });
179 });
180
181 return {
182 'type': 'Transformer',
183 'toString': transformer.toString(),
184 'port': port.sendPort
185 };
186 }
187
188 // Converts [group] into a serializable map.
189 Map _serializeTransformerGroup(TransformerGroup group) {
190 return {
191 'type': 'TransformerGroup',
192 'toString': group.toString(),
193 'phases': group.phases.map((phase) {
194 return phase.map(_serializeTransformerOrGroup).toList();
195 }).toList()
196 };
197 }
198
199 /// Converts a serializable map into an [AssetId].
200 AssetId _deserializeId(Map id) => new AssetId(id['package'], id['path']);
201
202 /// Converts [id] into a serializable map.
203 Map _serializeId(AssetId id) => {'package': id.package, 'path': id.path};
204
205 /// Converts [span] into a serializable map.
206 Map _serializeSpan(Span span) {
207 // TODO(nweiz): convert FileSpans to FileSpans.
208 return {
209 'type': 'fixed',
210 'sourceUrl': span.sourceUrl,
211 'start': _serializeLocation(span.start),
212 'text': span.text,
213 'isIdentifier': span.isIdentifier
214 };
215 }
216
217 /// Converts [location] into a serializable map.
218 Map _serializeLocation(Location location) {
219 // TODO(nweiz): convert FileLocations to FileLocations.
220 return {
221 'type': 'fixed',
222 'sourceUrl': location.sourceUrl,
223 'offset': location.offset,
224 'line': location.line,
225 'column': location.column
226 };
227 }
228
229 /// Responds to a message sent by [_call].
230 ///
231 /// [wrappedMessage] is the raw message sent by [_call]. This unwraps it and
232 /// passes the contents of the message to [callback], then sends the return
233 /// value of [callback] back to [_call]. If [callback] returns a Future or
234 /// throws an error, that will also be sent.
235 void _respond(wrappedMessage, callback(message)) {
236 var replyTo = wrappedMessage['replyTo'];
237 new Future.sync(() => callback(wrappedMessage['message']))
238 .then((result) => replyTo.send({'type': 'success', 'value': result}))
239 .catchError((error, stackTrace) {
240 replyTo.send({
241 'type': 'error',
242 'error': _serializeException(error, stackTrace)
243 });
244 });
245 }
246
247 /// Wraps [message] and sends it across [port], then waits for a response which
248 /// should be sent using [_respond].
249 ///
250 /// The returned Future will complete to the value or error returned by
251 /// [_respond].
252 Future _call(SendPort port, message) {
253 var receivePort = new ReceivePort();
254 port.send({
255 'message': message,
256 'replyTo': receivePort.sendPort
257 });
258
259 return receivePort.first.then((response) {
260 if (response['type'] == 'success') return response['value'];
261 assert(response['type'] == 'error');
262 var exception = _deserializeException(response['error']);
263 return new Future.error(exception, exception.stackTrace);
264 });
265 }
266
267 /// An exception that was originally raised in another isolate.
268 ///
269 /// Exception objects can't cross isolate boundaries in general, so this class
270 /// wraps as much information as can be consistently serialized.
271 class CrossIsolateException implements Exception {
272 /// The name of the type of exception thrown.
273 ///
274 /// This is the return value of [error.runtimeType.toString()]. Keep in mind
275 /// that objects in different libraries may have the same type name.
276 final String type;
277
278 /// The exception's message, or its [toString] if it didn't expose a `message`
279 /// property.
280 final String message;
281
282 /// The exception's stack chain, or `null` if no stack chain was available.
283 final Chain stackTrace;
284
285 /// Loads a [CrossIsolateException] from a serialized representation.
286 ///
287 /// [error] should be the result of [CrossIsolateException.serialize].
288 CrossIsolateException.deserialize(Map error)
289 : type = error['type'],
290 message = error['message'],
291 stackTrace = error['stack'] == null ? null :
292 new Chain.parse(error['stack']);
293
294 /// Serializes [error] to an object that can safely be passed across isolate
295 /// boundaries.
296 static Map serialize(error, [StackTrace stack]) {
297 if (stack == null && error is Error) stack = error.stackTrace;
298 return {
299 'type': error.runtimeType.toString(),
300 'message': getErrorMessage(error),
301 'stack': stack == null ? null : new Chain.forTrace(stack).toString()
302 };
303 }
304
305 String toString() => "$message\n$stackTrace";
306 }
307
308 /// An [AssetNotFoundException] that was originally raised in another isolate.
309 class _CrossIsolateAssetNotFoundException extends CrossIsolateException
310 implements AssetNotFoundException {
311 final AssetId id;
312
313 String get message => "Could not find asset $id.";
314
315 /// Loads a [_CrossIsolateAssetNotFoundException] from a serialized
316 /// representation.
317 ///
318 /// [error] should be the result of
319 /// [_CrossIsolateAssetNotFoundException.serialize].
320 _CrossIsolateAssetNotFoundException.deserialize(Map error)
321 : id = new AssetId(error['package'], error['path']),
322 super.deserialize(error);
323
324 /// Serializes [error] to an object that can safely be passed across isolate
325 /// boundaries.
326 static Map serialize(AssetNotFoundException error, [StackTrace stack]) {
327 var map = CrossIsolateException.serialize(error);
328 map['package'] = error.id.package;
329 map['path'] = error.id.path;
330 return map;
331 }
332 }
333
334 /// Serializes [error] to an object that can safely be passed across isolate
335 /// boundaries.
336 ///
337 /// This handles [AssetNotFoundException]s specially, ensuring that their
338 /// metadata is preserved.
339 Map _serializeException(error, [StackTrace stack]) {
340 if (error is AssetNotFoundException) {
341 return _CrossIsolateAssetNotFoundException.serialize(error, stack);
342 } else {
343 return CrossIsolateException.serialize(error, stack);
344 }
345 }
346
347 /// Loads an exception from a serialized representation.
348 ///
349 /// This handles [AssetNotFoundException]s specially, ensuring that their
350 /// metadata is preserved.
351 CrossIsolateException _deserializeException(Map error) {
352 if (error['type'] == 'AssetNotFoundException') {
353 return new _CrossIsolateAssetNotFoundException.deserialize(error);
354 } else {
355 return new CrossIsolateException.deserialize(error);
356 }
357 }
358
359 /// A regular expression to match the exception prefix that some exceptions'
360 /// [Object.toString] values contain.
361 final _exceptionPrefix = new RegExp(r'^([A-Z][a-zA-Z]*)?(Exception|Error): ');
362
363 /// Get a string description of an exception.
364 ///
365 /// Many exceptions include the exception class name at the beginning of their
366 /// [toString], so we remove that if it exists.
367 String getErrorMessage(error) =>
368 error.toString().replaceFirst(_exceptionPrefix, '');
369
370 /// Returns a buffered stream that will emit the same values as the stream
371 /// returned by [future] once [future] completes. If [future] completes to an
372 /// error, the return value will emit that error and then close.
373 Stream _futureStream(Future<Stream> future) {
374 var controller = new StreamController(sync: true);
375 future.then((stream) {
376 stream.listen(
377 controller.add,
378 onError: controller.addError,
379 onDone: controller.close);
380 }).catchError((e, stackTrace) {
381 controller.addError(e, stackTrace);
382 controller.close();
383 });
384 return controller.stream;
385 }
386
387 Stream callbackStream(Stream callback()) {
388 var subscription;
389 var controller;
390 controller = new StreamController(onListen: () {
391 subscription = callback().listen(controller.add,
392 onError: controller.addError,
393 onDone: controller.close);
394 },
395 onCancel: () => subscription.cancel(),
396 onPause: () => subscription.pause(),
397 onResume: () => subscription.resume(),
398 sync: true);
399 return controller.stream;
400 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart ('k') | sdk/lib/_internal/pub/asset/dart/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698