Package map processing abstraction.

Babysteps towards a cleaner context creation story.

Comments welcome!

BUG=
R=brianwilkerson@google.com

Review URL: https://codereview.chromium.org/2043963004 .
This commit is contained in:
pq 2016-06-07 14:01:18 -07:00
parent e3e72aeb01
commit 995a19b26f
3 changed files with 219 additions and 0 deletions

View file

@ -0,0 +1,136 @@
// Copyright (c) 2016, 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.src.context_factory;
import 'dart:convert';
import 'dart:core' hide Resource;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:yaml/yaml.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'dart:io' as io;
/// (Placeholder)
abstract class ContextFactory {
/// Create an analysis context for the given [source] directory or file, with
/// the given [defaultOptions].
AnalysisContext createContext(
io.FileSystemEntity source, AnalysisOptions defaultOptions);
}
/// Processes package maps, extracting SDK embedders and extenders, creating a
/// consolidated [libraryMap].
class PackageMapProcessor {
static const String _EMBEDDED_LIB_MAP_KEY = 'embedded_libs';
static const String _EMBEDDER_FILE_NAME = '_embedder.yaml';
static const String _SDK_EXT_NAME = '_sdkext';
/// Map of processed embedder libraries.
final LibraryMap embeddedLibraries = new LibraryMap();
/// Map of processed SDK extension libraries.
final LibraryMap extendedLibraries = new LibraryMap();
/// Combined map of processed libraries.
LibraryMap get libraryMap {
LibraryMap libraryMap = new LibraryMap();
// Add extenders first, allowing for overwrite by embedders who take precedence.
for (String uri in extendedLibraries.uris) {
libraryMap.setLibrary(uri, extendedLibraries.getLibrary(uri));
}
for (String uri in embeddedLibraries.uris) {
libraryMap.setLibrary(uri, embeddedLibraries.getLibrary(uri));
}
return libraryMap;
}
/// Create a processor for the given [packageMap].
PackageMapProcessor(Map<String, List<Folder>> packageMap) {
packageMap?.forEach(_processPackage);
}
/// Whether the package map contains an SDK embedder.
bool get hasEmbedder => embeddedLibraries.size() > 0;
/// Whether the package map contains an SDK extension.
bool get hasSdkExtension => extendedLibraries.size() > 0;
void _processEmbedderYaml(String embedderYaml, Folder libDir) {
try {
YamlNode map = loadYaml(embedderYaml);
if (map is YamlMap) {
YamlNode embedded_libs = map[_EMBEDDED_LIB_MAP_KEY];
if (embedded_libs is YamlMap) {
embedded_libs.forEach(
(k, v) => _processMapping(embeddedLibraries, k, v, libDir));
}
}
} catch (_) {
// Ignored.
}
}
void _processMapping(
LibraryMap libraryMap, String name, String file, Folder libDir) {
if (!_hasDartPrefix(name)) {
// SDK libraries must begin with 'dart:'.
return;
}
if (libraryMap.getLibrary(name) != null) {
// Libraries can't be redefined.
return;
}
String libPath = libDir.canonicalizePath(file);
SdkLibraryImpl library = new SdkLibraryImpl(name)..path = libPath;
libraryMap.setLibrary(name, library);
}
void _processPackage(String name, List<Folder> libDirs) {
for (Folder libDir in libDirs) {
String embedderYaml = _readEmbedderYaml(libDir);
if (embedderYaml != null) {
_processEmbedderYaml(embedderYaml, libDir);
}
String sdkExt = _readDotSdkExt(libDir);
if (sdkExt != null) {
_processSdkExt(sdkExt, libDir);
}
}
}
void _processSdkExt(String sdkExtJSON, Folder libDir) {
try {
var sdkExt = JSON.decode(sdkExtJSON);
if (sdkExt is Map) {
sdkExt.forEach(
(k, v) => _processMapping(extendedLibraries, k, v, libDir));
}
} catch (_) {
// Ignored.
}
}
static bool _hasDartPrefix(String uri) =>
uri.startsWith(DartSdk.DART_LIBRARY_PREFIX);
static String _readDotSdkExt(Folder libDir) =>
_safeRead(libDir.getChild(_SDK_EXT_NAME));
static String _readEmbedderYaml(Folder libDir) =>
_safeRead(libDir.getChild(_EMBEDDER_FILE_NAME));
static String _safeRead(Resource file) {
try {
if (file is File) {
return file.readAsStringSync();
}
} on FileSystemException {
// File can't be read.
}
return null;
}
}

View file

@ -37,6 +37,11 @@ abstract class DartSdk {
*/
static const String DART_HTML = "dart:html";
/**
* The prefix shared by all dart library URIs.
*/
static const String DART_LIBRARY_PREFIX = "dart:";
/**
* The version number that is returned when the real version number could not
* be determined.

View file

@ -0,0 +1,78 @@
// Copyright (c) 2016, 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.src.context.context_factory_test;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/context/context_factory.dart';
import 'package:unittest/unittest.dart';
import '../../reflective_tests.dart';
import '../../utils.dart';
main() {
initializeTestEnvironment();
runReflectiveTests(PackageMapProcessorTest);
}
@reflectiveTest
class PackageMapProcessorTest {
static MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
static Folder empty = resourceProvider.newFolder('/empty');
static Folder tmp_sdk_ext = resourceProvider.newFolder('/tmp_sdk_ext');
static Folder tmp_embedder = resourceProvider.newFolder('/tmp_embedder');
static Map<String, List<Folder>> packageMap = <String, List<Folder>>{
'empty': [empty],
'tmp_embedder': [tmp_embedder],
'tmp_sdk_ext': [tmp_sdk_ext]
};
void test_basic_processing() {
resourceProvider.newFile(
'/tmp_sdk_ext/_sdkext',
r'''
{
"dart:ui": "ui.dart"
}''');
resourceProvider.newFile(
'/tmp_embedder/_embedder.yaml',
r'''
embedded_libs:
"dart:core" : "core.dart"
"dart:fox": "slippy.dart"
"dart:bear": "grizzly.dart"
"dart:relative": "../relative.dart"
"dart:deep": "deep/directory/file.dart"
''');
PackageMapProcessor proc = new PackageMapProcessor(packageMap);
expect(proc.embeddedLibraries.size(), 5);
expect(proc.extendedLibraries.size(), 1);
}
void test_extenders_do_not_override() {
resourceProvider.newFile(
'/tmp_sdk_ext/_sdkext',
r'''
{
"dart:ui": "ui2.dart"
}''');
resourceProvider.newFile(
'/tmp_embedder/_embedder.yaml',
r'''
embedded_libs:
"dart:core" : "core.dart"
"dart:ui": "ui.dart"
''');
PackageMapProcessor proc = new PackageMapProcessor(packageMap);
expect(proc.embeddedLibraries.size(), 2);
expect(proc.extendedLibraries.size(), 1);
expect(proc.libraryMap.size(), 2);
expect(proc.libraryMap.getLibrary('dart:ui').path, '/tmp_embedder/ui.dart');
}
}