Skip to content

Commit

Permalink
Version 1.10.0-dev.1.5
Browse files Browse the repository at this point in the history
svn merge -c 45170 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 45183 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@45201 260f80e4-7a28-3924-810f-c04153c831b5
  • Loading branch information
ricowind committed May 27, 2015
1 parent 35e9fab commit 9e6129a
Show file tree
Hide file tree
Showing 11 changed files with 500 additions and 43 deletions.
10 changes: 6 additions & 4 deletions pkg/analysis_server/lib/src/analysis_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import 'package:analysis_server/src/protocol.dart' hide Element;
import 'package:analysis_server/src/services/correction/namespace.dart';
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/plugin/plugin.dart';
import 'package:analyzer/source/package_map_provider.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart';
Expand Down Expand Up @@ -249,7 +249,7 @@ class AnalysisServer {
* running a full analysis server.
*/
AnalysisServer(this.channel, this.resourceProvider,
PackageMapProvider packageMapProvider, Index _index,
OptimizingPubPackageMapProvider packageMapProvider, Index _index,
AnalysisServerOptions analysisServerOptions, this.defaultSdk,
this.instrumentationService, {this.rethrowExceptions: true})
: index = _index,
Expand Down Expand Up @@ -1161,7 +1161,8 @@ class AnalysisServer {
Set<AnalysisContext> contexts = new HashSet<AnalysisContext>();
resources.forEach((Resource resource) {
if (resource is Folder) {
contexts.addAll(contextDirectoryManager.contextsInAnalysisRoot(resource));
contexts
.addAll(contextDirectoryManager.contextsInAnalysisRoot(resource));
}
});
return contexts;
Expand Down Expand Up @@ -1299,7 +1300,8 @@ class ServerContextManager extends ContextManager {
StreamController<ContextsChangedEvent> _onContextsChangedController;

ServerContextManager(this.analysisServer, ResourceProvider resourceProvider,
PackageMapProvider packageMapProvider, InstrumentationService service)
OptimizingPubPackageMapProvider packageMapProvider,
InstrumentationService service)
: super(resourceProvider, packageMapProvider, service) {
_onContextsChangedController =
new StreamController<ContextsChangedEvent>.broadcast();
Expand Down
51 changes: 27 additions & 24 deletions pkg/analysis_server/lib/src/context_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import 'dart:async';
import 'dart:collection';

import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/source/package_map_provider.dart';
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_io.dart';
Expand Down Expand Up @@ -82,7 +82,7 @@ abstract class ContextManager {
* Provider which is used to determine the mapping from package name to
* package folder.
*/
final PackageMapProvider _packageMapProvider;
final OptimizingPubPackageMapProvider _packageMapProvider;

/**
* The instrumentation service used to report instrumentation data.
Expand Down Expand Up @@ -136,6 +136,20 @@ abstract class ContextManager {
return flushedFiles.toList(growable: false);
}

/**
* Return a list containing all of the contexts contained in the given
* [analysisRoot].
*/
List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) {
List<AnalysisContext> contexts = <AnalysisContext>[];
_contexts.forEach((Folder contextFolder, _ContextInfo info) {
if (analysisRoot.isOrContains(contextFolder.path)) {
contexts.add(info.context);
}
});
return contexts;
}

/**
* We have finished computing the package map.
*/
Expand All @@ -162,20 +176,6 @@ abstract class ContextManager {
return false;
}

/**
* Return a list containing all of the contexts contained in the given
* [analysisRoot].
*/
List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) {
List<AnalysisContext> contexts = <AnalysisContext>[];
_contexts.forEach((Folder contextFolder, _ContextInfo info) {
if (analysisRoot.isOrContains(contextFolder.path)) {
contexts.add(info.context);
}
});
return contexts;
}

/**
* Rebuild the set of contexts from scratch based on the data last sent to
* setRoots(). Only contexts contained in the given list of analysis [roots]
Expand Down Expand Up @@ -388,16 +388,17 @@ abstract class ContextManager {
*/
UriResolver _computePackageUriResolver(Folder folder, _ContextInfo info) {
if (info.packageRoot != null) {
info.packageMapDependencies = new Set<String>();
info.packageMapInfo = null;
return new PackageUriResolver([new JavaFile(info.packageRoot)]);
} else {
beginComputePackageMap();
PackageMapInfo packageMapInfo;
OptimizingPubPackageMapInfo packageMapInfo;
ServerPerformanceStatistics.pub.makeCurrentWhile(() {
packageMapInfo = _packageMapProvider.computePackageMap(folder);
packageMapInfo =
_packageMapProvider.computePackageMap(folder, info.packageMapInfo);
});
endComputePackageMap();
info.packageMapDependencies = packageMapInfo.dependencies;
info.packageMapInfo = packageMapInfo;
if (packageMapInfo.packageMap == null) {
return null;
}
Expand Down Expand Up @@ -594,7 +595,8 @@ abstract class ContextManager {
break;
}

if (info.packageMapDependencies.contains(path)) {
if (info.packageMapInfo != null &&
info.packageMapInfo.isChangedDependency(path, resourceProvider)) {
_recomputePackageUriResolver(info);
}
}
Expand Down Expand Up @@ -744,10 +746,11 @@ class _ContextInfo {
Map<String, Source> sources = new HashMap<String, Source>();

/**
* Dependencies of the context's package map.
* If any of these files changes, the package map needs to be recomputed.
* Info returned by the last call to
* [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the
* package map hasn't been computed for this context yet.
*/
Set<String> packageMapDependencies;
OptimizingPubPackageMapInfo packageMapInfo;

_ContextInfo(this.folder, File pubspecFile, this.children, this.packageRoot) {
pubspecPath = pubspecFile.path;
Expand Down
4 changes: 2 additions & 2 deletions pkg/analysis_server/lib/src/socket_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import 'package:analysis_server/src/plugin/server_plugin.dart';
import 'package:analysis_server/src/protocol.dart';
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analysis_server/src/services/index/local_file_index.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/plugin/plugin.dart';
import 'package:analyzer/source/pub_package_map_provider.dart';
import 'package:analyzer/src/generated/sdk_io.dart';

/**
Expand Down Expand Up @@ -75,7 +75,7 @@ class SocketServer {
}

analysisServer = new AnalysisServer(serverChannel, resourceProvider,
new PubPackageMapProvider(resourceProvider, defaultSdk), index,
new OptimizingPubPackageMapProvider(resourceProvider, defaultSdk), index,
analysisServerOptions, defaultSdk, instrumentationService,
rethrowExceptions: false);
_initializeHandlers(analysisServer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ typedef int WriteFile(File file, String content);
* [PubPackageMapProvider] extension which caches pub list results.
* These results are cached in memory and in a single place on disk that is
* shared cross session and between different simultaneous sessions.
*
* TODO(paulberry): before this class is used again, it should be ported over
* to extend OptimizingPubPackageMapProvider instead of PubPackageMapProvider.
*/
class CachingPubPackageMapProvider extends PubPackageMapProvider {
static const cacheKey = 'pub_list_cache';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library source.optimizing_pub_package_map_provider;

import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/source/package_map_provider.dart';
import 'package:analyzer/source/pub_package_map_provider.dart';
import 'package:analyzer/src/generated/sdk_io.dart';

/**
* Extension of [PackageMapInfo] that tracks the modification timestamps of
* pub dependencies. This allows the analysis server to avoid making redundant
* calls to "pub list" when nothing has changed.
*/
class OptimizingPubPackageMapInfo extends PackageMapInfo {
/**
* Map from file path to the file's modification timestamp prior to running
* "pub list". Since the set of dependencies is not always known prior to
* running "pub list", some or all of the dependencies may be missing from
* this map.
*/
final Map<String, int> modificationTimes;

OptimizingPubPackageMapInfo(Map<String, List<Folder>> packageMap,
Set<String> dependencies, this.modificationTimes)
: super(packageMap, dependencies);

/**
* Return `true` if the given [path] is listed as a dependency, and we cannot
* prove using modification timestamps that it is unchanged.
* [resourceProvider] is used (if necessary) to read the [path]'s
* modification time.
*/
bool isChangedDependency(String path, ResourceProvider resourceProvider) {
if (!dependencies.contains(path)) {
// Path is not a dependency.
return false;
}
int lastModificationTime = modificationTimes[path];
if (lastModificationTime != null) {
Resource resource = resourceProvider.getResource(path);
if (resource is File) {
try {
if (resource.modificationStamp == lastModificationTime) {
// Path is a dependency, but it hasn't changed since the last run
// of "pub list".
return false;
}
} on FileSystemException {
// Path is a dependency, but we can't read its timestamp. Assume
// it's changed to be safe.
}
}
}
// Path is a dependency, and we couldn't prove that it hadn't changed.
// Assume it's changed to be safe.
return true;
}
}

/**
* Extension of [PubPackageMapProvider] that outputs additional information to
* allow the analysis server to avoid making redundant calls to "pub list" when
* nothing has changed.
*/
class OptimizingPubPackageMapProvider extends PubPackageMapProvider {
OptimizingPubPackageMapProvider(
ResourceProvider resourceProvider, DirectoryBasedDartSdk sdk, [RunPubList runPubList])
: super(resourceProvider, sdk, runPubList);

/**
* Compute a package map for the given folder by executing "pub list". If
* [previousInfo] is provided, it is used as a guess of which files the
* package map is likely to depend on; the modification times of those files
* are captured prior to executing "pub list" so that they can be used to
* avoid making redundant calls to "pub list" in the future.
*
* Also, in the case where dependencies can't be determined because of an
* error, the dependencies from [previousInfo] will be preserved.
*/
OptimizingPubPackageMapInfo computePackageMap(Folder folder,
[OptimizingPubPackageMapInfo previousInfo]) {
// Prior to running "pub list", read the modification timestamps of all of
// the old dependencies (if known).
Map<String, int> modificationTimes = <String, int>{};
if (previousInfo != null) {
for (String path in previousInfo.dependencies) {
Resource resource = resourceProvider.getResource(path);
if (resource is File) {
try {
modificationTimes[path] = resource.modificationStamp;
} on FileSystemException {
// File no longer exists. Don't record a timestamp for it; this
// will ensure that if the file reappears, we will re-run "pub
// list" regardless of the timestamp it reappears with.
}
}
}
}

// Try running "pub list".
PackageMapInfo info = super.computePackageMap(folder);
if (info == null) {
// Computing the package map resulted in an error. Merge the old
// dependencies with the new ones, if possible.
info = super.computePackageMapError(folder);
if (previousInfo != null) {
info.dependencies.addAll(previousInfo.dependencies);
}
}

// Discard any elements of modificationTimes that are no longer
// dependencies.
if (previousInfo != null) {
for (String dependency
in previousInfo.dependencies.difference(info.dependencies)) {
modificationTimes.remove(dependency);
}
}

// Bundle the modificationTimes with the other info.
return new OptimizingPubPackageMapInfo(
info.packageMap, info.dependencies, modificationTimes);
}

@override
PackageMapInfo computePackageMapError(Folder folder) {
// Return null to indicate to our override of computePackageMap that there
// was an error, so it can compute dependencies correctly.
return null;
}
}
40 changes: 38 additions & 2 deletions pkg/analysis_server/test/context_manager_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ library test.context.directory.manager;
import 'dart:collection';

import 'package:analysis_server/src/context_manager.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/source/package_map_provider.dart';
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
Expand Down Expand Up @@ -858,6 +858,42 @@ class ContextManagerTest {
});
}

test_watch_modifyPackageMapDependency_redundantly() async {
// Create two dependency files
String dependencyPath1 = posix.join(projPath, 'dep1');
String dependencyPath2 = posix.join(projPath, 'dep2');
resourceProvider.newFile(dependencyPath1, 'contents');
resourceProvider.newFile(dependencyPath2, 'contents');
packageMapProvider.dependencies.add(dependencyPath1);
packageMapProvider.dependencies.add(dependencyPath2);
// Create a dart file
String dartFilePath = posix.join(projPath, 'main.dart');
resourceProvider.newFile(dartFilePath, 'contents');
// Verify that the created context has the expected empty package map.
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
_checkPackageMap(projPath, isEmpty);
expect(packageMapProvider.computeCount, 1);
// Set up a different package map
String packagePath = '/package/foo';
resourceProvider.newFolder(packagePath);
packageMapProvider.packageMap = {'foo': projPath};
// Change both dependencies.
resourceProvider.modifyFile(dependencyPath1, 'new contents');
resourceProvider.modifyFile(dependencyPath2, 'new contents');
// Arrange for the next call to computePackageMap to return the correct
// timestamps for the dependencies.
packageMapProvider.modificationTimes = <String, int>{};
for (String path in [dependencyPath1, dependencyPath2]) {
File resource = resourceProvider.getResource(path);
packageMapProvider.modificationTimes[path] = resource.modificationStamp;
}
// This should cause the new package map to be picked up, by executing
// computePackageMap just one additional time.
await pumpEventQueue();
_checkPackageMap(projPath, equals(packageMapProvider.packageMap));
expect(packageMapProvider.computeCount, 2);
}

/**
* Verify that package URI's for source files in [path] will be resolved
* using a package map matching [expectation].
Expand Down Expand Up @@ -917,7 +953,7 @@ class TestContextManager extends ContextManager {
<String, UriResolver>{};

TestContextManager(MemoryResourceProvider resourceProvider,
PackageMapProvider packageMapProvider)
OptimizingPubPackageMapProvider packageMapProvider)
: super(resourceProvider, packageMapProvider,
InstrumentationService.NULL_SERVICE);

Expand Down
Loading

0 comments on commit 9e6129a

Please sign in to comment.