feat(dart/transform): Track timing of transform tasks

This commit is contained in:
Tim Blasi
2015-10-06 17:02:51 -07:00
parent aee176115b
commit 07572652ff
17 changed files with 246 additions and 183 deletions

View File

@ -51,6 +51,24 @@ BuildLogger get logger {
return current == null ? new PrintLogger() : current;
}
/// Writes a log entry at `LogLevel.FINE` granularity with the time taken by
/// `asyncOperation`.
///
/// Returns the result of executing `asyncOperation`.
Future logElapsedAsync(Future asyncOperation(),
{String operationName: 'unknown', AssetId assetId}) async {
final timer = new Stopwatch()..start();
final result = await asyncOperation();
timer.stop();
final buf =
new StringBuffer('[$operationName] took ${timer.elapsedMilliseconds} ms');
if (assetId != null) {
buf.write(' on $assetId');
}
logger.fine(buf.toString(), asset: assetId);
return result;
}
class PrintLogger implements BuildLogger {
@override
final String detailsUri = '';

View File

@ -30,40 +30,42 @@ class Rewriter {
var node = parseCompilationUnit(code);
if (node == null) return null;
var visitor = new _FindDeferredLibraries(_reader, _entryPoint);
node.accept(visitor);
// Look to see if we found any deferred libraries
if (!visitor.hasDeferredLibrariesToRewrite()) return null;
// Remove any libraries that don't need angular codegen.
await visitor.cull();
// Check again if there are any deferred libraries.
if (!visitor.hasDeferredLibrariesToRewrite()) return null;
return logElapsedAsync(() async {
var visitor = new _FindDeferredLibraries(_reader, _entryPoint);
node.accept(visitor);
// Look to see if we found any deferred libraries
if (!visitor.hasDeferredLibrariesToRewrite()) return null;
// Remove any libraries that don't need angular codegen.
await visitor.cull();
// Check again if there are any deferred libraries.
if (!visitor.hasDeferredLibrariesToRewrite()) return null;
var compare = (AstNode a, AstNode b) => a.offset - b.offset;
visitor.deferredImports.sort(compare);
visitor.loadLibraryInvocations.sort(compare);
var compare = (AstNode a, AstNode b) => a.offset - b.offset;
visitor.deferredImports.sort(compare);
visitor.loadLibraryInvocations.sort(compare);
var buf = new StringBuffer();
var idx =
visitor.deferredImports.fold(0, (int lastIdx, ImportDirective node) {
buf.write(code.substring(lastIdx, node.offset));
var buf = new StringBuffer();
var idx =
visitor.deferredImports.fold(0, (int lastIdx, ImportDirective node) {
buf.write(code.substring(lastIdx, node.offset));
var import = code.substring(node.offset, node.end);
buf.write(import.replaceFirst('.dart', DEPS_EXTENSION));
return node.end;
});
var import = code.substring(node.offset, node.end);
buf.write(import.replaceFirst('.dart', DEPS_EXTENSION));
return node.end;
});
idx = visitor.loadLibraryInvocations.fold(idx,
(int lastIdx, MethodInvocation node) {
buf.write(code.substring(lastIdx, node.offset));
var value = node.realTarget as SimpleIdentifier;
var prefix = value.name;
// Chain a future that initializes the reflector.
buf.write('$prefix.loadLibrary().then((_) {$prefix.initReflector();})');
return node.end;
});
if (idx < code.length) buf.write(code.substring(idx));
return '$buf';
idx = visitor.loadLibraryInvocations.fold(idx,
(int lastIdx, MethodInvocation node) {
buf.write(code.substring(lastIdx, node.offset));
var value = node.realTarget as SimpleIdentifier;
var prefix = value.name;
// Chain a future that initializes the reflector.
buf.write('$prefix.loadLibrary().then((_) {$prefix.initReflector();})');
return node.end;
});
if (idx < code.length) buf.write(code.substring(idx));
return '$buf';
}, operationName: 'rewriteDeferredLibraries', assetId: _entryPoint);
}
}

View File

@ -20,42 +20,43 @@ import 'package:barback/barback.dart';
/// `isNgDeps` to `true` to signify that it is a dependency on which we need to
/// call `initReflector`.
Future<NgDepsModel> linkNgDeps(NgDepsModel ngDepsModel, AssetReader reader,
AssetId entryPoint, UrlResolver resolver) async {
AssetId assetId, UrlResolver resolver) async {
if (ngDepsModel == null) return null;
var linkedDepsMap =
await _processNgImports(ngDepsModel, reader, entryPoint, resolver);
return logElapsedAsync(() async {
var linkedDepsMap =
await _processNgImports(ngDepsModel, reader, assetId, resolver);
if (linkedDepsMap.isEmpty) {
// We are not calling `initReflector` on any other libraries, but we still
// return the model to ensure it is written to code.
// TODO(kegluneq): Continue using the protobuf format after this phase.
if (linkedDepsMap.isEmpty) {
// We are not calling `initReflector` on any other libraries, but we still
// return the model to ensure it is written to code.
// TODO(kegluneq): Continue using the protobuf format after this phase.
return ngDepsModel;
}
for (var i = ngDepsModel.imports.length - 1; i >= 0; --i) {
var import = ngDepsModel.imports[i];
if (linkedDepsMap.containsKey(import.uri)) {
var linkedModel = new ImportModel()
..isNgDeps = true
..uri = toDepsExtension(import.uri)
..prefix = 'i$i';
// TODO(kegluneq): Preserve combinators?
ngDepsModel.imports.insert(i + 1, linkedModel);
}
}
for (var i = 0, iLen = ngDepsModel.exports.length; i < iLen; ++i) {
var export = ngDepsModel.exports[i];
if (linkedDepsMap.containsKey(export.uri)) {
var linkedModel = new ImportModel()
..isNgDeps = true
..uri = toDepsExtension(export.uri)
..prefix = 'i${ngDepsModel.imports.length}';
// TODO(kegluneq): Preserve combinators?
ngDepsModel.imports.add(linkedModel);
}
}
return ngDepsModel;
}
for (var i = ngDepsModel.imports.length - 1; i >= 0; --i) {
var import = ngDepsModel.imports[i];
if (linkedDepsMap.containsKey(import.uri)) {
var linkedModel = new ImportModel()
..isNgDeps = true
..uri = toDepsExtension(import.uri)
..prefix = 'i$i';
// TODO(kegluneq): Preserve combinators?
ngDepsModel.imports.insert(i + 1, linkedModel);
}
}
for (var i = 0, iLen = ngDepsModel.exports.length; i < iLen; ++i) {
var export = ngDepsModel.exports[i];
if (linkedDepsMap.containsKey(export.uri)) {
var linkedModel = new ImportModel()
..isNgDeps = true
..uri = toDepsExtension(export.uri)
..prefix = 'i${ngDepsModel.imports.length}';
// TODO(kegluneq): Preserve combinators?
ngDepsModel.imports.add(linkedModel);
}
}
return ngDepsModel;
}, operationName: 'linkNgDeps', assetId: assetId);
}
bool _isNotDartDirective(dynamic model) => !isDartCoreUri(model.uri);

View File

@ -27,14 +27,16 @@ import 'ng_deps_linker.dart';
/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or
/// `DirectiveAlias` annotated constants in `entryPoint`.
Future<NgMeta> linkDirectiveMetadata(
AssetReader reader, AssetId entryPoint) async {
var ngMeta = await _readNgMeta(reader, entryPoint);
AssetReader reader, AssetId assetId) async {
var ngMeta = await _readNgMeta(reader, assetId);
if (ngMeta == null || ngMeta.isEmpty) return null;
await Future.wait([
linkNgDeps(ngMeta.ngDeps, reader, entryPoint, _urlResolver),
_linkDirectiveMetadataRecursive(
ngMeta, reader, entryPoint, new Set<String>())
linkNgDeps(ngMeta.ngDeps, reader, assetId, _urlResolver),
logElapsedAsync(() async {
await _linkRecursive(ngMeta, reader, assetId, new Set<String>());
return ngMeta;
}, operationName: 'linkDirectiveMetadata', assetId: assetId)
]);
return ngMeta;
}
@ -50,8 +52,8 @@ Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async {
final _urlResolver = const TransformerUrlResolver();
Future<NgMeta> _linkDirectiveMetadataRecursive(NgMeta ngMeta,
AssetReader reader, AssetId assetId, Set<String> seen) async {
Future _linkRecursive(NgMeta ngMeta, AssetReader reader, AssetId assetId,
Set<String> seen) async {
if (ngMeta == null ||
ngMeta.ngDeps == null ||
ngMeta.ngDeps.exports == null) {
@ -59,30 +61,23 @@ Future<NgMeta> _linkDirectiveMetadataRecursive(NgMeta ngMeta,
}
var assetUri = toAssetUri(assetId);
return Future
.wait(ngMeta.ngDeps.exports
.where((export) => !isDartCoreUri(export.uri))
.map((export) =>
_urlResolver.resolve(assetUri, toMetaExtension(export.uri)))
.where((uri) => !seen.contains(uri))
.map((uri) async {
return Future.wait(ngMeta.ngDeps.exports
.where((export) => !isDartCoreUri(export.uri))
.map((export) =>
_urlResolver.resolve(assetUri, toMetaExtension(export.uri)))
.where((uri) => !seen.contains(uri))
.map((uri) async {
seen.add(uri);
try {
final exportAssetId = fromUri(uri);
if (await reader.hasInput(exportAssetId)) {
var exportNgMetaJson = await reader.readAsString(exportAssetId);
if (exportNgMetaJson == null) return null;
var exportNgMeta = new NgMeta.fromJson(JSON.decode(exportNgMetaJson));
await _linkDirectiveMetadataRecursive(
exportNgMeta, reader, exportAssetId, seen);
if (exportNgMeta != null) {
ngMeta.addAll(exportNgMeta);
}
final exportNgMeta = await _readNgMeta(reader, exportAssetId);
if (exportNgMeta != null) {
await _linkRecursive(exportNgMeta, reader, exportAssetId, seen);
ngMeta.addAll(exportNgMeta);
}
} catch (err, st) {
// Log and continue.
logger.warning('Failed to fetch $uri. Message: $err.\n$st');
}
}))
.then((_) => ngMeta);
}));
}

View File

@ -37,7 +37,9 @@ Future<String> inlineParts(AssetReader reader, AssetId assetId) async {
// parent, so it does not need its own `.ng_deps.dart` file.
if (directivesVisitor.isPart) return null;
return _getAllDeclarations(reader, assetId, code, directivesVisitor);
return logElapsedAsync(() {
return _getAllDeclarations(reader, assetId, code, directivesVisitor);
}, operationName: 'inlineParts', assetId: assetId);
}
/// Processes `visitor.parts`, reading and appending their contents to the

View File

@ -17,7 +17,7 @@ import 'package:angular2/src/core/compiler/template_compiler.dart';
import 'inliner.dart';
/// Generates an instance of [NgMeta] describing the file at `assetId`.
Future<NgMeta> createNgDeps(AssetReader reader, AssetId assetId,
Future<NgMeta> createNgMeta(AssetReader reader, AssetId assetId,
AnnotationMatcher annotationMatcher) async {
// TODO(kegluneq): Shortcut if we can determine that there are no
// [Directive]s present, taking into account `export`s.
@ -26,18 +26,22 @@ Future<NgMeta> createNgDeps(AssetReader reader, AssetId assetId,
var parsedCode =
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
parsedCode.accept(ngDepsVisitor);
final ngDepsVisitor = await logElapsedAsync(() async {
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
parsedCode.accept(ngDepsVisitor);
return ngDepsVisitor;
}, operationName: 'createNgDeps', assetId: assetId);
var ngMeta = new NgMeta(ngDeps: ngDepsVisitor.model);
return logElapsedAsync(() async {
var ngMeta = new NgMeta(ngDeps: ngDepsVisitor.model);
var templateCompiler = createTemplateCompiler(reader);
var ngMetaVisitor = new _NgMetaVisitor(
ngMeta, assetId, annotationMatcher, _interfaceMatcher, templateCompiler);
parsedCode.accept(ngMetaVisitor);
await ngMetaVisitor.whenDone();
return ngMeta;
var templateCompiler = createTemplateCompiler(reader);
var ngMetaVisitor = new _NgMetaVisitor(ngMeta, assetId, annotationMatcher,
_interfaceMatcher, templateCompiler);
parsedCode.accept(ngMetaVisitor);
await ngMetaVisitor.whenDone();
return ngMeta;
}, operationName: 'createNgMeta', assetId: assetId);
}
// TODO(kegluneq): Allow the caller to provide an InterfaceMatcher.

View File

@ -45,7 +45,7 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
var primaryId = transform.primaryInput.id;
var reader = new AssetReader.fromTransform(transform);
var ngMeta =
await createNgDeps(reader, primaryId, options.annotationMatcher);
await createNgMeta(reader, primaryId, options.annotationMatcher);
if (ngMeta == null || ngMeta.isEmpty) {
return;
}

View File

@ -4,6 +4,7 @@ import 'dart:async';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/code/source_module.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/ng_compiler.dart';
import 'package:angular2/src/core/compiler/source_module.dart';
@ -21,11 +22,11 @@ Future<Iterable<Asset>> processStylesheet(
final stylesheetUrl = '${stylesheetId.package}|${stylesheetId.path}';
final templateCompiler = createTemplateCompiler(reader);
final cssText = await reader.readAsString(stylesheetId);
final sourceModules =
templateCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
return logElapsedAsync(() async {
final sourceModules =
templateCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
return sourceModules.map((SourceModule module) => new Asset.fromString(
new AssetId.parse('${module.moduleUrl}'),
writeSourceModule(module)));
return sourceModules.map((SourceModule module) => new Asset.fromString(
new AssetId.parse('${module.moduleUrl}'), writeSourceModule(module)));
}, operationName: 'processStylesheet', assetId: stylesheetId);
}

View File

@ -19,9 +19,11 @@ import 'package:code_transformers/assets.dart';
///
/// The returned value wraps the [NgDeps] at `entryPoint` as well as these
/// created objects.
Future<CompileDataResults> createCompileData(AssetReader reader,
AssetId entryPoint) async {
return new _CompileDataCreator(reader, entryPoint).createCompileData();
Future<CompileDataResults> createCompileData(
AssetReader reader, AssetId assetId) async {
return logElapsedAsync(() {
return new _CompileDataCreator(reader, assetId).createCompileData();
}, operationName: 'createCompileData', assetId: assetId);
}
class CompileDataResults {

View File

@ -10,6 +10,7 @@ import 'package:angular2/src/core/facade/lang.dart';
import 'package:angular2/src/core/reflection/reflection.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/code/source_module.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/ng_compiler.dart';
import 'package:angular2/src/transform/common/ng_deps.dart';
@ -21,14 +22,14 @@ import 'reflection/processor.dart' as reg;
import 'reflection/reflection_capabilities.dart';
import 'compile_data_creator.dart';
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
/// Reads the `.ng_deps.dart` file represented by `assetId` and parses any
/// Angular 2 `View` annotations it declares to generate `getter`s,
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
///
/// This method assumes a {@link DomAdapter} has been registered.
Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
{bool reflectPropertiesAsAttributes: false}) async {
var viewDefResults = await createCompileData(reader, entryPoint);
var viewDefResults = await createCompileData(reader, assetId);
var codegen = null;
if (viewDefResults.directiveMetadatas.isNotEmpty) {
var processor = new reg.Processor();
@ -44,16 +45,18 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
var compileData =
viewDefResults.viewDefinitions.values.toList(growable: false);
if (compileData.isEmpty) {
return new Outputs(entryPoint, ngDeps, codegen, null, null);
return new Outputs(assetId, ngDeps, codegen, null, null);
}
var savedReflectionCapabilities = reflector.reflectionCapabilities;
reflector.reflectionCapabilities = const NullReflectionCapabilities();
var compiledTemplates = templateCompiler.compileTemplatesCodeGen(compileData);
final compiledTemplates = await logElapsedAsync(() async {
return templateCompiler.compileTemplatesCodeGen(compileData);
}, operationName: 'compileTemplatesCodegen', assetId: assetId);
reflector.reflectionCapabilities = savedReflectionCapabilities;
return new Outputs(entryPoint, ngDeps, codegen,
viewDefResults.viewDefinitions, compiledTemplates);
return new Outputs(assetId, ngDeps, codegen, viewDefResults.viewDefinitions,
compiledTemplates);
}
AssetId templatesAssetId(AssetId ngDepsAssetId) =>
@ -87,7 +90,8 @@ class Outputs {
Map<RegisteredType,
NormalizedComponentWithViewDirectives> compileDataMap) {
var code = ngDeps.code;
if (accessors == null && (compileDataMap == null || compileDataMap.isEmpty)) return code;
if (accessors == null &&
(compileDataMap == null || compileDataMap.isEmpty)) return code;
if (ngDeps.registeredTypes.isEmpty) return code;
var beginRegistrationsIdx =
@ -106,7 +110,8 @@ class Outputs {
buf = new StringBuffer('${code.substring(0, beginRegistrationsIdx)}');
}
for (var registeredType in ngDeps.registeredTypes) {
if (compileDataMap != null && compileDataMap.containsKey(registeredType)) {
if (compileDataMap != null &&
compileDataMap.containsKey(registeredType)) {
// We generated a template for this type, so add the generated
// `CompiledTemplate` value as the final annotation in the list.
var annotations = registeredType.annotations as ListLiteral;