Chromium Code Reviews| OLD | NEW |
|---|---|
| 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.transform_node; | 5 library barback.transform_node; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'asset.dart'; | 9 import 'asset.dart'; |
| 10 import 'asset_id.dart'; | 10 import 'asset_id.dart'; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 48 bool get isDirty => _state != _State.NOT_PRIMARY && | 48 bool get isDirty => _state != _State.NOT_PRIMARY && |
| 49 _state != _State.APPLIED && _state != _State.DECLARED; | 49 _state != _State.APPLIED && _state != _State.DECLARED; |
| 50 | 50 |
| 51 /// Whether this transform is deferred. | 51 /// Whether this transform is deferred. |
| 52 /// | 52 /// |
| 53 /// A transform is deferred if either its transformer is lazy or if its | 53 /// A transform is deferred if either its transformer is lazy or if its |
| 54 /// transformer is declaring and its primary input comes from a deferred | 54 /// transformer is declaring and its primary input comes from a deferred |
| 55 /// transformer. | 55 /// transformer. |
| 56 final bool deferred; | 56 final bool deferred; |
| 57 | 57 |
| 58 /// Whether this is a deferred transform waiting for [force] to be called to | 58 /// Whether this transform has been forced since it last finished applying. |
| 59 /// generate inputs. | |
| 60 /// | 59 /// |
| 61 /// This defaults to `true` for deferred transforms and `false` otherwise. | 60 /// A transform being forced means it should run until it generates outputs |
| 62 /// During or after running `isPrimary` or `declareOutputs`, this may become | 61 /// and is no longer dirty. This is always true for non-[deferred] |
| 63 /// `false`, indicating that the transform has been forced and should generate | 62 /// transformers, since they always need to eagerly generate outputs. |
| 64 /// outputs as soon as possible. It will only be set back to `true` if an | 63 bool _forced; |
| 65 /// input changes *after* `apply` has completed. | |
| 66 bool _awaitingForce; | |
| 67 | 64 |
| 68 /// The subscriptions to each input's [AssetNode.onStateChange] stream. | 65 /// The subscriptions to each input's [AssetNode.onStateChange] stream. |
| 69 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); | 66 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); |
| 70 | 67 |
| 71 /// The controllers for the asset nodes emitted by this node. | 68 /// The controllers for the asset nodes emitted by this node. |
| 72 final _outputControllers = new Map<AssetId, AssetNodeController>(); | 69 final _outputControllers = new Map<AssetId, AssetNodeController>(); |
| 73 | 70 |
| 74 /// The ids of inputs the transformer tried and failed to read last time it | 71 /// The ids of inputs the transformer tried and failed to read last time it |
| 75 /// ran. | 72 /// ran. |
| 76 final _missingInputs = new Set<AssetId>(); | 73 final _missingInputs = new Set<AssetId>(); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 128 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its | 125 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its |
| 129 /// [declareOutputs] has been run successfully. | 126 /// [declareOutputs] has been run successfully. |
| 130 Set<AssetId> _declaredOutputs; | 127 Set<AssetId> _declaredOutputs; |
| 131 | 128 |
| 132 TransformNode(this.phase, Transformer transformer, AssetNode primary, | 129 TransformNode(this.phase, Transformer transformer, AssetNode primary, |
| 133 this._location) | 130 this._location) |
| 134 : transformer = transformer, | 131 : transformer = transformer, |
| 135 primary = primary, | 132 primary = primary, |
| 136 deferred = transformer is LazyTransformer || | 133 deferred = transformer is LazyTransformer || |
| 137 (transformer is DeclaringTransformer && primary.deferred) { | 134 (transformer is DeclaringTransformer && primary.deferred) { |
| 138 _awaitingForce = deferred; | 135 _forced = !deferred; |
| 139 | 136 |
| 140 _onLogPool.add(_onLogController.stream); | 137 _onLogPool.add(_onLogController.stream); |
| 141 | 138 |
| 142 _primarySubscription = primary.onStateChange.listen((state) { | 139 _primarySubscription = primary.onStateChange.listen((state) { |
| 143 if (state.isRemoved) { | 140 if (state.isRemoved) { |
| 144 remove(); | 141 remove(); |
| 145 } else { | 142 } else { |
| 146 if (state.isDirty && !deferred) primary.force(); | 143 if (state.isDirty && !deferred) primary.force(); |
| 147 // If this is deferred but applying, that means it must have been | 144 // If this is deferred but applying, that means it must have been |
| 148 // forced, so we should ensure its input remains forced as well. | 145 // forced, so we should ensure its input remains forced as well. |
| 149 if (deferred && _state == _State.APPLYING) primary.force(); | 146 if (deferred && _forced && _state == _State.APPLYING) primary.force(); |
| 150 _dirty(); | 147 _dirty(); |
| 151 } | 148 } |
| 152 }); | 149 }); |
| 153 | 150 |
| 154 _phaseSubscription = phase.previous.onAsset.listen((node) { | 151 _phaseSubscription = phase.previous.onAsset.listen((node) { |
| 155 if (!_missingInputs.contains(node.id)) return; | 152 if (!_missingInputs.contains(node.id)) return; |
| 156 if (!deferred) node.force(); | 153 if (!deferred) node.force(); |
| 157 _dirty(); | 154 _dirty(); |
| 158 }); | 155 }); |
| 159 | 156 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 182 _clearOutputs(); | 179 _clearOutputs(); |
| 183 if (_passThroughController != null) { | 180 if (_passThroughController != null) { |
| 184 _passThroughController.setRemoved(); | 181 _passThroughController.setRemoved(); |
| 185 _passThroughController = null; | 182 _passThroughController = null; |
| 186 } | 183 } |
| 187 } | 184 } |
| 188 | 185 |
| 189 /// If [this] is deferred, ensures that its concrete outputs will be | 186 /// If [this] is deferred, ensures that its concrete outputs will be |
| 190 /// generated. | 187 /// generated. |
| 191 void force() { | 188 void force() { |
| 192 if (!_awaitingForce) return; | 189 if (_forced || _state == _State.APPLIED) return; |
| 193 primary.force(); | 190 primary.force(); |
| 194 _awaitingForce = false; | 191 _forced = true; |
| 195 _dirty(); | 192 _dirty(); |
| 196 } | 193 } |
| 197 | 194 |
| 198 /// Marks this transform as dirty. | 195 /// Marks this transform as dirty. |
| 199 /// | 196 /// |
| 200 /// This causes all of the transform's outputs to be marked as dirty as well. | 197 /// This causes all of the transform's outputs to be marked as dirty as well. |
| 201 void _dirty() { | 198 void _dirty() { |
| 202 if (_state == _State.NOT_PRIMARY) { | 199 if (_state == _State.NOT_PRIMARY) { |
| 203 _emitPassThrough(); | 200 _emitPassThrough(); |
| 204 return; | 201 return; |
| 205 } | 202 } |
| 206 | 203 |
| 207 // If we're in the process of running [isPrimary] or [declareOutputs], we | 204 // If we're in the process of running [isPrimary] or [declareOutputs], we |
| 208 // already know that [apply] needs to be run so there's nothing we need to | 205 // already know that [apply] needs to be run so there's nothing we need to |
| 209 // mark as dirty. | 206 // mark as dirty. |
| 210 if (_state == _State.DECLARING) return; | 207 if (_state == _State.DECLARING) return; |
| 211 | 208 |
| 212 // If we're waiting until [force] is called to run [apply], we don't want to | 209 if (!_forced && |
| 213 // run [apply] too early. | 210 // If [transformer] is declaring and [primary] is available, we do want |
| 214 if (_awaitingForce) return; | 211 // to start running [apply] even if [force] hasn't been called, since |
| 212 // [transformer] should run eagerly if possible. | |
| 213 (transformer is LazyTransformer || !primary.state.isAvailable)) { | |
|
Bob Nystrom
2014/04/21 22:37:40
This condition is pretty dense. How about breaking
nweiz
2014/04/21 22:52:09
Done.
| |
| 214 // [forced] should only ever be false for a deferred transform. | |
| 215 assert(deferred); | |
| 215 | 216 |
| 216 if (_state == _State.APPLIED && deferred) { | 217 // If we've finished applying, transition to DECLARED, indicating that we |
| 217 // Transition to DECLARED, indicating that we know what outputs [apply] | 218 // know what outputs [apply] will emit but we're waiting to emit them |
| 218 // will emit but we're waiting to emit them concretely until [force] is | 219 // concretely until [force] is called. If we're still applying, we'll |
| 219 // called. | 220 // transition to DECLARED once we finish. |
| 220 _state = _State.DECLARED; | 221 if (_state == _State.APPLIED) _state = _State.DECLARED; |
| 221 _awaitingForce = true; | |
| 222 for (var controller in _outputControllers.values) { | 222 for (var controller in _outputControllers.values) { |
| 223 controller.setLazy(force); | 223 controller.setLazy(force); |
| 224 } | 224 } |
| 225 return; | 225 return; |
| 226 } | 226 } |
| 227 | 227 |
| 228 if (_passThroughController != null) _passThroughController.setDirty(); | 228 if (_passThroughController != null) _passThroughController.setDirty(); |
| 229 for (var controller in _outputControllers.values) { | 229 for (var controller in _outputControllers.values) { |
| 230 controller.setDirty(); | 230 controller.setDirty(); |
| 231 } | 231 } |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 250 // is so a broken transformer doesn't take down the whole graph. | 250 // is so a broken transformer doesn't take down the whole graph. |
| 251 phase.cascade.reportError(_wrapException(error, stackTrace)); | 251 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 252 | 252 |
| 253 return false; | 253 return false; |
| 254 }).then((isPrimary) { | 254 }).then((isPrimary) { |
| 255 if (_isRemoved) return null; | 255 if (_isRemoved) return null; |
| 256 if (isPrimary) { | 256 if (isPrimary) { |
| 257 if (!deferred) primary.force(); | 257 if (!deferred) primary.force(); |
| 258 return _declareOutputs().then((_) { | 258 return _declareOutputs().then((_) { |
| 259 if (_isRemoved) return; | 259 if (_isRemoved) return; |
| 260 if (_awaitingForce) { | 260 if (_forced) { |
| 261 _apply(); | |
| 262 } else { | |
| 261 _state = _State.DECLARED; | 263 _state = _State.DECLARED; |
| 262 _onDoneController.add(null); | 264 _onDoneController.add(null); |
| 263 } else { | |
| 264 _apply(); | |
| 265 } | 265 } |
| 266 }); | 266 }); |
| 267 } | 267 } |
| 268 | 268 |
| 269 _emitPassThrough(); | 269 _emitPassThrough(); |
| 270 _state = _State.NOT_PRIMARY; | 270 _state = _State.NOT_PRIMARY; |
| 271 _onDoneController.add(null); | 271 _onDoneController.add(null); |
| 272 }); | 272 }); |
| 273 } | 273 } |
| 274 | 274 |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 291 .where((id) => id.package != phase.cascade.package).toSet(); | 291 .where((id) => id.package != phase.cascade.package).toSet(); |
| 292 for (var id in invalidIds) { | 292 for (var id in invalidIds) { |
| 293 _declaredOutputs.remove(id); | 293 _declaredOutputs.remove(id); |
| 294 // TODO(nweiz): report this as a warning rather than a failing error. | 294 // TODO(nweiz): report this as a warning rather than a failing error. |
| 295 phase.cascade.reportError(new InvalidOutputException(info, id)); | 295 phase.cascade.reportError(new InvalidOutputException(info, id)); |
| 296 } | 296 } |
| 297 | 297 |
| 298 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); | 298 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); |
| 299 | 299 |
| 300 for (var id in _declaredOutputs) { | 300 for (var id in _declaredOutputs) { |
| 301 var controller = _awaitingForce | 301 var controller = _forced |
| 302 ? new AssetNodeController.lazy(id, force, this) | 302 ? new AssetNodeController(id, this) |
| 303 : new AssetNodeController(id, this); | 303 : new AssetNodeController.lazy(id, force, this); |
| 304 _outputControllers[id] = controller; | 304 _outputControllers[id] = controller; |
| 305 _onAssetController.add(controller.node); | 305 _onAssetController.add(controller.node); |
| 306 } | 306 } |
| 307 }).catchError((error, stackTrace) { | 307 }).catchError((error, stackTrace) { |
| 308 if (_isRemoved) return; | 308 if (_isRemoved) return; |
| 309 phase.cascade.reportError(_wrapException(error, stackTrace)); | 309 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 310 }); | 310 }); |
| 311 } | 311 } |
| 312 | 312 |
| 313 /// Applies this transform. | 313 /// Applies this transform. |
| 314 void _apply() { | 314 void _apply() { |
| 315 assert(!_isRemoved && !_awaitingForce); | 315 assert(!_isRemoved); |
| 316 | 316 |
| 317 // Clear input subscriptions here as well as in [_process] because [_apply] | 317 // Clear input subscriptions here as well as in [_process] because [_apply] |
| 318 // may be restarted independently if only a secondary input changes. | 318 // may be restarted independently if only a secondary input changes. |
| 319 _clearInputSubscriptions(); | 319 _clearInputSubscriptions(); |
| 320 _state = _State.APPLYING; | 320 _state = _State.APPLYING; |
| 321 _runApply().then((hadError) { | 321 _runApply().then((hadError) { |
| 322 if (_isRemoved) return; | 322 if (_isRemoved) return; |
| 323 | 323 |
| 324 if (_state == _State.DECLARED) return; | |
| 325 | |
| 324 if (_state == _State.NEEDS_APPLY) { | 326 if (_state == _State.NEEDS_APPLY) { |
| 325 _apply(); | 327 _apply(); |
| 326 return; | 328 return; |
| 327 } | 329 } |
| 328 | 330 |
| 331 if (deferred) _forced = false; | |
| 332 | |
| 329 assert(_state == _State.APPLYING); | 333 assert(_state == _State.APPLYING); |
| 330 if (hadError) { | 334 if (hadError) { |
| 331 _clearOutputs(); | 335 _clearOutputs(); |
| 332 // If the transformer threw an error, we don't want to emit the | 336 // If the transformer threw an error, we don't want to emit the |
| 333 // pass-through asset in case it will be overwritten by the transformer. | 337 // pass-through asset in case it will be overwritten by the transformer. |
| 334 // However, if the transformer declared that it wouldn't overwrite or | 338 // However, if the transformer declared that it wouldn't overwrite or |
| 335 // consume the pass-through asset, we can safely emit it. | 339 // consume the pass-through asset, we can safely emit it. |
| 336 if (_declaredOutputs != null && !_consumePrimary && | 340 if (_declaredOutputs != null && !_consumePrimary && |
| 337 !_declaredOutputs.contains(primary.id)) { | 341 !_declaredOutputs.contains(primary.id)) { |
| 338 _emitPassThrough(); | 342 _emitPassThrough(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 372 /// Returns whether or not an error occurred while running the transformer. | 376 /// Returns whether or not an error occurred while running the transformer. |
| 373 Future<bool> _runApply() { | 377 Future<bool> _runApply() { |
| 374 var transformController = new TransformController(this); | 378 var transformController = new TransformController(this); |
| 375 _onLogPool.add(transformController.onLog); | 379 _onLogPool.add(transformController.onLog); |
| 376 | 380 |
| 377 return primary.whenAvailable((_) { | 381 return primary.whenAvailable((_) { |
| 378 if (_isRemoved) return null; | 382 if (_isRemoved) return null; |
| 379 _state = _State.APPLYING; | 383 _state = _State.APPLYING; |
| 380 return syncFuture(() => transformer.apply(transformController.transform)); | 384 return syncFuture(() => transformer.apply(transformController.transform)); |
| 381 }).then((_) { | 385 }).then((_) { |
| 382 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; | 386 if (deferred && !_forced && !primary.state.isAvailable) { |
| 387 _state = _State.DECLARED; | |
| 388 _onDoneController.add(null); | |
| 389 return false; | |
| 390 } | |
| 391 | |
| 392 if (_isRemoved) return false; | |
| 393 if (_state == _State.NEEDS_APPLY) return false; | |
| 394 if (_state == _State.DECLARING) return false; | |
| 383 if (transformController.loggedError) return true; | 395 if (transformController.loggedError) return true; |
| 384 _handleApplyResults(transformController); | 396 _handleApplyResults(transformController); |
| 385 return false; | 397 return false; |
| 386 }).catchError((error, stackTrace) { | 398 }).catchError((error, stackTrace) { |
| 387 // If the transform became dirty while processing, ignore any errors from | 399 // If the transform became dirty while processing, ignore any errors from |
| 388 // it. | 400 // it. |
| 389 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; | 401 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; |
| 390 | 402 |
| 391 // Catch all transformer errors and pipe them to the results stream. This | 403 // Catch all transformer errors and pipe them to the results stream. This |
| 392 // is so a broken transformer doesn't take down the whole graph. | 404 // is so a broken transformer doesn't take down the whole graph. |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 549 /// | 561 /// |
| 550 /// This will never transition to another state. | 562 /// This will never transition to another state. |
| 551 static final NOT_PRIMARY = const _State._("not primary"); | 563 static final NOT_PRIMARY = const _State._("not primary"); |
| 552 | 564 |
| 553 final String name; | 565 final String name; |
| 554 | 566 |
| 555 const _State._(this.name); | 567 const _State._(this.name); |
| 556 | 568 |
| 557 String toString() => name; | 569 String toString() => name; |
| 558 } | 570 } |
| OLD | NEW |