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

Side by Side Diff: pkg/barback/lib/src/phase_input.dart

Issue 196273003: Move isPrimary computation from PhaseInput into TransformNode. (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) 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.phase_input; 5 library barback.phase_input;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'asset_forwarder.dart'; 9 import 'asset_forwarder.dart';
10 import 'asset_node.dart'; 10 import 'asset_node.dart';
11 import 'asset_node_set.dart';
12 import 'errors.dart';
13 import 'log.dart'; 11 import 'log.dart';
14 import 'phase.dart'; 12 import 'phase.dart';
15 import 'stream_pool.dart'; 13 import 'stream_pool.dart';
16 import 'transform_node.dart'; 14 import 'transform_node.dart';
17 import 'transformer.dart'; 15 import 'transformer.dart';
18 import 'utils.dart';
19 16
20 /// A class for watching a single [AssetNode] and running any transforms that 17 /// A class for watching a single [AssetNode] and running any transforms that
21 /// take that node as a primary input. 18 /// take that node as a primary input.
22 class PhaseInput { 19 class PhaseInput {
23 /// The phase for which this is an input. 20 /// The phase for which this is an input.
24 final Phase _phase; 21 final Phase _phase;
25 22
26 /// A string describing the location of [this] in the transformer graph. 23 /// A string describing the location of [this] in the transformer graph.
27 final String _location; 24 final String _location;
28 25
29 /// The transformers to (potentially) run against [input].
30 final Set<Transformer> _transformers;
31
32 /// The transforms currently applicable to [input]. 26 /// The transforms currently applicable to [input].
33 /// 27 ///
34 /// These are the transforms that have been "wired up": they represent a 28 /// These are the transforms that have been "wired up": they represent a
35 /// repeatable transformation of a single concrete set of inputs. "dart2js" is 29 /// repeatable transformation of a single concrete set of inputs. "dart2js" is
36 /// a transformer. "dart2js on web/main.dart" is a transform. 30 /// a transformer. "dart2js on web/main.dart" is a transform.
37 final _transforms = new Set<TransformNode>(); 31 final _transforms = new Set<TransformNode>();
38 32
39 /// A forwarder for the input [AssetNode] for this phase. 33 /// A forwarder for the input [AssetNode] for this phase.
40 /// 34 ///
41 /// This is used to mark the node as removed should the input ever be removed. 35 /// This is used to mark the node as removed should the input ever be removed.
42 final AssetForwarder _inputForwarder; 36 final AssetForwarder _inputForwarder;
43 37
44 /// The asset node for this input. 38 /// The asset node for this input.
45 AssetNode get input => _inputForwarder.node; 39 AssetNode get input => _inputForwarder.node;
46 40
47 /// The controller that's used for the output node if [input] isn't
48 /// overwritten by any transformers.
49 ///
50 /// This needs an intervening controller to ensure that the output can be
51 /// marked dirty when determining whether transforms will overwrite it, and be
52 /// marked removed if they do. It's null if the asset is not being passed
53 /// through.
54 AssetNodeController _passThroughController;
55
56 /// A stream that emits an event whenever [this] is no longer dirty. 41 /// A stream that emits an event whenever [this] is no longer dirty.
57 /// 42 ///
58 /// This is synchronous in order to guarantee that it will emit an event as 43 /// This is synchronous in order to guarantee that it will emit an event as
59 /// soon as [isDirty] flips from `true` to `false`. 44 /// soon as [isDirty] flips from `true` to `false`.
60 Stream get onDone => _onDoneController.stream; 45 Stream get onDone => _onDoneController.stream;
61 final _onDoneController = new StreamController.broadcast(sync: true); 46 final _onDoneController = new StreamController.broadcast(sync: true);
62 47
63 /// A stream that emits any new assets emitted by [this]. 48 /// A stream that emits any new assets emitted by [this].
64 /// 49 ///
65 /// Assets are emitted synchronously to ensure that any changes are thoroughly 50 /// Assets are emitted synchronously to ensure that any changes are thoroughly
66 /// propagated as soon as they occur. 51 /// propagated as soon as they occur.
67 Stream<AssetNode> get onAsset => _onAssetController.stream; 52 Stream<AssetNode> get onAsset => _onAssetPool.stream;
68 final _onAssetController = new StreamController<AssetNode>(sync: true); 53 final _onAssetPool = new StreamPool<AssetNode>();
69 54
70 /// Whether [this] is dirty and still has more processing to do. 55 /// Whether [this] is dirty and still has more processing to do.
71 bool get isDirty => _isAdjustingTransformers || 56 bool get isDirty => _transforms.any((transform) => transform.isDirty);
72 _transforms.any((transform) => transform.isDirty);
73
74 /// The set of assets emitted by the transformers for this input that have the
75 /// same id as [input].
76 final _overwritingOutputs = new AssetNodeSet();
77
78 /// Whether [this] has been rmeoved.
79 bool get _isRemoved => _onAssetController.isClosed;
80
81 /// Whether [input] has become dirty since [_adjustTransformers] last started
82 /// running.
83 bool _hasBecomeDirty = false;
84
85 /// Whether [_isAdjustingTransformers] is currently running.
86 bool _isAdjustingTransformers = false;
87 57
88 /// A stream that emits an event whenever any transforms that use [input] as 58 /// A stream that emits an event whenever any transforms that use [input] as
89 /// their primary input log an entry. 59 /// their primary input log an entry.
90 Stream<LogEntry> get onLog => _onLogPool.stream; 60 Stream<LogEntry> get onLog => _onLogPool.stream;
91 final _onLogPool = new StreamPool<LogEntry>.broadcast(); 61 final _onLogPool = new StreamPool<LogEntry>.broadcast();
92 62
93 PhaseInput(this._phase, AssetNode input, Iterable<Transformer> transformers, 63 PhaseInput(this._phase, AssetNode input, this._location)
94 this._location) 64 : _inputForwarder = new AssetForwarder(input) {
95 : _transformers = transformers.toSet(), 65 input.whenRemoved(remove);
96 _inputForwarder = new AssetForwarder(input) {
97 input.onStateChange.listen((state) {
98 if (state.isRemoved) {
99 remove();
100 } else {
101 _dirty();
102 }
103 });
104
105 _adjustTransformers();
106 } 66 }
107 67
108 /// Removes this input. 68 /// Removes this input.
109 /// 69 ///
110 /// This marks all outputs of the input as removed. 70 /// This marks all outputs of the input as removed.
111 void remove() { 71 void remove() {
112 _onDoneController.close(); 72 _onDoneController.close();
113 _hasBecomeDirty = false; 73 _onAssetPool.close();
114 _onAssetController.close();
115 _onLogPool.close(); 74 _onLogPool.close();
116 _inputForwarder.close(); 75 _inputForwarder.close();
117 if (_passThroughController != null) {
118 _passThroughController.setRemoved();
119 _passThroughController = null;
120 }
121 }
122
123 /// Mark [this] as dirty and start re-running [_adjustTransformers] if
124 /// necessary.
125 void _dirty() {
126 // If there's a pass-through for this input, mark it dirty until we figure
127 // out if a transformer will emit an asset with that id.
128 if (_passThroughController != null) _passThroughController.setDirty();
129 _hasBecomeDirty = true;
130 if (!_isAdjustingTransformers) _adjustTransformers();
131 } 76 }
132 77
133 /// Set this input's transformers to [transformers]. 78 /// Set this input's transformers to [transformers].
134 void updateTransformers(Iterable<Transformer> newTransformersIterable) { 79 void updateTransformers(Iterable<Transformer> newTransformersIterable) {
135 var newTransformers = newTransformersIterable.toSet(); 80 var newTransformers = newTransformersIterable.toSet();
136 var oldTransformers = _transformers.toSet(); 81 for (var transform in _transforms.toList()) {
137 var removedTransformers = oldTransformers.difference(newTransformers); 82 if (newTransformers.remove(transform.transformer)) continue;
138 for (var removedTransformer in removedTransformers) { 83 transform.remove();
139 _transformers.remove(removedTransformer);
140 } 84 }
141 85
142 var brandNewTransformers = newTransformers.difference(oldTransformers); 86 // The remaining [newTransformers] are those for which there are no
143 brandNewTransformers.forEach(_transformers.add); 87 // transforms in [_transforms].
88 for (var transformer in newTransformers) {
89 var transform = new TransformNode(
90 _phase, transformer, input, _location);
91 _transforms.add(transform);
144 92
145 if (removedTransformers.isNotEmpty || brandNewTransformers.isNotEmpty) { 93 transform.onDone.listen((_) {
146 _dirty(); 94 if (!isDirty) _onDoneController.add(null);
95 }, onDone: () => _transforms.remove(transform));
96
97 _onAssetPool.add(transform.onAsset);
98 _onLogPool.add(transform.onLog);
147 } 99 }
148 } 100 }
149 101
150 /// Force all [LazyTransformer]s' transforms in this input to begin producing 102 /// Force all [LazyTransformer]s' transforms in this input to begin producing
151 /// concrete assets. 103 /// concrete assets.
152 void forceAllTransforms() { 104 void forceAllTransforms() {
153 for (var transform in _transforms) { 105 for (var transform in _transforms) {
154 transform.force(); 106 transform.force();
155 } 107 }
156 } 108 }
157 109
158 /// Asynchronously determines which transformers can consume [input] as a
159 /// primary input and creates transforms for them.
160 ///
161 /// This ensures that if [input] is modified or removed during or after the
162 /// time it takes to adjust its transformers, they're appropriately
163 /// re-adjusted.
164 void _adjustTransformers() {
165 assert(!_isRemoved);
166
167 _isAdjustingTransformers = true;
168 input.whenAvailable((asset) {
169 _hasBecomeDirty = false;
170
171 // Take a snapshot of the existing transformers that apply to this input.
172 // Since [_removeStaleTransforms] will check each of these transformers to
173 // be sure [input] is still primary for them, we use this set to avoid
174 // needlessly re-checking in [_addFreshTransforms].
175 var oldTransformers =
176 _transforms.map((transform) => transform.transformer).toSet();
177
178 return _removeStaleTransforms().then((_) {
179 if (_hasBecomeDirty || _isRemoved) return null;
180 return _addFreshTransforms(oldTransformers);
181 });
182 }).catchError((error, stackTrace) {
183 if (error is! AssetNotFoundException || error.id != input.id) throw error;
184
185 // If the asset is removed, [input.whenAvailable] will throw an
186 // [AssetNotFoundException]. In that case, just remove it.
187 remove();
188 }).then((_) {
189 if (_isRemoved) return;
190
191 _isAdjustingTransformers = false;
192 if (_hasBecomeDirty) {
193 _adjustTransformers();
194 } else if (!isDirty) {
195 _adjustPassThrough();
196 _onDoneController.add(null);
197 }
198 });
199 }
200
201 // Remove any old transforms that used to have [input]'s asset as a primary
202 // asset but no longer apply to its new contents.
203 Future _removeStaleTransforms() {
204 assert(input.state.isAvailable);
205
206 return Future.wait(_transforms.map((transform) {
207 return syncFuture(() {
208 if (!_transformers.contains(transform.transformer)) return false;
209
210 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to
211 // results (issue 16162).
212 return transform.transformer.isPrimary(input.asset);
213 }).then((isPrimary) {
214 if (_hasBecomeDirty) return;
215 if (isPrimary) {
216 transform.markPrimary();
217 } else if (_transforms.remove(transform)) {
218 transform.remove();
219 }
220 });
221 }));
222 }
223
224 // Add new transforms for transformers that consider [input]'s asset to be a
225 // primary input.
226 //
227 // [oldTransformers] is the set of transformers for which there were
228 // transforms that had [input] as a primary input prior to this. They don't
229 // need to be checked, since their transforms were removed or preserved in
230 // [_removeStaleTransforms].
231 Future _addFreshTransforms(Set<Transformer> oldTransformers) {
232 assert(input.state.isAvailable);
233
234 return Future.wait(_transformers.map((transformer) {
235 if (oldTransformers.contains(transformer)) return new Future.value();
236
237 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to
238 // results.
239 return transformer.isPrimary(input.asset).then((isPrimary) {
240 if (_hasBecomeDirty || !isPrimary) return;
241 var transform = new TransformNode(
242 _phase, transformer, input, _location);
243 _transforms.add(transform);
244
245 transform.onStateChange.listen((_) {
246 if (isDirty) {
247 if (_passThroughController == null) return;
248 _passThroughController.setDirty();
249 } else {
250 _adjustPassThrough();
251 _onDoneController.add(null);
252 }
253 });
254
255 transform.onAsset.listen((asset) {
256 if (asset.id == input.id) {
257 _overwritingOutputs.add(asset);
258 asset.whenRemoved(_adjustPassThrough);
259 _adjustPassThrough();
260 }
261
262 _onAssetController.add(asset);
263 }, onDone: () {
264 _transforms.remove(transform);
265 // When a transform is removed, we need to re-adjust the pass-through
266 // in case its call to `consumePrimary` was preventing pass-through
267 _adjustPassThrough();
268 });
269
270 _onLogPool.add(transform.onLog);
271 });
272 }));
273 }
274
275 /// Adjust whether [input] is passed through the phase unmodified, based on
276 /// whether it's overwritten by other transforms in this phase.
277 ///
278 /// If [input] was already passed-through, this will update the passed-through
279 /// value.
280 void _adjustPassThrough() {
281 // If [input] is removed, [_adjustPassThrough] can still be called due to
282 // [TransformNode]s marking their outputs as removed.
283 if (!input.state.isAvailable) return;
284
285 // If there's an output with the same id as the primary input, that
286 // overwrites the input so it doesn't get passed through. A transformer
287 // explicitly consuming the input will also cause it not to get passed
288 // through. Otherwise, create a pass-through controller if none exists, or
289 // set the existing one available.
290 if (_overwritingOutputs.isNotEmpty ||
291 _transforms.any((transform) => transform.consumePrimary)) {
292 if (_passThroughController != null) {
293 _passThroughController.setRemoved();
294 _passThroughController = null;
295 }
296 } else if (isDirty) {
297 // If the input is dirty, we're still figuring out whether a transform
298 // will overwrite the input. As such, we shouldn't pass through the asset
299 // yet.
300 } else if (_passThroughController == null) {
301 _passThroughController = new AssetNodeController.from(input);
302 _onAssetController.add(_passThroughController.node);
303 } else if (_passThroughController.node.state.isDirty) {
304 _passThroughController.setAvailable(input.asset);
305 }
306 }
307
308 String toString() => "phase input in $_location for $input"; 110 String toString() => "phase input in $_location for $input";
309 } 111 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698