| // 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. |
| |
| import 'package:analyzer/file_system/file_system.dart' as resource; |
| import 'package:analyzer/file_system/memory_file_system.dart' as resource; |
| import 'package:analyzer/src/context/cache.dart' |
| show AnalysisCache, CachePartition; |
| import 'package:analyzer/src/context/context.dart' show AnalysisContextImpl; |
| import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine; |
| import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary; |
| import 'package:analyzer/src/generated/source.dart' |
| show DartUriResolver, Source, SourceFactory; |
| import 'package:analyzer/src/summary/idl.dart'; |
| import 'package:analyzer/src/summary/summary_file_builder.dart'; |
| |
| /// Mock SDK for testing purposes. |
| class MockSdk implements DartSdk { |
| static const String librariesContent = r''' |
| const Map<String, LibraryInfo> libraries = const { |
| "async": const LibraryInfo("async/async.dart"), |
| "collection": const LibraryInfo("collection/collection.dart"), |
| "convert": const LibraryInfo("convert/convert.dart"), |
| "core": const LibraryInfo("core/core.dart"), |
| "io": const LibraryInfo("io/io.dart'"), |
| "html": const LibraryInfo( |
| "html/dartium/html_dartium.dart", |
| dart2jsPath: "html/dart2js/html_dart2js.dart"), |
| "math": const LibraryInfo("math/math.dart"), |
| }; |
| '''; |
| |
| static const _MockSdkLibrary LIB_CORE = |
| _MockSdkLibrary('dart:core', '/lib/core/core.dart', ''' |
| library dart.core; |
| |
| import 'dart:async'; |
| |
| class Object { |
| const Object(); |
| |
| bool operator ==(other) => identical(this, other); |
| String toString() => 'a string'; |
| int get hashCode => 0; |
| } |
| |
| class Function {} |
| class StackTrace {} |
| class Symbol {} |
| class Type {} |
| |
| abstract class Comparable<T> { |
| int compareTo(T other); |
| } |
| |
| abstract class String extends Object implements Comparable<String> { |
| external factory String.fromCharCodes(Iterable<int> charCodes, |
| [int start = 0, int end]); |
| bool get isEmpty => false; |
| bool get isNotEmpty => false; |
| int get length => 0; |
| bool contains(String other, [int startIndex = 0]); |
| int indexOf(String other, [int start]); |
| String toUpperCase(); |
| List<int> get codeUnits; |
| } |
| |
| class bool extends Object {} |
| abstract class num extends Object implements Comparable<num> { |
| bool operator <(num other); |
| bool operator <=(num other); |
| bool operator >(num other); |
| bool operator >=(num other); |
| num operator +(num other); |
| num operator -(num other); |
| num operator *(num other); |
| num operator /(num other); |
| int toInt(); |
| num abs(); |
| int round(); |
| } |
| abstract class int extends num { |
| bool get isEven => false; |
| int operator -(); |
| external static int parse(String source, |
| { int radix, |
| int onError(String source) }); |
| } |
| class double extends num {} |
| class DateTime extends Object { |
| DateTime.now() {} |
| bool isBefore(DateTime other) => true; |
| } |
| class Null extends Object {} |
| |
| class Deprecated extends Object { |
| final String expires; |
| const Deprecated(this.expires); |
| } |
| const Object deprecated = const Deprecated("next release"); |
| |
| class Error { |
| Error(); |
| static String safeToString(Object object); |
| external static String _stringToSafeString(String string); |
| external static String _objectToString(Object object); |
| external StackTrace get stackTrace; |
| } |
| |
| class Iterator<E> { |
| bool moveNext(); |
| E get current; |
| } |
| |
| abstract class Iterable<E> { |
| const Iterable(); |
| const factory Iterable.empty() = Iterable; |
| Iterator<E> get iterator; |
| bool contains(Object element); |
| E firstWhere(bool test(E element), { E orElse()}); |
| bool get isEmpty; |
| bool get isNotEmpty; |
| E get first; |
| E get last; |
| int get length; |
| Set<E> toSet() => new Set<E>.from(this); |
| } |
| |
| abstract class List<E> implements Iterable<E> { |
| /* external */ factory List([int length]); |
| /* external */ factory List.filled(int length, E fill, {bool growable: false}); |
| void add(E value); |
| void addAll(Iterable<E> iterable); |
| E operator [](int index); |
| void operator []=(int index, E value); |
| Iterator<E> get iterator => null; |
| void clear(); |
| int indexOf(Object element); |
| bool get isEmpty; |
| bool get isNotEmpty; |
| Set<E> toSet() => new Set<E>.from(this); |
| } |
| |
| abstract class Map<K, V> extends Object { |
| /* external */ factory Map(); |
| factory Map.from(Map other) = LinkedHashMap<K, V>.from; |
| factory Map.fromIterable(Iterable iterable, |
| {K key(element), V value(element)}) = LinkedHashMap<K, V>.fromIterable; |
| factory Map.fromIterables(Iterable<K> keys, Iterable<V> values) = |
| LinkedHashMap<K, V>.fromIterables; |
| factory Map.of(Map<K, V> other) = LinkedHashMap<K, V>.of; |
| factory Map.identity() = LinkedHashMap<K, V>.identity; |
| /* external */ factory Map.unmodifiable(Map other); |
| Iterable<K> get keys; |
| bool get isEmpty; |
| bool get isNotEmpty; |
| int get length; |
| V putIfAbsent(K key, V ifAbsent()); |
| } |
| |
| abstract class Set<E> implements Iterable<E> { |
| Set<R> cast<R>(); |
| factory Set() = LinkedHashSet<E>; |
| factory Set.identity() = LinkedHashSet<E>.identity; |
| factory Set.from(Iterable elements) = LinkedHashSet<E>.from; |
| factory Set.of(Iterable<E> elements) = LinkedHashSet<E>.of; |
| } |
| |
| external bool identical(Object a, Object b); |
| |
| void print(Object object) {} |
| |
| class _Override { |
| const _Override(); |
| } |
| const Object override = const _Override(); |
| |
| |
| abstract class RegExp { |
| external factory RegExp(String source, {bool multiLine: false, |
| bool caseSensitive: true}); |
| } |
| '''); |
| |
| static _MockSdkLibrary LIB_ASYNC = |
| _MockSdkLibrary('dart:async', '/lib/async/async.dart', ''' |
| library dart.async; |
| |
| import 'dart:math'; |
| |
| class Stream<T> {} |
| abstract class StreamTransformer<S, T> {} |
| |
| class Future<T> { |
| factory Future.delayed(Duration duration, [T computation()]) => null; |
| factory Future.value([FutureOr<T> value]) => null; |
| static Future wait(List<Future> futures) => null; |
| } |
| |
| class FutureOr<T> {} |
| ''', <_MockSdkFile>[ |
| _MockSdkFile('/lib/async/stream.dart', r''' |
| part of dart.async; |
| ''') |
| ]); |
| |
| static _MockSdkLibrary LIB_COLLECTION = |
| _MockSdkLibrary('dart:collection', '/lib/collection/collection.dart', ''' |
| library dart.collection; |
| |
| abstract class HashMap<K, V> implements Map<K, V> {} |
| abstract class LinkedHashMap<K, V> implements Map<K, V> { |
| /* external */ factory LinkedHashMap( |
| {bool equals(K key1, K key2), |
| int hashCode(K key), |
| bool isValidKey(potentialKey)}); |
| /* external */ factory LinkedHashMap.identity(); |
| factory LinkedHashMap.from(Map other); |
| factory LinkedHashMap.fromIterable(Iterable iterable, |
| {K key(element), V value(element)}); |
| factory LinkedHashMap.fromIterables(Iterable<K> keys, Iterable<V> values); |
| factory LinkedHashMap.of(Map<K, V> other) => |
| new LinkedHashMap<K, V>()..addAll(other); |
| } |
| class LinkedHashSet<E> implements Set<E> { |
| /* external factory */ LinkedHashSet( |
| {bool equals(E e1, E e2), |
| int hashCode(E e), |
| bool isValidKey(potentialKey)}); |
| /* external factory */ LinkedHashSet.identity(); |
| factory LinkedHashSet.from(Iterable elements); |
| |
| factory LinkedHashSet.of(Iterable<E> elements) => |
| new LinkedHashSet<E>()..addAll(elements); |
| } |
| '''); |
| |
| static _MockSdkLibrary LIB_CONVERT = |
| _MockSdkLibrary('dart:convert', '/lib/convert/convert.dart', ''' |
| library dart.convert; |
| |
| import 'dart:async'; |
| |
| abstract class Converter<S, T> implements StreamTransformer {} |
| class JsonDecoder extends Converter<String, Object> {} |
| '''); |
| |
| static _MockSdkLibrary LIB_IO = |
| _MockSdkLibrary('dart:io', '/lib/io/io.dart', ''' |
| library dart.io; |
| |
| abstract class Directory implements FileSystemEntity { |
| factory Directory(String path) => null; |
| |
| Future<bool> exists() async => true; |
| bool existsSync() => true; |
| |
| Future<FileStat> stat() async => null; |
| FileStat statSync() => null; |
| } |
| |
| abstract class File implements FileSystemEntity { |
| factory File(String path) => null; |
| |
| Future<DateTime> lastModified(); |
| DateTime lastModifiedSync(); |
| |
| Future<bool> exists() async => true; |
| bool existsSync() => true; |
| |
| Future<FileStat> stat() async => null; |
| FileStat statSync() => null; |
| } |
| |
| abstract class FileSystemEntity { |
| static Future<bool> isDirectory(String path) => true; |
| static bool isDirectorySync(String path) => true; |
| |
| static Future<bool> isFile(String path) => true; |
| static bool isFileSync(String path) => true; |
| |
| static Future<bool> isLink(String path) => true; |
| static bool isLinkSync(String path) => true; |
| |
| static Future<FileSystemEntityType> type( |
| String path, {bool followLinks: true}) async => null; |
| static FileSystemEntityType typeSync( |
| String path, {bool followLinks: true}) => null; |
| } |
| '''); |
| |
| static _MockSdkLibrary LIB_MATH = |
| _MockSdkLibrary('dart:math', '/lib/math/math.dart', ''' |
| library dart.math; |
| const double E = 2.718281828459045; |
| const double PI = 3.1415926535897932; |
| const double LN10 = 2.302585092994046; |
| num min(num a, num b) => 0; |
| num max(num a, num b) => 0; |
| external double cos(num x); |
| external double sin(num x); |
| external double sqrt(num x); |
| class Random { |
| bool nextBool() => true; |
| double nextDouble() => 2.0; |
| int nextInt() => 1; |
| } |
| '''); |
| |
| static _MockSdkLibrary LIB_HTML = |
| _MockSdkLibrary('dart:html', '/lib/html/dartium/html_dartium.dart', ''' |
| library dart.html; |
| export 'dart:_dom_html'; |
| class HtmlElement {} |
| '''); |
| static _MockSdkLibrary LIB_DOM_HTML = _MockSdkLibrary( |
| 'dart:_dom_html', '/lib/html/dartium/dom_html_dartium.dart', ''' |
| library dart.dom.html; |
| class AnchorElement extends HtmlElement { |
| String href; |
| } |
| class EmbedElement extends HtmlEment { |
| String src; |
| } |
| class IFrameElement extends HtmlEment { |
| String src; |
| } |
| class ImageElement extends HtmlEment { |
| String src; |
| } |
| class ScriptElement { |
| String src; |
| String type; |
| } |
| '''); |
| |
| static List<SdkLibrary> LIBRARIES = [ |
| LIB_CORE, |
| LIB_ASYNC, |
| LIB_COLLECTION, |
| LIB_CONVERT, |
| LIB_IO, |
| LIB_MATH, |
| LIB_HTML, |
| LIB_DOM_HTML, |
| ]; |
| |
| /// The cached linked bundle of the SDK. |
| PackageBundle _bundle; |
| |
| final resource.MemoryResourceProvider provider; |
| |
| /// The [AnalysisContextImpl] which is used for all of the sources. |
| AnalysisContextImpl _analysisContext; |
| |
| MockSdk(this.provider) { |
| LIBRARIES.forEach((SdkLibrary library) { |
| if (library is _MockSdkLibrary) { |
| provider.newFile(provider.convertPath(library.path), library.content); |
| library.parts.forEach((file) { |
| provider.newFile(provider.convertPath(file.path), file.content); |
| }); |
| } |
| }); |
| provider.newFile( |
| provider.convertPath( |
| '/lib/_internal/sdk_library_metadata/lib/libraries.dart'), |
| librariesContent); |
| } |
| |
| @override |
| AnalysisContextImpl get context { |
| if (_analysisContext == null) { |
| _analysisContext = _SdkAnalysisContext(this); |
| SourceFactory factory = SourceFactory([DartUriResolver(this)]); |
| _analysisContext.sourceFactory = factory; |
| } |
| return _analysisContext; |
| } |
| |
| @override |
| List<SdkLibrary> get sdkLibraries => LIBRARIES; |
| |
| @override |
| String get sdkVersion => throw unimplemented; |
| |
| UnimplementedError get unimplemented => UnimplementedError(); |
| |
| @override |
| List<String> get uris => |
| sdkLibraries.map((SdkLibrary library) => library.shortName).toList(); |
| |
| @override |
| Source fromFileUri(Uri uri) { |
| String filePath = provider.pathContext.fromUri(uri); |
| if (!filePath.startsWith(provider.convertPath('/lib/'))) { |
| return null; |
| } |
| for (SdkLibrary library in sdkLibraries) { |
| String libraryPath = provider.convertPath(library.path); |
| if (filePath == libraryPath) { |
| try { |
| resource.File file = provider.getFile(filePath); |
| Uri dartUri = Uri.parse(library.shortName); |
| return file.createSource(dartUri); |
| } on FormatException { |
| return null; |
| } |
| } |
| String libraryRootPath = provider.pathContext.dirname(libraryPath) + |
| provider.pathContext.separator; |
| if (filePath.startsWith(libraryRootPath)) { |
| String pathInLibrary = filePath.substring(libraryRootPath.length); |
| String uriStr = '${library.shortName}/$pathInLibrary'; |
| try { |
| resource.File file = provider.getFile(filePath); |
| Uri dartUri = Uri.parse(uriStr); |
| return file.createSource(dartUri); |
| } on FormatException { |
| return null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| @override |
| PackageBundle getLinkedBundle() { |
| if (_bundle == null) { |
| resource.File summaryFile = |
| provider.getFile(provider.convertPath('/lib/_internal/strong.sum')); |
| List<int> bytes; |
| if (summaryFile.exists) { |
| bytes = summaryFile.readAsBytesSync(); |
| } else { |
| bytes = _computeLinkedBundleBytes(); |
| } |
| _bundle = PackageBundle.fromBuffer(bytes); |
| } |
| return _bundle; |
| } |
| |
| @override |
| SdkLibrary getSdkLibrary(String dartUri) { |
| for (SdkLibrary library in LIBRARIES) { |
| if (library.shortName == dartUri) { |
| return library; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| Source mapDartUri(String dartUri) { |
| Map<String, String> uriToPath = { |
| 'dart:core': '/lib/core/core.dart', |
| 'dart:html': '/lib/html/dartium/html_dartium.dart', |
| 'dart:_dom_html': '/lib/html/dartium/dom_html_dartium.dart', |
| 'dart:async': '/lib/async/async.dart', |
| 'dart:async/stream.dart': '/lib/async/stream.dart', |
| 'dart:collection': '/lib/collection/collection.dart', |
| 'dart:convert': '/lib/convert/convert.dart', |
| 'dart:io': '/lib/io/io.dart', |
| 'dart:math': '/lib/math/math.dart' |
| }; |
| |
| String path = uriToPath[dartUri]; |
| if (path != null) { |
| resource.File file = |
| provider.getResource(provider.convertPath(path)) as resource.File; |
| Uri uri = Uri(scheme: 'dart', path: dartUri.substring(5)); |
| return file.createSource(uri); |
| } |
| |
| // If we reach here then we tried to use a dartUri that's not in the |
| // table above. |
| return null; |
| } |
| |
| /// Compute the bytes of the linked bundle associated with this SDK. |
| List<int> _computeLinkedBundleBytes() { |
| List<Source> librarySources = sdkLibraries |
| .map((SdkLibrary library) => mapDartUri(library.shortName)) |
| .toList(); |
| return SummaryBuilder(librarySources, context).build(); |
| } |
| } |
| |
| class _MockSdkFile { |
| final String path; |
| final String content; |
| |
| _MockSdkFile(this.path, this.content); |
| } |
| |
| class _MockSdkLibrary implements SdkLibrary { |
| @override |
| final String shortName; |
| @override |
| final String path; |
| final String content; |
| final List<_MockSdkFile> parts; |
| |
| const _MockSdkLibrary(this.shortName, this.path, this.content, |
| [this.parts = const <_MockSdkFile>[]]); |
| |
| @override |
| String get category => throw UnimplementedError(); |
| |
| @override |
| bool get isDart2JsLibrary => throw UnimplementedError(); |
| |
| @override |
| bool get isDocumented => throw UnimplementedError(); |
| |
| @override |
| bool get isImplementation => throw UnimplementedError(); |
| |
| @override |
| bool get isInternal => shortName.startsWith('dart:_'); |
| |
| @override |
| bool get isShared => throw UnimplementedError(); |
| |
| @override |
| bool get isVmLibrary => throw UnimplementedError(); |
| } |
| |
| /// An [AnalysisContextImpl] that only contains sources for a Dart SDK. |
| class _SdkAnalysisContext extends AnalysisContextImpl { |
| final DartSdk sdk; |
| |
| _SdkAnalysisContext(this.sdk); |
| |
| @override |
| AnalysisCache createCacheFromSourceFactory(SourceFactory factory) { |
| if (factory == null) { |
| return super.createCacheFromSourceFactory(factory); |
| } |
| return AnalysisCache( |
| <CachePartition>[AnalysisEngine.instance.partitionManager.forSdk(sdk)]); |
| } |
| } |