Generalize DartSdkManager to support embedder SDKs

R=pquitslund@google.com, scheglov@google.com

Review URL: https://codereview.chromium.org/2107793002 .
This commit is contained in:
Brian Wilkerson 2016-06-28 13:48:57 -07:00
parent 3d4a7b1ca3
commit 9c5213a077
16 changed files with 283 additions and 44 deletions

View file

@ -43,6 +43,7 @@ import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/src/util/glob.dart';
import 'package:analyzer/task/dart.dart';
import 'package:plugin/plugin.dart';
import 'package:yaml/yaml.dart';
typedef void OptionUpdater(AnalysisOptionsImpl options);
@ -150,11 +151,6 @@ class AnalysisServer {
*/
List<RequestHandler> handlers;
/**
* The function used to create a new SDK using the default SDK.
*/
final SdkCreator defaultSdkCreator;
/**
* The object used to manage the SDK's known to this server.
*/
@ -317,7 +313,7 @@ class AnalysisServer {
Index _index,
this.serverPlugin,
this.options,
this.defaultSdkCreator,
this.sdkManager,
this.instrumentationService,
{ResolverProvider fileResolverProvider: null,
ResolverProvider packageResolverProvider: null,
@ -335,7 +331,6 @@ class AnalysisServer {
options.finerGrainedInvalidation;
defaultContextOptions.generateImplicitErrors = false;
operationQueue = new ServerOperationQueue();
sdkManager = new DartSdkManager(defaultSdkCreator);
if (useSingleContextManager) {
contextManager = new SingleContextManager(resourceProvider, sdkManager,
packageResolverProvider, analyzedFilesGlobs, defaultContextOptions);
@ -1639,15 +1634,30 @@ class ServerContextManagerCallbacks extends ContextManagerCallbacks {
// If no embedded URI resolver was provided, defer to a locator-backed one.
EmbedderYamlLocator locator =
disposition.getEmbedderLocator(resourceProvider);
EmbedderSdk sdk = new EmbedderSdk(locator.embedderYamls);
if (sdk.libraryMap.size() == 0) {
// The embedder file has no mappings, so use the default Dart SDK.
Map<Folder, YamlMap> embedderYamls = locator.embedderYamls;
EmbedderSdk embedderSdk = new EmbedderSdk(embedderYamls);
if (embedderSdk.libraryMap.size() == 0) {
// There was no embedder file, or the file was empty, so used the default
// SDK.
resolvers.add(new DartUriResolver(
analysisServer.sdkManager.getSdkForOptions(options)));
} else {
// The embedder uri resolver has mappings, use it instead of the default
// Dart SDK uri resolver.
resolvers.add(new DartUriResolver(sdk));
// The embedder file defines an alternate SDK, so use it.
List<String> paths = <String>[];
for (Folder folder in embedderYamls.keys) {
paths.add(folder
.getChildAssumingFile(EmbedderYamlLocator.EMBEDDER_FILE_NAME)
.path);
}
DartSdk dartSdk = analysisServer.sdkManager
.getSdk(new SdkDescription(paths, options), () {
embedderSdk.analysisOptions = options;
// TODO(brianwilkerson) Enable summary use after we have decided where
// summary files for embedder files will live.
embedderSdk.useSummary = false;
return embedderSdk;
});
resolvers.add(new DartUriResolver(dartSdk));
}
resolvers.addAll(packageUriResolvers);

View file

@ -1157,15 +1157,29 @@ class ContextManagerImpl implements ContextManager {
EmbedderYamlLocator locator =
disposition.getEmbedderLocator(resourceProvider);
EmbedderSdk sdk = new EmbedderSdk(locator.embedderYamls);
if (sdk.libraryMap.size() == 0) {
// The embedder uri resolver has no mappings. Use the default Dart SDK
// uri resolver.
Map<Folder, YamlMap> embedderYamls = locator.embedderYamls;
EmbedderSdk embedderSdk = new EmbedderSdk(embedderYamls);
if (embedderSdk.libraryMap.size() == 0) {
// There was no embedder file, or the file was empty, so used the default
// SDK.
resolvers.add(new DartUriResolver(sdkManager.getSdkForOptions(options)));
} else {
// The embedder uri resolver has mappings, use it instead of the default
// Dart SDK uri resolver.
resolvers.add(new DartUriResolver(sdk));
// The embedder file defines an alternate SDK, so use it.
List<String> paths = <String>[];
for (Folder folder in embedderYamls.keys) {
paths.add(folder
.getChildAssumingFile(EmbedderYamlLocator.EMBEDDER_FILE_NAME)
.path);
}
DartSdk dartSdk =
sdkManager.getSdk(new SdkDescription(paths, options), () {
embedderSdk.analysisOptions = options;
// TODO(brianwilkerson) Enable summary use after we have decided where
// summary files for embedder files will live.
embedderSdk.useSummary = false;
return embedderSdk;
});
resolvers.add(new DartUriResolver(dartSdk));
}
resolvers.addAll(packageUriResolvers);

View file

@ -448,7 +448,8 @@ class Driver implements ServerStarter {
//
socketServer = new SocketServer(
analysisServerOptions,
defaultSdkCreator,
new DartSdkManager(defaultSdkDirectory.getAbsolutePath(), useSummaries,
defaultSdkCreator),
defaultSdk,
service,
serverPlugin,

View file

@ -29,7 +29,7 @@ class SocketServer {
/**
* The function used to create a new SDK using the default SDK.
*/
final SdkCreator defaultSdkCreator;
final DartSdkManager sdkManager;
final DirectoryBasedDartSdk defaultSdk;
final InstrumentationService instrumentationService;
@ -51,7 +51,7 @@ class SocketServer {
SocketServer(
this.analysisServerOptions,
this.defaultSdkCreator,
this.sdkManager,
this.defaultSdk,
this.instrumentationService,
this.serverPlugin,
@ -97,7 +97,7 @@ class SocketServer {
index,
serverPlugin,
analysisServerOptions,
defaultSdkCreator,
sdkManager,
instrumentationService,
fileResolverProvider: fileResolverProvider,
packageResolverProvider: packageResolverProvider,

View file

@ -18,6 +18,7 @@ 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/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:linter/src/plugin/linter_plugin.dart';
import 'package:plugin/manager.dart';
import 'package:plugin/plugin.dart';
@ -129,7 +130,7 @@ class AbstractAnalysisTest {
index,
serverPlugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE);
}

View file

@ -19,6 +19,7 @@ import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:plugin/manager.dart';
import 'package:plugin/plugin.dart';
@ -144,7 +145,7 @@ import "../foo/foo.dart";
null,
plugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE,
rethrowExceptions: true);
processRequiredPlugins();

View file

@ -1820,9 +1820,8 @@ abstract class ContextManagerTest {
processRequiredPlugins();
resourceProvider = new MemoryResourceProvider();
packageMapProvider = new MockPackageMapProvider();
DartSdkManager sdkManager = new DartSdkManager((_) {
return new MockSdk();
});
DartSdkManager sdkManager =
new DartSdkManager('', false, (_) => new MockSdk());
manager = new ContextManagerImpl(
resourceProvider,
sdkManager,

View file

@ -13,6 +13,7 @@ import 'package:analysis_server/src/domain_analysis.dart';
import 'package:analysis_server/src/plugin/server_plugin.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:path/path.dart';
import 'package:plugin/manager.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -47,7 +48,7 @@ main() {
null,
serverPlugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE);
handler = new AnalysisDomainHandler(server);
});
@ -474,7 +475,7 @@ class AnalysisTestHelper {
null,
serverPlugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE);
handler = new AnalysisDomainHandler(server);
// listen for notifications

View file

@ -11,6 +11,7 @@ import 'package:analysis_server/src/plugin/server_plugin.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:plugin/manager.dart';
import 'package:plugin/plugin.dart';
import 'package:unittest/unittest.dart';
@ -53,7 +54,7 @@ main() {
null,
serverPlugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE);
handler = new DiagnosticDomainHandler(server);
});

View file

@ -16,6 +16,7 @@ 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/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:plugin/manager.dart';
@ -48,7 +49,7 @@ main() {
null,
serverPlugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE);
handler = new ExecutionDomainHandler(server);
});

View file

@ -11,6 +11,7 @@ import 'package:analysis_server/src/domain_server.dart';
import 'package:analysis_server/src/plugin/server_plugin.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:plugin/manager.dart';
import 'package:unittest/unittest.dart';
@ -37,7 +38,7 @@ main() {
null,
serverPlugin,
new AnalysisServerOptions(),
(_) => new MockSdk(),
new DartSdkManager('', false, (_) => new MockSdk()),
InstrumentationService.NULL_SERVICE);
handler = new ServerDomainHandler(server);
});

View file

@ -65,9 +65,8 @@ class SingleContextManagerTest {
packageResolver = new TestUriResolver();
_processRequiredPlugins();
DartSdkManager sdkManager = new DartSdkManager((_) {
return new MockSdk();
});
DartSdkManager sdkManager =
new DartSdkManager('', false, (_) => new MockSdk());
manager = new SingleContextManager(resourceProvider, sdkManager,
(_) => packageResolver, analysisFilesGlobs, new AnalysisOptionsImpl());
callbacks = new TestContextManagerCallbacks(resourceProvider);

View file

@ -116,7 +116,7 @@ class SocketServerTest {
new DirectoryBasedDartSdk(DirectoryBasedDartSdk.defaultSdkDirectory);
return new SocketServer(
new AnalysisServerOptions(),
sdkCreator,
new DartSdkManager('', false, sdkCreator),
sdkCreator(null),
InstrumentationService.NULL_SERVICE,
serverPlugin,

View file

@ -11,6 +11,7 @@ import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisContext, AnalysisOptions;
import 'package:analyzer/src/generated/source.dart' show Source;
import 'package:analyzer/src/generated/utilities_general.dart';
/**
* A function used to create a new DartSdk with the given [options]. If the
@ -95,21 +96,33 @@ abstract class DartSdk {
* produce different analysis results.
*/
class DartSdkManager {
/**
* The absolute path to the directory containing the default SDK.
*/
final String defaultSdkDirectory;
/**
* A flag indicating whether it is acceptable to use summaries when they are
* available.
*/
final bool canUseSummaries;
/**
* The function used to create new SDK's.
*/
final SdkCreator sdkCreator;
/**
* A table mapping (an encoding of) analysis options to the SDK that has been
* configured with those options.
* A table mapping (an encoding of) analysis options and SDK locations to the
* DartSdk from that location that has been configured with those options.
*/
Map<int, DartSdk> sdkMap = new HashMap<int, DartSdk>();
Map<SdkDescription, DartSdk> sdkMap = new HashMap<SdkDescription, DartSdk>();
/**
* Initialize a newly created manager.
*/
DartSdkManager(this.sdkCreator);
DartSdkManager(
this.defaultSdkDirectory, this.canUseSummaries, this.sdkCreator);
/**
* Return any SDK that has been created, or `null` if no SDKs have been
@ -122,14 +135,25 @@ class DartSdkManager {
return sdkMap.values.first;
}
/**
* Return the Dart SDK that is appropriate for the given analysis [options].
* If such an SDK has not yet been created, then the [sdkCreator] will be
* invoked to create it.
*/
DartSdk getSdk(SdkDescription description, DartSdk ifAbsent()) {
return sdkMap.putIfAbsent(description, ifAbsent);
}
/**
* Return the Dart SDK that is appropriate for the given analysis [options].
* If such an SDK has not yet been created, then the [sdkCreator] will be
* invoked to create it.
*/
DartSdk getSdkForOptions(AnalysisOptions options) {
int encoding = options.encodeCrossContextOptions();
return sdkMap.putIfAbsent(encoding, () => sdkCreator(options));
// TODO(brianwilkerson) Remove this method and the field sdkCreator.
SdkDescription description =
new SdkDescription(<String>[defaultSdkDirectory], options);
return getSdk(description, () => sdkCreator(options));
}
}
@ -173,6 +197,58 @@ class LibraryMap {
int size() => _libraryMap.length;
}
/**
* A description of a [DartSdk].
*/
class SdkDescription {
/**
* The paths to the files or directories that define the SDK.
*/
final List<String> paths;
/**
* The analysis options that will be used by the SDK's context.
*/
final AnalysisOptions options;
/**
* Initialize a newly created SDK description to describe an SDK based on the
* files or directories at the given [paths] that is analyzed using the given
* [options].
*/
SdkDescription(this.paths, this.options);
@override
int get hashCode {
int hashCode = options.encodeCrossContextOptions();
for (String path in paths) {
hashCode = JenkinsSmiHash.combine(hashCode, path.hashCode);
}
return JenkinsSmiHash.finish(hashCode);
}
@override
bool operator ==(Object other) {
if (other is SdkDescription) {
if (options.encodeCrossContextOptions() !=
other.options.encodeCrossContextOptions()) {
return false;
}
int length = paths.length;
if (other.paths.length != length) {
return false;
}
for (int i = 0; i < length; i++) {
if (other.paths[i] != paths[i]) {
return false;
}
}
return true;
}
return false;
}
}
class SdkLibrariesReader_LibraryBuilder extends RecursiveAstVisitor<Object> {
/**
* The prefix added to the name of a library to form the URI used in code to

View file

@ -0,0 +1,132 @@
// Copyright (c) 2014, 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 analyzer.test.generated.sdk_test;
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/scanner/reader.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:unittest/unittest.dart';
import '../reflective_tests.dart';
import '../src/context/mock_sdk.dart';
import '../utils.dart';
import 'test_support.dart';
main() {
initializeTestEnvironment();
runReflectiveTests(DartSdkManagerTest);
runReflectiveTests(SdkDescriptionTest);
}
@reflectiveTest
class DartSdkManagerTest extends EngineTestCase {
void test_anySdk() {
DartSdkManager manager =
new DartSdkManager('/a/b/c', false, _failIfCreated);
expect(manager.anySdk, isNull);
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription description = new SdkDescription(<String>['/c/d'], options);
DartSdk sdk = new MockSdk();
manager.getSdk(description, () => sdk);
expect(manager.anySdk, same(sdk));
}
void test_getSdk_differentDescriptors() {
DartSdkManager manager =
new DartSdkManager('/a/b/c', false, _failIfCreated);
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription description1 = new SdkDescription(<String>['/c/d'], options);
DartSdk sdk1 = new MockSdk();
DartSdk result1 = manager.getSdk(description1, () => sdk1);
expect(result1, same(sdk1));
SdkDescription description2 = new SdkDescription(<String>['/e/f'], options);
DartSdk sdk2 = new MockSdk();
DartSdk result2 = manager.getSdk(description2, () => sdk2);
expect(result2, same(sdk2));
manager.getSdk(description1, _failIfAbsent);
manager.getSdk(description2, _failIfAbsent);
}
void test_getSdk_sameDescriptor() {
DartSdkManager manager =
new DartSdkManager('/a/b/c', false, _failIfCreated);
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription description = new SdkDescription(<String>['/c/d'], options);
DartSdk sdk = new MockSdk();
DartSdk result = manager.getSdk(description, () => sdk);
expect(result, same(sdk));
manager.getSdk(description, _failIfAbsent);
}
DartSdk _failIfAbsent() {
fail('Use of ifAbsent function');
return null;
}
DartSdk _failIfCreated(_) {
fail('Use of sdkCreator');
return null;
}
}
@reflectiveTest
class SdkDescriptionTest extends EngineTestCase {
void test_equals_differentPaths_nested() {
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription left = new SdkDescription(<String>['/a/b/c'], options);
SdkDescription right = new SdkDescription(<String>['/a/b'], options);
expect(left == right, isFalse);
}
void test_equals_differentPaths_unrelated() {
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription left = new SdkDescription(<String>['/a/b/c'], options);
SdkDescription right = new SdkDescription(<String>['/d/e'], options);
expect(left == right, isFalse);
}
void test_equals_noPaths() {
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription left = new SdkDescription(<String>[], options);
SdkDescription right = new SdkDescription(<String>[], options);
expect(left == right, isTrue);
}
void test_equals_samePaths_differentOptions() {
String path = '/a/b/c';
AnalysisOptionsImpl leftOptions = new AnalysisOptionsImpl();
AnalysisOptionsImpl rightOptions = new AnalysisOptionsImpl();
rightOptions.strongMode = !leftOptions.strongMode;
SdkDescription left = new SdkDescription(<String>[path], leftOptions);
SdkDescription right = new SdkDescription(<String>[path], rightOptions);
expect(left == right, isFalse);
}
void test_equals_samePaths_sameOptions_multiple() {
String leftPath = '/a/b/c';
String rightPath = '/d/e';
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription left =
new SdkDescription(<String>[leftPath, rightPath], options);
SdkDescription right =
new SdkDescription(<String>[leftPath, rightPath], options);
expect(left == right, isTrue);
}
void test_equals_samePaths_sameOptions_single() {
String path = '/a/b/c';
AnalysisOptions options = new AnalysisOptionsImpl();
SdkDescription left = new SdkDescription(<String>[path], options);
SdkDescription right = new SdkDescription(<String>[path], options);
expect(left == right, isTrue);
}
}

View file

@ -28,6 +28,7 @@ import 'non_hint_code_test.dart' as non_hint_code_test;
import 'parser_test.dart' as parser_test;
import 'resolver_test.dart' as resolver_test;
import 'scanner_test.dart' as scanner_test;
import 'sdk_test.dart' as sdk_test;
import 'simple_resolver_test.dart' as simple_resolver_test;
import 'source_factory_test.dart' as source_factory_test;
import 'static_type_analyzer_test.dart' as static_type_analyzer_test;
@ -61,6 +62,7 @@ main() {
parser_test.main();
resolver_test.main();
scanner_test.main();
sdk_test.main();
simple_resolver_test.main();
source_factory_test.main();
static_type_analyzer_test.main();