Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Side by Side Diff: packages/analyzer/lib/src/generated/source.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 engine.source; 5 library analyzer.src.generated.source;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 import "dart:math" as math; 8 import "dart:math" as math;
9 9
10 import 'package:analyzer/file_system/file_system.dart'; 10 import 'package:analyzer/file_system/file_system.dart';
11 import 'package:analyzer/file_system/physical_file_system.dart';
12 import 'package:analyzer/source/package_map_resolver.dart'; 11 import 'package:analyzer/source/package_map_resolver.dart';
13 import 'package:analyzer/src/generated/utilities_dart.dart' as utils; 12 import 'package:analyzer/src/context/source.dart';
13 import 'package:analyzer/src/generated/engine.dart';
14 import 'package:analyzer/src/generated/java_engine.dart';
15 import 'package:analyzer/src/generated/java_io.dart' show JavaFile;
16 import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
17 import 'package:analyzer/src/generated/source_io.dart' show FileBasedSource;
14 import 'package:analyzer/task/model.dart'; 18 import 'package:analyzer/task/model.dart';
15 import 'package:package_config/packages.dart'; 19 import 'package:package_config/packages.dart';
16 import 'package:path/path.dart' as pathos; 20 import 'package:path/path.dart' as pathos;
17 21
18 import 'engine.dart';
19 import 'java_core.dart';
20 import 'java_engine.dart';
21 import 'java_io.dart' show JavaFile;
22 import 'sdk.dart' show DartSdk;
23 import 'source_io.dart' show FileBasedSource;
24
25 /** 22 /**
26 * A function that is used to visit [ContentCache] entries. 23 * A function that is used to visit [ContentCache] entries.
27 */ 24 */
28 typedef void ContentCacheVisitor(String fullPath, int stamp, String contents); 25 typedef void ContentCacheVisitor(String fullPath, int stamp, String contents);
29 26
30 /** 27 /**
31 * A cache used to override the default content of a [Source]. 28 * A cache used to override the default content of a [Source].
32 */ 29 */
33 class ContentCache { 30 class ContentCache {
34 /** 31 /**
35 * A table mapping the full path of sources to the contents of those sources. 32 * A table mapping the full path of sources to the contents of those sources.
36 * This is used to override the default contents of a source. 33 * This is used to override the default contents of a source.
37 */ 34 */
38 HashMap<String, String> _contentMap = new HashMap<String, String>(); 35 HashMap<String, String> _contentMap = new HashMap<String, String>();
39 36
40 /** 37 /**
41 * A table mapping the full path of sources to the modification stamps of 38 * A table mapping the full path of sources to the modification stamps of
42 * those sources. This is used when the default contents of a source has been 39 * those sources. This is used when the default contents of a source has been
43 * overridden. 40 * overridden.
44 */ 41 */
45 HashMap<String, int> _stampMap = new HashMap<String, int>(); 42 HashMap<String, int> _stampMap = new HashMap<String, int>();
46 43
44 int _nextStamp = 0;
45
47 /** 46 /**
48 * Visit all entries of this cache. 47 * Visit all entries of this cache.
49 */ 48 */
50 void accept(ContentCacheVisitor visitor) { 49 void accept(ContentCacheVisitor visitor) {
51 _contentMap.forEach((String fullPath, String contents) { 50 _contentMap.forEach((String fullPath, String contents) {
52 int stamp = _stampMap[fullPath]; 51 int stamp = _stampMap[fullPath];
53 visitor(fullPath, stamp, contents); 52 visitor(fullPath, stamp, contents);
54 }); 53 });
55 } 54 }
56 55
(...skipping 20 matching lines...) Expand all
77 * the effect of overriding the default contents of the source. If the 76 * the effect of overriding the default contents of the source. If the
78 * contents are `null` the override is removed so that the default contents 77 * contents are `null` the override is removed so that the default contents
79 * will be returned. 78 * will be returned.
80 */ 79 */
81 String setContents(Source source, String contents) { 80 String setContents(Source source, String contents) {
82 String fullName = source.fullName; 81 String fullName = source.fullName;
83 if (contents == null) { 82 if (contents == null) {
84 _stampMap.remove(fullName); 83 _stampMap.remove(fullName);
85 return _contentMap.remove(fullName); 84 return _contentMap.remove(fullName);
86 } else { 85 } else {
87 int newStamp = JavaSystem.currentTimeMillis(); 86 int newStamp = _nextStamp++;
88 int oldStamp = _stampMap[fullName]; 87 int oldStamp = _stampMap[fullName];
89 _stampMap[fullName] = newStamp; 88 _stampMap[fullName] = newStamp;
90 // Occasionally, if this method is called in rapid succession, the 89 // Occasionally, if this method is called in rapid succession, the
91 // timestamps are equal. Guard against this by artificially incrementing 90 // timestamps are equal. Guard against this by artificially incrementing
92 // the new timestamp. 91 // the new timestamp.
93 if (newStamp == oldStamp) { 92 if (newStamp == oldStamp) {
94 _stampMap[fullName] = newStamp + 1; 93 _stampMap[fullName] = newStamp + 1;
95 } 94 }
96 String oldContent = _contentMap[fullName]; 95 String oldContent = _contentMap[fullName];
97 _contentMap[fullName] = contents; 96 _contentMap[fullName] = contents;
98 return oldContent; 97 return oldContent;
99 } 98 }
100 } 99 }
101 } 100 }
102 101
102 @deprecated
103 class CustomUriResolver extends UriResolver { 103 class CustomUriResolver extends UriResolver {
104 final Map<String, String> _urlMappings; 104 final Map<String, String> _urlMappings;
105 105
106 CustomUriResolver(this._urlMappings); 106 CustomUriResolver(this._urlMappings);
107 107
108 @override 108 @override
109 Source resolveAbsolute(Uri uri, [Uri actualUri]) { 109 Source resolveAbsolute(Uri uri, [Uri actualUri]) {
110 String mapping = _urlMappings[uri.toString()]; 110 String mapping = _urlMappings[uri.toString()];
111 if (mapping == null) return null; 111 if (mapping == null) return null;
112 112
113 Uri fileUri = new Uri.file(mapping); 113 Uri fileUri = new Uri.file(mapping);
114 if (!fileUri.isAbsolute) return null; 114 if (!fileUri.isAbsolute) return null;
115 115
116 JavaFile javaFile = new JavaFile.fromUri(fileUri); 116 JavaFile javaFile = new JavaFile.fromUri(fileUri);
117 return new FileBasedSource(javaFile, actualUri != null ? actualUri : uri); 117 return new FileBasedSource(javaFile, actualUri ?? uri);
118 } 118 }
119 } 119 }
120 120
121 /** 121 /**
122 * Instances of the class `DartUriResolver` resolve `dart` URI's. 122 * Instances of the class `DartUriResolver` resolve `dart` URI's.
123 */ 123 */
124 class DartUriResolver extends UriResolver { 124 class DartUriResolver extends UriResolver {
125 /** 125 /**
126 * The name of the `dart` scheme. 126 * The name of the `dart` scheme.
127 */ 127 */
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 /** 172 /**
173 * Return `true` if the given URI is a `dart:` URI. 173 * Return `true` if the given URI is a `dart:` URI.
174 * 174 *
175 * @param uri the URI being tested 175 * @param uri the URI being tested
176 * @return `true` if the given URI is a `dart:` URI 176 * @return `true` if the given URI is a `dart:` URI
177 */ 177 */
178 static bool isDartUri(Uri uri) => DART_SCHEME == uri.scheme; 178 static bool isDartUri(Uri uri) => DART_SCHEME == uri.scheme;
179 } 179 }
180 180
181 /** 181 /**
182 * Instances of the class `LineInfo` encapsulate information about line and colu mn information 182 * Information about line and column information within a source file.
183 * within a source file.
184 */ 183 */
185 class LineInfo { 184 class LineInfo {
186 /** 185 /**
187 * An array containing the offsets of the first character of each line in the source code. 186 * A list containing the offsets of the first character of each line in the
187 * source code.
188 */ 188 */
189 final List<int> _lineStarts; 189 final List<int> lineStarts;
190 190
191 /** 191 /**
192 * The zero-based [_lineStarts] index resulting from the last call to 192 * The zero-based [lineStarts] index resulting from the last call to
193 * [getLocation]. 193 * [getLocation].
194 */ 194 */
195 int _previousLine = 0; 195 int _previousLine = 0;
196 196
197 /** 197 /**
198 * Initialize a newly created set of line information to represent the data en coded in the given 198 * Initialize a newly created set of line information to represent the data
199 * array. 199 * encoded in the given list of [lineStarts].
200 *
201 * @param lineStarts the offsets of the first character of each line in the so urce code
202 */ 200 */
203 LineInfo(this._lineStarts) { 201 factory LineInfo(List<int> lineStarts) => new LineInfoWithCount(lineStarts);
204 if (_lineStarts == null) { 202
205 throw new IllegalArgumentException("lineStarts must be non-null"); 203 /**
206 } else if (_lineStarts.length < 1) { 204 * Initialize a newly created set of line information corresponding to the
207 throw new IllegalArgumentException("lineStarts must be non-empty"); 205 * given file [content].
206 */
207 factory LineInfo.fromContent(String content) =>
208 new LineInfoWithCount(StringUtilities.computeLineStarts(content));
209
210 /**
211 * Initialize a newly created set of line information to represent the data
212 * encoded in the given list of [lineStarts].
213 */
214 LineInfo._(this.lineStarts) {
215 if (lineStarts == null) {
216 throw new ArgumentError("lineStarts must be non-null");
217 } else if (lineStarts.length < 1) {
218 throw new ArgumentError("lineStarts must be non-empty");
208 } 219 }
209 } 220 }
210 221
211 /** 222 /**
212 * Return the location information for the character at the given offset. 223 * The number of lines.
213 * 224 */
214 * @param offset the offset of the character for which location information is to be returned 225 int get lineCount => lineStarts.length;
215 * @return the location information for the character at the given offset 226
227 /**
228 * Return the location information for the character at the given [offset].
216 */ 229 */
217 LineInfo_Location getLocation(int offset) { 230 LineInfo_Location getLocation(int offset) {
218 var min = 0; 231 var min = 0;
219 var max = _lineStarts.length - 1; 232 var max = lineStarts.length - 1;
220 233
221 // Subsequent calls to [getLocation] are often for offsets near each other. 234 // Subsequent calls to [getLocation] are often for offsets near each other.
222 // To take advantage of that, we cache the index of the line start we found 235 // To take advantage of that, we cache the index of the line start we found
223 // when this was last called. If the current offset is on that line or 236 // when this was last called. If the current offset is on that line or
224 // later, we'll skip those early indices completely when searching. 237 // later, we'll skip those early indices completely when searching.
225 if (offset >= _lineStarts[_previousLine]) { 238 if (offset >= lineStarts[_previousLine]) {
226 min = _previousLine; 239 min = _previousLine;
227 240
228 // Before kicking off a full binary search, do a quick check here to see 241 // Before kicking off a full binary search, do a quick check here to see
229 // if the new offset is on that exact line. 242 // if the new offset is on that exact line.
230 if (min == _lineStarts.length - 1 || offset < _lineStarts[min + 1]) { 243 if (min == lineStarts.length - 1 || offset < lineStarts[min + 1]) {
231 return new LineInfo_Location(min + 1, offset - _lineStarts[min] + 1); 244 return new LineInfo_Location(min + 1, offset - lineStarts[min] + 1);
232 } 245 }
233 } 246 }
234 247
235 // Binary search to fine the line containing this offset. 248 // Binary search to fine the line containing this offset.
236 while (min < max) { 249 while (min < max) {
237 var midpoint = (max - min + 1) ~/ 2 + min; 250 var midpoint = (max - min + 1) ~/ 2 + min;
238 251
239 if (_lineStarts[midpoint] > offset) { 252 if (lineStarts[midpoint] > offset) {
240 max = midpoint - 1; 253 max = midpoint - 1;
241 } else { 254 } else {
242 min = midpoint; 255 min = midpoint;
243 } 256 }
244 } 257 }
245 258
246 _previousLine = min; 259 _previousLine = min;
247 260
248 return new LineInfo_Location(min + 1, offset - _lineStarts[min] + 1); 261 return new LineInfo_Location(min + 1, offset - lineStarts[min] + 1);
262 }
263
264 /**
265 * Return the offset of the first character on the line with the given
266 * [lineNumber].
267 */
268 int getOffsetOfLine(int lineNumber) {
269 if (lineNumber < 0 || lineNumber >= lineCount) {
270 throw new ArgumentError(
271 'Invalid line number: $lineNumber; must be between 0 and ${lineCount - 1}');
272 }
273 return lineStarts[lineNumber];
249 } 274 }
250 } 275 }
251 276
252 /** 277 /**
253 * Instances of the class `Location` represent the location of a character as a line and 278 * Instances of the class `Location` represent the location of a character as a line and
254 * column pair. 279 * column pair.
255 */ 280 */
256 class LineInfo_Location { 281 class LineInfo_Location {
257 /** 282 /**
258 * The one-based index of the line containing the character. 283 * The one-based index of the line containing the character.
(...skipping 12 matching lines...) Expand all
271 * @param lineNumber the one-based index of the line containing the character 296 * @param lineNumber the one-based index of the line containing the character
272 * @param columnNumber the one-based index of the column containing the charac ter 297 * @param columnNumber the one-based index of the column containing the charac ter
273 */ 298 */
274 LineInfo_Location(this.lineNumber, this.columnNumber); 299 LineInfo_Location(this.lineNumber, this.columnNumber);
275 300
276 @override 301 @override
277 String toString() => '$lineNumber:$columnNumber'; 302 String toString() => '$lineNumber:$columnNumber';
278 } 303 }
279 304
280 /** 305 /**
306 * Information about line and column information within a source file,
307 * including a count of the total number of lines.
308 *
309 * TODO(paulberry): in the next major version roll of analyzer, merge this
310 * class into [LineInfo].
311 */
312 class LineInfoWithCount extends LineInfo {
313 /**
314 * Initialize a newly created set of line information to represent the data
315 * encoded in the given list of [lineStarts].
316 */
317 LineInfoWithCount(List<int> lineStarts) : super._(lineStarts);
318 }
319
320 /**
281 * Instances of interface `LocalSourcePredicate` are used to determine if the gi ven 321 * Instances of interface `LocalSourcePredicate` are used to determine if the gi ven
282 * [Source] is "local" in some sense, so can be updated. 322 * [Source] is "local" in some sense, so can be updated.
283 */ 323 */
284 abstract class LocalSourcePredicate { 324 abstract class LocalSourcePredicate {
285 /** 325 /**
286 * Instance of [LocalSourcePredicate] that always returns `false`. 326 * Instance of [LocalSourcePredicate] that always returns `false`.
287 */ 327 */
288 static final LocalSourcePredicate FALSE = new LocalSourcePredicate_FALSE(); 328 static final LocalSourcePredicate FALSE = new LocalSourcePredicate_FALSE();
289 329
290 /** 330 /**
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 /** 366 /**
327 * An implementation of an non-existing [Source]. 367 * An implementation of an non-existing [Source].
328 */ 368 */
329 class NonExistingSource extends Source { 369 class NonExistingSource extends Source {
330 @override 370 @override
331 final String fullName; 371 final String fullName;
332 372
333 @override 373 @override
334 final Uri uri; 374 final Uri uri;
335 375
376 @override
336 final UriKind uriKind; 377 final UriKind uriKind;
337 378
338 NonExistingSource(this.fullName, this.uri, this.uriKind); 379 NonExistingSource(this.fullName, this.uri, this.uriKind);
339 380
340 @override 381 @override
341 TimestampedData<String> get contents { 382 TimestampedData<String> get contents {
342 throw new UnsupportedOperationException('$fullName does not exist.'); 383 throw new UnsupportedError('$fullName does not exist.');
343 } 384 }
344 385
345 @override 386 @override
346 String get encoding { 387 String get encoding => uri.toString();
347 throw new UnsupportedOperationException('$fullName does not exist.');
348 }
349 388
350 @override 389 @override
351 int get hashCode => fullName.hashCode; 390 int get hashCode => fullName.hashCode;
352 391
353 @override 392 @override
354 bool get isInSystemLibrary => false; 393 bool get isInSystemLibrary => false;
355 394
356 @override 395 @override
357 int get modificationStamp => -1; 396 int get modificationStamp => -1;
358 397
359 @override 398 @override
360 String get shortName => pathos.basename(fullName); 399 String get shortName => pathos.basename(fullName);
361 400
362 @override 401 @override
363 bool operator ==(Object other) { 402 bool operator ==(Object other) {
364 if (other is NonExistingSource) { 403 if (other is NonExistingSource) {
365 return other.uriKind == uriKind && other.fullName == fullName; 404 return other.uriKind == uriKind && other.fullName == fullName;
366 } 405 }
367 return false; 406 return false;
368 } 407 }
369 408
370 @override 409 @override
371 bool exists() => false; 410 bool exists() => false;
372
373 @override
374 Uri resolveRelativeUri(Uri relativeUri) {
375 throw new UnsupportedOperationException('$fullName does not exist.');
376 }
377 } 411 }
378 412
379 /** 413 /**
380 * The interface `Source` defines the behavior of objects representing source co de that can be 414 * The interface `Source` defines the behavior of objects representing source co de that can be
381 * analyzed by the analysis engine. 415 * analyzed by the analysis engine.
382 * 416 *
383 * Implementations of this interface need to be aware of some assumptions made b y the analysis 417 * Implementations of this interface need to be aware of some assumptions made b y the analysis
384 * engine concerning sources: 418 * engine concerning sources:
385 * * Sources are not required to be unique. That is, there can be multiple insta nces representing 419 * * Sources are not required to be unique. That is, there can be multiple insta nces representing
386 * the same source. 420 * the same source.
387 * * Sources are long lived. That is, the engine is allowed to hold on to a sour ce for an extended 421 * * Sources are long lived. That is, the engine is allowed to hold on to a sour ce for an extended
388 * period of time and that source must continue to report accurate and up-to-dat e information. 422 * period of time and that source must continue to report accurate and up-to-dat e information.
389 * Because of these assumptions, most implementations will not maintain any stat e but will delegate 423 * Because of these assumptions, most implementations will not maintain any stat e but will delegate
390 * to an authoritative system of record in order to implement this API. For exam ple, a source that 424 * to an authoritative system of record in order to implement this API. For exam ple, a source that
391 * represents files on disk would typically query the file system to determine t he state of the 425 * represents files on disk would typically query the file system to determine t he state of the
392 * file. 426 * file.
393 * 427 *
394 * If the instances that implement this API are the system of record, then they will typically be 428 * If the instances that implement this API are the system of record, then they will typically be
395 * unique. In that case, sources that are created that represent non-existent fi les must also be 429 * unique. In that case, sources that are created that represent non-existent fi les must also be
396 * retained so that if those files are created at a later date the long-lived so urces representing 430 * retained so that if those files are created at a later date the long-lived so urces representing
397 * those files will know that they now exist. 431 * those files will know that they now exist.
398 */ 432 */
399 abstract class Source implements AnalysisTarget { 433 abstract class Source implements AnalysisTarget {
400 /** 434 /**
401 * An empty list of sources. 435 * An empty list of sources.
402 */ 436 */
403 @deprecated // Use Source.EMPTY_LIST
404 static const List<Source> EMPTY_ARRAY = EMPTY_LIST;
405
406 /**
407 * An empty list of sources.
408 */
409 static const List<Source> EMPTY_LIST = const <Source>[]; 437 static const List<Source> EMPTY_LIST = const <Source>[];
410 438
411 /** 439 /**
412 * Get the contents and timestamp of this source. 440 * Get the contents and timestamp of this source.
413 * 441 *
414 * Clients should consider using the the method [AnalysisContext.getContents] 442 * Clients should consider using the method [AnalysisContext.getContents]
415 * because contexts can have local overrides of the content of a source that t he source is not 443 * because contexts can have local overrides of the content of a source that t he source is not
416 * aware of. 444 * aware of.
417 * 445 *
418 * @return the contents and timestamp of the source 446 * @return the contents and timestamp of the source
419 * @throws Exception if the contents of this source could not be accessed 447 * @throws Exception if the contents of this source could not be accessed
420 */ 448 */
421 TimestampedData<String> get contents; 449 TimestampedData<String> get contents;
422 450
423 /** 451 /**
424 * Return an encoded representation of this source that can be used to create a source that is 452 * Return an encoded representation of this source that can be used to create a source that is
(...skipping 22 matching lines...) Expand all
447 @override 475 @override
448 int get hashCode; 476 int get hashCode;
449 477
450 /** 478 /**
451 * Return `true` if this source is in one of the system libraries. 479 * Return `true` if this source is in one of the system libraries.
452 * 480 *
453 * @return `true` if this is in a system library 481 * @return `true` if this is in a system library
454 */ 482 */
455 bool get isInSystemLibrary; 483 bool get isInSystemLibrary;
456 484
485 @override
486 Source get librarySource => null;
487
457 /** 488 /**
458 * Return the modification stamp for this source, or a negative value if the 489 * Return the modification stamp for this source, or a negative value if the
459 * source does not exist. A modification stamp is a non-negative integer with 490 * source does not exist. A modification stamp is a non-negative integer with
460 * the property that if the contents of the source have not been modified 491 * the property that if the contents of the source have not been modified
461 * since the last time the modification stamp was accessed then the same value 492 * since the last time the modification stamp was accessed then the same value
462 * will be returned, but if the contents of the source have been modified one 493 * will be returned, but if the contents of the source have been modified one
463 * or more times (even if the net change is zero) the stamps will be different . 494 * or more times (even if the net change is zero) the stamps will be different .
464 * 495 *
465 * Clients should consider using the the method 496 * Clients should consider using the method
466 * [AnalysisContext.getModificationStamp] because contexts can have local 497 * [AnalysisContext.getModificationStamp] because contexts can have local
467 * overrides of the content of a source that the source is not aware of. 498 * overrides of the content of a source that the source is not aware of.
468 */ 499 */
469 int get modificationStamp; 500 int get modificationStamp;
470 501
471 /** 502 /**
472 * Return a short version of the name that can be displayed to the user to den ote this source. For 503 * Return a short version of the name that can be displayed to the user to den ote this source. For
473 * example, for a source representing a file this would typically be the name of the file. 504 * example, for a source representing a file this would typically be the name of the file.
474 * 505 *
475 * @return a name that can be displayed to the user to denote this source 506 * @return a name that can be displayed to the user to denote this source
(...skipping 28 matching lines...) Expand all
504 * @return `true` if the given object is a source that represents the same sou rce code as 535 * @return `true` if the given object is a source that represents the same sou rce code as
505 * this source 536 * this source
506 * See [Object.==]. 537 * See [Object.==].
507 */ 538 */
508 @override 539 @override
509 bool operator ==(Object object); 540 bool operator ==(Object object);
510 541
511 /** 542 /**
512 * Return `true` if this source exists. 543 * Return `true` if this source exists.
513 * 544 *
514 * Clients should consider using the the method [AnalysisContext.exists] becau se 545 * Clients should consider using the method [AnalysisContext.exists] because
515 * contexts can have local overrides of the content of a source that the sourc e is not aware of 546 * contexts can have local overrides of the content of a source that the sourc e is not aware of
516 * and a source with local content is considered to exist even if there is no file on disk. 547 * and a source with local content is considered to exist even if there is no file on disk.
517 * 548 *
518 * @return `true` if this source exists 549 * @return `true` if this source exists
519 */ 550 */
520 bool exists(); 551 bool exists();
521
522 /**
523 * Resolve the relative URI against the URI associated with this source object .
524 *
525 * Note: This method is not intended for public use, it is only visible out of necessity. It is
526 * only intended to be invoked by a [SourceFactory]. Source factories will
527 * only invoke this method if the URI is relative, so implementations of this method are not
528 * required to, and generally do not, verify the argument. The result of invok ing this method with
529 * an absolute URI is intentionally left unspecified.
530 *
531 * @param relativeUri the relative URI to be resolved against this source
532 * @return the URI to which given URI was resolved
533 * @throws AnalysisException if the relative URI could not be resolved
534 */
535 Uri resolveRelativeUri(Uri relativeUri);
536 } 552 }
537 553
538 /** 554 /**
539 * The interface `ContentReceiver` defines the behavior of objects that can rece ive the 555 * The interface `ContentReceiver` defines the behavior of objects that can rece ive the
540 * content of a source. 556 * content of a source.
541 */ 557 */
542 abstract class Source_ContentReceiver { 558 abstract class Source_ContentReceiver {
543 /** 559 /**
544 * Accept the contents of a source. 560 * Accept the contents of a source.
545 * 561 *
(...skipping 18 matching lines...) Expand all
564 * @param source the source in question 580 * @param source the source in question
565 * @return `true` if the receiver contains the source, else `false` 581 * @return `true` if the receiver contains the source, else `false`
566 */ 582 */
567 bool contains(Source source); 583 bool contains(Source source);
568 } 584 }
569 585
570 /** 586 /**
571 * Instances of the class `SourceFactory` resolve possibly relative URI's agains t an existing 587 * Instances of the class `SourceFactory` resolve possibly relative URI's agains t an existing
572 * [Source]. 588 * [Source].
573 */ 589 */
574 class SourceFactory { 590 abstract class SourceFactory {
575 /** 591 /**
576 * The analysis context that this source factory is associated with. 592 * The analysis context that this source factory is associated with.
577 */ 593 */
578 AnalysisContext context; 594 AnalysisContext context;
579 595
580 /** 596 /**
581 * URI processor used to find mappings for `package:` URIs found in a `.packag es` config 597 * Initialize a newly created source factory with the given absolute URI
582 * file. 598 * [resolvers] and optional [packages] resolution helper.
583 */ 599 */
584 final Packages _packages; 600 factory SourceFactory(List<UriResolver> resolvers,
601 [Packages packages,
602 ResourceProvider resourceProvider]) = SourceFactoryImpl;
585 603
586 /** 604 /**
587 * Resource provider used in working with package maps. 605 * Return the [DartSdk] associated with this [SourceFactory], or `null` if
588 */ 606 * there is no such SDK.
589 final ResourceProvider _resourceProvider;
590
591 /**
592 * The resolvers used to resolve absolute URI's.
593 */
594 final List<UriResolver> _resolvers;
595
596 /**
597 * The predicate to determine is [Source] is local.
598 */
599 LocalSourcePredicate _localSourcePredicate = LocalSourcePredicate.NOT_SDK;
600
601 /**
602 * Initialize a newly created source factory with the given absolute URI [reso lvers] and
603 * optional [packages] resolution helper.
604 */
605 SourceFactory(this._resolvers,
606 [this._packages, ResourceProvider resourceProvider])
607 : _resourceProvider = resourceProvider != null
608 ? resourceProvider
609 : PhysicalResourceProvider.INSTANCE;
610
611 /**
612 * Return the [DartSdk] associated with this [SourceFactory], or `null` if the re
613 * is no such SDK.
614 * 607 *
615 * @return the [DartSdk] associated with this [SourceFactory], or `null` if 608 * @return the [DartSdk] associated with this [SourceFactory], or `null` if
616 * there is no such SDK 609 * there is no such SDK
617 */ 610 */
618 DartSdk get dartSdk { 611 DartSdk get dartSdk;
619 for (UriResolver resolver in _resolvers) {
620 if (resolver is DartUriResolver) {
621 DartUriResolver dartUriResolver = resolver;
622 return dartUriResolver.dartSdk;
623 }
624 }
625 return null;
626 }
627 612
628 /** 613 /**
629 * Sets the [LocalSourcePredicate]. 614 * Sets the [LocalSourcePredicate].
630 * 615 *
631 * @param localSourcePredicate the predicate to determine is [Source] is local 616 * @param localSourcePredicate the predicate to determine is [Source] is local
632 */ 617 */
633 void set localSourcePredicate(LocalSourcePredicate localSourcePredicate) { 618 void set localSourcePredicate(LocalSourcePredicate localSourcePredicate);
634 this._localSourcePredicate = localSourcePredicate;
635 }
636 619
637 /// A table mapping package names to paths of directories containing 620 /// A table mapping package names to paths of directories containing
638 /// the package (or [null] if there is no registered package URI resolver). 621 /// the package (or [null] if there is no registered package URI resolver).
639 Map<String, List<Folder>> get packageMap { 622 Map<String, List<Folder>> get packageMap;
640 // Start by looking in .packages.
641 if (_packages != null) {
642 Map<String, List<Folder>> packageMap = <String, List<Folder>>{};
643 _packages.asMap().forEach((String name, Uri uri) {
644 if (uri.scheme == 'file' || uri.scheme == '' /* unspecified */) {
645 packageMap[name] = <Folder>[
646 _resourceProvider.getFolder(uri.toFilePath())
647 ];
648 }
649 });
650 return packageMap;
651 }
652
653 // Default to the PackageMapUriResolver.
654 PackageMapUriResolver resolver = _resolvers
655 .firstWhere((r) => r is PackageMapUriResolver, orElse: () => null);
656 return resolver != null ? resolver.packageMap : null;
657 }
658 623
659 /** 624 /**
660 * Return a source object representing the given absolute URI, or `null` if th e URI is not a 625 * Return a source factory that will resolve URI's in the same way that this
661 * valid URI or if it is not an absolute URI. 626 * source factory does.
627 */
628 SourceFactory clone();
629
630 /**
631 * Return a source object representing the given absolute URI, or `null` if
632 * the URI is not a valid URI or if it is not an absolute URI.
662 * 633 *
663 * @param absoluteUri the absolute URI to be resolved 634 * @param absoluteUri the absolute URI to be resolved
664 * @return a source object representing the absolute URI 635 * @return a source object representing the absolute URI
665 */ 636 */
666 Source forUri(String absoluteUri) { 637 Source forUri(String absoluteUri);
667 try {
668 Uri uri = parseUriWithException(absoluteUri);
669 if (uri.isAbsolute) {
670 return _internalResolveUri(null, uri);
671 }
672 } catch (exception, stackTrace) {
673 AnalysisEngine.instance.logger.logError(
674 "Could not resolve URI: $absoluteUri",
675 new CaughtException(exception, stackTrace));
676 }
677 return null;
678 }
679 638
680 /** 639 /**
681 * Return a source object representing the given absolute URI, or `null` if th e URI is not 640 * Return a source object representing the given absolute URI, or `null` if
682 * an absolute URI. 641 * the URI is not an absolute URI.
683 * 642 *
684 * @param absoluteUri the absolute URI to be resolved 643 * @param absoluteUri the absolute URI to be resolved
685 * @return a source object representing the absolute URI 644 * @return a source object representing the absolute URI
686 */ 645 */
687 Source forUri2(Uri absoluteUri) { 646 Source forUri2(Uri absoluteUri);
688 if (absoluteUri.isAbsolute) {
689 try {
690 return _internalResolveUri(null, absoluteUri);
691 } on AnalysisException catch (exception, stackTrace) {
692 AnalysisEngine.instance.logger.logError(
693 "Could not resolve URI: $absoluteUri",
694 new CaughtException(exception, stackTrace));
695 }
696 }
697 return null;
698 }
699 647
700 /** 648 /**
701 * Return a source object that is equal to the source object used to obtain th e given encoding. 649 * Return a source object that is equal to the source object used to obtain
650 * the given encoding.
702 * 651 *
703 * @param encoding the encoding of a source object 652 * @param encoding the encoding of a source object
704 * @return a source object that is described by the given encoding 653 * @return a source object that is described by the given encoding
705 * @throws IllegalArgumentException if the argument is not a valid encoding 654 * @throws IllegalArgumentException if the argument is not a valid encoding
706 * See [Source.encoding]. 655 * See [Source.encoding].
707 */ 656 */
708 Source fromEncoding(String encoding) { 657 Source fromEncoding(String encoding);
709 Source source = forUri(encoding);
710 if (source == null) {
711 throw new IllegalArgumentException(
712 "Invalid source encoding: '$encoding'");
713 }
714 return source;
715 }
716 658
717 /** 659 /**
718 * Determines if the given [Source] is local. 660 * Determines if the given [Source] is local.
719 * 661 *
720 * @param source the [Source] to analyze 662 * @param source the [Source] to analyze
721 * @return `true` if the given [Source] is local 663 * @return `true` if the given [Source] is local
722 */ 664 */
723 bool isLocalSource(Source source) => _localSourcePredicate.isLocal(source); 665 bool isLocalSource(Source source);
724 666
725 /** 667 /**
726 * Return a source object representing the URI that results from resolving the given (possibly 668 * Return a source representing the URI that results from resolving the given
727 * relative) contained URI against the URI associated with an existing source object, whether or 669 * (possibly relative) [containedUri] against the URI associated with the
728 * not the resulting source exists, or `null` if either the contained URI is i nvalid or if 670 * [containingSource], whether or not the resulting source exists, or `null`
729 * it cannot be resolved against the source object's URI. 671 * if either the [containedUri] is invalid or if it cannot be resolved against
730 * 672 * the [containingSource]'s URI.
731 * @param containingSource the source containing the given URI
732 * @param containedUri the (possibly relative) URI to be resolved against the containing source
733 * @return the source representing the contained URI
734 */ 673 */
735 Source resolveUri(Source containingSource, String containedUri) { 674 Source resolveUri(Source containingSource, String containedUri);
736 if (containedUri == null || containedUri.isEmpty) {
737 return null;
738 }
739 try {
740 // Force the creation of an escaped URI to deal with spaces, etc.
741 return _internalResolveUri(
742 containingSource, parseUriWithException(containedUri));
743 } catch (exception, stackTrace) {
744 String containingFullName =
745 containingSource != null ? containingSource.fullName : '<null>';
746 AnalysisEngine.instance.logger.logError(
747 "Could not resolve URI ($containedUri) relative to source ($containing FullName)",
748 new CaughtException(exception, stackTrace));
749 return null;
750 }
751 }
752 675
753 /** 676 /**
754 * Return an absolute URI that represents the given source, or `null` if a val id URI cannot 677 * Return an absolute URI that represents the given source, or `null` if a
755 * be computed. 678 * valid URI cannot be computed.
756 * 679 *
757 * @param source the source to get URI for 680 * @param source the source to get URI for
758 * @return the absolute URI representing the given source 681 * @return the absolute URI representing the given source
759 */ 682 */
760 Uri restoreUri(Source source) { 683 Uri restoreUri(Source source);
761 // First see if a resolver can restore the URI.
762 for (UriResolver resolver in _resolvers) {
763 Uri uri = resolver.restoreAbsolute(source);
764 if (uri != null) {
765 // Now see if there's a package mapping.
766 Uri packageMappedUri = _getPackageMapping(uri);
767 if (packageMappedUri != null) {
768 return packageMappedUri;
769 }
770 // Fall back to the resolver's computed URI.
771 return uri;
772 }
773 }
774
775 return null;
776 }
777
778 Uri _getPackageMapping(Uri sourceUri) {
779 if (_packages == null) {
780 return null;
781 }
782 if (sourceUri.scheme != 'file') {
783 //TODO(pquitslund): verify this works for non-file URIs.
784 return null;
785 }
786
787 Uri packageUri;
788 _packages.asMap().forEach((String name, Uri uri) {
789 if (packageUri == null) {
790 if (utils.startsWith(sourceUri, uri)) {
791 packageUri = Uri.parse(
792 'package:$name/${sourceUri.path.substring(uri.path.length)}');
793 }
794 }
795 });
796 return packageUri;
797 }
798
799 /**
800 * Return a source object representing the URI that results from resolving the given (possibly
801 * relative) contained URI against the URI associated with an existing source object, or
802 * `null` if the URI could not be resolved.
803 *
804 * @param containingSource the source containing the given URI
805 * @param containedUri the (possibly relative) URI to be resolved against the containing source
806 * @return the source representing the contained URI
807 * @throws AnalysisException if either the contained URI is invalid or if it c annot be resolved
808 * against the source object's URI
809 */
810 Source _internalResolveUri(Source containingSource, Uri containedUri) {
811 if (!containedUri.isAbsolute) {
812 if (containingSource == null) {
813 throw new AnalysisException(
814 "Cannot resolve a relative URI without a containing source: $contain edUri");
815 }
816 containedUri = containingSource.resolveRelativeUri(containedUri);
817 }
818
819 Uri actualUri = containedUri;
820
821 // Check .packages and update target and actual URIs as appropriate.
822 if (_packages != null && containedUri.scheme == 'package') {
823 Uri packageUri =
824 _packages.resolve(containedUri, notFound: (Uri packageUri) => null);
825
826 if (packageUri != null) {
827 // Ensure scheme is set.
828 if (packageUri.scheme == '') {
829 packageUri = packageUri.replace(scheme: 'file');
830 }
831 containedUri = packageUri;
832 }
833 }
834
835 for (UriResolver resolver in _resolvers) {
836 Source result = resolver.resolveAbsolute(containedUri, actualUri);
837 if (result != null) {
838 return result;
839 }
840 }
841
842 return null;
843 }
844 } 684 }
845 685
846 /** 686 /**
847 * The enumeration `SourceKind` defines the different kinds of sources that are known to the 687 * The enumeration `SourceKind` defines the different kinds of sources that are
848 * analysis engine. 688 * known to the analysis engine.
849 */ 689 */
850 class SourceKind extends Enum<SourceKind> { 690 class SourceKind implements Comparable<SourceKind> {
851 /** 691 /**
852 * A source containing HTML. The HTML might or might not contain Dart scripts. 692 * A source containing HTML. The HTML might or might not contain Dart scripts.
853 */ 693 */
854 static const SourceKind HTML = const SourceKind('HTML', 0); 694 static const SourceKind HTML = const SourceKind('HTML', 0);
855 695
856 /** 696 /**
857 * A Dart compilation unit that is not a part of another library. Libraries mi ght or might not 697 * A Dart compilation unit that is not a part of another library. Libraries
858 * contain any directives, including a library directive. 698 * might or might not contain any directives, including a library directive.
859 */ 699 */
860 static const SourceKind LIBRARY = const SourceKind('LIBRARY', 1); 700 static const SourceKind LIBRARY = const SourceKind('LIBRARY', 1);
861 701
862 /** 702 /**
863 * A Dart compilation unit that is part of another library. Parts contain a pa rt-of directive. 703 * A Dart compilation unit that is part of another library. Parts contain a
704 * part-of directive.
864 */ 705 */
865 static const SourceKind PART = const SourceKind('PART', 2); 706 static const SourceKind PART = const SourceKind('PART', 2);
866 707
867 /** 708 /**
868 * An unknown kind of source. Used both when it is not possible to identify th e kind of a source 709 * An unknown kind of source. Used both when it is not possible to identify
869 * and also when the kind of a source is not known without performing a comput ation and the client 710 * the kind of a source and also when the kind of a source is not known
870 * does not want to spend the time to identify the kind. 711 * without performing a computation and the client does not want to spend the
712 * time to identify the kind.
871 */ 713 */
872 static const SourceKind UNKNOWN = const SourceKind('UNKNOWN', 3); 714 static const SourceKind UNKNOWN = const SourceKind('UNKNOWN', 3);
873 715
874 static const List<SourceKind> values = const [HTML, LIBRARY, PART, UNKNOWN]; 716 static const List<SourceKind> values = const [HTML, LIBRARY, PART, UNKNOWN];
875 717
876 const SourceKind(String name, int ordinal) : super(name, ordinal); 718 /**
719 * The name of this source kind.
720 */
721 final String name;
722
723 /**
724 * The ordinal value of the source kind.
725 */
726 final int ordinal;
727
728 const SourceKind(this.name, this.ordinal);
729
730 @override
731 int get hashCode => ordinal;
732
733 @override
734 int compareTo(SourceKind other) => ordinal - other.ordinal;
735
736 @override
737 String toString() => name;
877 } 738 }
878 739
879 /** 740 /**
880 * A source range defines an [Element]'s source coordinates relative to its [Sou rce]. 741 * A source range defines an [Element]'s source coordinates relative to its [Sou rce].
881 */ 742 */
882 class SourceRange { 743 class SourceRange {
883 /** 744 /**
884 * An empty [SourceRange] with offset `0` and length `0`. 745 * An empty [SourceRange] with offset `0` and length `0`.
885 */ 746 */
886 static SourceRange EMPTY = new SourceRange(0, 0); 747 static SourceRange EMPTY = new SourceRange(0, 0);
(...skipping 21 matching lines...) Expand all
908 /** 769 /**
909 * @return the 0-based index of the after-last character of the source code fo r this element, 770 * @return the 0-based index of the after-last character of the source code fo r this element,
910 * relative to the source buffer in which this element is contained. 771 * relative to the source buffer in which this element is contained.
911 */ 772 */
912 int get end => offset + length; 773 int get end => offset + length;
913 774
914 @override 775 @override
915 int get hashCode => 31 * offset + length; 776 int get hashCode => 31 * offset + length;
916 777
917 @override 778 @override
918 bool operator ==(Object obj) { 779 bool operator ==(Object other) {
919 if (obj is! SourceRange) { 780 return other is SourceRange &&
920 return false; 781 other.offset == offset &&
921 } 782 other.length == length;
922 SourceRange sourceRange = obj as SourceRange;
923 return sourceRange.offset == offset && sourceRange.length == length;
924 } 783 }
925 784
926 /** 785 /**
927 * @return `true` if <code>x</code> is in [offset, offset + length) interval. 786 * @return `true` if <code>x</code> is in [offset, offset + length) interval.
928 */ 787 */
929 bool contains(int x) => offset <= x && x < offset + length; 788 bool contains(int x) => offset <= x && x < offset + length;
930 789
931 /** 790 /**
932 * @return `true` if <code>x</code> is in (offset, offset + length) interval. 791 * @return `true` if <code>x</code> is in (offset, offset + length) interval.
933 */ 792 */
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
1000 bool startsIn(SourceRange otherRange) => otherRange.contains(offset); 859 bool startsIn(SourceRange otherRange) => otherRange.contains(offset);
1001 860
1002 @override 861 @override
1003 String toString() => '[offset=$offset, length=$length]'; 862 String toString() => '[offset=$offset, length=$length]';
1004 } 863 }
1005 864
1006 /** 865 /**
1007 * The enumeration `UriKind` defines the different kinds of URI's that are known to the 866 * The enumeration `UriKind` defines the different kinds of URI's that are known to the
1008 * analysis engine. These are used to keep track of the kind of URI associated w ith a given source. 867 * analysis engine. These are used to keep track of the kind of URI associated w ith a given source.
1009 */ 868 */
1010 class UriKind extends Enum<UriKind> { 869 class UriKind implements Comparable<UriKind> {
1011 /** 870 /**
1012 * A 'dart:' URI. 871 * A 'dart:' URI.
1013 */ 872 */
1014 static const UriKind DART_URI = const UriKind('DART_URI', 0, 0x64); 873 static const UriKind DART_URI = const UriKind('DART_URI', 0, 0x64);
1015 874
1016 /** 875 /**
1017 * A 'file:' URI. 876 * A 'file:' URI.
1018 */ 877 */
1019 static const UriKind FILE_URI = const UriKind('FILE_URI', 1, 0x66); 878 static const UriKind FILE_URI = const UriKind('FILE_URI', 1, 0x66);
1020 879
1021 /** 880 /**
1022 * A 'package:' URI. 881 * A 'package:' URI.
1023 */ 882 */
1024 static const UriKind PACKAGE_URI = const UriKind('PACKAGE_URI', 2, 0x70); 883 static const UriKind PACKAGE_URI = const UriKind('PACKAGE_URI', 2, 0x70);
1025 884
1026 static const List<UriKind> values = const [DART_URI, FILE_URI, PACKAGE_URI]; 885 static const List<UriKind> values = const [DART_URI, FILE_URI, PACKAGE_URI];
1027 886
1028 /** 887 /**
888 * The name of this URI kind.
889 */
890 final String name;
891
892 /**
893 * The ordinal value of the URI kind.
894 */
895 final int ordinal;
896
897 /**
1029 * The single character encoding used to identify this kind of URI. 898 * The single character encoding used to identify this kind of URI.
1030 */ 899 */
1031 final int encoding; 900 final int encoding;
1032 901
1033 /** 902 /**
1034 * Initialize a newly created URI kind to have the given encoding. 903 * Initialize a newly created URI kind to have the given encoding.
1035 *
1036 * @param encoding the single character encoding used to identify this kind of URI.
1037 */ 904 */
1038 const UriKind(String name, int ordinal, this.encoding) : super(name, ordinal); 905 const UriKind(this.name, this.ordinal, this.encoding);
906
907 @override
908 int get hashCode => ordinal;
909
910 @override
911 int compareTo(UriKind other) => ordinal - other.ordinal;
912
913 @override
914 String toString() => name;
1039 915
1040 /** 916 /**
1041 * Return the URI kind represented by the given encoding, or `null` if there i s no kind with 917 * Return the URI kind represented by the given [encoding], or `null` if there
1042 * the given encoding. 918 * is no kind with the given encoding.
1043 *
1044 * @param encoding the single character encoding used to identify the URI kind to be returned
1045 * @return the URI kind represented by the given encoding
1046 */ 919 */
1047 static UriKind fromEncoding(int encoding) { 920 static UriKind fromEncoding(int encoding) {
1048 while (true) { 921 while (true) {
1049 if (encoding == 0x64) { 922 if (encoding == 0x64) {
1050 return DART_URI; 923 return DART_URI;
1051 } else if (encoding == 0x66) { 924 } else if (encoding == 0x66) {
1052 return FILE_URI; 925 return FILE_URI;
1053 } else if (encoding == 0x70) { 926 } else if (encoding == 0x70) {
1054 return PACKAGE_URI; 927 return PACKAGE_URI;
1055 } 928 }
1056 break; 929 break;
1057 } 930 }
1058 return null; 931 return null;
1059 } 932 }
933
934 /**
935 * Return the URI kind corresponding to the given scheme string.
936 */
937 static UriKind fromScheme(String scheme) {
938 if (scheme == PackageMapUriResolver.PACKAGE_SCHEME) {
939 return UriKind.PACKAGE_URI;
940 } else if (scheme == DartUriResolver.DART_SCHEME) {
941 return UriKind.DART_URI;
942 } else if (scheme == ResourceUriResolver.FILE_SCHEME) {
943 return UriKind.FILE_URI;
944 }
945 return UriKind.FILE_URI;
946 }
1060 } 947 }
1061 948
1062 /** 949 /**
1063 * The abstract class `UriResolver` defines the behavior of objects that are use d to resolve 950 * The abstract class `UriResolver` defines the behavior of objects that are use d to resolve
1064 * URI's for a source factory. Subclasses of this class are expected to resolve a single scheme of 951 * URI's for a source factory. Subclasses of this class are expected to resolve a single scheme of
1065 * absolute URI. 952 * absolute URI.
1066 */ 953 */
1067 abstract class UriResolver { 954 abstract class UriResolver {
1068 /** 955 /**
1069 * Resolve the given absolute URI. Return a [Source] representing the file to which 956 * Resolve the given absolute URI. Return a [Source] representing the file to which
1070 * it was resolved, whether or not the resulting source exists, or `null` if i t could not be 957 * it was resolved, whether or not the resulting source exists, or `null` if i t could not be
1071 * resolved because the URI is invalid. 958 * resolved because the URI is invalid.
1072 * 959 *
1073 * @param uri the URI to be resolved 960 * @param uri the URI to be resolved
1074 * @param actualUri the actual uri for this source -- if `null`, the value of [uri] will be used 961 * @param actualUri the actual uri for this source -- if `null`, the value of [uri] will be used
1075 * @return a [Source] representing the file to which given URI was resolved 962 * @return a [Source] representing the file to which given URI was resolved
1076 */ 963 */
1077 Source resolveAbsolute(Uri uri, [Uri actualUri]); 964 Source resolveAbsolute(Uri uri, [Uri actualUri]);
1078 965
1079 /** 966 /**
1080 * Return an absolute URI that represents the given [source], or `null` if a 967 * Return an absolute URI that represents the given [source], or `null` if a
1081 * valid URI cannot be computed. 968 * valid URI cannot be computed.
1082 * 969 *
1083 * The computation should be based solely on [source.fullName]. 970 * The computation should be based solely on [source.fullName].
1084 */ 971 */
1085 Uri restoreAbsolute(Source source) => null; 972 Uri restoreAbsolute(Source source) => null;
1086 } 973 }
OLDNEW
« no previous file with comments | « packages/analyzer/lib/src/generated/sdk_io.dart ('k') | packages/analyzer/lib/src/generated/source_io.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698