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

Side by Side Diff: pkg/analyzer/lib/src/context/context.dart

Issue 1182303006: Re-enable and add new HTML support (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Move a private constant Created 5 years, 6 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) 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.src.context.context; 5 library analyzer.src.context.context;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 9
10 import 'package:analyzer/instrumentation/instrumentation.dart'; 10 import 'package:analyzer/instrumentation/instrumentation.dart';
(...skipping 18 matching lines...) Expand all
29 import 'package:analyzer/src/generated/scanner.dart'; 29 import 'package:analyzer/src/generated/scanner.dart';
30 import 'package:analyzer/src/generated/sdk.dart' show DartSdk; 30 import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
31 import 'package:analyzer/src/generated/source.dart'; 31 import 'package:analyzer/src/generated/source.dart';
32 import 'package:analyzer/src/generated/utilities_collection.dart'; 32 import 'package:analyzer/src/generated/utilities_collection.dart';
33 import 'package:analyzer/src/task/dart.dart'; 33 import 'package:analyzer/src/task/dart.dart';
34 import 'package:analyzer/src/task/dart_work_manager.dart'; 34 import 'package:analyzer/src/task/dart_work_manager.dart';
35 import 'package:analyzer/src/task/driver.dart'; 35 import 'package:analyzer/src/task/driver.dart';
36 import 'package:analyzer/src/task/manager.dart'; 36 import 'package:analyzer/src/task/manager.dart';
37 import 'package:analyzer/task/dart.dart'; 37 import 'package:analyzer/task/dart.dart';
38 import 'package:analyzer/task/general.dart'; 38 import 'package:analyzer/task/general.dart';
39 import 'package:analyzer/task/html.dart';
39 import 'package:analyzer/task/model.dart'; 40 import 'package:analyzer/task/model.dart';
41 import 'package:html/dom.dart' show Document;
40 42
41 /** 43 /**
42 * Type of callback functions used by PendingFuture. Functions of this type 44 * Type of callback functions used by PendingFuture. Functions of this type
43 * should perform a computation based on the data in [entry] and return it. If 45 * should perform a computation based on the data in [entry] and return it. If
44 * the computation can't be performed yet because more analysis is needed, 46 * the computation can't be performed yet because more analysis is needed,
45 * `null` should be returned. 47 * `null` should be returned.
46 * 48 *
47 * The function may also throw an exception, in which case the corresponding 49 * The function may also throw an exception, in which case the corresponding
48 * future will be completed with failure. 50 * future will be completed with failure.
49 * 51 *
(...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after
553 buffer.write(tokens[i].lexeme); 555 buffer.write(tokens[i].lexeme);
554 } 556 }
555 return buffer.toString(); 557 return buffer.toString();
556 } 558 }
557 nameNode = nameNode.parent; 559 nameNode = nameNode.parent;
558 } 560 }
559 return null; 561 return null;
560 } 562 }
561 563
562 @override 564 @override
563 List<AnalysisError> computeErrors(Source source) => 565 List<AnalysisError> computeErrors(Source source) {
564 _computeResult(source, DART_ERRORS); 566 String name = source.shortName;
567 if (AnalysisEngine.isDartFileName(name)) {
568 return _computeResult(source, DART_ERRORS);
569 } else if (AnalysisEngine.isHtmlFileName(name)) {
570 return _computeResult(source, HTML_ERRORS);
571 }
572 return AnalysisError.NO_ERRORS;
573 }
565 574
566 @override 575 @override
567 List<Source> computeExportedLibraries(Source source) => 576 List<Source> computeExportedLibraries(Source source) =>
568 _computeResult(source, EXPORTED_LIBRARIES); 577 _computeResult(source, EXPORTED_LIBRARIES);
569 578
570 @override 579 @override
571 // TODO(brianwilkerson) Implement this. 580 HtmlElement computeHtmlElement(Source source) {
572 HtmlElement computeHtmlElement(Source source) => null; 581 // TODO(brianwilkerson) Implement this. For the time being, we compute the
582 // data that will implicitly be computed by creating an element.
583 _computeResult(source, REFERENCED_LIBRARIES);
584 return null;
585 }
573 586
574 @override 587 @override
575 List<Source> computeImportedLibraries(Source source) => 588 List<Source> computeImportedLibraries(Source source) =>
576 _computeResult(source, EXPLICITLY_IMPORTED_LIBRARIES); 589 _computeResult(source, EXPLICITLY_IMPORTED_LIBRARIES);
577 590
578 @override 591 @override
579 SourceKind computeKindOf(Source source) { 592 SourceKind computeKindOf(Source source) {
580 String name = source.shortName; 593 String name = source.shortName;
581 if (AnalysisEngine.isDartFileName(name)) { 594 if (AnalysisEngine.isDartFileName(name)) {
582 return _computeResult(source, SOURCE_KIND); 595 return _computeResult(source, SOURCE_KIND);
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 } catch (exception) { 764 } catch (exception) {
752 // If the location cannot be decoded for some reason then the underlying 765 // If the location cannot be decoded for some reason then the underlying
753 // cause should have been logged already and we can fall though to return 766 // cause should have been logged already and we can fall though to return
754 // null. 767 // null.
755 } 768 }
756 return null; 769 return null;
757 } 770 }
758 771
759 @override 772 @override
760 AnalysisErrorInfo getErrors(Source source) { 773 AnalysisErrorInfo getErrors(Source source) {
761 return dartWorkManager.getErrors(source); 774 String name = source.shortName;
775 if (AnalysisEngine.isDartFileName(name)) {
776 return dartWorkManager.getErrors(source);
777 } else if (AnalysisEngine.isHtmlFileName(name)) {
778 List<AnalysisError> errors = analysisCache.getValue(source, HTML_ERRORS);
779 // TODO(brianwilkerson) We don't currently have line info for HTML files.
780 return new AnalysisErrorInfoImpl(errors, null);
781 }
782 return new AnalysisErrorInfoImpl(null, null);
Paul Berry 2015/06/15 21:34:38 The old task model does this instead: return new
Brian Wilkerson 2015/06/16 16:28:08 Done
762 } 783 }
763 784
764 @override 785 @override
765 HtmlElement getHtmlElement(Source source) { 786 HtmlElement getHtmlElement(Source source) {
766 // TODO(brianwilkerson) Implement this. 787 // TODO(brianwilkerson) Implement this.
767 // SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); 788 // SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
768 // if (sourceEntry is HtmlEntry) { 789 // if (sourceEntry is HtmlEntry) {
769 // return sourceEntry.getValue(HtmlEntry.ELEMENT); 790 // return sourceEntry.getValue(HtmlEntry.ELEMENT);
770 // } 791 // }
771 return null; 792 return null;
772 } 793 }
773 794
774 @override 795 @override
775 List<Source> getHtmlFilesReferencing(Source source) { 796 List<Source> getHtmlFilesReferencing(Source source) {
Paul Berry 2015/06/15 21:34:38 What's the contract on this function? Is it suppo
Brian Wilkerson 2015/06/16 16:28:08 Yes.
776 // TODO(brianwilkerson) Implement this. 797 if (!AnalysisEngine.isDartFileName(source.shortName)) {
777 SourceKind sourceKind = getKindOf(source);
778 if (sourceKind == null) {
779 return Source.EMPTY_LIST; 798 return Source.EMPTY_LIST;
780 } 799 }
781 List<Source> htmlSources = <Source>[]; 800 List<Source> htmlSources = <Source>[];
782 // while (true) { 801 List<Source> librarySources = getLibrariesContaining(source);
783 // if (sourceKind == SourceKind.PART) { 802 for (Source source in _cache.sources) {
784 // List<Source> librarySources = getLibrariesContaining(source); 803 if (AnalysisEngine.isHtmlFileName(source.shortName)) {
785 // for (Source source in _cache.sources) { 804 List<Source> referencedLibraries =
786 // CacheEntry entry = _cache.get(source); 805 analysisCache.getValue(source, REFERENCED_LIBRARIES);
787 // if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) { 806 if (_containsAny(referencedLibraries, librarySources)) {
788 // List<Source> referencedLibraries = 807 htmlSources.add(source);
789 // (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES); 808 }
790 // if (_containsAny(referencedLibraries, librarySources)) { 809 }
791 // htmlSources.add(source); 810 }
792 // }
793 // }
794 // }
795 // } else {
796 // for (Source source in _cache.sources) {
797 // CacheEntry entry = _cache.get(source);
798 // if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) {
799 // List<Source> referencedLibraries =
800 // (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES);
801 // if (_contains(referencedLibraries, source)) {
802 // htmlSources.add(source);
803 // }
804 // }
805 // }
806 // }
807 // break;
808 // }
809 if (htmlSources.isEmpty) { 811 if (htmlSources.isEmpty) {
810 return Source.EMPTY_LIST; 812 return Source.EMPTY_LIST;
Paul Berry 2015/06/15 21:34:38 Nit: it seems like the benefit this confers is tin
Brian Wilkerson 2015/06/16 16:28:08 The places where we've seen benefit are where the
811 } 813 }
812 return htmlSources; 814 return htmlSources;
813 } 815 }
814 816
815 @override 817 @override
816 SourceKind getKindOf(Source source) { 818 SourceKind getKindOf(Source source) {
817 String name = source.shortName; 819 String name = source.shortName;
818 if (AnalysisEngine.isDartFileName(name)) { 820 if (AnalysisEngine.isDartFileName(name)) {
819 return _cache.getValue(source, SOURCE_KIND); 821 return _cache.getValue(source, SOURCE_KIND);
820 } else if (AnalysisEngine.isHtmlFileName(name)) { 822 } else if (AnalysisEngine.isHtmlFileName(name)) {
(...skipping 25 matching lines...) Expand all
846 } 848 }
847 } 849 }
848 } 850 }
849 if (dependentLibraries.isEmpty) { 851 if (dependentLibraries.isEmpty) {
850 return Source.EMPTY_LIST; 852 return Source.EMPTY_LIST;
851 } 853 }
852 return dependentLibraries; 854 return dependentLibraries;
853 } 855 }
854 856
855 @override 857 @override
856 List<Source> getLibrariesReferencedFromHtml(Source htmlSource) { 858 List<Source> getLibrariesReferencedFromHtml(Source htmlSource) {
Paul Berry 2015/06/15 21:34:38 Similar concern about the contract on this functio
Brian Wilkerson 2015/06/16 16:28:08 In general, the "get" methods are lazy, returning
857 // TODO(brianwilkerson) Implement this. 859 CacheEntry entry = _cache.get(htmlSource);
858 // cache.CacheEntry entry = getReadableSourceEntryOrNull(htmlSource); 860 if (entry != null) {
859 // if (entry is HtmlEntry) { 861 return entry.getValue(REFERENCED_LIBRARIES);
860 // HtmlEntry htmlEntry = entry; 862 }
861 // return htmlEntry.getValue(HtmlEntry.REFERENCED_LIBRARIES);
862 // }
863 return Source.EMPTY_LIST; 863 return Source.EMPTY_LIST;
864 } 864 }
865 865
866 @override 866 @override
867 LibraryElement getLibraryElement(Source source) => 867 LibraryElement getLibraryElement(Source source) =>
868 _cache.getValue(source, LIBRARY_ELEMENT); 868 _cache.getValue(source, LIBRARY_ELEMENT);
869 869
870 @override 870 @override
871 LineInfo getLineInfo(Source source) => _cache.getValue(source, LINE_INFO); 871 LineInfo getLineInfo(Source source) => _cache.getValue(source, LINE_INFO);
872 872
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
1023 try { 1023 try {
1024 getContents(source); 1024 getContents(source);
1025 } catch (exception, stackTrace) { 1025 } catch (exception, stackTrace) {
1026 throw new AnalysisException('Could not get contents of $source', 1026 throw new AnalysisException('Could not get contents of $source',
1027 new CaughtException(exception, stackTrace)); 1027 new CaughtException(exception, stackTrace));
1028 } 1028 }
1029 return _computeResult(source, PARSED_UNIT); 1029 return _computeResult(source, PARSED_UNIT);
1030 } 1030 }
1031 1031
1032 @override 1032 @override
1033 ht.HtmlUnit parseHtmlUnit(Source source) { 1033 Document parseDocument(Source source) {
Paul Berry 2015/06/15 21:34:38 How about calling this "parseHtmlDocument" instead
Brian Wilkerson 2015/06/16 16:28:08 Done
1034 if (!AnalysisEngine.isHtmlFileName(source.shortName)) { 1034 if (!AnalysisEngine.isHtmlFileName(source.shortName)) {
1035 return null; 1035 return null;
1036 } 1036 }
1037 // TODO(brianwilkerson) Implement HTML analysis. 1037 return _computeResult(source, DOCUMENT);
1038 return null; //_computeResult(source, null);
1039 } 1038 }
1040 1039
1041 @override 1040 @override
1041 @deprecated // use parseDocument(source)
1042 ht.HtmlUnit parseHtmlUnit(Source source) {
1043 return null;
Paul Berry 2015/06/15 21:34:38 Instead of returning null, I'd suggest doing "thro
Brian Wilkerson 2015/06/16 16:28:08 Done
1044 }
1045
1046 @override
1042 AnalysisResult performAnalysisTask() { 1047 AnalysisResult performAnalysisTask() {
1043 return PerformanceStatistics.performAnaysis.makeCurrentWhile(() { 1048 return PerformanceStatistics.performAnaysis.makeCurrentWhile(() {
1044 _evaluatePendingFutures(); 1049 _evaluatePendingFutures();
1045 bool done = !driver.performAnalysisTask(); 1050 bool done = !driver.performAnalysisTask();
1046 if (done) { 1051 if (done) {
1047 done = !_validateCacheConsistency(); 1052 done = !_validateCacheConsistency();
1048 } 1053 }
1049 List<ChangeNotice> notices = _getChangeNotices(done); 1054 List<ChangeNotice> notices = _getChangeNotices(done);
1050 if (notices != null) { 1055 if (notices != null) {
1051 int noticeCount = notices.length; 1056 int noticeCount = notices.length;
1052 for (int i = 0; i < noticeCount; i++) { 1057 for (int i = 0; i < noticeCount; i++) {
1053 ChangeNotice notice = notices[i]; 1058 ChangeNotice notice = notices[i];
1054 _notifyErrors(notice.source, notice.errors, notice.lineInfo); 1059 _notifyErrors(notice.source, notice.errors, notice.lineInfo);
1055 } 1060 }
1056 } 1061 }
1057 return new AnalysisResult(notices, -1, '', -1); 1062 return new AnalysisResult(notices, -1, '', -1);
1058 }); 1063 });
1059 } 1064 }
1060 1065
1061 void _evaluatePendingFutures() {
1062 for (AnalysisTarget target in _pendingFutureTargets.keys) {
1063 CacheEntry cacheEntry = _cache.get(target);
1064 List<PendingFuture> pendingFutures = _pendingFutureTargets[target];
1065 for (int i = 0; i < pendingFutures.length;) {
1066 if (pendingFutures[i].evaluate(cacheEntry)) {
1067 pendingFutures.removeAt(i);
1068 } else {
1069 i++;
1070 }
1071 }
1072 }
1073 }
1074
1075 @override 1066 @override
1076 void recordLibraryElements(Map<Source, LibraryElement> elementMap) { 1067 void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
1077 elementMap.forEach((Source librarySource, LibraryElement library) { 1068 elementMap.forEach((Source librarySource, LibraryElement library) {
1078 // 1069 //
1079 // Cache the element in the library's info. 1070 // Cache the element in the library's info.
1080 // 1071 //
1081 CacheEntry entry = getCacheEntry(librarySource); 1072 CacheEntry entry = getCacheEntry(librarySource);
1082 setValue(ResultDescriptor result, value) { 1073 setValue(ResultDescriptor result, value) {
1083 entry.setValue(result, value, TargetedResult.EMPTY_LIST); 1074 entry.setValue(result, value, TargetedResult.EMPTY_LIST);
1084 } 1075 }
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
1320 bool _contains(List<Source> sources, Source targetSource) { 1311 bool _contains(List<Source> sources, Source targetSource) {
1321 for (Source source in sources) { 1312 for (Source source in sources) {
1322 if (source == targetSource) { 1313 if (source == targetSource) {
1323 return true; 1314 return true;
1324 } 1315 }
1325 } 1316 }
1326 return false; 1317 return false;
1327 } 1318 }
1328 1319
1329 /** 1320 /**
1321 * Return `true` if the given list of [sources] contains any of the given
1322 * [targetSources].
1323 */
1324 bool _containsAny(List<Source> sources, List<Source> targetSources) {
1325 for (Source targetSource in targetSources) {
1326 if (_contains(sources, targetSource)) {
1327 return true;
1328 }
1329 }
1330 return false;
1331 }
1332
1333 /**
1330 * Set the contents of the given [source] to the given [contents] and mark the 1334 * Set the contents of the given [source] to the given [contents] and mark the
1331 * source as having changed. The additional [offset], [oldLength] and 1335 * source as having changed. The additional [offset], [oldLength] and
1332 * [newLength] information is used by the context to determine what reanalysis 1336 * [newLength] information is used by the context to determine what reanalysis
1333 * is necessary. The method [setChangedContents] triggers a source changed 1337 * is necessary. The method [setChangedContents] triggers a source changed
1334 * event where as this method does not. 1338 * event where as this method does not.
1335 */ 1339 */
1336 bool _contentRangeChanged(Source source, String contents, int offset, 1340 bool _contentRangeChanged(Source source, String contents, int offset,
1337 int oldLength, int newLength) { 1341 int oldLength, int newLength) {
1338 bool changed = false; 1342 bool changed = false;
1339 String originalContents = _contentCache.setContents(source, contents); 1343 String originalContents = _contentCache.setContents(source, contents);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1395 List<CacheEntry> entries = <CacheEntry>[]; 1399 List<CacheEntry> entries = <CacheEntry>[];
1396 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); 1400 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator();
1397 while (iterator.moveNext()) { 1401 while (iterator.moveNext()) {
1398 if (iterator.key.source == source) { 1402 if (iterator.key.source == source) {
1399 entries.add(iterator.value); 1403 entries.add(iterator.value);
1400 } 1404 }
1401 } 1405 }
1402 return entries; 1406 return entries;
1403 } 1407 }
1404 1408
1409 void _evaluatePendingFutures() {
1410 for (AnalysisTarget target in _pendingFutureTargets.keys) {
1411 CacheEntry cacheEntry = _cache.get(target);
1412 List<PendingFuture> pendingFutures = _pendingFutureTargets[target];
1413 for (int i = 0; i < pendingFutures.length;) {
1414 if (pendingFutures[i].evaluate(cacheEntry)) {
1415 pendingFutures.removeAt(i);
1416 } else {
1417 i++;
1418 }
1419 }
1420 }
1421 }
1422
1405 /** 1423 /**
1406 * Return a list containing all of the change notices that are waiting to be 1424 * Return a list containing all of the change notices that are waiting to be
1407 * returned. If there are no notices, then return either `null` or an empty 1425 * returned. If there are no notices, then return either `null` or an empty
1408 * list, depending on the value of [nullIfEmpty]. 1426 * list, depending on the value of [nullIfEmpty].
1409 */ 1427 */
1410 List<ChangeNotice> _getChangeNotices(bool nullIfEmpty) { 1428 List<ChangeNotice> _getChangeNotices(bool nullIfEmpty) {
1411 if (_pendingNotices.isEmpty) { 1429 if (_pendingNotices.isEmpty) {
1412 if (nullIfEmpty) { 1430 if (nullIfEmpty) {
1413 return null; 1431 return null;
1414 } 1432 }
1415 return ChangeNoticeImpl.EMPTY_LIST; 1433 return ChangeNoticeImpl.EMPTY_LIST;
1416 } 1434 }
1417 List<ChangeNotice> notices = new List.from(_pendingNotices.values); 1435 List<ChangeNotice> notices = new List.from(_pendingNotices.values);
1418 _pendingNotices.clear(); 1436 _pendingNotices.clear();
1419 return notices; 1437 return notices;
1420 } 1438 }
1421 1439
1422 /** 1440 /**
1423 * Return a list containing all of the sources known to this context that have 1441 * Return a list containing all of the sources known to this context that have
1424 * the given [kind]. 1442 * the given [kind].
1425 */ 1443 */
1426 List<Source> _getSources(SourceKind kind) { 1444 List<Source> _getSources(SourceKind kind) {
1427 List<Source> sources = <Source>[]; 1445 List<Source> sources = <Source>[];
1428 for (Source source in _cache.sources) { 1446 if (kind == SourceKind.LIBRARY || kind == SourceKind.PART) {
1429 CacheEntry entry = _cache.get(source); 1447 for (Source source in _cache.sources) {
1430 if (entry.getValue(SOURCE_KIND) == kind) { 1448 CacheEntry entry = _cache.get(source);
1431 sources.add(source); 1449 if (entry.getValue(SOURCE_KIND) == kind) {
1450 sources.add(source);
1451 }
1432 } 1452 }
1453 } else if (kind == SourceKind.HTML) {
1454 for (Source source in _cache.sources) {
1455 if (AnalysisEngine.isHtmlFileName(source.shortName)) {
1456 sources.add(source);
1457 }
1458 }
1459 }
1460 if (sources == null) {
1461 return Source.EMPTY_LIST;
Paul Berry 2015/06/15 21:34:38 Unreachable code. Sources will never be null. Se
Brian Wilkerson 2015/06/16 16:28:08 Done
1433 } 1462 }
1434 return sources; 1463 return sources;
1435 } 1464 }
1436 1465
1437 /** 1466 /**
1438 * Look at the given [source] to see whether a task needs to be performed 1467 * Look at the given [source] to see whether a task needs to be performed
1439 * related to it. If so, add the source to the set of sources that need to be 1468 * related to it. If so, add the source to the set of sources that need to be
1440 * processed. This method is intended to be used for testing purposes only. 1469 * processed. This method is intended to be used for testing purposes only.
1441 */ 1470 */
1442 void _getSourcesNeedingProcessing(Source source, CacheEntry entry, 1471 void _getSourcesNeedingProcessing(Source source, CacheEntry entry,
(...skipping 532 matching lines...) Expand 10 before | Expand all | Expand 10 after
1975 new PendingFuture<T>(_context, target, computeValue); 2004 new PendingFuture<T>(_context, target, computeValue);
1976 if (!pendingFuture.evaluate(entry)) { 2005 if (!pendingFuture.evaluate(entry)) {
1977 _context._pendingFutureTargets 2006 _context._pendingFutureTargets
1978 .putIfAbsent(target, () => <PendingFuture>[]) 2007 .putIfAbsent(target, () => <PendingFuture>[])
1979 .add(pendingFuture); 2008 .add(pendingFuture);
1980 scheduleComputation(); 2009 scheduleComputation();
1981 } 2010 }
1982 return pendingFuture.future; 2011 return pendingFuture.future;
1983 } 2012 }
1984 } 2013 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/engine.dart » ('j') | pkg/analyzer/lib/src/generated/engine.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698