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

Side by Side Diff: third_party/pkg/barback-0.13.0/lib/src/phase.dart

Issue 291843011: Run pub tests against older versions of barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 6 years, 6 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 barback.graph.phase; 5 library barback.phase;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import '../asset/asset_id.dart';
10 import '../asset/asset_node.dart';
11 import '../asset/asset_node_set.dart';
12 import '../errors.dart';
13 import '../log.dart';
14 import '../transformer/transformer.dart';
15 import '../transformer/transformer_group.dart';
16 import '../utils.dart';
17 import '../utils/multiset.dart';
18 import 'asset_cascade.dart'; 9 import 'asset_cascade.dart';
10 import 'asset_id.dart';
11 import 'asset_node.dart';
12 import 'errors.dart';
19 import 'group_runner.dart'; 13 import 'group_runner.dart';
20 import 'node_status.dart'; 14 import 'log.dart';
21 import 'node_streams.dart'; 15 import 'multiset.dart';
22 import 'phase_forwarder.dart'; 16 import 'phase_forwarder.dart';
17 import 'phase_input.dart';
23 import 'phase_output.dart'; 18 import 'phase_output.dart';
24 import 'transformer_classifier.dart'; 19 import 'stream_pool.dart';
20 import 'transformer.dart';
21 import 'transformer_group.dart';
22 import 'utils.dart';
25 23
26 /// One phase in the ordered series of transformations in an [AssetCascade]. 24 /// One phase in the ordered series of transformations in an [AssetCascade].
27 /// 25 ///
28 /// Each phase can access outputs from previous phases and can in turn pass 26 /// Each phase can access outputs from previous phases and can in turn pass
29 /// outputs to later phases. Phases are processed strictly serially. All 27 /// outputs to later phases. Phases are processed strictly serially. All
30 /// transforms in a phase will be complete before moving on to the next phase. 28 /// transforms in a phase will be complete before moving on to the next phase.
31 /// Within a single phase, all transforms will be run in parallel. 29 /// Within a single phase, all transforms will be run in parallel.
32 /// 30 ///
33 /// Building can be interrupted between phases. For example, a source is added 31 /// Building can be interrupted between phases. For example, a source is added
34 /// which starts the background process. Sometime during, say, phase 2 (which 32 /// which starts the background process. Sometime during, say, phase 2 (which
35 /// is running asynchronously) that source is modified. When the process queue 33 /// is running asynchronously) that source is modified. When the process queue
36 /// goes to advance to phase 3, it will see that modification and start the 34 /// goes to advance to phase 3, it will see that modification and start the
37 /// waterfall from the beginning again. 35 /// waterfall from the beginning again.
38 class Phase { 36 class Phase {
39 /// The cascade that owns this phase. 37 /// The cascade that owns this phase.
40 final AssetCascade cascade; 38 final AssetCascade cascade;
41 39
42 /// A string describing the location of [this] in the transformer graph. 40 /// A string describing the location of [this] in the transformer graph.
43 final String _location; 41 final String _location;
44 42
45 /// The index of [this] in its parent cascade or group. 43 /// The index of [this] in its parent cascade or group.
46 final int _index; 44 final int _index;
47 45
46 /// The transformers that can access [inputs].
47 ///
48 /// Their outputs will be available to the next phase.
49 final _transformers = new Set<Transformer>();
50
48 /// The groups for this phase. 51 /// The groups for this phase.
49 final _groups = new Map<TransformerGroup, GroupRunner>(); 52 final _groups = new Map<TransformerGroup, GroupRunner>();
50 53
51 /// The inputs for this phase. 54 /// The inputs for this phase.
52 /// 55 ///
53 /// For the first phase, these will be the source assets. For all other 56 /// For the first phase, these will be the source assets. For all other
54 /// phases, they will be the outputs from the previous phase. 57 /// phases, they will be the outputs from the previous phase.
55 final _inputs = new AssetNodeSet(); 58 final _inputs = new Map<AssetId, PhaseInput>();
56
57 /// The transformer classifiers for this phase.
58 final _classifiers = new Map<Transformer, TransformerClassifier>();
59 59
60 /// The forwarders for this phase. 60 /// The forwarders for this phase.
61 final _forwarders = new Map<AssetId, PhaseForwarder>(); 61 final _forwarders = new Map<AssetId, PhaseForwarder>();
62 62
63 /// The outputs for this phase. 63 /// The outputs for this phase.
64 final _outputs = new Map<AssetId, PhaseOutput>(); 64 final _outputs = new Map<AssetId, PhaseOutput>();
65 65
66 /// The set of all [AssetNode.origin] properties of the input assets for this 66 /// The set of all [AssetNode.origin] properties of the input assets for this
67 /// phase. 67 /// phase.
68 /// 68 ///
69 /// This is used to determine which assets have been passed unmodified through 69 /// This is used to determine which assets have been passed unmodified through
70 /// [_classifiers] or [_groups]. It's possible that a given asset was consumed 70 /// [_inputs] or [_groups]. Each input asset has a PhaseInput in [_inputs]. If
71 /// by a group and not an individual transformer, and so shouldn't be 71 /// that input isn't consumed by any transformers, it will be forwarded
72 /// forwarded through the phase as a whole. 72 /// through the PhaseInput. However, it's possible that it was consumed by a
73 /// group, and so shouldn't be forwarded through the phase as a whole.
73 /// 74 ///
74 /// In order to detect whether an output has been forwarded through a group or 75 /// In order to detect whether an output has been forwarded through a group or
75 /// a classifier, we must be able to distinguish it from other outputs with 76 /// a PhaseInput, we must be able to distinguish it from other outputs with
76 /// the same id. To do so, we check if its origin is in [_inputOrigins]. If 77 /// the same id. To do so, we check if its origin is in [_inputOrigins]. If
77 /// so, it's been forwarded unmodified. 78 /// so, it's been forwarded unmodified.
78 final _inputOrigins = new Multiset<AssetNode>(); 79 final _inputOrigins = new Multiset<AssetNode>();
79 80
80 /// The streams exposed by this phase. 81 /// A stream that emits an event whenever [this] is no longer dirty.
81 final _streams = new NodeStreams(); 82 ///
82 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; 83 /// This is synchronous in order to guarantee that it will emit an event as
83 Stream<AssetNode> get onAsset => _streams.onAsset; 84 /// soon as [isDirty] flips from `true` to `false`.
84 Stream<LogEntry> get onLog => _streams.onLog; 85 Stream get onDone => _onDoneController.stream;
86 final _onDoneController = new StreamController.broadcast(sync: true);
85 87
86 /// How far along [this] is in processing its assets. 88 /// A stream that emits any new assets emitted by [this].
87 NodeStatus get status { 89 ///
88 // Before any transformers are added, the phase should be dirty if and only 90 /// Assets are emitted synchronously to ensure that any changes are thoroughly
89 // if any input is dirty. 91 /// propagated as soon as they occur. Only a phase with no [next] phase will
90 if (_classifiers.isEmpty && _groups.isEmpty && previous == null) { 92 /// emit assets.
91 return _inputs.any((input) => input.state.isDirty) ? 93 Stream<AssetNode> get onAsset => _onAssetController.stream;
92 NodeStatus.RUNNING : NodeStatus.IDLE; 94 final _onAssetController =
93 } 95 new StreamController<AssetNode>.broadcast(sync: true);
94 96
95 var classifierStatus = NodeStatus.dirtiest( 97 /// Whether [this] is dirty and still has more processing to do.
96 _classifiers.values.map((classifier) => classifier.status)); 98 ///
97 var groupStatus = NodeStatus.dirtiest( 99 /// A phase is considered dirty if any of the previous phases in the same
98 _groups.values.map((group) => group.status)); 100 /// cascade are dirty, since those phases could emit an asset that this phase
99 return (previous == null ? NodeStatus.IDLE : previous.status) 101 /// will then need to process.
100 .dirtier(classifierStatus) 102 bool get isDirty => (previous != null && previous.isDirty) ||
101 .dirtier(groupStatus); 103 _inputs.values.any((input) => input.isDirty) ||
102 } 104 _groups.values.any((group) => group.isDirty);
105
106 /// A stream that emits an event whenever any transforms in this phase logs
107 /// an entry.
108 Stream<LogEntry> get onLog => _onLogPool.stream;
109 final _onLogPool = new StreamPool<LogEntry>.broadcast();
103 110
104 /// The previous phase in the cascade, or null if this is the first phase. 111 /// The previous phase in the cascade, or null if this is the first phase.
105 final Phase previous; 112 final Phase previous;
106 113
107 /// The subscription to [previous]'s [onStatusChange] stream. 114 /// The subscription to [previous]'s [onDone] stream.
108 StreamSubscription _previousStatusSubscription; 115 StreamSubscription _previousOnDoneSubscription;
109 116
110 /// The subscription to [previous]'s [onAsset] stream. 117 /// The subscription to [previous]'s [onAsset] stream.
111 StreamSubscription<AssetNode> _previousOnAssetSubscription; 118 StreamSubscription<AssetNode> _previousOnAssetSubscription;
112 119
113 /// A map of asset ids to completers for [getInput] requests. 120 /// A map of asset ids to completers for [getInput] requests.
114 /// 121 ///
115 /// If an asset node is requested before it's available, we put a completer in 122 /// If an asset node is requested before it's available, we put a completer in
116 /// this map to wait for the asset to be generated. If it's not generated, the 123 /// this map to wait for the asset to be generated. If it's not generated, the
117 /// completer should complete to `null`. 124 /// completer should complete to `null`.
118 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>(); 125 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>();
119 126
120 /// Returns all currently-available output assets for this phase. 127 /// Returns all currently-available output assets for this phase.
121 Set<AssetNode> get availableOutputs { 128 Set<AssetNode> get availableOutputs {
122 return _outputs.values 129 return _outputs.values
123 .map((output) => output.output) 130 .map((output) => output.output)
124 .where((node) => node.state.isAvailable) 131 .where((node) => node.state.isAvailable)
125 .toSet(); 132 .toSet();
126 } 133 }
127 134
128 // TODO(nweiz): Rather than passing the cascade and the phase everywhere, 135 // TODO(nweiz): Rather than passing the cascade and the phase everywhere,
129 // create an interface that just exposes [getInput]. Emit errors via 136 // create an interface that just exposes [getInput]. Emit errors via
130 // [AssetNode]s. 137 // [AssetNode]s.
131 Phase(AssetCascade cascade, String location) 138 Phase(AssetCascade cascade, String location)
132 : this._(cascade, location, 0); 139 : this._(cascade, location, 0);
133 140
134 Phase._(this.cascade, this._location, this._index, [this.previous]) { 141 Phase._(this.cascade, this._location, this._index, [this.previous]) {
135 if (previous != null) { 142 if (previous != null) {
136 _previousOnAssetSubscription = previous.onAsset.listen(addInput); 143 _previousOnAssetSubscription = previous.onAsset.listen(addInput);
137 _previousStatusSubscription = previous.onStatusChange 144 _previousOnDoneSubscription = previous.onDone.listen((_) {
138 .listen((_) => _streams.changeStatus(status)); 145 if (!isDirty) _onDoneController.add(null);
146 });
139 } 147 }
140 148
141 onStatusChange.listen((status) { 149 onDone.listen((_) {
142 if (status == NodeStatus.RUNNING) return; 150 // All the previous phases have finished building. If anyone's still
143 151 // waiting for outputs, cut off the wait; we won't be generating them,
144 // All the previous phases have finished declaring or producing their 152 // at least until a source asset changes.
145 // outputs. If anyone's still waiting for outputs, cut off the wait; we
146 // won't be generating them, at least until a source asset changes.
147 for (var completer in _pendingOutputRequests.values) { 153 for (var completer in _pendingOutputRequests.values) {
148 completer.complete(null); 154 completer.complete(null);
149 } 155 }
150 _pendingOutputRequests.clear(); 156 _pendingOutputRequests.clear();
151 }); 157 });
152 } 158 }
153 159
154 /// Adds a new asset as an input for this phase. 160 /// Adds a new asset as an input for this phase.
155 /// 161 ///
156 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 162 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
157 /// will automatically begin determining which transforms can consume it as a 163 /// will automatically begin determining which transforms can consume it as a
158 /// primary input. The transforms themselves won't be applied until [process] 164 /// primary input. The transforms themselves won't be applied until [process]
159 /// is called, however. 165 /// is called, however.
160 /// 166 ///
161 /// This should only be used for brand-new assets or assets that have been 167 /// This should only be used for brand-new assets or assets that have been
162 /// removed and re-created. The phase will automatically handle updated assets 168 /// removed and re-created. The phase will automatically handle updated assets
163 /// using the [AssetNode.onStateChange] stream. 169 /// using the [AssetNode.onStateChange] stream.
164 void addInput(AssetNode node) { 170 void addInput(AssetNode node) {
171 if (_inputs.containsKey(node.id)) _inputs[node.id].remove();
172
173 node.force();
174
165 // Each group is one channel along which an asset may be forwarded, as is 175 // Each group is one channel along which an asset may be forwarded, as is
166 // each transformer. 176 // each transformer.
167 var forwarder = new PhaseForwarder( 177 var forwarder = new PhaseForwarder(
168 node, _classifiers.length, _groups.length); 178 node, _transformers.length, _groups.length);
169 _forwarders[node.id] = forwarder; 179 _forwarders[node.id] = forwarder;
170 forwarder.onAsset.listen(_handleOutputWithoutForwarder); 180 forwarder.onAsset.listen(_handleOutputWithoutForwarder);
171 if (forwarder.output != null) { 181 if (forwarder.output != null) {
172 _handleOutputWithoutForwarder(forwarder.output); 182 _handleOutputWithoutForwarder(forwarder.output);
173 } 183 }
174 184
175 _inputOrigins.add(node.origin); 185 _inputOrigins.add(node.origin);
176 _inputs.add(node); 186 var input = new PhaseInput(this, node, "$_location.$_index");
177 node.onStateChange.listen((state) { 187 _inputs[node.id] = input;
178 if (state.isRemoved) { 188 input.input.whenRemoved(() {
179 _inputOrigins.remove(node.origin); 189 _inputOrigins.remove(node.origin);
180 _forwarders.remove(node.id).remove(); 190 _inputs.remove(node.id);
181 } 191 _forwarders.remove(node.id).remove();
182 _streams.changeStatus(status); 192 if (!isDirty) _onDoneController.add(null);
193 });
194 input.onAsset.listen(_handleOutput);
195 _onLogPool.add(input.onLog);
196 input.onDone.listen((_) {
197 if (!isDirty) _onDoneController.add(null);
183 }); 198 });
184 199
185 for (var classifier in _classifiers.values) { 200 input.updateTransformers(_transformers);
186 classifier.addInput(node); 201
202 for (var group in _groups.values) {
203 group.addInput(node);
187 } 204 }
188 } 205 }
189 206
190 // TODO(nweiz): If the output is available when this is called, it's 207 // TODO(nweiz): If the output is available when this is called, it's
191 // theoretically possible for it to become unavailable between the call and 208 // theoretically possible for it to become unavailable between the call and
192 // the return. If it does so, it won't trigger the rebuilding process. To 209 // the return. If it does so, it won't trigger the rebuilding process. To
193 // avoid this, we should have this and the methods it calls take explicit 210 // avoid this, we should have this and the methods it calls take explicit
194 // callbacks, as in [AssetNode.whenAvailable]. 211 // callbacks, as in [AssetNode.whenAvailable].
195 /// Gets the asset node for an output [id]. 212 /// Gets the asset node for an output [id].
196 /// 213 ///
(...skipping 15 matching lines...) Expand all
212 // try again, since it could be generated again. 229 // try again, since it could be generated again.
213 output.force(); 230 output.force();
214 return output.whenAvailable((_) { 231 return output.whenAvailable((_) {
215 return output; 232 return output;
216 }).catchError((error) { 233 }).catchError((error) {
217 if (error is! AssetNotFoundException) throw error; 234 if (error is! AssetNotFoundException) throw error;
218 return getOutput(id); 235 return getOutput(id);
219 }); 236 });
220 } 237 }
221 238
222 // If this phase and the previous phases are fully declared or done, the 239 // If neither this phase nor the previous phases are dirty, the requested
223 // requested output won't be generated and we can safely return null. 240 // output won't be generated and we can safely return null.
224 if (status != NodeStatus.RUNNING) return null; 241 if (!isDirty) return null;
225 242
226 // Otherwise, store a completer for the asset node. If it's generated in 243 // Otherwise, store a completer for the asset node. If it's generated in
227 // the future, we'll complete this completer. 244 // the future, we'll complete this completer.
228 var completer = _pendingOutputRequests.putIfAbsent(id, 245 var completer = _pendingOutputRequests.putIfAbsent(id,
229 () => new Completer.sync()); 246 () => new Completer.sync());
230 return completer.future; 247 return completer.future;
231 }); 248 });
232 } 249 }
233 250
234 /// Set this phase's transformers to [transformers]. 251 /// Set this phase's transformers to [transformers].
235 void updateTransformers(Iterable transformers) { 252 void updateTransformers(Iterable transformers) {
236 var newTransformers = transformers.where((op) => op is Transformer) 253 var actualTransformers = transformers.where((op) => op is Transformer);
237 .toSet(); 254 _transformers.clear();
238 var oldTransformers = _classifiers.keys.toSet(); 255 _transformers.addAll(actualTransformers);
239 for (var removed in oldTransformers.difference(newTransformers)) { 256 for (var input in _inputs.values) {
240 _classifiers.remove(removed).remove(); 257 input.updateTransformers(actualTransformers);
241 }
242
243 for (var transformer in newTransformers.difference(oldTransformers)) {
244 var classifier = new TransformerClassifier(
245 this, transformer, "$_location.$_index");
246 _classifiers[transformer] = classifier;
247 classifier.onAsset.listen(_handleOutput);
248 _streams.onLogPool.add(classifier.onLog);
249 classifier.onStatusChange.listen((_) => _streams.changeStatus(status));
250 for (var input in _inputs) {
251 classifier.addInput(input);
252 }
253 } 258 }
254 259
255 var newGroups = transformers.where((op) => op is TransformerGroup) 260 var newGroups = transformers.where((op) => op is TransformerGroup)
256 .toSet(); 261 .toSet();
257 var oldGroups = _groups.keys.toSet(); 262 var oldGroups = _groups.keys.toSet();
258 for (var removed in oldGroups.difference(newGroups)) { 263 for (var removed in oldGroups.difference(newGroups)) {
259 _groups.remove(removed).remove(); 264 _groups.remove(removed).remove();
260 } 265 }
261 266
262 for (var added in newGroups.difference(oldGroups)) { 267 for (var added in newGroups.difference(oldGroups)) {
263 var runner = new GroupRunner(previous, added, "$_location.$_index"); 268 var runner = new GroupRunner(cascade, added, "$_location.$_index");
264 _groups[added] = runner; 269 _groups[added] = runner;
265 runner.onAsset.listen(_handleOutput); 270 runner.onAsset.listen(_handleOutput);
266 _streams.onLogPool.add(runner.onLog); 271 _onLogPool.add(runner.onLog);
267 runner.onStatusChange.listen((_) => _streams.changeStatus(status)); 272 runner.onDone.listen((_) {
273 if (!isDirty) _onDoneController.add(null);
274 });
275 for (var input in _inputs.values) {
276 runner.addInput(input.input);
277 }
268 } 278 }
269 279
270 for (var forwarder in _forwarders.values) { 280 for (var forwarder in _forwarders.values) {
271 forwarder.updateTransformers(_classifiers.length, _groups.length); 281 forwarder.updateTransformers(_transformers.length, _groups.length);
272 } 282 }
273
274 _streams.changeStatus(status);
275 } 283 }
276 284
277 /// Force all [LazyTransformer]s' transforms in this phase to begin producing 285 /// Force all [LazyTransformer]s' transforms in this phase to begin producing
278 /// concrete assets. 286 /// concrete assets.
279 void forceAllTransforms() { 287 void forceAllTransforms() {
280 for (var classifier in _classifiers.values) { 288 for (var group in _groups.values) {
281 classifier.forceAllTransforms(); 289 group.forceAllTransforms();
282 } 290 }
283 291
284 for (var group in _groups.values) { 292 for (var input in _inputs.values) {
285 group.forceAllTransforms(); 293 input.forceAllTransforms();
286 } 294 }
287 } 295 }
288 296
289 /// Add a new phase after this one. 297 /// Add a new phase after this one.
290 /// 298 ///
291 /// The new phase will have a location annotation describing its place in the 299 /// This may only be called on a phase with no phase following it.
292 /// package graph. By default, this annotation will describe it as being 300 Phase addPhase() {
293 /// directly after [this]. If [location] is passed, though, it's described as 301 var next = new Phase._(cascade, _location, _index + 1, this);
294 /// being the first phase in that location.
295 Phase addPhase([String location]) {
296 var index = 0;
297 if (location == null) {
298 location = _location;
299 index = _index + 1;
300 }
301
302 var next = new Phase._(cascade, location, index, this);
303 for (var output in _outputs.values.toList()) { 302 for (var output in _outputs.values.toList()) {
304 // Remove [output]'s listeners because now they should get the asset from 303 // Remove [output]'s listeners because now they should get the asset from
305 // [next], rather than this phase. Any transforms consuming [output] will 304 // [next], rather than this phase. Any transforms consuming [output] will
306 // be re-run and will consume the output from the new final phase. 305 // be re-run and will consume the output from the new final phase.
307 output.removeListeners(); 306 output.removeListeners();
308 } 307 }
309 return next; 308 return next;
310 } 309 }
311 310
312 /// Mark this phase as removed. 311 /// Mark this phase as removed.
313 /// 312 ///
314 /// This will remove all the phase's outputs. 313 /// This will remove all the phase's outputs.
315 void remove() { 314 void remove() {
316 for (var classifier in _classifiers.values.toList()) { 315 for (var input in _inputs.values.toList()) {
317 classifier.remove(); 316 input.remove();
318 } 317 }
319 for (var group in _groups.values) { 318 for (var group in _groups.values) {
320 group.remove(); 319 group.remove();
321 } 320 }
322 _streams.close(); 321 _onAssetController.close();
323 if (_previousStatusSubscription != null) { 322 _onLogPool.close();
324 _previousStatusSubscription.cancel(); 323 if (_previousOnDoneSubscription != null) {
324 _previousOnDoneSubscription.cancel();
325 } 325 }
326 if (_previousOnAssetSubscription != null) { 326 if (_previousOnAssetSubscription != null) {
327 _previousOnAssetSubscription.cancel(); 327 _previousOnAssetSubscription.cancel();
328 } 328 }
329 } 329 }
330 330
331 /// Add [asset] as an output of this phase. 331 /// Add [asset] as an output of this phase.
332 void _handleOutput(AssetNode asset) { 332 void _handleOutput(AssetNode asset) {
333 if (_inputOrigins.contains(asset.origin)) { 333 if (_inputOrigins.contains(asset.origin)) {
334 _forwarders[asset.id].addIntermediateAsset(asset); 334 _forwarders[asset.id].addIntermediateAsset(asset);
(...skipping 16 matching lines...) Expand all
351 351
352 var exception = _outputs[asset.id].collisionException; 352 var exception = _outputs[asset.id].collisionException;
353 if (exception != null) cascade.reportError(exception); 353 if (exception != null) cascade.reportError(exception);
354 } 354 }
355 355
356 /// Emit [asset] as an output of this phase. 356 /// Emit [asset] as an output of this phase.
357 /// 357 ///
358 /// This should be called after [_handleOutput], so that collisions are 358 /// This should be called after [_handleOutput], so that collisions are
359 /// resolved. 359 /// resolved.
360 void _emit(AssetNode asset) { 360 void _emit(AssetNode asset) {
361 _streams.onAssetController.add(asset); 361 _onAssetController.add(asset);
362 _providePendingAsset(asset); 362 _providePendingAsset(asset);
363 } 363 }
364 364
365 /// Provide an asset to a pending [getOutput] call. 365 /// Provide an asset to a pending [getOutput] call.
366 void _providePendingAsset(AssetNode asset) { 366 void _providePendingAsset(AssetNode asset) {
367 // If anyone's waiting for this asset, provide it to them. 367 // If anyone's waiting for this asset, provide it to them.
368 var request = _pendingOutputRequests.remove(asset.id); 368 var request = _pendingOutputRequests.remove(asset.id);
369 if (request == null) return; 369 if (request == null) return;
370 370
371 if (asset.state.isAvailable) { 371 if (asset.state.isAvailable) {
372 request.complete(asset); 372 request.complete(asset);
373 return; 373 return;
374 } 374 }
375 375
376 // A lazy asset may be emitted while still dirty. If so, we wait until it's 376 // A lazy asset may be emitted while still dirty. If so, we wait until it's
377 // either available or removed before trying again to access it. 377 // either available or removed before trying again to access it.
378 assert(asset.state.isDirty); 378 assert(asset.state.isDirty);
379 asset.force(); 379 asset.force();
380 asset.whenStateChanges().then((state) { 380 asset.whenStateChanges().then((state) {
381 if (state.isRemoved) return getOutput(asset.id); 381 if (state.isRemoved) return getOutput(asset.id);
382 return asset; 382 return asset;
383 }).then(request.complete).catchError(request.completeError); 383 }).then(request.complete).catchError(request.completeError);
384 } 384 }
385 385
386 String toString() => "phase $_location.$_index"; 386 String toString() => "phase $_location.$_index";
387 } 387 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698