feat(dart/transform): Bail early for files with no deferred libraries

In `DeferredRewriter` if we can determine that there are no `deferred`
libraries imported, bail early to avoid parsing the entire file.
This commit is contained in:
Tim Blasi 2015-11-12 11:21:59 -08:00
parent 8f91ff84c7
commit f80321fd26
2 changed files with 39 additions and 22 deletions

View File

@ -14,39 +14,54 @@ import 'package:quiver/iterables.dart' as it;
class Rewriter { class Rewriter {
final AssetId _entryPoint; final AssetId _entryPoint;
final AssetReader _reader; final AssetReader _reader;
final _FindDeferredLibraries _visitor;
Rewriter(this._entryPoint, this._reader); Rewriter(AssetId entryPoint, AssetReader reader)
: _entryPoint = entryPoint,
_reader = reader,
_visitor = new _FindDeferredLibraries(reader, entryPoint);
/// Rewrites the provided code by finding all the deferred library imports /// Rewrites `loadLibrary` calls to initialize libraries once loaded.
/// and loadLibrary invocations. Then it removes any libraries that don't ///
/// require angular codegen. For the remaining libraries it rewrites the /// 1. Finds all the deferred library imports and loadLibrary invocations in
/// import to the corresponding ng_dep file, and chains a future which /// `_entryPoint`
/// first initializes the library. /// 2. Removes any libraries that don't require angular codegen.
/// 3. For the remaining libraries, rewrites the import to the corresponding
/// `ng_deps.dart` file.
/// 4. Chains a future to the `loadLibrary` call which initializes the
/// library.
/// ///
/// To the extent possible, this method does not change line numbers or /// To the extent possible, this method does not change line numbers or
/// offsets in the provided code to facilitate debugging via source maps. /// offsets in the provided code to facilitate debugging via source maps.
Future<String> rewrite() async { Future<String> rewrite() async {
var code = await _reader.readAsString(_entryPoint); var code = await _reader.readAsString(_entryPoint);
var node = parseCompilationUnit(code);
// If we can determine there are no deferred libraries, avoid parsing the
// entire file and bail early.
var onlyDirectives = parseDirectives(code, name: _entryPoint.path);
if (onlyDirectives == null) return null;
onlyDirectives.directives.accept(_visitor);
if (_visitor.deferredImports.isEmpty) return null;
var node = parseCompilationUnit(code, name: _entryPoint.path);
if (node == null) return null; if (node == null) return null;
return logElapsedAsync(() async { return logElapsedAsync(() async {
var visitor = new _FindDeferredLibraries(_reader, _entryPoint); node.declarations.accept(_visitor);
node.accept(visitor);
// Look to see if we found any deferred libraries // Look to see if we found any deferred libraries
if (!visitor.hasDeferredLibrariesToRewrite()) return null; if (!_visitor.hasDeferredLibrariesToRewrite()) return null;
// Remove any libraries that don't need angular codegen. // Remove any libraries that don't need angular codegen.
await visitor.cull(); await _visitor.cull();
// Check again if there are any deferred libraries. // Check again if there are any deferred libraries.
if (!visitor.hasDeferredLibrariesToRewrite()) return null; if (!_visitor.hasDeferredLibrariesToRewrite()) return null;
var compare = (AstNode a, AstNode b) => a.offset - b.offset; var compare = (AstNode a, AstNode b) => a.offset - b.offset;
visitor.deferredImports.sort(compare); _visitor.deferredImports.sort(compare);
visitor.loadLibraryInvocations.sort(compare); _visitor.loadLibraryInvocations.sort(compare);
var buf = new StringBuffer(); var buf = new StringBuffer();
var idx = var idx =
visitor.deferredImports.fold(0, (int lastIdx, ImportDirective node) { _visitor.deferredImports.fold(0, (int lastIdx, ImportDirective node) {
buf.write(code.substring(lastIdx, node.offset)); buf.write(code.substring(lastIdx, node.offset));
var import = code.substring(node.offset, node.end); var import = code.substring(node.offset, node.end);
@ -54,7 +69,7 @@ class Rewriter {
return node.end; return node.end;
}); });
idx = visitor.loadLibraryInvocations.fold(idx, idx = _visitor.loadLibraryInvocations.fold(idx,
(int lastIdx, MethodInvocation node) { (int lastIdx, MethodInvocation node) {
buf.write(code.substring(lastIdx, node.offset)); buf.write(code.substring(lastIdx, node.offset));
var value = node.realTarget as SimpleIdentifier; var value = node.realTarget as SimpleIdentifier;

View File

@ -44,13 +44,15 @@ void _testRewriteDeferredLibraries(String name, String inputPath) {
path.dirname(inputPath), 'expected', path.basename(inputPath)); path.dirname(inputPath), 'expected', path.basename(inputPath));
var expectedId = _assetIdForPath(expectedPath); var expectedId = _assetIdForPath(expectedPath);
var output = await rewriteDeferredLibraries(reader, inputId); var actualOutput = await rewriteDeferredLibraries(reader, inputId);
var input = await reader.readAsString(expectedId); var expectedOutput = await reader.readAsString(expectedId);
if (input == null) { if (expectedOutput == null) {
// Null input signals no output. Ensure that is true. // Null expectedOutput signals no output. Ensure that is true.
expect(output).toBeNull(); expect(actualOutput).toBeNull();
} else { } else {
expect(formatter.format(output)).toEqual(formatter.format(input)); expect(actualOutput).toBeNotNull();
expect(formatter.format(actualOutput))
.toEqual(formatter.format(expectedOutput));
} }
}, log: new RecordingLogger()); }, log: new RecordingLogger());
}); });