OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library analyzer.source.embedder; | 5 library analyzer.source.embedder; |
6 | 6 |
7 import 'dart:collection' show HashMap; | 7 import 'dart:collection' show HashMap; |
8 import 'dart:core' hide Resource; | 8 import 'dart:core' hide Resource; |
9 | 9 |
10 import 'package:analyzer/file_system/file_system.dart'; | 10 import 'package:analyzer/file_system/file_system.dart'; |
11 import 'package:analyzer/source/package_map_provider.dart' | 11 import 'package:analyzer/source/package_map_provider.dart' |
12 show PackageMapProvider; | 12 show PackageMapProvider; |
13 import 'package:analyzer/src/context/context.dart'; | 13 import 'package:analyzer/src/context/context.dart'; |
14 import 'package:analyzer/src/generated/engine.dart'; | 14 import 'package:analyzer/src/generated/engine.dart'; |
15 import 'package:analyzer/src/generated/java_core.dart'; | 15 import 'package:analyzer/src/generated/java_core.dart'; |
16 import 'package:analyzer/src/generated/java_engine.dart'; | 16 import 'package:analyzer/src/generated/java_engine.dart'; |
17 import 'package:analyzer/src/generated/java_io.dart' show JavaFile; | 17 import 'package:analyzer/src/generated/java_io.dart' show JavaFile; |
18 import 'package:analyzer/src/generated/sdk.dart'; | 18 import 'package:analyzer/src/generated/sdk.dart'; |
19 import 'package:analyzer/src/generated/source.dart'; | 19 import 'package:analyzer/src/generated/source.dart'; |
20 import 'package:analyzer/src/generated/source_io.dart' show FileBasedSource; | 20 import 'package:analyzer/src/generated/source_io.dart' show FileBasedSource; |
21 import 'package:yaml/yaml.dart'; | 21 import 'package:yaml/yaml.dart'; |
22 | 22 |
23 const String _DART_COLON_PREFIX = 'dart:'; | 23 const String _DART_COLON_PREFIX = 'dart:'; |
24 const String _EMBEDDED_LIB_MAP_KEY = 'embedded_libs'; | 24 const String _EMBEDDED_LIB_MAP_KEY = 'embedded_libs'; |
25 | 25 |
26 /// Check if this map defines embedded libraries. | 26 /// Check if this map defines embedded libraries. |
27 bool definesEmbeddedLibs(Map map) => map[_EMBEDDED_LIB_MAP_KEY] != null; | 27 bool definesEmbeddedLibs(Map map) => map[_EMBEDDED_LIB_MAP_KEY] != null; |
28 | 28 |
| 29 /// An SDK backed by URI mappings derived from an `_embedder.yaml` file. |
29 class EmbedderSdk implements DartSdk { | 30 class EmbedderSdk implements DartSdk { |
30 // TODO(danrubel) Refactor this with DirectoryBasedDartSdk | 31 /// The resolver associated with this SDK. |
31 | |
32 /// The resolver associated with this embedder sdk. | |
33 EmbedderUriResolver _resolver; | 32 EmbedderUriResolver _resolver; |
34 | 33 |
35 /// The [AnalysisContext] which is used for all of the sources in this sdk. | 34 /// The [AnalysisContext] used for this SDK's sources. |
36 InternalAnalysisContext _analysisContext; | 35 InternalAnalysisContext _analysisContext; |
37 | 36 |
38 /// The library map that is populated by visiting the AST structure parsed fro
m | |
39 /// the contents of the libraries file. | |
40 final LibraryMap _librariesMap = new LibraryMap(); | 37 final LibraryMap _librariesMap = new LibraryMap(); |
41 | 38 |
| 39 final Map<String, String> _urlMappings = new HashMap<String, String>(); |
| 40 |
| 41 /// Analysis options for this SDK. |
| 42 AnalysisOptions analysisOptions; |
| 43 |
| 44 EmbedderSdk([Map<Folder, YamlMap> embedderYamls]) { |
| 45 embedderYamls?.forEach(_processEmbedderYaml); |
| 46 _resolver = new EmbedderUriResolver(this); |
| 47 } |
| 48 |
42 @override | 49 @override |
43 AnalysisContext get context { | 50 AnalysisContext get context { |
44 if (_analysisContext == null) { | 51 if (_analysisContext == null) { |
45 _analysisContext = new SdkAnalysisContext(null); | 52 _analysisContext = new SdkAnalysisContext(analysisOptions); |
46 SourceFactory factory = new SourceFactory([_resolver]); | 53 SourceFactory factory = new SourceFactory([_resolver]); |
47 _analysisContext.sourceFactory = factory; | 54 _analysisContext.sourceFactory = factory; |
48 List<String> uris = this.uris; | 55 |
49 ChangeSet changeSet = new ChangeSet(); | 56 ChangeSet changeSet = new ChangeSet(); |
50 for (String uri in uris) { | 57 for (String uri in uris) { |
51 changeSet.addedSource(factory.forUri(uri)); | 58 changeSet.addedSource(factory.forUri(uri)); |
52 } | 59 } |
53 _analysisContext.applyChanges(changeSet); | 60 _analysisContext.applyChanges(changeSet); |
54 } | 61 } |
55 return _analysisContext; | 62 return _analysisContext; |
56 } | 63 } |
57 | 64 |
58 @override | 65 @override |
59 List<SdkLibrary> get sdkLibraries => _librariesMap.sdkLibraries; | 66 List<SdkLibrary> get sdkLibraries => _librariesMap.sdkLibraries; |
60 | 67 |
61 // TODO(danrubel) Determine SDK version | 68 // TODO(danrubel) Determine SDK version |
62 @override | 69 @override |
63 String get sdkVersion => '0'; | 70 String get sdkVersion => '0'; |
64 | 71 |
65 @override | 72 @override |
66 List<String> get uris => _librariesMap.uris; | 73 List<String> get uris => _librariesMap.uris; |
67 | 74 |
| 75 /// The url mappings for this SDK. |
| 76 Map<String, String> get urlMappings => _urlMappings; |
| 77 |
68 @override | 78 @override |
69 Source fromFileUri(Uri uri) { | 79 Source fromFileUri(Uri uri) { |
70 JavaFile file = new JavaFile.fromUri(uri); | 80 JavaFile file = new JavaFile.fromUri(uri); |
71 String filePath = file.getAbsolutePath(); | 81 String filePath = file.getAbsolutePath(); |
72 | 82 |
73 String path; | 83 String path; |
74 for (SdkLibrary library in _librariesMap.sdkLibraries) { | 84 for (SdkLibrary library in _librariesMap.sdkLibraries) { |
75 String libraryPath = library.path.replaceAll('/', JavaFile.separator); | 85 String libraryPath = library.path.replaceAll('/', JavaFile.separator); |
76 if (filePath == libraryPath) { | 86 if (filePath == libraryPath) { |
77 path = '$_DART_COLON_PREFIX${library.shortName}'; | 87 path = library.shortName; |
78 break; | 88 break; |
79 } | 89 } |
80 } | 90 } |
81 if (path == null) { | 91 if (path == null) { |
82 for (SdkLibrary library in _librariesMap.sdkLibraries) { | 92 for (SdkLibrary library in _librariesMap.sdkLibraries) { |
83 String libraryPath = library.path.replaceAll('/', JavaFile.separator); | 93 String libraryPath = library.path.replaceAll('/', JavaFile.separator); |
84 int index = libraryPath.lastIndexOf(JavaFile.separator); | 94 int index = libraryPath.lastIndexOf(JavaFile.separator); |
85 if (index == -1) { | 95 if (index == -1) { |
86 continue; | 96 continue; |
87 } | 97 } |
88 String prefix = libraryPath.substring(0, index + 1); | 98 String prefix = libraryPath.substring(0, index + 1); |
89 if (!filePath.startsWith(prefix)) { | 99 if (!filePath.startsWith(prefix)) { |
90 continue; | 100 continue; |
91 } | 101 } |
92 var relPath = filePath | 102 var relPath = filePath |
93 .substring(prefix.length) | 103 .substring(prefix.length) |
94 .replaceAll(JavaFile.separator, '/'); | 104 .replaceAll(JavaFile.separator, '/'); |
95 path = '$_DART_COLON_PREFIX${library.shortName}/$relPath'; | 105 path = '${library.shortName}/$relPath'; |
96 break; | 106 break; |
97 } | 107 } |
98 } | 108 } |
99 | 109 |
100 if (path != null) { | 110 if (path != null) { |
101 try { | 111 try { |
102 return new FileBasedSource(file, parseUriWithException(path)); | 112 return new FileBasedSource(file, parseUriWithException(path)); |
103 } on URISyntaxException catch (exception, stackTrace) { | 113 } on URISyntaxException catch (exception, stackTrace) { |
104 AnalysisEngine.instance.logger.logInformation( | 114 AnalysisEngine.instance.logger.logInformation( |
105 "Failed to create URI: $path", | 115 "Failed to create URI: $path", |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 srcPath = '$prefix$relativePath'; | 155 srcPath = '$prefix$relativePath'; |
146 } | 156 } |
147 String filePath = srcPath.replaceAll('/', JavaFile.separator); | 157 String filePath = srcPath.replaceAll('/', JavaFile.separator); |
148 try { | 158 try { |
149 JavaFile file = new JavaFile(filePath); | 159 JavaFile file = new JavaFile(filePath); |
150 return new FileBasedSource(file, parseUriWithException(dartUri)); | 160 return new FileBasedSource(file, parseUriWithException(dartUri)); |
151 } on URISyntaxException { | 161 } on URISyntaxException { |
152 return null; | 162 return null; |
153 } | 163 } |
154 } | 164 } |
| 165 |
| 166 /// Install the mapping from [name] to [libDir]/[file]. |
| 167 void _processEmbeddedLibs(String name, String file, Folder libDir) { |
| 168 if (!name.startsWith(_DART_COLON_PREFIX)) { |
| 169 // SDK libraries must begin with 'dart:'. |
| 170 return; |
| 171 } |
| 172 String libPath = libDir.canonicalizePath(file); |
| 173 _urlMappings[name] = libPath; |
| 174 SdkLibraryImpl library = new SdkLibraryImpl(name); |
| 175 library.path = libPath; |
| 176 _librariesMap.setLibrary(name, library); |
| 177 } |
| 178 |
| 179 /// Given the 'embedderYamls' from [EmbedderYamlLocator] check each one for th
e |
| 180 /// top level key 'embedded_libs'. Under the 'embedded_libs' key are key value |
| 181 /// pairs. Each key is a 'dart:' library uri and each value is a path |
| 182 /// (relative to the directory containing `_embedder.yaml`) to a dart script |
| 183 /// for the given library. For example: |
| 184 /// |
| 185 /// embedded_libs: |
| 186 /// 'dart:io': '../../sdk/io/io.dart' |
| 187 /// |
| 188 /// If a key doesn't begin with `dart:` it is ignored. |
| 189 void _processEmbedderYaml(Folder libDir, YamlMap map) { |
| 190 YamlNode embedded_libs = map[_EMBEDDED_LIB_MAP_KEY]; |
| 191 if (embedded_libs is YamlMap) { |
| 192 embedded_libs.forEach((k, v) => _processEmbeddedLibs(k, v, libDir)); |
| 193 } |
| 194 } |
155 } | 195 } |
156 | 196 |
157 /// Given the 'embedderYamls' from [EmbedderYamlLocator] check each one for the | 197 /// Given the 'embedderYamls' from [EmbedderYamlLocator] check each one for the |
158 /// top level key 'embedded_libs'. Under the 'embedded_libs' key are key value | 198 /// top level key 'embedded_libs'. Under the 'embedded_libs' key are key value |
159 /// pairs. Each key is a 'dart:' library uri and each value is a path | 199 /// pairs. Each key is a 'dart:' library uri and each value is a path |
160 /// (relative to the directory containing `_embedder.yaml`) to a dart script | 200 /// (relative to the directory containing `_embedder.yaml`) to a dart script |
161 /// for the given library. For example: | 201 /// for the given library. For example: |
162 /// | 202 /// |
163 /// embedded_libs: | 203 /// embedded_libs: |
164 /// 'dart:io': '../../sdk/io/io.dart' | 204 /// 'dart:io': '../../sdk/io/io.dart' |
165 /// | 205 /// |
166 /// If a key doesn't begin with `dart:` it is ignored. | 206 /// If a key doesn't begin with `dart:` it is ignored. |
167 /// | 207 /// |
168 class EmbedderUriResolver extends DartUriResolver { | 208 class EmbedderUriResolver implements DartUriResolver { |
169 final Map<String, String> _urlMappings = <String, String>{}; | 209 EmbedderSdk _embedderSdk; |
| 210 DartUriResolver _dartUriResolver; |
170 | 211 |
171 /// Construct a [EmbedderUriResolver] from a package map | 212 /// Construct a [EmbedderUriResolver] from a package map |
172 /// (see [PackageMapProvider]). | 213 /// (see [PackageMapProvider]). |
173 EmbedderUriResolver(Map<Folder, YamlMap> embedderYamls) | 214 EmbedderUriResolver(this._embedderSdk) { |
174 : super(new EmbedderSdk()) { | 215 _dartUriResolver = new DartUriResolver(_embedderSdk); |
175 (dartSdk as EmbedderSdk)._resolver = this; | |
176 if (embedderYamls == null) { | |
177 return; | |
178 } | |
179 embedderYamls.forEach(_processEmbedderYaml); | |
180 } | 216 } |
181 | 217 |
| 218 @override |
| 219 DartSdk get dartSdk => _embedderSdk; |
| 220 |
182 /// Number of embedded libraries. | 221 /// Number of embedded libraries. |
183 int get length => _urlMappings.length; | 222 int get length => _embedderSdk?.urlMappings?.length ?? 0; |
| 223 |
| 224 @override |
| 225 Source resolveAbsolute(Uri uri, [Uri actualUri]) => |
| 226 _dartUriResolver.resolveAbsolute(uri, actualUri); |
184 | 227 |
185 @override | 228 @override |
186 Uri restoreAbsolute(Source source) { | 229 Uri restoreAbsolute(Source source) { |
187 String path = source.fullName; | 230 String path = source.fullName; |
188 if (path.length > 3 && path[1] == ':' && path[2] == '\\') { | 231 if (path.length > 3 && path[1] == ':' && path[2] == '\\') { |
189 path = '/${path[0]}:${path.substring(2).replaceAll('\\', '/')}'; | 232 path = '/${path[0]}:${path.substring(2).replaceAll('\\', '/')}'; |
190 } | 233 } |
191 Source sdkSource = dartSdk.fromFileUri(Uri.parse('file://$path')); | 234 Source sdkSource = dartSdk.fromFileUri(Uri.parse('file://$path')); |
192 return sdkSource?.uri; | 235 return sdkSource?.uri; |
193 } | 236 } |
194 | |
195 /// Install the mapping from [name] to [libDir]/[file]. | |
196 void _processEmbeddedLibs(String name, String file, Folder libDir) { | |
197 if (!name.startsWith(_DART_COLON_PREFIX)) { | |
198 // SDK libraries must begin with 'dart:'. | |
199 // TODO(pquitslund): Notify developer that something is wrong with the | |
200 // _embedder.yaml file in libDir. | |
201 return; | |
202 } | |
203 String libPath = libDir.canonicalizePath(file); | |
204 _urlMappings[name] = libPath; | |
205 String shortName = name.substring(_DART_COLON_PREFIX.length); | |
206 SdkLibraryImpl library = new SdkLibraryImpl(shortName); | |
207 library.path = libPath; | |
208 (dartSdk as EmbedderSdk)._librariesMap.setLibrary(name, library); | |
209 } | |
210 | |
211 void _processEmbedderYaml(Folder libDir, YamlMap map) { | |
212 YamlNode embedded_libs = map[_EMBEDDED_LIB_MAP_KEY]; | |
213 if (embedded_libs == null) { | |
214 return; | |
215 } | |
216 if (embedded_libs is YamlMap) { | |
217 embedded_libs.forEach((k, v) => _processEmbeddedLibs(k, v, libDir)); | |
218 } | |
219 } | |
220 } | 237 } |
221 | 238 |
222 /// Given a packageMap, check in each package's lib directory for the | 239 /// Given a packageMap, check in each package's lib directory for the |
223 /// existence of an `_embedder.yaml` file. If the file contains a top level | 240 /// existence of an `_embedder.yaml` file. If the file contains a top level |
224 /// YamlMap, it will be added to the [embedderYamls] map. | 241 /// YamlMap, it will be added to the [embedderYamls] map. |
225 class EmbedderYamlLocator { | 242 class EmbedderYamlLocator { |
226 static const String EMBEDDER_FILE_NAME = '_embedder.yaml'; | 243 static const String EMBEDDER_FILE_NAME = '_embedder.yaml'; |
227 | 244 |
228 // Map from package's library directory to the parsed | 245 /// Map from package's library directory to the parsed YamlMap. |
229 // YamlMap. | |
230 final Map<Folder, YamlMap> embedderYamls = new HashMap<Folder, YamlMap>(); | 246 final Map<Folder, YamlMap> embedderYamls = new HashMap<Folder, YamlMap>(); |
231 | 247 |
232 EmbedderYamlLocator(Map<String, List<Folder>> packageMap) { | 248 EmbedderYamlLocator(Map<String, List<Folder>> packageMap) { |
233 if (packageMap != null) { | 249 if (packageMap != null) { |
234 refresh(packageMap); | 250 refresh(packageMap); |
235 } | 251 } |
236 } | 252 } |
237 | 253 |
238 /// Programatically add an _embedder.yaml mapping. | 254 /// Programatically add an _embedder.yaml mapping. |
239 void addEmbedderYaml(Folder libDir, String embedderYaml) { | 255 void addEmbedderYaml(Folder libDir, String embedderYaml) { |
240 _processEmbedderYaml(libDir, embedderYaml); | 256 _processEmbedderYaml(libDir, embedderYaml); |
241 } | 257 } |
242 | 258 |
243 void refresh(Map<String, List<Folder>> packageMap) { | 259 void refresh(Map<String, List<Folder>> packageMap) { |
244 // Clear existing. | 260 // Clear existing. |
245 embedderYamls.clear(); | 261 embedderYamls.clear(); |
246 if (packageMap == null) { | 262 if (packageMap == null) { |
247 return; | 263 return; |
248 } | 264 } |
249 packageMap.forEach(_processPackage); | 265 packageMap.forEach(_processPackage); |
250 } | 266 } |
251 | 267 |
252 /// Given the yaml for an embedder ([embedderYaml]) and a folder | 268 /// Given the yaml for an embedder ([embedderYaml]) and a folder |
253 /// ([libDir]), setup the uri mapping. | 269 /// ([libDir]), setup the uri mapping. |
254 void _processEmbedderYaml(Folder libDir, String embedderYaml) { | 270 void _processEmbedderYaml(Folder libDir, String embedderYaml) { |
255 YamlNode yaml; | 271 YamlNode yaml; |
256 try { | 272 try { |
257 yaml = loadYaml(embedderYaml); | 273 yaml = loadYaml(embedderYaml); |
258 } catch (_) { | 274 } catch (_) { |
259 // TODO(pquitslund): Notify developer that something is wrong with the | |
260 // _embedder.yaml file in libDir. | |
261 return; | |
262 } | |
263 if (yaml == null) { | |
264 // TODO(pquitslund): Notify developer that something is wrong with the | |
265 // _embedder.yaml file in libDir. | |
266 return; | 275 return; |
267 } | 276 } |
268 if (yaml is! YamlMap) { | 277 if (yaml is! YamlMap) { |
269 // TODO(pquitslund): Notify developer that something is wrong with the | |
270 // _embedder.yaml file in libDir. | |
271 return; | 278 return; |
272 } | 279 } |
273 embedderYamls[libDir] = yaml; | 280 embedderYamls[libDir] = yaml; |
274 } | 281 } |
275 | 282 |
276 /// Given a package [name] and a list of folders ([libDirs]), | 283 /// Given a package [name] and a list of folders ([libDirs]), |
277 /// add any found `_embedder.yaml` files. | 284 /// add any found `_embedder.yaml` files. |
278 void _processPackage(String name, List<Folder> libDirs) { | 285 void _processPackage(String name, List<Folder> libDirs) { |
279 for (Folder libDir in libDirs) { | 286 for (Folder libDir in libDirs) { |
280 String embedderYaml = _readEmbedderYaml(libDir); | 287 String embedderYaml = _readEmbedderYaml(libDir); |
281 if (embedderYaml != null) { | 288 if (embedderYaml != null) { |
282 _processEmbedderYaml(libDir, embedderYaml); | 289 _processEmbedderYaml(libDir, embedderYaml); |
283 } | 290 } |
284 } | 291 } |
285 } | 292 } |
286 | 293 |
287 /// Read the contents of [libDir]/[EMBEDDER_FILE_NAME] as a string. | 294 /// Read the contents of [libDir]/[EMBEDDER_FILE_NAME] as a string. |
288 /// Returns null if the file doesn't exist. | 295 /// Returns null if the file doesn't exist. |
289 String _readEmbedderYaml(Folder libDir) { | 296 String _readEmbedderYaml(Folder libDir) { |
290 File file = libDir.getChild(EMBEDDER_FILE_NAME); | 297 File file = libDir.getChild(EMBEDDER_FILE_NAME); |
291 try { | 298 try { |
292 return file.readAsStringSync(); | 299 return file.readAsStringSync(); |
293 } on FileSystemException { | 300 } on FileSystemException { |
294 // File can't be read. | 301 // File can't be read. |
295 return null; | 302 return null; |
296 } | 303 } |
297 } | 304 } |
298 } | 305 } |
OLD | NEW |