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

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

Powered by Google App Engine
This is Rietveld 408576698