perf(dart/transform): Only process deferred libs when necessary

Previously, every .dart file in a package was processed to ensure proper
initialization of deferred loaded libraries.

Update the transformer to avoid processing libraries which we know do
not import any deferred libraries.

Closes #6745
This commit is contained in:
Tim Blasi 2016-01-27 17:25:57 -08:00 committed by Timothy Blasi
parent 3a40cd79f0
commit f56df65d48
3 changed files with 73 additions and 11 deletions

View File

@ -6,6 +6,7 @@ const SETUP_METHOD_NAME = 'initReflector';
const REFLECTOR_VAR_NAME = 'reflector';
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
const CSS_EXTENSION = '.css';
const DEFERRED_EXTENSION = '.dart.deferredCount';
const SHIMMED_STYLESHEET_EXTENSION = '.css.shim.dart';
const NON_SHIMMED_STYLESHEET_EXTENSION = '.css.dart';
const META_EXTENSION = '.ng_meta.json';
@ -21,8 +22,10 @@ const TEMPLATE_EXTENSION = '.template.dart';
/// Note that due to the implementation of `_toExtension`, ordering is
/// important. For example, putting '.dart' first in this list will cause
/// incorrect behavior.
/// incorrect behavior because it will (incompletely) match '.template.dart'
/// files.
const ALL_EXTENSIONS = const [
DEFERRED_EXTENSION,
META_EXTENSION,
SUMMARY_META_EXTENSION,
TEMPLATE_EXTENSION,
@ -36,6 +39,7 @@ const ALL_EXTENSIONS = const [
/// any files named like transformer outputs will be reported as generated.
bool isGenerated(String uri) {
return const [
DEFERRED_EXTENSION,
META_EXTENSION,
NON_SHIMMED_STYLESHEET_EXTENSION,
SHIMMED_STYLESHEET_EXTENSION,
@ -44,6 +48,10 @@ bool isGenerated(String uri) {
].any((ext) => uri.endsWith(ext));
}
/// Returns `uri` with its extension updated to [DEFERRED_EXTENSION].
String toDeferredExtension(String uri) =>
_toExtension(uri, ALL_EXTENSIONS, DEFERRED_EXTENSION);
/// Returns `uri` with its extension updated to [META_EXTENSION].
String toMetaExtension(String uri) =>
_toExtension(uri, ALL_EXTENSIONS, META_EXTENSION);

View File

@ -14,7 +14,7 @@ import 'rewriter.dart';
/// Transformer responsible for rewriting deferred library loads to enable
/// initializing the reflector in a deferred way to keep the code with the
/// deferred library.
class DeferredRewriter extends Transformer implements LazyTransformer {
class DeferredRewriter extends AggregateTransformer implements LazyTransformer {
final TransformerOptions options;
DeferredRewriter(this.options);
@ -25,23 +25,60 @@ class DeferredRewriter extends Transformer implements LazyTransformer {
}
@override
bool isPrimary(AssetId id) =>
id.extension.endsWith('dart') && _isNotGenerated(id);
dynamic classifyPrimary(AssetId id) {
// Map <name>.dart and <name>.dart.deferredCount => <name>.
// Anything else to `null`.
var extension = null;
if (id.path.endsWith(DEFERRED_EXTENSION)) {
extension = DEFERRED_EXTENSION;
} else if (id.path.endsWith('.dart') && !isGenerated(id.path)) {
extension = '.dart';
} else {
return null;
}
return id.path.substring(0, id.path.length - extension.length);
}
@override
Future apply(Transform transform) async {
Future apply(AggregateTransform transform) async {
return zone.exec(() async {
var asset = transform.primaryInput;
var reader = new AssetReader.fromTransform(transform);
var transformedCode = await rewriteDeferredLibraries(reader, asset.id);
final dartAsset = await _assetToProcess(transform);
if (dartAsset == null) return;
var transformedCode = await rewriteDeferredLibraries(
new AssetReader.fromTransform(transform), dartAsset.id);
if (transformedCode != null) {
transform.addOutput(new Asset.fromString(asset.id, transformedCode));
transform
.addOutput(new Asset.fromString(dartAsset.id, transformedCode));
}
}, log: transform.logger);
}
}
bool _isNotGenerated(AssetId id) => !isGenerated(id.path);
/// Returns the asset we need to process or `null` if none exists.
///
/// Consumes the .dart.deferredCount asset if it is present.
Future<Asset> _assetToProcess(AggregateTransform transform) async {
// We only need to process .dart files that have an associated
// .dart.deferredCount file with a value != "0".
//
// The .dart.deferredCount file is generated by a previous phase for files
// which have deferred imports. An absent .dart.deferredCount asset is the
// treated the same as a .dart.deferredCount asset with value "0".
var dartAsset, deferredCountAsset;
await for (Asset a in transform.primaryInputs) {
if (a.id.path.endsWith(DEFERRED_EXTENSION)) {
deferredCountAsset = a;
} else if (a.id.path.endsWith('.dart')) {
dartAsset = a;
}
}
if (deferredCountAsset == null) return null;
// No longer necessary.
transform.consumePrimary(deferredCountAsset.id);
if ((await deferredCountAsset.readAsString()) == "0") return null;
return dartAsset;
}
}
// Visible for testing
Future<String> rewriteDeferredLibraries(AssetReader reader, AssetId id) async {

View File

@ -33,6 +33,7 @@ class DirectiveProcessor extends Transformer implements LazyTransformer {
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(_deferredAssetId(transform.primaryId));
transform.declareOutput(_ngSummaryAssetId(transform.primaryId));
}
@ -49,6 +50,17 @@ class DirectiveProcessor extends Transformer implements LazyTransformer {
}
transform.addOutput(new Asset.fromString(
_ngSummaryAssetId(primaryId), _encoder.convert(ngMeta.toJson())));
var deferredCount = 0;
if (ngMeta.ngDeps != null) {
deferredCount = ngMeta.ngDeps.imports.where((i) => i.isDeferred).length;
}
if (deferredCount > 0) {
// The existence of this file with the value != "0" signals
// DeferredRewriter that the associated .dart file needs attention.
transform.addOutput(new Asset.fromString(
_deferredAssetId(primaryId), deferredCount.toString()));
}
}, log: transform.logger);
}
}
@ -57,3 +69,8 @@ AssetId _ngSummaryAssetId(AssetId primaryInputId) {
return new AssetId(
primaryInputId.package, toSummaryExtension(primaryInputId.path));
}
AssetId _deferredAssetId(AssetId primaryInputId) {
return new AssetId(
primaryInputId.package, toDeferredExtension(primaryInputId.path));
}