Parse the 'patches' argument in SDK libraries declarations.

R=brianwilkerson@google.com, paulberry@google.com
BUG=

Review URL: https://codereview.chromium.org/2408863002 .
This commit is contained in:
Konstantin Shcheglov 2016-10-11 09:19:27 -07:00
parent 2495f6a060
commit 1a70ff791a
4 changed files with 229 additions and 2 deletions

View file

@ -397,4 +397,7 @@ class MockSdkLibrary implements SdkLibrary {
bool get isVmLibrary => throw unimplemented;
UnimplementedError get unimplemented => new UnimplementedError();
@override
List<String> getPatches(int platform) => const <String>[];
}

View file

@ -7,6 +7,7 @@ library analyzer.src.generated.sdk;
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisContext, AnalysisOptions, AnalysisOptionsImpl;
@ -274,6 +275,7 @@ class SdkDescription {
buffer.write(optionName);
needsSeparator = true;
}
for (String path in paths) {
add(path);
}
@ -307,6 +309,11 @@ class SdkLibrariesReader_LibraryBuilder extends RecursiveAstVisitor<Object> {
*/
static String _DART2JS_PATH = "dart2jsPath";
/**
* The name of the `dart2js` platform.
*/
static String _DART2JS_PLATFORM = 'DART2JS_PLATFORM';
/**
* The name of the optional parameter used to indicate whether the library is
* documented.
@ -319,6 +326,12 @@ class SdkLibrariesReader_LibraryBuilder extends RecursiveAstVisitor<Object> {
*/
static String _CATEGORIES = "categories";
/**
* The name of the optional parameter used to specify the patches for
* the library.
*/
static String _PATCHES = "patches";
/**
* The name of the optional parameter used to specify the platforms on which
* the library can be used.
@ -397,6 +410,28 @@ class SdkLibrariesReader_LibraryBuilder extends RecursiveAstVisitor<Object> {
library._implementation = (expression as BooleanLiteral).value;
} else if (name == _DOCUMENTED) {
library.documented = (expression as BooleanLiteral).value;
} else if (name == _PATCHES) {
if (expression is MapLiteral) {
expression.entries.forEach((MapLiteralEntry entry) {
int platforms = _convertPlatforms(entry.key);
Expression pathsListLiteral = entry.value;
if (pathsListLiteral is ListLiteral) {
List<String> paths = <String>[];
pathsListLiteral.elements.forEach((Expression pathExpr) {
if (pathExpr is SimpleStringLiteral) {
paths.add(pathExpr.value);
} else {
throw new ArgumentError(
'The "patch" argument items must be simple strings.');
}
});
library.setPatchPaths(platforms, paths);
} else {
throw new ArgumentError(
'The "patch" argument values must be list literals.');
}
});
}
} else if (name == _PLATFORMS) {
if (expression is SimpleIdentifier) {
String identifier = expression.name;
@ -417,6 +452,43 @@ class SdkLibrariesReader_LibraryBuilder extends RecursiveAstVisitor<Object> {
}
return null;
}
/**
* Return the platform constant value for the given [expr].
* Throw [ArgumentError] if not a valid platform name given.
*/
static int _convertPlatform(Expression expr) {
if (expr is SimpleIdentifier) {
String name = expr.name;
if (name == _DART2JS_PLATFORM) {
return SdkLibraryImpl.DART2JS_PLATFORM;
}
if (name == _VM_PLATFORM) {
return SdkLibraryImpl.VM_PLATFORM;
}
throw new ArgumentError('Invalid platform name: $name');
}
throw new ArgumentError('Invalid platform type: ${expr.runtimeType}');
}
/**
* Return the platforms combination value for the [expr], which should be
* either `name1 | name2` or `name`. Throw [ArgumentError] if any of the
* names is not a valid platform name.
*/
static int _convertPlatforms(Expression expr) {
if (expr is BinaryExpression) {
TokenType operator = expr.operator?.type;
if (operator == TokenType.BAR) {
return _convertPlatforms(expr.leftOperand) |
_convertPlatforms(expr.rightOperand);
} else {
throw new ArgumentError('Invalid platforms combination: $operator');
}
} else {
return _convertPlatform(expr);
}
}
}
/**
@ -469,6 +541,12 @@ abstract class SdkLibrary {
* including `dart:`.
*/
String get shortName;
/**
* Return the list of paths to the patch files that should be applied
* to this library for the given [platform], not `null`.
*/
List<String> getPatches(int platform);
}
/**
@ -520,6 +598,14 @@ class SdkLibraryImpl implements SdkLibrary {
*/
int _platforms = 0;
/**
* The mapping from the platform combination to the list of paths (relative
* to the `sdk/lib` folder) of patches that should be applied to this library
* on every platform in the combination.
*/
final Map<int, List<String>> _platformsToPatchPaths =
new HashMap<int, List<String>>();
/**
* Initialize a newly created library to represent the library with the given
* [name].
@ -551,6 +637,17 @@ class SdkLibraryImpl implements SdkLibrary {
@override
bool get isVmLibrary => (_platforms & VM_PLATFORM) != 0;
@override
List<String> getPatches(int platform) {
List<String> paths = <String>[];
_platformsToPatchPaths.forEach((int platforms, List<String> value) {
if ((platforms & platform) != 0) {
paths.addAll(value);
}
});
return paths;
}
/**
* Record that this library can be compiled to JavaScript by dart2js.
*/
@ -558,6 +655,14 @@ class SdkLibraryImpl implements SdkLibrary {
_platforms |= DART2JS_PLATFORM;
}
/**
* Add a new patch with the given [path] that should be applied for the
* given [platforms].
*/
void setPatchPaths(int platforms, List<String> paths) {
_platformsToPatchPaths[platforms] = paths;
}
/**
* Record that this library can be run on the VM.
*/

View file

@ -498,6 +498,9 @@ class _MockSdkLibrary implements SdkLibrary {
@override
bool get isVmLibrary => throw new UnimplementedError();
@override
List<String> getPatches(int platform) => const <String>[];
}
/**

View file

@ -25,7 +25,8 @@ main() {
defineReflectiveTests(EmbedderSdkTest);
defineReflectiveTests(FolderBasedDartSdkTest);
defineReflectiveTests(SdkExtensionFinderTest);
defineReflectiveTests(SDKLibrariesReaderTest);
defineReflectiveTests(SdkLibrariesReaderTest);
defineReflectiveTests(SdkLibraryImplTest);
});
}
@ -391,7 +392,7 @@ class SdkExtensionFinderTest {
}
@reflectiveTest
class SDKLibrariesReaderTest extends EngineTestCase {
class SdkLibrariesReaderTest extends EngineTestCase {
/**
* The resource provider used by these tests.
*/
@ -473,4 +474,119 @@ final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
expect(second.isImplementation, true);
expect(second.isVmLibrary, false);
}
void test_readFrom_patches() {
LibraryMap libraryMap = new SdkLibrariesReader(false).readFromFile(
resourceProvider.getFile('/libs.dart'),
r'''
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'foo' : const LibraryInfo(
'foo/foo.dart',
patches: {
DART2JS_PLATFORM | VM_PLATFORM: ['a', 'b'],
DART2JS_PLATFORM: ['c', 'd'],
VM_PLATFORM: ['e']}),
};''');
expect(libraryMap, isNotNull);
expect(libraryMap.size(), 1);
SdkLibrary library = libraryMap.getLibrary('dart:foo');
expect(library, isNotNull);
expect(library.path, 'foo/foo.dart');
expect(library.shortName, 'dart:foo');
expect(library.getPatches(SdkLibraryImpl.DART2JS_PLATFORM),
unorderedEquals(['a', 'b', 'c', 'd']));
expect(library.getPatches(SdkLibraryImpl.VM_PLATFORM),
unorderedEquals(['a', 'b', 'e']));
}
void test_readFrom_patches_invalid_notList() {
expect(() {
new SdkLibrariesReader(false).readFromFile(
resourceProvider.getFile('/libs.dart'),
r'''
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'foo' : const LibraryInfo(
'foo/foo.dart',
patches: {
VM_PLATFORM: 'X'}),
};''');
}, throwsArgumentError);
}
void test_readFrom_patches_invalid_notString_inList() {
expect(() {
new SdkLibrariesReader(false).readFromFile(
resourceProvider.getFile('/libs.dart'),
r'''
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'foo' : const LibraryInfo(
'foo/foo.dart',
patches: {
VM_PLATFORM: [42]}),
};''');
}, throwsArgumentError);
}
void test_readFrom_patches_invalid_platformCombinator() {
expect(() {
new SdkLibrariesReader(false).readFromFile(
resourceProvider.getFile('/libs.dart'),
r'''
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'foo' : const LibraryInfo(
'foo/foo.dart',
patches: {
DART2JS_PLATFORM + VM_PLATFORM: ['X']}),
};''');
}, throwsArgumentError);
}
void test_readFrom_patches_invalid_unknownPlatform() {
expect(() {
new SdkLibrariesReader(false).readFromFile(
resourceProvider.getFile('/libs.dart'),
r'''
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'foo' : const LibraryInfo(
'foo/foo.dart',
patches: {
MY_UNKNOWN_PLATFORM: ['foo/bar_patch.dart']}),
};''');
}, throwsArgumentError);
}
void test_readFrom_patches_no() {
LibraryMap libraryMap = new SdkLibrariesReader(false).readFromFile(
resourceProvider.getFile('/libs.dart'),
r'''
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'my' : const LibraryInfo('my/my.dart')
};''');
expect(libraryMap, isNotNull);
expect(libraryMap.size(), 1);
SdkLibrary library = libraryMap.getLibrary('dart:my');
expect(library, isNotNull);
expect(library.path, 'my/my.dart');
expect(library.shortName, 'dart:my');
expect(library.getPatches(SdkLibraryImpl.VM_PLATFORM), isEmpty);
expect(library.getPatches(SdkLibraryImpl.DART2JS_PLATFORM), isEmpty);
}
}
@reflectiveTest
class SdkLibraryImplTest extends EngineTestCase {
void test_patches() {
SdkLibraryImpl library = new SdkLibraryImpl('dart:foo');
// Set patches.
library.setPatchPaths(
SdkLibraryImpl.DART2JS_PLATFORM | SdkLibraryImpl.VM_PLATFORM,
['a', 'b']);
library.setPatchPaths(SdkLibraryImpl.DART2JS_PLATFORM, ['c', 'd']);
library.setPatchPaths(SdkLibraryImpl.VM_PLATFORM, ['e']);
// Get patches.
expect(library.getPatches(SdkLibraryImpl.DART2JS_PLATFORM),
unorderedEquals(['a', 'b', 'c', 'd']));
expect(library.getPatches(SdkLibraryImpl.VM_PLATFORM),
unorderedEquals(['a', 'b', 'e']));
}
}