| 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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 /// The subscription to [phase]'s [Phase.onAsset] stream. | 45 /// The subscription to [phase]'s [Phase.onAsset] stream. |
| 46 StreamSubscription<AssetNode> _phaseSubscription; | 46 StreamSubscription<AssetNode> _phaseSubscription; |
| 47 | 47 |
| 48 /// How far along [this] is in processing its assets. | 48 /// How far along [this] is in processing its assets. |
| 49 NodeStatus get status { | 49 NodeStatus get status { |
| 50 if (_state == _State.NOT_PRIMARY || _state == _State.APPLIED || | 50 if (_state == _State.NOT_PRIMARY || _state == _State.APPLIED || |
| 51 _state == _State.DECLARED) { | 51 _state == _State.DECLARED) { |
| 52 return NodeStatus.IDLE; | 52 return NodeStatus.IDLE; |
| 53 } | 53 } |
| 54 | 54 |
| 55 if (transformer is DeclaringTransformer && _state != _State.DECLARING) { | 55 if (_declaring && _state != _State.DECLARING) { |
| 56 return NodeStatus.MATERIALIZING; | 56 return NodeStatus.MATERIALIZING; |
| 57 } else { | 57 } else { |
| 58 return NodeStatus.RUNNING; | 58 return NodeStatus.RUNNING; |
| 59 } | 59 } |
| 60 } | 60 } |
| 61 | 61 |
| 62 /// Whether this transform is deferred. | 62 /// Whether this is a declaring transform. |
| 63 /// | 63 /// |
| 64 /// A transform is deferred if either its transformer is lazy or if its | 64 /// This is usually identical to `transformer is DeclaringTransformer`, but if |
| 65 /// transformer is declaring and its primary input comes from a deferred | 65 /// a declaring and non-lazy transformer emits an error during |
| 66 /// transformer. | 66 /// `declareOutputs` it's treated as though it wasn't declaring. |
| 67 final bool deferred; | 67 bool get _declaring => transformer is DeclaringTransformer && |
| 68 (_state == _State.DECLARING || _declaredOutputs != null); |
| 68 | 69 |
| 69 /// Whether this transform has been forced since it last finished applying. | 70 /// Whether this transform has been forced since it last finished applying. |
| 70 /// | 71 /// |
| 71 /// A transform being forced means it should run until it generates outputs | 72 /// A transform being forced means it should run until it generates outputs |
| 72 /// and is no longer dirty. This is always true for non-[deferred] | 73 /// and is no longer dirty. This is always true for non-declaring |
| 73 /// transformers, since they always need to eagerly generate outputs. | 74 /// transformers, since they always need to eagerly generate outputs. |
| 74 bool _forced; | 75 bool _forced; |
| 75 | 76 |
| 76 /// The subscriptions to each input's [AssetNode.onStateChange] stream. | 77 /// The subscriptions to each input's [AssetNode.onStateChange] stream. |
| 77 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); | 78 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); |
| 78 | 79 |
| 79 /// The controllers for the asset nodes emitted by this node. | 80 /// The controllers for the asset nodes emitted by this node. |
| 80 final _outputControllers = new Map<AssetId, AssetNodeController>(); | 81 final _outputControllers = new Map<AssetId, AssetNodeController>(); |
| 81 | 82 |
| 82 /// The ids of inputs the transformer tried and failed to read last time it | 83 /// The ids of inputs the transformer tried and failed to read last time it |
| (...skipping 14 matching lines...) Expand all Loading... |
| 97 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; | 98 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; |
| 98 Stream<AssetNode> get onAsset => _streams.onAsset; | 99 Stream<AssetNode> get onAsset => _streams.onAsset; |
| 99 Stream<LogEntry> get onLog => _streams.onLog; | 100 Stream<LogEntry> get onLog => _streams.onLog; |
| 100 | 101 |
| 101 /// The current state of [this]. | 102 /// The current state of [this]. |
| 102 var _state = _State.DECLARING; | 103 var _state = _State.DECLARING; |
| 103 | 104 |
| 104 /// Whether [this] has been marked as removed. | 105 /// Whether [this] has been marked as removed. |
| 105 bool get _isRemoved => _streams.onAssetController.isClosed; | 106 bool get _isRemoved => _streams.onAssetController.isClosed; |
| 106 | 107 |
| 108 // If [transformer] is declaring but not lazy and [primary] is available, we |
| 109 // can run [apply] even if [force] hasn't been called, since [transformer] |
| 110 // should run eagerly if possible. |
| 111 bool get _canRunDeclaringEagerly => |
| 112 _declaring && transformer is! LazyTransformer && |
| 113 primary.state.isAvailable; |
| 114 |
| 107 /// Whether the most recent run of this transform has declared that it | 115 /// Whether the most recent run of this transform has declared that it |
| 108 /// consumes the primary input. | 116 /// consumes the primary input. |
| 109 /// | 117 /// |
| 110 /// Defaults to `false`. This is not meaningful unless [_state] is | 118 /// Defaults to `false`. This is not meaningful unless [_state] is |
| 111 /// [_State.APPLIED] or [_State.DECLARED]. | 119 /// [_State.APPLIED] or [_State.DECLARED]. |
| 112 bool _consumePrimary = false; | 120 bool _consumePrimary = false; |
| 113 | 121 |
| 114 /// The set of output ids that [transformer] declared it would emit. | 122 /// The set of output ids that [transformer] declared it would emit. |
| 115 /// | 123 /// |
| 116 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its | 124 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its |
| 117 /// [declareOutputs] has been run successfully. | 125 /// [declareOutputs] has been run successfully. |
| 118 Set<AssetId> _declaredOutputs; | 126 Set<AssetId> _declaredOutputs; |
| 119 | 127 |
| 120 TransformNode(this.phase, Transformer transformer, AssetNode primary, | 128 TransformNode(this.phase, Transformer transformer, AssetNode primary, |
| 121 this._location) | 129 this._location) |
| 122 : transformer = transformer, | 130 : transformer = transformer, |
| 123 primary = primary, | 131 primary = primary { |
| 124 deferred = transformer is LazyTransformer || | 132 _forced = transformer is! DeclaringTransformer; |
| 125 (transformer is DeclaringTransformer && primary.deferred) { | |
| 126 _forced = !deferred; | |
| 127 | 133 |
| 128 _primarySubscription = primary.onStateChange.listen((state) { | 134 _primarySubscription = primary.onStateChange.listen((state) { |
| 129 if (state.isRemoved) { | 135 if (state.isRemoved) { |
| 130 remove(); | 136 remove(); |
| 131 } else { | 137 } else { |
| 132 if (state.isDirty && !deferred) primary.force(); | 138 if (_forced) primary.force(); |
| 133 // If this is deferred but applying, that means it must have been | |
| 134 // forced, so we should ensure its input remains forced as well. | |
| 135 if (deferred && _forced && _state == _State.APPLYING) primary.force(); | |
| 136 _dirty(); | 139 _dirty(); |
| 137 } | 140 } |
| 138 }); | 141 }); |
| 139 | 142 |
| 140 _phaseSubscription = phase.previous.onAsset.listen((node) { | 143 _phaseSubscription = phase.previous.onAsset.listen((node) { |
| 141 if (!_missingInputs.contains(node.id)) return; | 144 if (!_missingInputs.contains(node.id)) return; |
| 142 if (!deferred) node.force(); | 145 if (_forced) node.force(); |
| 143 _dirty(); | 146 _dirty(); |
| 144 }); | 147 }); |
| 145 | 148 |
| 146 _isPrimary(); | 149 _isPrimary(); |
| 147 } | 150 } |
| 148 | 151 |
| 149 /// The [TransformInfo] describing this node. | 152 /// The [TransformInfo] describing this node. |
| 150 /// | 153 /// |
| 151 /// [TransformInfo] is the publicly-visible representation of a transform | 154 /// [TransformInfo] is the publicly-visible representation of a transform |
| 152 /// node. | 155 /// node. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 169 _passThroughController = null; | 172 _passThroughController = null; |
| 170 } | 173 } |
| 171 } | 174 } |
| 172 | 175 |
| 173 /// If [this] is deferred, ensures that its concrete outputs will be | 176 /// If [this] is deferred, ensures that its concrete outputs will be |
| 174 /// generated. | 177 /// generated. |
| 175 void force() { | 178 void force() { |
| 176 if (_forced || _state == _State.APPLIED) return; | 179 if (_forced || _state == _State.APPLIED) return; |
| 177 primary.force(); | 180 primary.force(); |
| 178 _forced = true; | 181 _forced = true; |
| 179 _dirty(); | 182 if (_state == _State.DECLARED) _dirty(); |
| 180 } | 183 } |
| 181 | 184 |
| 182 /// Marks this transform as dirty. | 185 /// Marks this transform as dirty. |
| 183 /// | 186 /// |
| 184 /// This causes all of the transform's outputs to be marked as dirty as well. | 187 /// This causes all of the transform's outputs to be marked as dirty as well. |
| 185 void _dirty() { | 188 void _dirty() { |
| 186 if (_state == _State.NOT_PRIMARY) { | 189 if (_state == _State.NOT_PRIMARY) { |
| 187 _emitPassThrough(); | 190 _emitPassThrough(); |
| 188 return; | 191 return; |
| 189 } | 192 } |
| 190 | 193 |
| 191 // If we're in the process of running [isPrimary] or [declareOutputs], we | 194 // If we're in the process of running [isPrimary] or [declareOutputs], we |
| 192 // already know that [apply] needs to be run so there's nothing we need to | 195 // already know that [apply] needs to be run so there's nothing we need to |
| 193 // mark as dirty. | 196 // mark as dirty. |
| 194 if (_state == _State.DECLARING) return; | 197 if (_state == _State.DECLARING) return; |
| 195 | 198 |
| 196 // If [transformer] is declaring but not lazy and [primary] is available, we | 199 if (!_forced && !_canRunDeclaringEagerly) { |
| 197 // do want to start running [apply] even if [force] hasn't been called, | 200 // [forced] should only ever be false for a declaring transformer. |
| 198 // since [transformer] should run eagerly if possible. | 201 assert(_declaring); |
| 199 var canRunDeclaringEagerly = | |
| 200 transformer is! LazyTransformer && primary.state.isAvailable; | |
| 201 if (!_forced && !canRunDeclaringEagerly) { | |
| 202 // [forced] should only ever be false for a deferred transform. | |
| 203 assert(deferred); | |
| 204 | 202 |
| 205 // If we've finished applying, transition to MATERIALIZING, indicating | 203 // If we've finished applying, transition to MATERIALIZING, indicating |
| 206 // that we know what outputs [apply] will emit but we're waiting to emit | 204 // that we know what outputs [apply] will emit but we're waiting to emit |
| 207 // them concretely until [force] is called. If we're still applying, we'll | 205 // them concretely until [force] is called. If we're still applying, we'll |
| 208 // transition to MATERIALIZING once we finish. | 206 // transition to MATERIALIZING once we finish. |
| 209 if (_state == _State.APPLIED) _state = _State.DECLARED; | 207 if (_state == _State.APPLIED) _state = _State.DECLARED; |
| 210 for (var controller in _outputControllers.values) { | 208 for (var controller in _outputControllers.values) { |
| 211 controller.setLazy(force); | 209 controller.setLazy(force); |
| 212 } | 210 } |
| 213 _emitDeclaredOutputs(); | 211 _emitDeclaredOutputs(); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 239 if (_isRemoved) return false; | 237 if (_isRemoved) return false; |
| 240 | 238 |
| 241 // Catch all transformer errors and pipe them to the results stream. This | 239 // Catch all transformer errors and pipe them to the results stream. This |
| 242 // is so a broken transformer doesn't take down the whole graph. | 240 // is so a broken transformer doesn't take down the whole graph. |
| 243 phase.cascade.reportError(_wrapException(error, stackTrace)); | 241 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 244 | 242 |
| 245 return false; | 243 return false; |
| 246 }).then((isPrimary) { | 244 }).then((isPrimary) { |
| 247 if (_isRemoved) return null; | 245 if (_isRemoved) return null; |
| 248 if (isPrimary) { | 246 if (isPrimary) { |
| 249 if (!deferred) primary.force(); | 247 if (_forced) primary.force(); |
| 250 return _declareOutputs().then((_) { | 248 return _declareOutputs().then((_) { |
| 251 if (_isRemoved) return; | 249 if (_isRemoved) return; |
| 252 if (_forced) { | 250 if (_forced || _canRunDeclaringEagerly) { |
| 253 _apply(); | 251 _apply(); |
| 254 } else { | 252 } else { |
| 255 _state = _State.DECLARED; | 253 _state = _State.DECLARED; |
| 256 _streams.changeStatus(NodeStatus.IDLE); | 254 _streams.changeStatus(NodeStatus.IDLE); |
| 257 } | 255 } |
| 258 }); | 256 }); |
| 259 } | 257 } |
| 260 | 258 |
| 261 _emitPassThrough(); | 259 _emitPassThrough(); |
| 262 _state = _State.NOT_PRIMARY; | 260 _state = _State.NOT_PRIMARY; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 284 for (var id in invalidIds) { | 282 for (var id in invalidIds) { |
| 285 _declaredOutputs.remove(id); | 283 _declaredOutputs.remove(id); |
| 286 // TODO(nweiz): report this as a warning rather than a failing error. | 284 // TODO(nweiz): report this as a warning rather than a failing error. |
| 287 phase.cascade.reportError(new InvalidOutputException(info, id)); | 285 phase.cascade.reportError(new InvalidOutputException(info, id)); |
| 288 } | 286 } |
| 289 | 287 |
| 290 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); | 288 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); |
| 291 _emitDeclaredOutputs(); | 289 _emitDeclaredOutputs(); |
| 292 }).catchError((error, stackTrace) { | 290 }).catchError((error, stackTrace) { |
| 293 if (_isRemoved) return; | 291 if (_isRemoved) return; |
| 292 if (transformer is! LazyTransformer) _forced = true; |
| 294 phase.cascade.reportError(_wrapException(error, stackTrace)); | 293 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 295 }); | 294 }); |
| 296 } | 295 } |
| 297 | 296 |
| 298 /// Emits a dirty asset node for all outputs that were declared by the | 297 /// Emits a dirty asset node for all outputs that were declared by the |
| 299 /// transformer. | 298 /// transformer. |
| 300 /// | 299 /// |
| 301 /// This won't emit any outputs for which there already exist output | 300 /// This won't emit any outputs for which there already exist output |
| 302 /// controllers. It should only be called for transforms that have declared | 301 /// controllers. It should only be called for transforms that have declared |
| 303 /// their outputs. | 302 /// their outputs. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 325 _runApply().then((hadError) { | 324 _runApply().then((hadError) { |
| 326 if (_isRemoved) return; | 325 if (_isRemoved) return; |
| 327 | 326 |
| 328 if (_state == _State.DECLARED) return; | 327 if (_state == _State.DECLARED) return; |
| 329 | 328 |
| 330 if (_state == _State.NEEDS_APPLY) { | 329 if (_state == _State.NEEDS_APPLY) { |
| 331 _apply(); | 330 _apply(); |
| 332 return; | 331 return; |
| 333 } | 332 } |
| 334 | 333 |
| 335 if (deferred) _forced = false; | 334 if (_declaring) _forced = false; |
| 336 | 335 |
| 337 assert(_state == _State.APPLYING); | 336 assert(_state == _State.APPLYING); |
| 338 if (hadError) { | 337 if (hadError) { |
| 339 _clearOutputs(); | 338 _clearOutputs(); |
| 340 // If the transformer threw an error, we don't want to emit the | 339 // If the transformer threw an error, we don't want to emit the |
| 341 // pass-through asset in case it will be overwritten by the transformer. | 340 // pass-through asset in case it will be overwritten by the transformer. |
| 342 // However, if the transformer declared that it wouldn't overwrite or | 341 // However, if the transformer declared that it wouldn't overwrite or |
| 343 // consume the pass-through asset, we can safely emit it. | 342 // consume the pass-through asset, we can safely emit it. |
| 344 if (_declaredOutputs != null && !_consumePrimary && | 343 if (_declaredOutputs != null && !_consumePrimary && |
| 345 !_declaredOutputs.contains(primary.id)) { | 344 !_declaredOutputs.contains(primary.id)) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 380 /// Returns whether or not an error occurred while running the transformer. | 379 /// Returns whether or not an error occurred while running the transformer. |
| 381 Future<bool> _runApply() { | 380 Future<bool> _runApply() { |
| 382 var transformController = new TransformController(this); | 381 var transformController = new TransformController(this); |
| 383 _streams.onLogPool.add(transformController.onLog); | 382 _streams.onLogPool.add(transformController.onLog); |
| 384 | 383 |
| 385 return primary.whenAvailable((_) { | 384 return primary.whenAvailable((_) { |
| 386 if (_isRemoved) return null; | 385 if (_isRemoved) return null; |
| 387 _state = _State.APPLYING; | 386 _state = _State.APPLYING; |
| 388 return syncFuture(() => transformer.apply(transformController.transform)); | 387 return syncFuture(() => transformer.apply(transformController.transform)); |
| 389 }).then((_) { | 388 }).then((_) { |
| 390 if (deferred && !_forced && !primary.state.isAvailable) { | 389 if (!_forced && !primary.state.isAvailable) { |
| 391 _state = _State.DECLARED; | 390 _state = _State.DECLARED; |
| 392 _streams.changeStatus(NodeStatus.IDLE); | 391 _streams.changeStatus(NodeStatus.IDLE); |
| 393 return false; | 392 return false; |
| 394 } | 393 } |
| 395 | 394 |
| 396 if (_isRemoved) return false; | 395 if (_isRemoved) return false; |
| 397 if (_state == _State.NEEDS_APPLY) return false; | 396 if (_state == _State.NEEDS_APPLY) return false; |
| 398 if (_state == _State.DECLARING) return false; | 397 if (_state == _State.DECLARING) return false; |
| 399 if (transformController.loggedError) return true; | 398 if (transformController.loggedError) return true; |
| 400 _handleApplyResults(transformController); | 399 _handleApplyResults(transformController); |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 } | 504 } |
| 506 } | 505 } |
| 507 | 506 |
| 508 /// Emit a warning about the transformer on [id]. | 507 /// Emit a warning about the transformer on [id]. |
| 509 void _warn(String message) { | 508 void _warn(String message) { |
| 510 _streams.onLogController.add( | 509 _streams.onLogController.add( |
| 511 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); | 510 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); |
| 512 } | 511 } |
| 513 | 512 |
| 514 String toString() => | 513 String toString() => |
| 515 "transform node in $_location for $transformer on $primary"; | 514 "transform node in $_location for $transformer on $primary ($_state, " |
| 515 "$status, ${_forced ? '' : 'un'}forced)"; |
| 516 } | 516 } |
| 517 | 517 |
| 518 /// The enum of states that [TransformNode] can be in. | 518 /// The enum of states that [TransformNode] can be in. |
| 519 class _State { | 519 class _State { |
| 520 /// The transform is running [Transformer.isPrimary] followed by | 520 /// The transform is running [Transformer.isPrimary] followed by |
| 521 /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]). | 521 /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]). |
| 522 /// | 522 /// |
| 523 /// This is the initial state of the transformer, and it will only occur once | 523 /// This is the initial state of the transformer, and it will only occur once |
| 524 /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs] | 524 /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs] |
| 525 /// are independent of the contents of the primary input. Once the two methods | 525 /// are independent of the contents of the primary input. Once the two methods |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 565 /// | 565 /// |
| 566 /// This will never transition to another state. | 566 /// This will never transition to another state. |
| 567 static final NOT_PRIMARY = const _State._("not primary"); | 567 static final NOT_PRIMARY = const _State._("not primary"); |
| 568 | 568 |
| 569 final String name; | 569 final String name; |
| 570 | 570 |
| 571 const _State._(this.name); | 571 const _State._(this.name); |
| 572 | 572 |
| 573 String toString() => name; | 573 String toString() => name; |
| 574 } | 574 } |
| OLD | NEW |