Version 1.9.0-dev.10.5
svn merge -c 44256 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44278 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44279 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44296 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44306 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44307 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44308 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44335 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44336 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44337 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44338 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44339 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44341 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44345 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44346 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44347 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44352 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44371 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44375 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44376 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44377 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44381 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44383 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44384 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44386 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44388 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
Merged patch from:
https://code.google.com/p/dart/issues/detail?id=22734
git-svn-id: http://dart.googlecode.com/svn/trunk@44394 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 922f2ad..8168764 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -367,26 +367,7 @@
* path.
*/
AnalysisContext getAnalysisContext(String path) {
- // try to find a containing context
- Folder containingFolder = null;
- for (Folder folder in folderMap.keys) {
- if (folder.path == path || folder.contains(path)) {
- if (containingFolder == null) {
- containingFolder = folder;
- } else if (containingFolder.path.length < folder.path.length) {
- containingFolder = folder;
- }
- }
- }
- if (containingFolder != null) {
- return folderMap[containingFolder];
- }
- Resource resource = resourceProvider.getResource(path);
- if (resource is Folder) {
- return null;
- }
- // check if there is a context that analyzed this source
- return getAnalysisContextForSource(_getSourceWithoutContext(path));
+ return getContextSourcePair(path).context;
}
/**
@@ -412,6 +393,67 @@
}
/**
+ * Return the primary [ContextSourcePair] representing the given [path].
+ *
+ * The [AnalysisContext] of this pair will be the context that explicitly
+ * contains the path, if any such context exists, otherwise it will be the
+ * first context that implicitly analyzes it.
+ *
+ * If the [path] is not analyzed by any context, a [ContextSourcePair] with
+ * `null` context and `file` [Source] is returned.
+ *
+ * If the [path] dosn't represent a file, `null` is returned as a [Source].
+ *
+ * Does not return `null`.
+ */
+ ContextSourcePair getContextSourcePair(String path) {
+ // try SDK
+ {
+ Uri uri = resourceProvider.pathContext.toUri(path);
+ Source sdkSource = defaultSdk.fromFileUri(uri);
+ if (sdkSource != null) {
+ AnalysisContext anyContext = folderMap.values.first;
+ return new ContextSourcePair(anyContext, sdkSource);
+ }
+ }
+ // try to find the deep-most containing context
+ Resource resource = resourceProvider.getResource(path);
+ File file = resource is File ? resource : null;
+ {
+ Folder containingFolder = null;
+ AnalysisContext containingContext = null;
+ folderMap.forEach((Folder folder, AnalysisContext context) {
+ if (folder.isOrContains(path)) {
+ if (containingFolder == null ||
+ containingFolder.path.length < folder.path.length) {
+ containingFolder = folder;
+ containingContext = context;
+ }
+ }
+ });
+ if (containingContext != null) {
+ Source source = file != null
+ ? ContextManager.createSourceInContext(containingContext, file)
+ : null;
+ return new ContextSourcePair(containingContext, source);
+ }
+ }
+ // try to find a context that analysed the file
+ for (AnalysisContext context in folderMap.values) {
+ Source source = file != null
+ ? ContextManager.createSourceInContext(context, file)
+ : null;
+ SourceKind kind = context.getKindOf(source);
+ if (kind != SourceKind.UNKNOWN) {
+ return new ContextSourcePair(context, source);
+ }
+ }
+ // file-based source
+ Source fileSource = file != null ? file.createSource() : null;
+ return new ContextSourcePair(null, fileSource);
+ }
+
+ /**
* Returns [Element]s at the given [offset] of the given [file].
*
* May be empty if cannot be resolved, but not `null`.
@@ -460,37 +502,18 @@
* the current state.
*/
AnalysisErrorInfo getErrors(String file) {
- // prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(file);
+ ContextSourcePair contextSource = getContextSourcePair(file);
+ AnalysisContext context = contextSource.context;
+ Source source = contextSource.source;
if (context == null) {
return null;
}
- // prepare Source
- Source source = getSource(file);
- if (context.getKindOf(source) == SourceKind.UNKNOWN) {
+ if (!source.exists()) {
return null;
}
- // get errors for the file
return context.getErrors(source);
}
- /**
- * Returns resolved [AstNode]s at the given [offset] of the given [file].
- *
- * May be empty, but not `null`.
- */
- List<AstNode> getNodesAtOffset(String file, int offset) {
- List<CompilationUnit> units = getResolvedCompilationUnits(file);
- List<AstNode> nodes = <AstNode>[];
- for (CompilationUnit unit in units) {
- AstNode node = new NodeLocator.con1(offset).searchWithin(unit);
- if (node != null) {
- nodes.add(node);
- }
- }
- return nodes;
- }
-
// TODO(brianwilkerson) Add the following method after 'prioritySources' has
// been added to InternalAnalysisContext.
// /**
@@ -509,19 +532,37 @@
// }
/**
+ * Returns resolved [AstNode]s at the given [offset] of the given [file].
+ *
+ * May be empty, but not `null`.
+ */
+ List<AstNode> getNodesAtOffset(String file, int offset) {
+ List<CompilationUnit> units = getResolvedCompilationUnits(file);
+ List<AstNode> nodes = <AstNode>[];
+ for (CompilationUnit unit in units) {
+ AstNode node = new NodeLocator.con1(offset).searchWithin(unit);
+ if (node != null) {
+ nodes.add(node);
+ }
+ }
+ return nodes;
+ }
+
+ /**
* Returns resolved [CompilationUnit]s of the Dart file with the given [path].
*
* May be empty, but not `null`.
*/
List<CompilationUnit> getResolvedCompilationUnits(String path) {
List<CompilationUnit> units = <CompilationUnit>[];
+ ContextSourcePair contextSource = getContextSourcePair(path);
// prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(path);
+ AnalysisContext context = contextSource.context;
if (context == null) {
return units;
}
// add a unit for each unit/library combination
- Source unitSource = getSource(path);
+ Source unitSource = contextSource.source;
List<Source> librarySources = context.getLibrariesContaining(unitSource);
for (Source librarySource in librarySources) {
CompilationUnit unit =
@@ -535,50 +576,6 @@
}
/**
- * Returns the [CompilationUnit] of the Dart file with the given [path] that
- * should be used to resend notifications for already resolved unit.
- * Returns `null` if the file is not a part of any context, library has not
- * been yet resolved, or any problem happened.
- */
- CompilationUnit getResolvedCompilationUnitToResendNotification(String path) {
- // prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(path);
- if (context == null) {
- return null;
- }
- // prepare sources
- Source unitSource = getSource(path);
- List<Source> librarySources = context.getLibrariesContaining(unitSource);
- if (librarySources.isEmpty) {
- return null;
- }
- // if library has not been resolved yet, the unit will be resolved later
- Source librarySource = librarySources[0];
- if (context.getLibraryElement(librarySource) == null) {
- return null;
- }
- // if library has been already resolved, resolve unit
- return context.resolveCompilationUnit2(unitSource, librarySource);
- }
-
- /**
- * Return the [Source] of the Dart file with the given [path].
- */
- Source getSource(String path) {
- // try SDK
- {
- Uri uri = resourceProvider.pathContext.toUri(path);
- Source sdkSource = defaultSdk.fromFileUri(uri);
- if (sdkSource != null) {
- return sdkSource;
- }
- }
- // file-based source
- File file = resourceProvider.getResource(path);
- return ContextManager.createSourceInContext(getAnalysisContext(path), file);
- }
-
- /**
* Handle a [request] that was read from the communication channel.
*/
void handleRequest(Request request) {
@@ -852,16 +849,17 @@
Set<String> todoFiles =
oldFiles != null ? newFiles.difference(oldFiles) : newFiles;
for (String file in todoFiles) {
- Source source = getSource(file);
+ ContextSourcePair contextSource = getContextSourcePair(file);
// prepare context
- AnalysisContext context = getAnalysisContext(file);
+ AnalysisContext context = contextSource.context;
if (context == null) {
continue;
}
// Dart unit notifications.
if (AnalysisEngine.isDartFileName(file)) {
+ Source source = contextSource.source;
CompilationUnit dartUnit =
- getResolvedCompilationUnitToResendNotification(file);
+ _getResolvedCompilationUnitToResendNotification(context, source);
if (dartUnit != null) {
switch (service) {
case AnalysisService.HIGHLIGHTS:
@@ -875,6 +873,7 @@
sendAnalysisNotificationOccurrences(this, file, dartUnit);
break;
case AnalysisService.OUTLINE:
+ AnalysisContext context = dartUnit.element.context;
LineInfo lineInfo = context.getLineInfo(source);
sendAnalysisNotificationOutline(this, file, lineInfo, dartUnit);
break;
@@ -901,9 +900,11 @@
Map<AnalysisContext, List<Source>> sourceMap =
new HashMap<AnalysisContext, List<Source>>();
List<String> unanalyzed = new List<String>();
+ Source firstSource = null;
files.forEach((file) {
- AnalysisContext preferredContext = getAnalysisContext(file);
- Source source = getSource(file);
+ ContextSourcePair contextSource = getContextSourcePair(file);
+ AnalysisContext preferredContext = contextSource.context;
+ Source source = contextSource.source;
bool contextFound = false;
for (AnalysisContext context in folderMap.values) {
if (context == preferredContext ||
@@ -912,6 +913,9 @@
contextFound = true;
}
}
+ if (firstSource == null) {
+ firstSource = source;
+ }
if (!contextFound) {
unanalyzed.add(file);
}
@@ -933,7 +937,6 @@
schedulePerformAnalysisOperation(context);
});
operationQueue.reschedule();
- Source firstSource = files.length > 0 ? getSource(files[0]) : null;
_onPriorityChangeController.add(new PriorityChangeEvent(firstSource));
}
@@ -962,8 +965,9 @@
void test_flushResolvedUnit(String file) {
if (AnalysisEngine.isDartFileName(file)) {
- AnalysisContextImpl context = getAnalysisContext(file);
- Source source = getSource(file);
+ ContextSourcePair contextSource = getContextSourcePair(file);
+ AnalysisContextImpl context = contextSource.context;
+ Source source = contextSource.source;
DartEntry dartEntry = context.getReadableSourceEntryOrNull(source);
dartEntry.flushAstStructures();
}
@@ -989,7 +993,8 @@
*/
void updateContent(String id, Map<String, dynamic> changes) {
changes.forEach((file, change) {
- Source source = getSource(file);
+ ContextSourcePair contextSource = getContextSourcePair(file);
+ Source source = contextSource.source;
operationQueue.sourceAboutToChange(source);
// Prepare the new contents.
String oldContents = overlayState.getContents(source);
@@ -1020,29 +1025,32 @@
overlayState.setContents(source, newContents);
// Update all contexts.
for (InternalAnalysisContext context in folderMap.values) {
- if (context.handleContentsChanged(
- source, oldContents, newContents, true)) {
- schedulePerformAnalysisOperation(context);
- } else {
- // When the client sends any change for a source, we should resend
- // subscribed notifications, even if there were no changes in the
- // source contents.
- // TODO(scheglov) consider checking if there are subscriptions.
- if (AnalysisEngine.isDartFileName(file)) {
- List<CompilationUnit> dartUnits =
- context.ensureResolvedDartUnits(source);
- if (dartUnits != null) {
- AnalysisErrorInfo errorInfo = context.getErrors(source);
- for (var dartUnit in dartUnits) {
- scheduleNotificationOperations(this, file, errorInfo.lineInfo,
- context, null, dartUnit, errorInfo.errors);
- scheduleIndexOperation(this, file, context, dartUnit);
+ List<Source> sources = context.getSourcesWithFullName(file);
+ sources.forEach((Source source) {
+ if (context.handleContentsChanged(
+ source, oldContents, newContents, true)) {
+ schedulePerformAnalysisOperation(context);
+ } else {
+ // When the client sends any change for a source, we should resend
+ // subscribed notifications, even if there were no changes in the
+ // source contents.
+ // TODO(scheglov) consider checking if there are subscriptions.
+ if (AnalysisEngine.isDartFileName(file)) {
+ List<CompilationUnit> dartUnits =
+ context.ensureResolvedDartUnits(source);
+ if (dartUnits != null) {
+ AnalysisErrorInfo errorInfo = context.getErrors(source);
+ for (var dartUnit in dartUnits) {
+ scheduleNotificationOperations(this, file, errorInfo.lineInfo,
+ context, null, dartUnit, errorInfo.errors);
+ scheduleIndexOperation(this, file, context, dartUnit);
+ }
+ } else {
+ schedulePerformAnalysisOperation(context);
}
- } else {
- schedulePerformAnalysisOperation(context);
}
}
- }
+ });
}
});
}
@@ -1073,21 +1081,24 @@
}
/**
- * Return the [Source] of the Dart file with the given [path], assuming that
- * we do not know the context in which the path should be interpreted.
+ * Returns the [CompilationUnit] of the Dart file with the given [source] that
+ * should be used to resend notifications for already resolved unit.
+ * Returns `null` if the file is not a part of any context, library has not
+ * been yet resolved, or any problem happened.
*/
- Source _getSourceWithoutContext(String path) {
- // try SDK
- {
- Uri uri = resourceProvider.pathContext.toUri(path);
- Source sdkSource = defaultSdk.fromFileUri(uri);
- if (sdkSource != null) {
- return sdkSource;
- }
+ CompilationUnit _getResolvedCompilationUnitToResendNotification(
+ AnalysisContext context, Source source) {
+ List<Source> librarySources = context.getLibrariesContaining(source);
+ if (librarySources.isEmpty) {
+ return null;
}
- // file-based source
- File file = resourceProvider.getResource(path);
- return file.createSource();
+ // if library has not been resolved yet, the unit will be resolved later
+ Source librarySource = librarySources[0];
+ if (context.getLibraryElement(librarySource) == null) {
+ return null;
+ }
+ // if library has been already resolved, resolve unit
+ return context.resolveCompilationUnit2(source, librarySource);
}
/**
@@ -1156,6 +1167,27 @@
}
/**
+ * Information about a file - an [AnalysisContext] that analyses the file,
+ * and the [Source] representing the file in this context.
+ */
+class ContextSourcePair {
+ /**
+ * A context that analysis the file.
+ * May be `null` if the file is not analyzed by any context.
+ */
+ final AnalysisContext context;
+
+ /**
+ * The source that corresponds to the file.
+ * May be `null` if the file is not a regular file.
+ * If the file cannot be found in the [context], then it has a `file` uri.
+ */
+ final Source source;
+
+ ContextSourcePair(this.context, this.source);
+}
+
+/**
* A [PriorityChangeEvent] indicates the set the priority files has changed.
*/
class PriorityChangeEvent {
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 8af64b3..ec26ccb 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -18,6 +18,7 @@
import 'package:analyzer/src/generated/source_io.dart';
import 'package:path/path.dart' as pathos;
import 'package:watcher/watcher.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
/**
* The name of `packages` folders.
@@ -301,7 +302,14 @@
if (info.excludesResource(folder) || folder.shortName.startsWith('.')) {
return;
}
- List<Resource> children = folder.getChildren();
+ List<Resource> children = null;
+ try {
+ children = folder.getChildren();
+ } on FileSystemException {
+ // The directory either doesn't exist or cannot be read. Either way, there
+ // are no children that need to be added.
+ return;
+ }
for (Resource child in children) {
String path = child.path;
// ignore excluded files or folders
@@ -393,11 +401,16 @@
}
// try to find subfolders with pubspec files
List<_ContextInfo> children = <_ContextInfo>[];
- for (Resource child in folder.getChildren()) {
- if (child is Folder) {
- List<_ContextInfo> childContexts = _createContexts(child, true);
- children.addAll(childContexts);
+ try {
+ for (Resource child in folder.getChildren()) {
+ if (child is Folder) {
+ List<_ContextInfo> childContexts = _createContexts(child, true);
+ children.addAll(childContexts);
+ }
}
+ } on FileSystemException {
+ // The directory either doesn't exist or cannot be read. Either way, there
+ // are no subfolders that need to be added.
}
// no pubspec, done
if (withPubspecOnly) {
@@ -427,8 +440,8 @@
*/
void _destroyContext(Folder folder) {
_contexts[folder].changeSubscription.cancel();
- _contexts.remove(folder);
removeContext(folder);
+ _contexts.remove(folder);
}
/**
@@ -466,6 +479,10 @@
}
void _handleWatchEvent(Folder folder, _ContextInfo info, WatchEvent event) {
+ // TODO(brianwilkerson) If a file is explicitly included in one context
+ // but implicitly referenced in another context, we will only send a
+ // changeSet to the context that explicitly includes the file (because
+ // that's the only context that's watching the file).
_instrumentationService.logWatchEvent(
folder.path, event.path, event.type.toString());
String path = event.path;
@@ -509,19 +526,23 @@
_mergeContext(info);
return;
}
- Source source = info.sources[path];
- if (source != null) {
+ List<Source> sources = info.context.getSourcesWithFullName(path);
+ if (!sources.isEmpty) {
ChangeSet changeSet = new ChangeSet();
- changeSet.removedSource(source);
+ sources.forEach((Source source) {
+ changeSet.removedSource(source);
+ });
applyChangesToContext(folder, changeSet);
info.sources.remove(path);
}
break;
case ChangeType.MODIFY:
- Source source = info.sources[path];
- if (source != null) {
+ List<Source> sources = info.context.getSourcesWithFullName(path);
+ if (!sources.isEmpty) {
ChangeSet changeSet = new ChangeSet();
- changeSet.changedSource(source);
+ sources.forEach((Source source) {
+ changeSet.changedSource(source);
+ });
applyChangesToContext(folder, changeSet);
}
break;
@@ -612,6 +633,9 @@
if (context == null) {
return source;
}
+ if (context.sourceFactory == null) {
+ return null;
+ }
Uri uri = context.sourceFactory.restoreUri(source);
return file.createSource(uri);
}
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index e6bb328..affe17c 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -174,8 +174,9 @@
new CompletionGetSuggestionsParams.fromRequest(request);
// schedule completion analysis
String completionId = (_nextCompletionId++).toString();
- AnalysisContext context = server.getAnalysisContext(params.file);
- Source source = server.getSource(params.file);
+ ContextSourcePair contextSource = server.getContextSourcePair(params.file);
+ AnalysisContext context = contextSource.context;
+ Source source = contextSource.source;
recordRequest(performance, context, source, params.offset);
if (manager == null) {
manager = completionManagerFor(context, source);
diff --git a/pkg/analysis_server/lib/src/domain_execution.dart b/pkg/analysis_server/lib/src/domain_execution.dart
index d3323c7..08ff54e 100644
--- a/pkg/analysis_server/lib/src/domain_execution.dart
+++ b/pkg/analysis_server/lib/src/domain_execution.dart
@@ -114,7 +114,8 @@
return new Response.invalidParameter(
request, 'file', 'Must not refer to a directory');
}
- Source source = server.getSource(file);
+ ContextSourcePair contextSource = server.getContextSourcePair(file);
+ Source source = contextSource.source;
uri = context.sourceFactory.restoreUri(source).toString();
return new ExecutionMapUriResult(uri: uri).toResponse(request.id);
} else if (uri != null) {
@@ -157,6 +158,9 @@
return;
}
AnalysisContext context = server.getAnalysisContext(filePath);
+ if (context == null) {
+ return;
+ }
if (AnalysisEngine.isDartFileName(filePath)) {
ExecutableKind kind = ExecutableKind.NOT_EXECUTABLE;
if (context.isClientLibrary(source)) {
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index bc23ad5..edf92e7 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -58,12 +58,14 @@
EditFormatParams params = new EditFormatParams.fromRequest(request);
String file = params.file;
- engine.AnalysisContext context = server.getAnalysisContext(file);
+ ContextSourcePair contextSource = server.getContextSourcePair(file);
+
+ engine.AnalysisContext context = contextSource.context;
if (context == null) {
return new Response.formatInvalidFile(request);
}
- Source source = server.getSource(file);
+ Source source = contextSource.source;
engine.TimestampedData<String> contents;
try {
contents = context.getContents(source);
@@ -485,8 +487,9 @@
}
}
if (kind == RefactoringKind.MOVE_FILE) {
- engine.AnalysisContext context = server.getAnalysisContext(file);
- Source source = server.getSource(file);
+ ContextSourcePair contextSource = server.getContextSourcePair(file);
+ engine.AnalysisContext context = contextSource.context;
+ Source source = contextSource.source;
refactoring = new MoveFileRefactoring(
server.resourceProvider.pathContext, searchEngine, context, source);
}
diff --git a/pkg/analysis_server/lib/src/operation/operation_analysis.dart b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
index aa751e3..ecbb97c 100644
--- a/pkg/analysis_server/lib/src/operation/operation_analysis.dart
+++ b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
@@ -24,7 +24,9 @@
*/
void scheduleIndexOperation(AnalysisServer server, String file,
AnalysisContext context, CompilationUnit dartUnit) {
- server.addOperation(new _DartIndexOperation(context, file, dartUnit));
+ if (server.index != null) {
+ server.addOperation(new _DartIndexOperation(context, file, dartUnit));
+ }
}
/**
@@ -34,16 +36,6 @@
void scheduleNotificationOperations(AnalysisServer server, String file,
LineInfo lineInfo, AnalysisContext context, CompilationUnit parsedDartUnit,
CompilationUnit resolvedDartUnit, List<AnalysisError> errors) {
- // Only send notifications if the current context is the preferred
- // context for the file. This avoids redundant notification messages
- // being sent to the client (see dartbug.com/22210).
- // TODO(paulberry): note that there is a small risk that this will cause
- // notifications to be lost if the preferred context for a file changes
- // while analysis is in progress (e.g. because the client sent an
- // analysis.setAnalysisRoots message).
- if (server.getAnalysisContext(file) != context) {
- return;
- }
// Dart
CompilationUnit dartUnit =
resolvedDartUnit != null ? resolvedDartUnit : parsedDartUnit;
@@ -246,8 +238,7 @@
}
void _updateIndex(AnalysisServer server, List<ChangeNotice> notices) {
- Index index = server.index;
- if (index == null) {
+ if (server.index == null) {
return;
}
for (ChangeNotice notice in notices) {
diff --git a/pkg/analysis_server/test/analysis_server_test.dart b/pkg/analysis_server/test/analysis_server_test.dart
index 94f5e40..bd78f9f 100644
--- a/pkg/analysis_server/test/analysis_server_test.dart
+++ b/pkg/analysis_server/test/analysis_server_test.dart
@@ -14,6 +14,7 @@
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
+import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/source.dart';
@@ -34,6 +35,7 @@
MockServerChannel channel;
AnalysisServer server;
MemoryResourceProvider resourceProvider;
+ MockPackageMapProvider packageMapProvider;
/**
* Verify that getAnalysisContextForSource returns the correct contexts even
@@ -112,10 +114,10 @@
void setUp() {
channel = new MockServerChannel();
resourceProvider = new MemoryResourceProvider();
- server = new AnalysisServer(channel, resourceProvider,
- new MockPackageMapProvider(), null, new AnalysisServerOptions(),
- new MockSdk(), InstrumentationService.NULL_SERVICE,
- rethrowExceptions: true);
+ packageMapProvider = new MockPackageMapProvider();
+ server = new AnalysisServer(channel, resourceProvider, packageMapProvider,
+ null, new AnalysisServerOptions(), new MockSdk(),
+ InstrumentationService.NULL_SERVICE, rethrowExceptions: true);
}
Future test_contextDisposed() {
@@ -131,34 +133,6 @@
});
}
- test_getAnalysisContext_nested() {
- String dir1Path = '/dir1';
- String dir2Path = dir1Path + '/dir2';
- String filePath = dir2Path + '/file.dart';
- Folder dir1 = resourceProvider.newFolder(dir1Path);
- Folder dir2 = resourceProvider.newFolder(dir2Path);
- resourceProvider.newFile(filePath, 'library lib;');
-
- AnalysisContext context1 = AnalysisEngine.instance.createAnalysisContext();
- AnalysisContext context2 = AnalysisEngine.instance.createAnalysisContext();
- server.folderMap[dir1] = context1;
- server.folderMap[dir2] = context2;
-
- expect(server.getAnalysisContext(filePath), context2);
- }
-
- test_getAnalysisContext_simple() {
- String dirPath = '/dir';
- String filePath = dirPath + '/file.dart';
- Folder dir = resourceProvider.newFolder(dirPath);
- resourceProvider.newFile(filePath, 'library lib;');
-
- AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
- server.folderMap[dir] = context;
-
- expect(server.getAnalysisContext(filePath), context);
- }
-
Future test_contextsChangedEvent() {
resourceProvider.newFolder('/foo');
@@ -246,51 +220,67 @@
});
}
- /**
- * Test that having multiple analysis contexts analyze the same file doesn't
- * cause that file to receive duplicate notifications when it's modified.
- */
- Future test_no_duplicate_notifications() async {
- // Subscribe to STATUS so we'll know when analysis is done.
- server.serverServices = [ServerService.STATUS].toSet();
- resourceProvider.newFolder('/foo');
- resourceProvider.newFolder('/bar');
- resourceProvider.newFile('/foo/foo.dart', 'import "../bar/bar.dart";');
- File bar = resourceProvider.newFile('/bar/bar.dart', 'library bar;');
- server.setAnalysisRoots('0', ['/foo', '/bar'], [], {});
- Map<AnalysisService, Set<String>> subscriptions =
- <AnalysisService, Set<String>>{};
- for (AnalysisService service in AnalysisService.VALUES) {
- subscriptions[service] = <String>[bar.path].toSet();
- }
- server.setAnalysisSubscriptions(subscriptions);
- await pumpEventQueue(100);
- expect(server.statusAnalyzing, isFalse);
- channel.notificationsReceived.clear();
- server.updateContent(
- '0', {bar.path: new AddContentOverlay('library bar; void f() {}')});
- await pumpEventQueue(100);
- expect(server.statusAnalyzing, isFalse);
- expect(channel.notificationsReceived, isNotEmpty);
- Set<String> notificationTypesReceived = new Set<String>();
- for (Notification notification in channel.notificationsReceived) {
- String notificationType = notification.event;
- switch (notificationType) {
- case 'server.status':
- case 'analysis.errors':
- // It's normal for these notifications to be sent multiple times.
- break;
- case 'analysis.outline':
- // It's normal for this notification to be sent twice.
- // TODO(paulberry): why?
- break;
- default:
- if (!notificationTypesReceived.add(notificationType)) {
- fail('Notification type $notificationType received more than once');
- }
- break;
- }
- }
+ test_getContextSourcePair_nested() {
+ String dir1Path = '/dir1';
+ String dir2Path = dir1Path + '/dir2';
+ String filePath = dir2Path + '/file.dart';
+ Folder dir1 = resourceProvider.newFolder(dir1Path);
+ Folder dir2 = resourceProvider.newFolder(dir2Path);
+ resourceProvider.newFile(filePath, 'library lib;');
+
+ AnalysisContext context1 = AnalysisEngine.instance.createAnalysisContext();
+ AnalysisContext context2 = AnalysisEngine.instance.createAnalysisContext();
+ _configureSourceFactory(context1);
+ _configureSourceFactory(context2);
+ server.folderMap[dir1] = context1;
+ server.folderMap[dir2] = context2;
+
+ ContextSourcePair pair = server.getContextSourcePair(filePath);
+ Source source = pair.source;
+ expect(pair.context, same(context2));
+ expect(source, isNotNull);
+ expect(source.uri.scheme, 'file');
+ expect(source.fullName, filePath);
+ }
+
+ test_getContextSourcePair_package_inRoot() {
+ String rootPath = '/my_package';
+ String filePath = rootPath + '/lib/file.dart';
+ Folder rootFolder = resourceProvider.newFolder(rootPath);
+ resourceProvider.newFile(filePath, 'library lib;');
+
+ packageMapProvider.packageMap = <String, List<Folder>>{
+ 'my_package': <Folder>[rootFolder]
+ };
+
+ AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
+ _configureSourceFactory(context);
+ server.folderMap[rootFolder] = context;
+
+ ContextSourcePair pair = server.getContextSourcePair(filePath);
+ Source source = pair.source;
+ expect(pair.context, same(context));
+ expect(source, isNotNull);
+ expect(source.uri.scheme, 'package');
+ expect(source.fullName, filePath);
+ }
+
+ test_getContextSourcePair_simple() {
+ String dirPath = '/dir';
+ String filePath = dirPath + '/file.dart';
+ Folder dir = resourceProvider.newFolder(dirPath);
+ resourceProvider.newFile(filePath, 'library lib;');
+
+ AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
+ _configureSourceFactory(context);
+ server.folderMap[dir] = context;
+
+ ContextSourcePair pair = server.getContextSourcePair(filePath);
+ Source source = pair.source;
+ expect(pair.context, same(context));
+ expect(source, isNotNull);
+ expect(source.uri.scheme, 'file');
+ expect(source.fullName, filePath);
}
Future test_prioritySourcesChangedEvent() {
@@ -394,6 +384,14 @@
expect(response.error, isNotNull);
});
}
+
+ void _configureSourceFactory(AnalysisContext context) {
+ var resourceUriResolver = new ResourceUriResolver(resourceProvider);
+ var packageUriResolver = new PackageMapUriResolver(
+ resourceProvider, packageMapProvider.packageMap);
+ context.sourceFactory =
+ new SourceFactory([packageUriResolver, resourceUriResolver]);
+ }
}
class EchoHandler implements RequestHandler {
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index eab479f..61a3c09 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -847,6 +847,11 @@
int now = 0;
/**
+ * The analysis context that was created.
+ */
+ AnalysisContextImpl currentContext;
+
+ /**
* Map from context to the timestamp when the context was created.
*/
Map<String, int> currentContextTimestamps = <String, int>{};
@@ -888,10 +893,10 @@
currentContextFilePaths[path] = <String, int>{};
currentContextSources[path] = new HashSet<Source>();
currentContextPackageUriResolvers[path] = packageUriResolver;
- AnalysisContextImpl context = new AnalysisContextImpl();
- context.sourceFactory = new SourceFactory(
+ currentContext = new AnalysisContextImpl();
+ currentContext.sourceFactory = new SourceFactory(
packageUriResolver == null ? [] : [packageUriResolver]);
- return context;
+ return currentContext;
}
@override
@@ -913,6 +918,8 @@
expect(filePaths, contains(source.fullName));
filePaths[source.fullName] = now;
}
+
+ currentContext.applyChanges(changeSet);
}
void assertContextFiles(String contextPath, List<String> expectedFiles) {
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index d13a5a0..a41e086 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -24,6 +24,7 @@
groupSep = ' | ';
runReflectiveTests(AnalysisDomainTest);
+ runReflectiveTests(SetSubscriptionsTest);
MockServerChannel serverChannel;
MemoryResourceProvider resourceProvider;
@@ -40,7 +41,6 @@
});
group('updateContent', testUpdateContent);
- group('setSubscriptions', test_setSubscriptions);
group('AnalysisDomainHandler', () {
group('setAnalysisRoots', () {
@@ -144,69 +144,6 @@
});
}
-void test_setSubscriptions() {
- test('before analysis', () {
- AnalysisTestHelper helper = new AnalysisTestHelper();
- // subscribe
- helper.addAnalysisSubscriptionHighlights(helper.testFile);
- // create project
- helper.createSingleFileProject('int V = 42;');
- // wait, there are highlight regions
- helper.onAnalysisComplete.then((_) {
- var highlights = helper.getHighlights(helper.testFile);
- expect(highlights, isNotEmpty);
- });
- });
-
- test('after analysis', () {
- AnalysisTestHelper helper = new AnalysisTestHelper();
- // create project
- helper.createSingleFileProject('int V = 42;');
- // wait, no regions initially
- return helper.onAnalysisComplete.then((_) {
- var highlights = helper.getHighlights(helper.testFile);
- expect(highlights, isEmpty);
- // subscribe
- helper.addAnalysisSubscriptionHighlights(helper.testFile);
- // wait, has regions
- return helper.onAnalysisComplete.then((_) {
- var highlights = helper.getHighlights(helper.testFile);
- expect(highlights, isNotEmpty);
- });
- });
- });
-
- test('after analysis, no such file', () {
- AnalysisTestHelper helper = new AnalysisTestHelper();
- helper.createSingleFileProject('int V = 42;');
- return helper.onAnalysisComplete.then((_) {
- String noFile = '/no-such-file.dart';
- helper.addAnalysisSubscriptionHighlights(noFile);
- return helper.onAnalysisComplete.then((_) {
- var highlights = helper.getHighlights(noFile);
- expect(highlights, isEmpty);
- });
- });
- });
-
- test('after analysis, SDK file', () {
- AnalysisTestHelper helper = new AnalysisTestHelper();
- helper.createSingleFileProject('''
-main() {
- print(42);
-}
-''');
- return helper.onAnalysisComplete.then((_) {
- String file = '/lib/core/core.dart';
- helper.addAnalysisSubscriptionNavigation(file);
- return helper.onAnalysisComplete.then((_) {
- var navigationRegions = helper.getNavigation(file);
- expect(navigationRegions, isNotEmpty);
- });
- });
- });
-}
-
testUpdateContent() {
test('bad type', () {
AnalysisTestHelper helper = new AnalysisTestHelper();
@@ -607,3 +544,130 @@
return code as String;
}
}
+
+@reflectiveTest
+class SetSubscriptionsTest extends AbstractAnalysisTest {
+ Map<String, List<HighlightRegion>> filesHighlights = {};
+
+ void processNotification(Notification notification) {
+ if (notification.event == ANALYSIS_HIGHLIGHTS) {
+ var params = new AnalysisHighlightsParams.fromNotification(notification);
+ filesHighlights[params.file] = params.regions;
+ }
+ }
+
+ test_afterAnalysis() async {
+ addTestFile('int V = 42;');
+ createProject();
+ // wait for analysis, no results initially
+ await waitForTasksFinished();
+ expect(filesHighlights[testFile], isNull);
+ // subscribe
+ addAnalysisSubscription(AnalysisService.HIGHLIGHTS, testFile);
+ await server.onAnalysisComplete;
+ // there are results
+ expect(filesHighlights[testFile], isNotEmpty);
+ }
+
+ test_afterAnalysis_noSuchFile() async {
+ String file = '/no-such-file.dart';
+ addTestFile('// no matter');
+ createProject();
+ // wait for analysis, no results initially
+ await waitForTasksFinished();
+ expect(filesHighlights[testFile], isNull);
+ // subscribe
+ addAnalysisSubscription(AnalysisService.HIGHLIGHTS, file);
+ await server.onAnalysisComplete;
+ // there are results
+ expect(filesHighlights[file], isNull);
+ }
+
+ test_afterAnalysis_packageFile_external() async {
+ String pkgFile = '/packages/pkgA/lib/libA.dart';
+ resourceProvider.newFile(pkgFile, '''
+library lib_a;
+class A {}
+''');
+ packageMapProvider.packageMap = {
+ 'pkgA': [(resourceProvider.newFolder('/packages/pkgA/lib'))]
+ };
+ //
+ addTestFile('''
+import 'package:pkgA/libA.dart';
+main() {
+ new A();
+}
+''');
+ createProject();
+ // wait for analysis, no results initially
+ await waitForTasksFinished();
+ expect(filesHighlights[pkgFile], isNull);
+ // subscribe
+ addAnalysisSubscription(AnalysisService.HIGHLIGHTS, pkgFile);
+ await server.onAnalysisComplete;
+ // there are results
+ expect(filesHighlights[pkgFile], isNotEmpty);
+ }
+
+ test_afterAnalysis_packageFile_inRoot() async {
+ String pkgA = '/pkgA';
+ String pkgB = '/pkgA';
+ String pkgFileA = '$pkgA/lib/libA.dart';
+ String pkgFileB = '$pkgA/lib/libB.dart';
+ resourceProvider.newFile(pkgFileA, '''
+library lib_a;
+class A {}
+''');
+ resourceProvider.newFile(pkgFileB, '''
+import 'package:pkgA/libA.dart';
+main() {
+ new A();
+}
+''');
+ packageMapProvider.packageMap = {
+ 'pkgA': [
+ resourceProvider.newFolder('$pkgA/lib'),
+ resourceProvider.newFolder('$pkgB/lib')
+ ]
+ };
+ // add 'pkgA' and 'pkgB' as projects
+ {
+ resourceProvider.newFolder(projectPath);
+ handleSuccessfulRequest(
+ new AnalysisSetAnalysisRootsParams([pkgA, pkgB], []).toRequest('0'));
+ }
+ // wait for analysis, no results initially
+ await waitForTasksFinished();
+ expect(filesHighlights[pkgFileA], isNull);
+ // subscribe
+ addAnalysisSubscription(AnalysisService.HIGHLIGHTS, pkgFileA);
+ await server.onAnalysisComplete;
+ // there are results
+ expect(filesHighlights[pkgFileA], isNotEmpty);
+ }
+
+ test_afterAnalysis_sdkFile() async {
+ String file = '/lib/core/core.dart';
+ addTestFile('// no matter');
+ createProject();
+ // wait for analysis, no results initially
+ await waitForTasksFinished();
+ expect(filesHighlights[file], isNull);
+ // subscribe
+ addAnalysisSubscription(AnalysisService.HIGHLIGHTS, file);
+ await server.onAnalysisComplete;
+ // there are results
+ expect(filesHighlights[file], isNotEmpty);
+ }
+
+ test_beforeAnalysis() async {
+ addTestFile('int V = 42;');
+ createProject();
+ // subscribe
+ addAnalysisSubscription(AnalysisService.HIGHLIGHTS, testFile);
+ // wait for analysis
+ await waitForTasksFinished();
+ expect(filesHighlights[testFile], isNotEmpty);
+ }
+}
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 83dd415..20f6a41 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -192,8 +192,9 @@
sendRequest(testFile);
return pumpEventQueue().then((_) {
expect(completionDomain.manager, isNotNull);
+ ContextSourcePair contextSource = server.getContextSourcePair(testFile2);
ChangeSet changeSet = new ChangeSet();
- changeSet.changedSource(server.getSource(testFile2));
+ changeSet.changedSource(contextSource.source);
completionDomain.sourcesChanged(new SourcesChangedEvent(changeSet));
expect(completionDomain.manager, isNull);
});
@@ -617,9 +618,16 @@
: super(channel, resourceProvider, packageMapProvider, index,
analysisServerOptions, defaultSdk, instrumentationService);
+ @override
AnalysisContext getAnalysisContext(String path) {
return mockContext;
}
+
+ @override
+ ContextSourcePair getContextSourcePair(String path) {
+ ContextSourcePair pair = super.getContextSourcePair(path);
+ return new ContextSourcePair(mockContext, pair.source);
+ }
}
/**
diff --git a/pkg/analysis_server/test/mock_sdk.dart b/pkg/analysis_server/test/mock_sdk.dart
index 1967994..504ecd9 100644
--- a/pkg/analysis_server/test/mock_sdk.dart
+++ b/pkg/analysis_server/test/mock_sdk.dart
@@ -223,10 +223,9 @@
for (SdkLibrary library in LIBRARIES) {
String libraryPath = library.path;
if (filePath.replaceAll('\\', '/') == libraryPath) {
- String path = library.shortName;
try {
resource.File file = provider.getResource(uri.path);
- Uri dartUri = new Uri(scheme: 'dart', path: path);
+ Uri dartUri = Uri.parse(library.shortName);
return file.createSource(dartUri);
} catch (exception) {
return null;
diff --git a/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart b/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart
index 5f2354d..a1d86e0 100644
--- a/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart
+++ b/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart
@@ -21,6 +21,7 @@
import '../../analysis_abstract.dart';
import '../../mocks.dart';
import '../../reflective_tests.dart';
+import 'package:analysis_server/src/analysis_server.dart' show ContextSourcePair;
main() {
groupSep = ' | ';
@@ -94,8 +95,9 @@
CompletionDomainHandler domainHandler = new CompletionDomainHandler(server);
handler = domainHandler;
- AnalysisContext context = server.getAnalysisContext(params.file);
- Source source = server.getSource(params.file);
+ ContextSourcePair contextSource = server.getContextSourcePair(params.file);
+ AnalysisContext context = contextSource.context;
+ Source source = contextSource.source;
DartCompletionManager completionManager = new DartCompletionManager(context,
server.searchEngine, source, new DartCompletionCache(context, source),
null, new CommonUsageComputer(selectorRelevance));
diff --git a/pkg/analyzer/lib/file_system/memory_file_system.dart b/pkg/analyzer/lib/file_system/memory_file_system.dart
index 7f4f954..da68f08 100644
--- a/pkg/analyzer/lib/file_system/memory_file_system.dart
+++ b/pkg/analyzer/lib/file_system/memory_file_system.dart
@@ -252,15 +252,32 @@
* An in-memory implementation of [Source].
*/
class _MemoryFileSource extends Source {
- final _MemoryFile _file;
+ /**
+ * Map from encoded URI/filepath pair to a unique integer identifier. This
+ * identifier is used for equality tests and hash codes.
+ *
+ * The URI and filepath are joined into a pair by separating them with an '@'
+ * character.
+ */
+ static final Map<String, int> _idTable = new HashMap<String, int>();
+
+ final _MemoryFile file;
final Uri uri;
- _MemoryFileSource(this._file, this.uri);
+ /**
+ * The unique ID associated with this [_MemoryFileSource].
+ */
+ final int id;
+
+ _MemoryFileSource(_MemoryFile file, Uri uri)
+ : uri = uri,
+ file = file,
+ id = _idTable.putIfAbsent('$uri@${file.path}', () => _idTable.length);
@override
TimestampedData<String> get contents {
- return new TimestampedData<String>(modificationStamp, _file._content);
+ return new TimestampedData<String>(modificationStamp, file._content);
}
@override
@@ -269,10 +286,10 @@
}
@override
- String get fullName => _file.path;
+ String get fullName => file.path;
@override
- int get hashCode => _file.hashCode;
+ int get hashCode => id;
@override
bool get isInSystemLibrary => uriKind == UriKind.DART_URI;
@@ -280,14 +297,14 @@
@override
int get modificationStamp {
try {
- return _file.modificationStamp;
+ return file.modificationStamp;
} on FileSystemException catch (e) {
return -1;
}
}
@override
- String get shortName => _file.shortName;
+ String get shortName => file.shortName;
@override
UriKind get uriKind {
@@ -304,14 +321,11 @@
@override
bool operator ==(other) {
- if (other is _MemoryFileSource) {
- return other._file == _file;
- }
- return false;
+ return other is _MemoryFileSource && other.id == id;
}
@override
- bool exists() => _file.exists;
+ bool exists() => file.exists;
@override
Uri resolveRelativeUri(Uri relativeUri) {
@@ -319,7 +333,7 @@
}
@override
- String toString() => _file.toString();
+ String toString() => file.toString();
}
/**
diff --git a/pkg/analyzer/lib/src/analyzer_impl.dart b/pkg/analyzer/lib/src/analyzer_impl.dart
index 849356f..a9f72a3 100644
--- a/pkg/analyzer/lib/src/analyzer_impl.dart
+++ b/pkg/analyzer/lib/src/analyzer_impl.dart
@@ -144,31 +144,48 @@
return _analyzeSync(printMode);
}
- void prepareAnalysisContext(JavaFile sourceFile, Source source) {
+ Source computeLibrarySource() {
+ JavaFile sourceFile = new JavaFile(sourcePath);
+ Source source = sdk.fromFileUri(sourceFile.toURI());
+ if (source != null) {
+ return source;
+ }
+ source = new FileBasedSource.con2(sourceFile.toURI(), sourceFile);
+ Uri uri = context.sourceFactory.restoreUri(source);
+ if (uri == null) {
+ return source;
+ }
+ return new FileBasedSource.con2(uri, sourceFile);
+ }
+
+ /**
+ * Create and return the source factory to be used by the analysis context.
+ */
+ SourceFactory createSourceFactory() {
List<UriResolver> resolvers = [
new CustomUriResolver(options.customUrlMappings),
- new DartUriResolver(sdk),
- new FileUriResolver()
+ new DartUriResolver(sdk)
];
- // maybe add package resolver
- {
- JavaFile packageDirectory;
- if (options.packageRootPath != null) {
- packageDirectory = new JavaFile(options.packageRootPath);
- resolvers.add(new PackageUriResolver([packageDirectory]));
- } else {
- PubPackageMapProvider pubPackageMapProvider =
- new PubPackageMapProvider(PhysicalResourceProvider.INSTANCE, sdk);
- PackageMapInfo packageMapInfo = pubPackageMapProvider.computePackageMap(
- PhysicalResourceProvider.INSTANCE.getResource('.'));
- Map<String, List<Folder>> packageMap = packageMapInfo.packageMap;
- if (packageMap != null) {
- resolvers.add(new PackageMapUriResolver(
- PhysicalResourceProvider.INSTANCE, packageMap));
- }
+ if (options.packageRootPath != null) {
+ JavaFile packageDirectory = new JavaFile(options.packageRootPath);
+ resolvers.add(new PackageUriResolver([packageDirectory]));
+ } else {
+ PubPackageMapProvider pubPackageMapProvider =
+ new PubPackageMapProvider(PhysicalResourceProvider.INSTANCE, sdk);
+ PackageMapInfo packageMapInfo = pubPackageMapProvider.computePackageMap(
+ PhysicalResourceProvider.INSTANCE.getResource('.'));
+ Map<String, List<Folder>> packageMap = packageMapInfo.packageMap;
+ if (packageMap != null) {
+ resolvers.add(new PackageMapUriResolver(
+ PhysicalResourceProvider.INSTANCE, packageMap));
}
}
- sourceFactory = new SourceFactory(resolvers);
+ resolvers.add(new FileUriResolver());
+ return new SourceFactory(resolvers);
+ }
+
+ void prepareAnalysisContext() {
+ sourceFactory = createSourceFactory();
context = AnalysisEngine.instance.createAnalysisContext();
context.sourceFactory = sourceFactory;
Map<String, String> definedVariables = options.definedVariables;
@@ -191,9 +208,11 @@
contextOptions.generateSdkErrors = options.showSdkWarnings;
context.analysisOptions = contextOptions;
+ librarySource = computeLibrarySource();
+
// Create and add a ChangeSet
ChangeSet changeSet = new ChangeSet();
- changeSet.addedSource(source);
+ changeSet.addedSource(librarySource);
context.applyChanges(changeSet);
}
@@ -222,12 +241,8 @@
if (sourcePath == null) {
throw new ArgumentError("sourcePath cannot be null");
}
- JavaFile sourceFile = new JavaFile(sourcePath);
- Uri uri = getUri(sourceFile);
- librarySource = new FileBasedSource.con2(uri, sourceFile);
-
// prepare context
- prepareAnalysisContext(sourceFile, librarySource);
+ prepareAnalysisContext();
}
/// The async version of the analysis
@@ -403,24 +418,6 @@
}
/**
- * Returns the [Uri] for the given input file.
- *
- * Usually it is a `file:` [Uri], but if [file] is located in the `lib`
- * directory of the [sdk], then returns a `dart:` [Uri].
- */
- static Uri getUri(JavaFile file) {
- // may be file in SDK
- {
- Source source = sdk.fromFileUri(file.toURI());
- if (source != null) {
- return source.uri;
- }
- }
- // some generic file
- return file.toURI();
- }
-
- /**
* Convert [sourcePath] into an absolute path.
*/
static String _normalizeSourcePath(String sourcePath) {
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 4625d2c..399959c 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -451,6 +451,12 @@
void set sourceFactory(SourceFactory factory);
/**
+ * Returns a type provider for this context or throws [AnalysisException] if
+ * `dart:core` or `dart:async` cannot be resolved.
+ */
+ TypeProvider get typeProvider;
+
+ /**
* Add the given listener to the list of objects that are to be notified when various analysis
* results are produced in this context.
*
@@ -781,6 +787,12 @@
ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource);
/**
+ * Return a list of the sources being analyzed in this context whose full path
+ * is equal to the given [path].
+ */
+ List<Source> getSourcesWithFullName(String path);
+
+ /**
* Return `true` if the given source is known to be the defining compilation unit of a
* library that can be run on a client (references 'dart:html', either directly or indirectly).
*
@@ -1121,7 +1133,8 @@
void set analysisOptions(AnalysisOptions options) {
bool needsRecompute = this._options.analyzeFunctionBodiesPredicate !=
options.analyzeFunctionBodiesPredicate ||
- this._options.generateImplicitErrors != options.generateImplicitErrors ||
+ this._options.generateImplicitErrors !=
+ options.generateImplicitErrors ||
this._options.generateSdkErrors != options.generateSdkErrors ||
this._options.dart2jsHint != options.dart2jsHint ||
(this._options.hint && !options.hint) ||
@@ -1147,7 +1160,7 @@
}
this._options.analyzeFunctionBodiesPredicate =
options.analyzeFunctionBodiesPredicate;
- this._options.generateImplicitErrors = options.generateImplicitErrors;
+ this._options.generateImplicitErrors = options.generateImplicitErrors;
this._options.generateSdkErrors = options.generateSdkErrors;
this._options.dart2jsHint = options.dart2jsHint;
this._options.hint = options.hint;
@@ -2158,6 +2171,18 @@
}
@override
+ List<Source> getSourcesWithFullName(String path) {
+ List<Source> sources = <Source>[];
+ MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+ while (iterator.moveNext()) {
+ if (iterator.key.fullName == path) {
+ sources.add(iterator.key);
+ }
+ }
+ return sources;
+ }
+
+ @override
bool handleContentsChanged(
Source source, String originalContents, String newContents, bool notify) {
SourceEntry sourceEntry = _cache.get(source);
@@ -2619,20 +2644,6 @@
}
/**
- * Return `true` if errors should be produced for the given [source]. The
- * [dartEntry] associated with the source is passed in for efficiency.
- */
- bool _shouldErrorsBeAnalyzed(Source source, DartEntry dartEntry) {
- if (source.isInSystemLibrary) {
- return _generateSdkErrors;
- } else if (!dartEntry.explicitlyAdded) {
- return _generateImplicitErrors;
- } else {
- return true;
- }
- }
-
- /**
* Visit all entries of the content cache.
*/
void visitContentCache(ContentCacheVisitor visitor) {
@@ -4323,6 +4334,28 @@
}
}
+ /**
+ * Record the results produced by performing a [task] and return the cache
+ * entry associated with the results.
+ */
+ DartEntry _recordBuildUnitElementTask(BuildUnitElementTask task) {
+ Source source = task.source;
+ Source library = task.library;
+ DartEntry dartEntry = _cache.get(source);
+ CaughtException thrownException = task.exception;
+ if (thrownException != null) {
+ dartEntry.recordBuildElementErrorInLibrary(library, thrownException);
+ throw new AnalysisException('<rethrow>', thrownException);
+ }
+ dartEntry.setValueInLibrary(DartEntry.BUILT_UNIT, library, task.unit);
+ dartEntry.setValueInLibrary(
+ DartEntry.BUILT_ELEMENT, library, task.unitElement);
+ ChangeNoticeImpl notice = _getNotice(source);
+ LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO);
+ notice.setErrors(dartEntry.allErrors, lineInfo);
+ return dartEntry;
+ }
+
// /**
// * Notify all of the analysis listeners that the given source is no longer included in the set of
// * sources that are being analyzed.
@@ -4402,28 +4435,6 @@
// }
/**
- * Record the results produced by performing a [task] and return the cache
- * entry associated with the results.
- */
- DartEntry _recordBuildUnitElementTask(BuildUnitElementTask task) {
- Source source = task.source;
- Source library = task.library;
- DartEntry dartEntry = _cache.get(source);
- CaughtException thrownException = task.exception;
- if (thrownException != null) {
- dartEntry.recordBuildElementErrorInLibrary(library, thrownException);
- throw new AnalysisException('<rethrow>', thrownException);
- }
- dartEntry.setValueInLibrary(DartEntry.BUILT_UNIT, library, task.unit);
- dartEntry.setValueInLibrary(
- DartEntry.BUILT_ELEMENT, library, task.unitElement);
- ChangeNoticeImpl notice = _getNotice(source);
- LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO);
- notice.setErrors(dartEntry.allErrors, lineInfo);
- return dartEntry;
- }
-
- /**
* Given a cache entry and a library element, record the library element and other information
* gleaned from the element in the cache entry.
*
@@ -4819,6 +4830,20 @@
}
/**
+ * Return `true` if errors should be produced for the given [source]. The
+ * [dartEntry] associated with the source is passed in for efficiency.
+ */
+ bool _shouldErrorsBeAnalyzed(Source source, DartEntry dartEntry) {
+ if (source.isInSystemLibrary) {
+ return _generateSdkErrors;
+ } else if (!dartEntry.explicitlyAdded) {
+ return _generateImplicitErrors;
+ } else {
+ return true;
+ }
+ }
+
+ /**
* Create an entry for the newly added [source] and invalidate any sources
* that referenced the source before it existed.
*
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index 4a45619..5832ef8 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -472,14 +472,18 @@
} else {
_assertEquals(nodeDefault.toSource(), element.defaultValueCode);
}
+ _assertCompatibleParameter(node.parameter, element);
} else if (node is FieldFormalParameter) {
_assertTrue(element.isInitializingFormal);
+ _assertCompatibleParameters(node.parameters, element.parameters);
} else if (node is FunctionTypedFormalParameter) {
+ _assertFalse(element.isInitializingFormal);
_assertTrue(element.type is FunctionType);
FunctionType elementType = element.type;
_assertCompatibleParameters(node.parameters, element.parameters);
_assertSameType(node.returnType, elementType.returnType);
} else if (node is SimpleFormalParameter) {
+ _assertFalse(element.isInitializingFormal);
_assertSameType(node.type, element.type);
}
}
@@ -1250,7 +1254,8 @@
}
// fail if a comment change outside the bodies
if (firstPair.kind == _TokenDifferenceKind.COMMENT) {
- if (beginOffsetOld <= oldNode.offset || beginOffsetNew <= newNode.offset) {
+ if (beginOffsetOld <= oldNode.offset ||
+ beginOffsetNew <= newNode.offset) {
logger.log('Failure: comment outside a function body.');
return false;
}
diff --git a/pkg/analyzer/test/generated/engine_test.dart b/pkg/analyzer/test/generated/engine_test.dart
index 35e9660..0949de7 100644
--- a/pkg/analyzer/test/generated/engine_test.dart
+++ b/pkg/analyzer/test/generated/engine_test.dart
@@ -1280,6 +1280,26 @@
expect(_context.sourceFactory, same(_sourceFactory));
}
+ void test_getSourcesWithFullName() {
+ String filePath = '/foo/lib/file.dart';
+ List<Source> expected = <Source>[];
+ ChangeSet changeSet = new ChangeSet();
+
+ TestSourceWithUri source1 =
+ new TestSourceWithUri(filePath, Uri.parse('file://$filePath'));
+ expected.add(source1);
+ changeSet.addedSource(source1);
+
+ TestSourceWithUri source2 =
+ new TestSourceWithUri(filePath, Uri.parse('package:foo/file.dart'));
+ expected.add(source2);
+ changeSet.addedSource(source2);
+
+ _context.applyChanges(changeSet);
+ expect(
+ _context.getSourcesWithFullName(filePath), unorderedEquals(expected));
+ }
+
void test_getStatistics() {
AnalysisContextStatistics statistics = _context.statistics;
expect(statistics, isNotNull);
@@ -5649,6 +5669,11 @@
return null;
}
@override
+ List<Source> getSourcesWithFullName(String path) {
+ fail("Unexpected invocation of getSourcesWithFullName");
+ return null;
+ }
+ @override
bool handleContentsChanged(
Source source, String originalContents, String newContents, bool notify) {
fail("Unexpected invocation of handleContentsChanged");
@@ -5713,6 +5738,7 @@
void setContents(Source source, String contents) {
fail("Unexpected invocation of setContents");
}
+
@override
void visitCacheItems(void callback(Source source, SourceEntry dartEntry,
DataDescriptor rowDesc, CacheState state)) {
diff --git a/pkg/analyzer/test/generated/incremental_resolver_test.dart b/pkg/analyzer/test/generated/incremental_resolver_test.dart
index 3eb51e3..3c57a97 100644
--- a/pkg/analyzer/test/generated/incremental_resolver_test.dart
+++ b/pkg/analyzer/test/generated/incremental_resolver_test.dart
@@ -589,6 +589,118 @@
''');
}
+ void test_false_fieldFormalParameter_add() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(field);
+}
+''', r'''
+class A {
+ final field;
+ A(this.field);
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_add_function() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(field(a));
+}
+''', r'''
+class A {
+ final field;
+ A(this.field(a));
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_parameters_add() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(this.field(a));
+}
+''', r'''
+class A {
+ final field;
+ A(this.field(a, b));
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_parameters_remove() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(this.field(a, b));
+}
+''', r'''
+class A {
+ final field;
+ A(this.field(a));
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_parameters_typeEdit() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(this.field(int p));
+}
+''', r'''
+class A {
+ final field;
+ A(this.field(String p));
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_remove_default() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A([this.field = 0]);
+}
+''', r'''
+class A {
+ final field;
+ A([field = 0]);
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_remove_function() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(this.field(a));
+}
+''', r'''
+class A {
+ final field;
+ A(field(a));
+}
+''');
+ }
+
+ void test_false_fieldFormalParameter_remove_normal() {
+ _assertDoesNotMatch(r'''
+class A {
+ final field;
+ A(this.field);
+}
+''', r'''
+class A {
+ final field;
+ A(field);
+}
+''');
+ }
+
void test_false_fieldFormalParameterElement_wasSimple() {
_assertDoesNotMatch(r'''
class A {
@@ -1718,6 +1830,20 @@
''');
}
+ void test_true_fieldFormalParameter_function() {
+ _assertMatches(r'''
+class A {
+ final field;
+ A(this.field(int a, String b));
+}
+''', r'''
+class A {
+ final field;
+ A(this.field(int a, String b));
+}
+''');
+ }
+
void test_true_functionTypeAlias_list_reorder() {
_assertMatches(r'''
typedef A(int pa);
@@ -2843,6 +2969,23 @@
''');
}
+ void test_endOfLineComment_localFunction_inTopLevelVariable() {
+ _resolveUnit(r'''
+typedef int Binary(one, two, three);
+
+int Global = f((a, b, c) {
+ return 0; // Some comment
+});
+''');
+ _updateAndValidate(r'''
+typedef int Binary(one, two, three);
+
+int Global = f((a, b, c) {
+ return 0; // Some comment
+});
+''');
+ }
+
void test_endOfLineComment_outBody_add() {
_resolveUnit(r'''
main() {
@@ -2892,23 +3035,6 @@
''', expectedSuccess: false);
}
- void test_endOfLineComment_localFunction_inTopLevelVariable() {
- _resolveUnit(r'''
-typedef int Binary(one, two, three);
-
-int Global = f((a, b, c) {
- return 0; // Some comment
-});
-''');
- _updateAndValidate(r'''
-typedef int Binary(one, two, three);
-
-int Global = f((a, b, c) {
- return 0; // Some comment
-});
-''');
- }
-
void test_endOfLineComment_remove() {
_resolveUnit(r'''
main() {
diff --git a/pkg/analyzer/test/generated/test_support.dart b/pkg/analyzer/test/generated/test_support.dart
index c59d375..e5a05d0 100644
--- a/pkg/analyzer/test/generated/test_support.dart
+++ b/pkg/analyzer/test/generated/test_support.dart
@@ -615,3 +615,32 @@
_contents = value;
}
}
+
+class TestSourceWithUri extends TestSource {
+ final Uri uri;
+
+ TestSourceWithUri(String path, this.uri, [String content])
+ : super(path, content);
+
+ UriKind get uriKind {
+ if (uri == null) {
+ return UriKind.FILE_URI;
+ } else if (uri.scheme == 'dart') {
+ return UriKind.DART_URI;
+ } else if (uri.scheme == 'package') {
+ return UriKind.PACKAGE_URI;
+ }
+ return UriKind.FILE_URI;
+ }
+
+ bool operator ==(Object other) {
+ if (other is TestSource) {
+ return other.uri == uri;
+ }
+ return false;
+ }
+
+ Uri resolveRelativeUri(Uri uri) {
+ return this.uri.resolveUri(uri);
+ }
+}
diff --git a/pkg/analyzer/test/src/task/driver_test.dart b/pkg/analyzer/test/src/task/driver_test.dart
index 9a7afd9..f82d327 100644
--- a/pkg/analyzer/test/src/task/driver_test.dart
+++ b/pkg/analyzer/test/src/task/driver_test.dart
@@ -709,6 +709,11 @@
}
@override
+ List<Source> getSourcesWithFullName(String path) {
+ return baseContext.getSourcesWithFullName(path);
+ }
+
+ @override
bool handleContentsChanged(
Source source, String originalContents, String newContents, bool notify) {
return baseContext.handleContentsChanged(
diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart
index c2863c5..6540829 100644
--- a/pkg/compiler/lib/src/js/rewrite_async.dart
+++ b/pkg/compiler/lib/src/js/rewrite_async.dart
@@ -2412,7 +2412,10 @@
@override
bool visitSwitch(js.Switch node) {
loopsAndSwitches.add(node);
- bool result = visit(node.key);
+ // If the key has an `await` expression, do not transform the
+ // body of the switch.
+ visit(node.key);
+ bool result = false;
for (js.SwitchClause clause in node.cases) {
if (visit(clause)) result = true;
}
diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart
index f5eec7e..d8c586c 100644
--- a/runtime/bin/builtin.dart
+++ b/runtime/bin/builtin.dart
@@ -116,7 +116,15 @@
// Corelib 'Uri.base' implementation.
Uri _uriBase() {
- return new Uri.file(_getCurrentDirectoryPath() + "/");
+ // We are not using Dircetory.current here to limit the dependency
+ // on dart:io. This code is the same as:
+ // return new Uri.file(Directory.current.path + "/");
+ var result = _getCurrentDirectoryPath();
+ if (result is OSError) {
+ throw new FileSystemException(
+ "Getting current working directory failed", "", result);
+ }
+ return new Uri.file(result + "/");
}
_getUriBaseClosure() => _uriBase;
diff --git a/runtime/lib/core_patch.dart b/runtime/lib/core_patch.dart
index 810eb31..5a26fc5 100644
--- a/runtime/lib/core_patch.dart
+++ b/runtime/lib/core_patch.dart
@@ -24,6 +24,7 @@
// below can access it because it uses the same name suffix.
static const List<String> _enum_names = null;
String toString() => _enum_names[index];
+ int get hashCode => _enum_names[index].hashCode;
}
diff --git a/runtime/lib/isolate_patch.dart b/runtime/lib/isolate_patch.dart
index 84928be..cd71ced 100644
--- a/runtime/lib/isolate_patch.dart
+++ b/runtime/lib/isolate_patch.dart
@@ -335,6 +335,11 @@
static const _RESUME = 2;
static const _PING = 3;
static const _KILL = 4;
+ static const _ADD_EXIT = 5;
+ static const _DEL_EXIT = 6;
+ static const _ADD_ERROR = 7;
+ static const _DEL_ERROR = 8;
+ static const _ERROR_FATAL = 9;
static void _spawnFunction(SendPort readyPort, Function topLevelFunction,
@@ -367,15 +372,28 @@
}
/* patch */ void addOnExitListener(SendPort responsePort) {
- throw new UnsupportedError("addOnExitListener");
+ var msg = new List(3)
+ ..[0] = 0 // Make room for OOB message type.
+ ..[1] = _ADD_EXIT
+ ..[2] = responsePort;
+ _sendOOB(controlPort, msg);
}
/* patch */ void removeOnExitListener(SendPort responsePort) {
- throw new UnsupportedError("removeOnExitListener");
+ var msg = new List(3)
+ ..[0] = 0 // Make room for OOB message type.
+ ..[1] = _DEL_EXIT
+ ..[2] = responsePort;
+ _sendOOB(controlPort, msg);
}
/* patch */ void setErrorsFatal(bool errorsAreFatal) {
- throw new UnsupportedError("setErrorsFatal");
+ var msg = new List(4)
+ ..[0] = 0 // Make room for OOB message type.
+ ..[1] = _ERROR_FATAL
+ ..[2] = terminateCapability
+ ..[3] = errorsAreFatal;
+ _sendOOB(controlPort, msg);
}
/* patch */ void kill([int priority = BEFORE_NEXT_EVENT]) {
@@ -397,11 +415,19 @@
}
/* patch */ void addErrorListener(SendPort port) {
- throw new UnsupportedError("addErrorListener");
+ var msg = new List(3)
+ ..[0] = 0 // Make room for OOB message type.
+ ..[1] = _ADD_ERROR
+ ..[2] = port;
+ _sendOOB(controlPort, msg);
}
/* patch */ void removeErrorListener(SendPort port) {
- throw new UnsupportedError("removeErrorListener");
+ var msg = new List(3)
+ ..[0] = 0 // Make room for OOB message type.
+ ..[1] = _DEL_ERROR
+ ..[2] = port;
+ _sendOOB(controlPort, msg);
}
static Isolate _getCurrentIsolate() {
diff --git a/runtime/vm/ast_transformer.cc b/runtime/vm/ast_transformer.cc
index 87c6170..3acd1d0 100644
--- a/runtime/vm/ast_transformer.cc
+++ b/runtime/vm/ast_transformer.cc
@@ -540,37 +540,26 @@
void AwaitTransformer::VisitLoadLocalNode(LoadLocalNode* node) {
- LocalVariable* result = AddToPreambleNewTempVar(
- new(Z) LoadLocalNode(node->token_pos(), &node->local()));
- result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
+ result_ = node;
}
void AwaitTransformer::VisitStoreLocalNode(StoreLocalNode* node) {
AstNode* new_value = Transform(node->value());
- LocalVariable* result = AddToPreambleNewTempVar(
- new(Z) StoreLocalNode(node->token_pos(),
- &node->local(),
- new_value));
- result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
+ result_ = new(Z) StoreLocalNode(node->token_pos(), &node->local(), new_value);
}
void AwaitTransformer::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
- LocalVariable* result = AddToPreambleNewTempVar(
- new(Z) LoadStaticFieldNode(node->token_pos(),
- node->field()));
- result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
+ result_ = node;
}
void AwaitTransformer::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
AstNode* new_value = Transform(node->value());
- LocalVariable* result = AddToPreambleNewTempVar(
- new(Z) StoreStaticFieldNode(node->token_pos(),
- node->field(),
- new_value));
- result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result);
+ result_ = new(Z) StoreStaticFieldNode(node->token_pos(),
+ node->field(),
+ new_value);
}
@@ -612,7 +601,18 @@
void AwaitTransformer::VisitLetNode(LetNode* node) {
- // TODO(mlippautz): Check initializers and their temps.
+ // Add all the initializer nodes and their temporary variables
+ // to the preamble. The temporary variables will be captured
+ // as a side effect of being added to a scope, and the subsequent
+ // nodes that are added to the preample can access them.
+ for (intptr_t i = 0; i < node->num_temps(); i++) {
+ preamble_->scope()->AddVariable(node->TempAt(i));
+ AstNode* new_init_val = Transform(node->InitializerAt(i));
+ preamble_->Add(new(Z) StoreLocalNode(node->token_pos(),
+ node->TempAt(i),
+ new_init_val));
+ }
+ // The transformed LetNode does not have any temporary variables.
LetNode* result = new(Z) LetNode(node->token_pos());
for (intptr_t i = 0; i < node->nodes().length(); i++) {
result->AddNode(Transform(node->nodes()[i]));
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 93815ca..a7468b9 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -2098,8 +2098,7 @@
ASSERT(!mixin_type.IsNull());
ASSERT(mixin_type.HasResolvedTypeClass());
const Class& mixin_cls = Class::Handle(isolate, mixin_type.type_class());
- const Error& error = Error::Handle(mixin_cls.EnsureIsFinalized(isolate));
- ASSERT(error.IsNull());
+ FinalizeClass(mixin_cls);
// If the mixin is a mixin application alias class, there are no members to
// apply here. A new synthesized class representing the aliased mixin
// application class was inserted in the super chain of this mixin application
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 16af510..ee1a43b 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -5873,7 +5873,7 @@
LocationSummary* BinaryUint32OpInstr::MakeLocationSummary(Zone* zone,
bool opt) const {
const intptr_t kNumInputs = 2;
- const intptr_t kNumTemps = (op_kind() == Token::kMUL) ? 1 : 0;
+ const intptr_t kNumTemps = 0;
LocationSummary* summary = new(zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 5b26207..214956a 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -123,13 +123,21 @@
virtual Isolate* isolate() const { return isolate_; }
private:
- // Keep in sync with isolate_patch.dart.
+ // Keep both these enums in sync with isolate_patch.dart.
+ // The different Isolate API message types.
enum {
kPauseMsg = 1,
kResumeMsg = 2,
kPingMsg = 3,
kKillMsg = 4,
-
+ kAddExitMsg = 5,
+ kDelExitMsg = 6,
+ kAddErrorMsg = 7,
+ kDelErrorMsg = 8,
+ kErrorFatalMsg = 9,
+ };
+ // The different Isolate API message priorities for ping and kill messages.
+ enum {
kImmediateAction = 0,
kBeforeNextEventAction = 1,
kAsEventAction = 2
@@ -164,8 +172,8 @@
if (message.Length() < 2) return true;
const Object& type = Object::Handle(I, message.At(1));
if (!type.IsSmi()) return true;
- const Smi& msg_type = Smi::Cast(type);
- switch (msg_type.Value()) {
+ const intptr_t msg_type = Smi::Cast(type).Value();
+ switch (msg_type) {
case kPauseMsg: {
// [ OOB, kPauseMsg, pause capability, resume capability ]
if (message.Length() != 4) return true;
@@ -252,6 +260,45 @@
}
break;
}
+ case kAddExitMsg:
+ case kDelExitMsg:
+ case kAddErrorMsg:
+ case kDelErrorMsg: {
+ // [ OOB, msg, listener port ]
+ if (message.Length() != 3) return true;
+ const Object& obj = Object::Handle(I, message.At(2));
+ if (!obj.IsSendPort()) return true;
+ const SendPort& listener = SendPort::Cast(obj);
+ switch (msg_type) {
+ case kAddExitMsg:
+ I->AddExitListener(listener);
+ break;
+ case kDelExitMsg:
+ I->RemoveExitListener(listener);
+ break;
+ case kAddErrorMsg:
+ I->AddErrorListener(listener);
+ break;
+ case kDelErrorMsg:
+ I->RemoveErrorListener(listener);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ }
+ case kErrorFatalMsg: {
+ // [ OOB, kErrorFatalMsg, terminate capability, val ]
+ if (message.Length() != 4) return true;
+ // Check that the terminate capability has been passed correctly.
+ Object& obj = Object::Handle(I, message.At(2));
+ if (!I->VerifyTerminateCapability(obj)) return true;
+ // Get the value to be set.
+ obj = message.At(3);
+ if (!obj.IsBool()) return true;
+ I->SetErrorsFatal(Bool::Cast(obj).value());
+ break;
+ }
#if defined(DEBUG)
// Malformed OOB messages are silently ignored in release builds.
default:
@@ -417,8 +464,35 @@
Dart_ExitScope();
}
- I->object_store()->set_sticky_error(result);
- return false;
+ // Generate the error and stacktrace strings for the error message.
+ String& exc_str = String::Handle(I);
+ String& stacktrace_str = String::Handle(I);
+ if (result.IsUnhandledException()) {
+ const UnhandledException& uhe = UnhandledException::Cast(result);
+ const Instance& exception = Instance::Handle(I, uhe.exception());
+ Object& tmp = Object::Handle(I);
+ tmp = DartLibraryCalls::ToString(exception);
+ if (!tmp.IsString()) {
+ tmp = String::New(exception.ToCString());
+ }
+ exc_str ^= tmp.raw();
+
+ const Instance& stacktrace = Instance::Handle(I, uhe.stacktrace());
+ tmp = DartLibraryCalls::ToString(stacktrace);
+ if (!tmp.IsString()) {
+ tmp = String::New(stacktrace.ToCString());
+ }
+ stacktrace_str ^= tmp.raw();;
+ } else {
+ exc_str = String::New(result.ToErrorCString());
+ }
+ I->NotifyErrorListeners(exc_str, stacktrace_str);
+
+ if (I->ErrorsFatal()) {
+ I->object_store()->set_sticky_error(result);
+ return false;
+ }
+ return true;
}
@@ -449,6 +523,7 @@
origin_id_(0),
pause_capability_(0),
terminate_capability_(0),
+ errors_fatal_(true),
heap_(NULL),
object_store_(NULL),
top_exit_frame_info_(0),
@@ -514,6 +589,7 @@
main_port_(0),
pause_capability_(0),
terminate_capability_(0),
+ errors_fatal_(true),
heap_(NULL),
object_store_(NULL),
top_exit_frame_info_(0),
@@ -869,7 +945,7 @@
bool Isolate::AddResumeCapability(const Capability& capability) {
// Ensure a limit for the number of resume capabilities remembered.
- static const intptr_t kMaxResumeCapabilities = kSmiMax / (6*kWordSize);
+ static const intptr_t kMaxResumeCapabilities = kSmiMax / (6 * kWordSize);
const GrowableObjectArray& caps = GrowableObjectArray::Handle(
this, object_store()->resume_capabilities());
@@ -917,6 +993,148 @@
}
+// TODO(iposva): Remove duplicated code and start using some hash based
+// structure instead of these linear lookups.
+void Isolate::AddExitListener(const SendPort& listener) {
+ // Ensure a limit for the number of listeners remembered.
+ static const intptr_t kMaxListeners = kSmiMax / (6 * kWordSize);
+
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
+ this, object_store()->exit_listeners());
+ SendPort& current = SendPort::Handle(this);
+ intptr_t insertion_index = -1;
+ for (intptr_t i = 0; i < listeners.Length(); i++) {
+ current ^= listeners.At(i);
+ if (current.IsNull()) {
+ if (insertion_index < 0) {
+ insertion_index = i;
+ }
+ } else if (current.Id() == listener.Id()) {
+ return;
+ }
+ }
+ if (insertion_index < 0) {
+ if (listeners.Length() >= kMaxListeners) {
+ // Cannot grow the array of listeners beyond its max. Additional
+ // listeners are ignored. In practice will never happen as we will
+ // run out of memory beforehand.
+ return;
+ }
+ listeners.Add(listener);
+ } else {
+ listeners.SetAt(insertion_index, listener);
+ }
+}
+
+
+void Isolate::RemoveExitListener(const SendPort& listener) {
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
+ this, object_store()->exit_listeners());
+ SendPort& current = SendPort::Handle(this);
+ for (intptr_t i = 0; i < listeners.Length(); i++) {
+ current ^= listeners.At(i);
+ if (!current.IsNull() && (current.Id() == listener.Id())) {
+ // Remove the matching listener from the list.
+ current = SendPort::null();
+ listeners.SetAt(i, current);
+ return;
+ }
+ }
+}
+
+
+void Isolate::NotifyExitListeners() {
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
+ this, this->object_store()->exit_listeners());
+ if (listeners.IsNull()) return;
+
+ SendPort& listener = SendPort::Handle(this);
+ for (intptr_t i = 0; i < listeners.Length(); i++) {
+ listener ^= listeners.At(i);
+ if (!listener.IsNull()) {
+ Dart_Port port_id = listener.Id();
+ uint8_t* data = NULL;
+ intptr_t len = 0;
+ SerializeObject(Object::null_instance(), &data, &len, false);
+ Message* msg = new Message(port_id, data, len, Message::kNormalPriority);
+ PortMap::PostMessage(msg);
+ }
+ }
+}
+
+
+void Isolate::AddErrorListener(const SendPort& listener) {
+ // Ensure a limit for the number of listeners remembered.
+ static const intptr_t kMaxListeners = kSmiMax / (6 * kWordSize);
+
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
+ this, object_store()->error_listeners());
+ SendPort& current = SendPort::Handle(this);
+ intptr_t insertion_index = -1;
+ for (intptr_t i = 0; i < listeners.Length(); i++) {
+ current ^= listeners.At(i);
+ if (current.IsNull()) {
+ if (insertion_index < 0) {
+ insertion_index = i;
+ }
+ } else if (current.Id() == listener.Id()) {
+ return;
+ }
+ }
+ if (insertion_index < 0) {
+ if (listeners.Length() >= kMaxListeners) {
+ // Cannot grow the array of listeners beyond its max. Additional
+ // listeners are ignored. In practice will never happen as we will
+ // run out of memory beforehand.
+ return;
+ }
+ listeners.Add(listener);
+ } else {
+ listeners.SetAt(insertion_index, listener);
+ }
+}
+
+
+void Isolate::RemoveErrorListener(const SendPort& listener) {
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
+ this, object_store()->error_listeners());
+ SendPort& current = SendPort::Handle(this);
+ for (intptr_t i = 0; i < listeners.Length(); i++) {
+ current ^= listeners.At(i);
+ if (!current.IsNull() && (current.Id() == listener.Id())) {
+ // Remove the matching listener from the list.
+ current = SendPort::null();
+ listeners.SetAt(i, current);
+ return;
+ }
+ }
+}
+
+
+void Isolate::NotifyErrorListeners(const String& msg,
+ const String& stacktrace) {
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
+ this, this->object_store()->error_listeners());
+ if (listeners.IsNull()) return;
+
+ const Array& arr = Array::Handle(this, Array::New(2));
+ arr.SetAt(0, msg);
+ arr.SetAt(1, stacktrace);
+ SendPort& listener = SendPort::Handle(this);
+ for (intptr_t i = 0; i < listeners.Length(); i++) {
+ listener ^= listeners.At(i);
+ if (!listener.IsNull()) {
+ Dart_Port port_id = listener.Id();
+ uint8_t* data = NULL;
+ intptr_t len = 0;
+ SerializeObject(arr, &data, &len, false);
+ Message* msg = new Message(port_id, data, len, Message::kNormalPriority);
+ PortMap::PostMessage(msg);
+ }
+ }
+}
+
+
static void StoreError(Isolate* isolate, const Object& obj) {
ASSERT(obj.IsError());
isolate->object_store()->set_sticky_error(Error::Cast(obj));
@@ -1149,6 +1367,11 @@
StackZone stack_zone(this);
HandleScope handle_scope(this);
+ // Notify exit listeners that this isolate is shutting down.
+ if (object_store() != NULL) {
+ NotifyExitListeners();
+ }
+
// Clean up debugger resources.
debugger()->Shutdown();
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 5d61b05..5953fb2 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -74,6 +74,7 @@
class RawInt32x4;
class RawUserTag;
class SampleBuffer;
+class SendPort;
class Simulator;
class StackResource;
class StackZone;
@@ -410,6 +411,17 @@
bool AddResumeCapability(const Capability& capability);
bool RemoveResumeCapability(const Capability& capability);
+ void AddExitListener(const SendPort& listener);
+ void RemoveExitListener(const SendPort& listener);
+ void NotifyExitListeners();
+
+ void AddErrorListener(const SendPort& listener);
+ void RemoveErrorListener(const SendPort& listener);
+ void NotifyErrorListeners(const String& msg, const String& stacktrace);
+
+ bool ErrorsFatal() const { return errors_fatal_; }
+ void SetErrorsFatal(bool val) { errors_fatal_ = val; }
+
Random* random() { return &random_; }
Simulator* simulator() const { return simulator_; }
@@ -674,6 +686,7 @@
Dart_Port origin_id_; // Isolates created by spawnFunc have some origin id.
uint64_t pause_capability_;
uint64_t terminate_capability_;
+ bool errors_fatal_;
Heap* heap_;
ObjectStore* object_store_;
uword top_exit_frame_info_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 718a227..ea3c6d9 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -4911,7 +4911,8 @@
RawTypeArguments* TypeArguments::CloneUninstantiated(
- const Class& new_owner) const {
+ const Class& new_owner,
+ GrowableObjectArray* trail) const {
ASSERT(!IsNull());
ASSERT(IsFinalized());
ASSERT(!IsInstantiated());
@@ -4922,7 +4923,7 @@
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
if (!type.IsInstantiated()) {
- type = type.CloneUninstantiated(new_owner);
+ type = type.CloneUninstantiated(new_owner, trail);
}
clone.SetTypeAt(i, type);
}
@@ -14199,7 +14200,8 @@
RawAbstractType* AbstractType::CloneUninstantiated(
- const Class& new_owner) const {
+ const Class& new_owner,
+ GrowableObjectArray* trail) const {
// AbstractType is an abstract class.
UNREACHABLE();
return NULL;
@@ -14911,16 +14913,29 @@
}
-RawAbstractType* Type::CloneUninstantiated(const Class& new_owner) const {
+RawAbstractType* Type::CloneUninstantiated(const Class& new_owner,
+ GrowableObjectArray* trail) const {
ASSERT(IsFinalized());
ASSERT(!IsMalformed());
if (IsInstantiated()) {
return raw();
}
- TypeArguments& type_args = TypeArguments::Handle(arguments());
- type_args = type_args.CloneUninstantiated(new_owner);
+ // We may recursively encounter a type already being cloned, because we clone
+ // the upper bounds of its uninstantiated type arguments in the same pass.
+ Type& clone = Type::Handle();
+ clone ^= OnlyBuddyInTrail(trail);
+ if (!clone.IsNull()) {
+ return clone.raw();
+ }
const Class& type_cls = Class::Handle(type_class());
- const Type& clone = Type::Handle(Type::New(type_cls, type_args, token_pos()));
+ clone = Type::New(type_cls, TypeArguments::Handle(), token_pos());
+ TypeArguments& type_args = TypeArguments::Handle(arguments());
+ // Upper bounds of uninstantiated type arguments may form a cycle.
+ if (type_args.IsRecursive() || !type_args.IsInstantiated()) {
+ AddOnlyBuddyToTrail(&trail, clone);
+ }
+ type_args = type_args.CloneUninstantiated(new_owner, trail);
+ clone.set_arguments(type_args);
clone.SetIsFinalized();
return clone.raw();
}
@@ -15199,7 +15214,7 @@
ASSERT(!ref_type.IsTypeRef());
AbstractType& instantiated_ref_type = AbstractType::Handle();
instantiated_ref_type = ref_type.InstantiateFrom(
- instantiator_type_arguments, bound_error, trail);
+ instantiator_type_arguments, bound_error, trail);
ASSERT(!instantiated_ref_type.IsTypeRef());
instantiated_type_ref = TypeRef::New(instantiated_ref_type);
AddOnlyBuddyToTrail(&trail, instantiated_type_ref);
@@ -15207,6 +15222,24 @@
}
+RawTypeRef* TypeRef::CloneUninstantiated(const Class& new_owner,
+ GrowableObjectArray* trail) const {
+ TypeRef& cloned_type_ref = TypeRef::Handle();
+ cloned_type_ref ^= OnlyBuddyInTrail(trail);
+ if (!cloned_type_ref.IsNull()) {
+ return cloned_type_ref.raw();
+ }
+ AbstractType& ref_type = AbstractType::Handle(type());
+ ASSERT(!ref_type.IsTypeRef());
+ AbstractType& cloned_ref_type = AbstractType::Handle();
+ cloned_ref_type = ref_type.CloneUninstantiated(new_owner, trail);
+ ASSERT(!cloned_ref_type.IsTypeRef());
+ cloned_type_ref = TypeRef::New(cloned_ref_type);
+ AddOnlyBuddyToTrail(&trail, cloned_type_ref);
+ return cloned_type_ref.raw();
+}
+
+
void TypeRef::set_type(const AbstractType& value) const {
ASSERT(value.HasResolvedTypeClass());
ASSERT(!value.IsTypeRef());
@@ -15463,10 +15496,11 @@
RawAbstractType* TypeParameter::CloneUninstantiated(
- const Class& new_owner) const {
+ const Class& new_owner,
+ GrowableObjectArray* trail) const {
ASSERT(IsFinalized());
AbstractType& upper_bound = AbstractType::Handle(bound());
- upper_bound = upper_bound.CloneUninstantiated(new_owner);
+ upper_bound = upper_bound.CloneUninstantiated(new_owner, trail);
const Class& old_owner = Class::Handle(parameterized_class());
const intptr_t new_index = index() +
new_owner.NumTypeArguments() - old_owner.NumTypeArguments();
@@ -15702,16 +15736,17 @@
RawAbstractType* BoundedType::CloneUninstantiated(
- const Class& new_owner) const {
+ const Class& new_owner,
+ GrowableObjectArray* trail) const {
if (IsInstantiated()) {
return raw();
}
AbstractType& bounded_type = AbstractType::Handle(type());
- bounded_type = bounded_type.CloneUninstantiated(new_owner);
+ bounded_type = bounded_type.CloneUninstantiated(new_owner, trail);
AbstractType& upper_bound = AbstractType::Handle(bound());
- upper_bound = upper_bound.CloneUninstantiated(new_owner);
+ upper_bound = upper_bound.CloneUninstantiated(new_owner, trail);
TypeParameter& type_param = TypeParameter::Handle(type_parameter());
- type_param ^= type_param.CloneUninstantiated(new_owner);
+ type_param ^= type_param.CloneUninstantiated(new_owner, trail);
return BoundedType::New(bounded_type, upper_bound, type_param);
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index b4002ce..0b0eb5e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1596,7 +1596,9 @@
// Clone this type argument vector and clone all uninstantiated type
// arguments, changing the class owner of type parameters.
// Instantiated type arguments are shared.
- RawTypeArguments* CloneUninstantiated(const Class& new_owner) const;
+ RawTypeArguments* CloneUninstantiated(
+ const Class& new_owner,
+ GrowableObjectArray* trail = NULL) const;
// Canonicalize only if instantiated, otherwise returns 'this'.
RawTypeArguments* Canonicalize(GrowableObjectArray* trail = NULL) const;
@@ -4891,7 +4893,9 @@
// but belonging to the new owner class.
// Apply recursively to type arguments, i.e. instantiated type arguments of
// an uninstantiated type are not cloned, but shared.
- virtual RawAbstractType* CloneUninstantiated(const Class& new_owner) const;
+ virtual RawAbstractType* CloneUninstantiated(
+ const Class& new_owner,
+ GrowableObjectArray* trail = NULL) const;
virtual RawInstance* CheckAndCanonicalize(const char** error_str) const {
return Canonicalize();
@@ -5053,7 +5057,9 @@
Error* malformed_error,
GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* CloneUnfinalized() const;
- virtual RawAbstractType* CloneUninstantiated(const Class& new_owner) const;
+ virtual RawAbstractType* CloneUninstantiated(
+ const Class& new_owner,
+ GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* Canonicalize(
GrowableObjectArray* trail = NULL) const;
@@ -5172,6 +5178,9 @@
const TypeArguments& instantiator_type_arguments,
Error* bound_error,
GrowableObjectArray* trail = NULL) const;
+ virtual RawTypeRef* CloneUninstantiated(
+ const Class& new_owner,
+ GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* Canonicalize(
GrowableObjectArray* trail = NULL) const;
@@ -5253,7 +5262,9 @@
Error* bound_error,
GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* CloneUnfinalized() const;
- virtual RawAbstractType* CloneUninstantiated(const Class& new_owner) const;
+ virtual RawAbstractType* CloneUninstantiated(
+ const Class& new_owner,
+ GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* Canonicalize(
GrowableObjectArray* trail = NULL) const {
return raw();
@@ -5338,7 +5349,9 @@
Error* bound_error,
GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* CloneUnfinalized() const;
- virtual RawAbstractType* CloneUninstantiated(const Class& new_owner) const;
+ virtual RawAbstractType* CloneUninstantiated(
+ const Class& new_owner,
+ GrowableObjectArray* trail = NULL) const;
virtual RawAbstractType* Canonicalize(
GrowableObjectArray* trail = NULL) const {
return raw();
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 985f0b5..7ba0aba 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -75,6 +75,8 @@
pending_functions_(GrowableObjectArray::null()),
pending_deferred_loads_(GrowableObjectArray::null()),
resume_capabilities_(GrowableObjectArray::null()),
+ exit_listeners_(GrowableObjectArray::null()),
+ error_listeners_(GrowableObjectArray::null()),
sticky_error_(Error::null()),
empty_context_(Context::null()),
stack_overflow_(Instance::null()),
@@ -125,6 +127,8 @@
this->pending_deferred_loads_ = GrowableObjectArray::New();
this->resume_capabilities_ = GrowableObjectArray::New();
+ this->exit_listeners_ = GrowableObjectArray::New();
+ this->error_listeners_ = GrowableObjectArray::New();
Object& result = Object::Handle();
const Library& library = Library::Handle(Library::CoreLibrary());
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 23dad67..8d1519a 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -342,6 +342,14 @@
return resume_capabilities_;
}
+ RawGrowableObjectArray* exit_listeners() const {
+ return exit_listeners_;
+ }
+
+ RawGrowableObjectArray* error_listeners() const {
+ return error_listeners_;
+ }
+
RawError* sticky_error() const { return sticky_error_; }
void set_sticky_error(const Error& value) {
ASSERT(!value.IsNull());
@@ -488,6 +496,8 @@
RawGrowableObjectArray* pending_functions_;
RawGrowableObjectArray* pending_deferred_loads_;
RawGrowableObjectArray* resume_capabilities_;
+ RawGrowableObjectArray* exit_listeners_;
+ RawGrowableObjectArray* error_listeners_;
RawError* sticky_error_;
RawContext* empty_context_;
RawInstance* stack_overflow_;
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index e57d0a9..f79e6d4 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -4649,6 +4649,13 @@
to_string_func = to_string_func.Clone(cls);
enum_members.AddFunction(to_string_func);
+ // Clone the hashCode getter function from the helper class.
+ Function& hash_code_func = Function::Handle(I,
+ helper_class.LookupDynamicFunctionAllowPrivate(Symbols::hashCode()));
+ ASSERT(!hash_code_func.IsNull());
+ hash_code_func = hash_code_func.Clone(cls);
+ enum_members.AddFunction(hash_code_func);
+
cls.AddFields(enum_members.fields());
const Array& functions =
Array::Handle(Z, Array::MakeArray(enum_members.functions()));
@@ -5523,7 +5530,13 @@
accessor_end_pos = TokenPos();
ExpectToken(Token::kRBRACE);
} else if (CurrentToken() == Token::kARROW) {
+ if (is_getter && ((func_modifier & RawFunction::kGeneratorBit) != 0)) {
+ ReportError(modifier_pos,
+ "=> style getter may not be sync* or async* generator");
+ }
ConsumeToken();
+ BoolScope allow_await(&this->await_is_keyword_,
+ func_modifier != RawFunction::kNoModifier);
SkipExpr();
accessor_end_pos = TokenPos();
ExpectSemicolon();
@@ -6076,11 +6089,12 @@
TryStack* try_statement = PopTry();
ASSERT(try_stack_ == NULL); // We popped the outermost try block.
- // Finally block: closing the stream and returning. (Note: the return
- // is necessary otherwise the back-end will append a rethrow of the
- // current exception.)
+ // Finally block: closing the stream and returning. Instead of simply
+ // returning, create an await state and suspend. There may be outstanding
+ // calls to schedule the generator body. This suspension ensures that we
+ // do not repeat any code of the generator body.
// :controller.close();
- // return;
+ // suspend;
// We need to inline this code in all recorded exit points.
intptr_t node_index = 0;
SequenceNode* finally_clause = NULL;
@@ -6094,8 +6108,13 @@
Symbols::Close(),
no_args));
- ReturnNode* return_node = new(Z) ReturnNode(Scanner::kNoSourcePos);
- current_block_->statements->Add(return_node);
+ // Suspend after the close.
+ AwaitMarkerNode* await_marker = new(Z) AwaitMarkerNode();
+ await_marker->set_scope(current_block_->scope);
+ current_block_->statements->Add(await_marker);
+ ReturnNode* continuation_ret = new(Z) ReturnNode(try_end_pos);
+ continuation_ret->set_return_type(ReturnNode::kContinuationTarget);
+ current_block_->statements->Add(continuation_ret);
finally_clause = CloseBlock();
AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index);
@@ -8613,7 +8632,7 @@
ExpectToken(Token::kRPAREN);
return NULL;
}
- AstNode* condition = ParseExpr(kAllowConst, kConsumeCascades);
+ AstNode* condition = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
const intptr_t condition_end = TokenPos();
ExpectToken(Token::kRPAREN);
condition = InsertClosureCallNodes(condition);
@@ -10703,7 +10722,7 @@
if (primary_node->primary().IsFunction()) {
left = LoadClosure(primary_node);
} else if (primary_node->primary().IsTypeParameter()) {
- if (current_function().is_static()) {
+ if (ParsingStaticMember()) {
const String& name = String::ZoneHandle(Z,
TypeParameter::Cast(primary_node->primary()).name());
ReportError(primary_pos,
@@ -10789,7 +10808,7 @@
ASSERT(!type.IsMalformed());
array = new(Z) TypeNode(primary_pos, type);
} else if (primary_node->primary().IsTypeParameter()) {
- if (current_function().is_static()) {
+ if (ParsingStaticMember()) {
const String& name = String::ZoneHandle(Z,
TypeParameter::Cast(primary_node->primary()).name());
ReportError(primary_pos,
@@ -10857,7 +10876,7 @@
} else if (primary_node->primary().IsTypeParameter()) {
const String& name = String::ZoneHandle(Z,
TypeParameter::Cast(primary_node->primary()).name());
- if (current_function().is_static()) {
+ if (ParsingStaticMember()) {
// Treat as this.T(), because T is in scope.
ReportError(primary_pos,
"cannot access type parameter '%s' "
@@ -10903,7 +10922,7 @@
ASSERT(!type.IsMalformed());
left = new(Z) TypeNode(primary_pos, type);
} else if (primary_node->primary().IsTypeParameter()) {
- if (current_function().is_static()) {
+ if (ParsingStaticMember()) {
const String& name = String::ZoneHandle(Z,
TypeParameter::Cast(primary_node->primary()).name());
ReportError(primary_pos,
diff --git a/tests/compiler/dart2js/async_await_js_transform_test.dart b/tests/compiler/dart2js/async_await_js_transform_test.dart
index c301f6a..2483ceb 100644
--- a/tests/compiler/dart2js/async_await_js_transform_test.dart
+++ b/tests/compiler/dart2js/async_await_js_transform_test.dart
@@ -1015,4 +1015,52 @@
}
return thenHelper(null, __body, __completer, null);
}""");
+ testTransform("""
+ function(l) async {
+ switch(await l) {
+ case 1:
+ print(1);
+ break;
+ case 2:
+ print(1);
+ // Fallthrough
+ default:
+ print(2);
+ break;
+ }
+ }""", """
+function(l) {
+ var __goto = 0, __completer = new Completer(), __handler = 1, __currentError;
+ function __body(__errorCode, __result) {
+ if (__errorCode === 1) {
+ __currentError = __result;
+ __goto = __handler;
+ }
+ while (true)
+ switch (__goto) {
+ case 0:
+ // Function start
+ __goto = 2;
+ return thenHelper(l, __body, __completer);
+ case 2:
+ // returning from await.
+ switch (__result) {
+ case 1:
+ print(1);
+ break;
+ case 2:
+ print(1);
+ default:
+ print(2);
+ break;
+ }
+ // implicit return
+ return thenHelper(null, 0, __completer, null);
+ case 1:
+ // rethrow
+ return thenHelper(__currentError, 1, __completer);
+ }
+ }
+ return thenHelper(null, __body, __completer, null);
+}""");
}
diff --git a/tests/isolate/handle_error2_test.dart b/tests/isolate/handle_error2_test.dart
index 8a72a0e..8f5df26 100644
--- a/tests/isolate/handle_error2_test.dart
+++ b/tests/isolate/handle_error2_test.dart
@@ -47,7 +47,6 @@
main(){
asyncStart();
RawReceivePort reply = new RawReceivePort(null);
- RawReceivePort reply2 = new RawReceivePort(null);
// Create two isolates waiting for commands, with errors non-fatal.
Future iso1 = spawn(isomain1);
Future iso2 = spawn(isomain1);
diff --git a/tests/isolate/handle_error3_test.dart b/tests/isolate/handle_error3_test.dart
index f992df0..6e76625 100644
--- a/tests/isolate/handle_error3_test.dart
+++ b/tests/isolate/handle_error3_test.dart
@@ -92,7 +92,7 @@
case 2:
Expect.equals(new RangeError.value(37).toString(), "$error");
state2++;
- reply.close();
+ reply2.close();
isolate2.removeErrorListener(errorPort2.sendPort);
errorPort2.close();
asyncEnd();
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index 30a218e..bfacb76 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -11,16 +11,9 @@
[ $compiler == none || $compiler == dart2dart ]
compile_time_error_test/01: Skip # Issue 12587
-ondone_test: Fail # Not implemented yet
ping_test: Skip # Resolve test issues
ping_pause_test: Skip # Resolve test issues
-kill_test: Fail # Not implemented yet
-kill2_test: Fail # Not implemented yet
-kill3_test: Fail # Not implemented yet
-kill_self_test: Fail # Not implemented yet
-handle_error_test: Fail # Not implemented yet
-handle_error2_test: Fail # Not implemented yet
-handle_error3_test: Fail # Not implemented yet
+kill3_test: Pass, Fail # Bad test: expects total message order
message3_test/constList_identical: RuntimeError # Issue 21816
message3_test/constMap: RuntimeError # Issue 21816
@@ -113,6 +106,14 @@
spawn_uri_missing_test: SkipSlow # Times out.
isolate_current_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
function_send_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+ondone_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+kill_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+kill2_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+kill3_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+kill_self_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+handle_error_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+handle_error2_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
+handle_error3_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
[ $compiler == dartanalyzer || $compiler == dart2analyzer ]
browser/typed_data_message_test: StaticWarning
diff --git a/tests/isolate/message_enum_test.dart b/tests/isolate/message_enum_test.dart
new file mode 100644
index 0000000..7dbaec3
--- /dev/null
+++ b/tests/isolate/message_enum_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// SharedOptions=--enable-enum
+
+import 'package:expect/expect.dart';
+import "dart:isolate";
+
+enum Foo { BAR, BAZ }
+
+main() {
+ var p;
+ p = new RawReceivePort((map) {
+ Expect.equals(1, map.keys.length);
+ Expect.equals(42, map.values.first);
+ var key = map.keys.first;
+ Expect.equals(42, map[key]);
+ p.close();
+ });
+ Isolate.spawn(sendIt, p.sendPort);
+}
+
+void sendIt(port) {
+ var map = { Foo.BAR: 42 };
+ port.send(map);
+}
diff --git a/tests/language/async_await_syntax_test.dart b/tests/language/async_await_syntax_test.dart
index b1affb9..2b28e47 100644
--- a/tests/language/async_await_syntax_test.dart
+++ b/tests/language/async_await_syntax_test.dart
@@ -58,6 +58,9 @@
get sync* {} /// a12b: compile-time error
get async {} /// a12c: ok
get async* {} /// a12d: compile-time error
+get a12e sync* => null; /// a12e: compile-time error
+get a12f async* => null; /// a12f: compile-time error
+get a12g async => null; /// a12g: ok
int sync; /// a13a: ok
int sync*; /// a13b: compile-time error
@@ -123,6 +126,9 @@
get sync* {} /// b12b: compile-time error
get async {} /// b12c: ok
get async* {} /// b12d: compile-time error
+ get b12e sync* => null; /// b12e: compile-time error
+ get b12f async* => null; /// b12f: compile-time error
+ get b12g async => null; /// b12g: ok
int sync; /// b13a: ok
int sync*; /// b13b: compile-time error
@@ -219,6 +225,9 @@
a = sync; /// a12b: continued
a = async; /// a12c: continued
a = async; /// a12d: continued
+ a = a12e; /// a12e: continued
+ a = a12f; /// a12f: continued
+ a = a12g; /// a12g: continued
a = sync; /// a13a: continued
a = sync; /// a13b: continued
a = async; /// a13c: continued
@@ -258,6 +267,9 @@
a = c.sync; /// b12b: continued
a = c.async; /// b12c: continued
a = c.async; /// b12d: continued
+ a = c.b12e; /// b12e: continued
+ a = c.b12f; /// b12f: continued
+ a = c.b12g; /// b12g: continued
a = c.sync; /// b13a: continued
a = c.sync; /// b13b: continued
a = c.async; /// b13c: continued
diff --git a/tests/language/async_switch_test.dart b/tests/language/async_switch_test.dart
index 5e70ad6..1e37321 100644
--- a/tests/language/async_switch_test.dart
+++ b/tests/language/async_switch_test.dart
@@ -50,6 +50,20 @@
return k;
}
+foo4(value) async {
+ int k = 0;
+ switch(await value) {
+ case 1:
+ k += 1;
+ break;
+ case 2:
+ k += 2;
+ return 2 + k;
+ default: k = 2; /// withDefault: ok
+ }
+ return k;
+}
+
futureOf(a) async => await a;
test() async {
@@ -65,6 +79,10 @@
Expect.equals(4, await foo3(2));
Expect.equals(2, await foo3(3)); /// withDefault: ok
Expect.equals(0, await foo3(3)); /// none: ok
+ Expect.equals(1, await foo4(futureOf(1)));
+ Expect.equals(4, await foo4(futureOf(2)));
+ Expect.equals(2, await foo4(futureOf(3))); /// withDefault: ok
+ Expect.equals(0, await foo4(futureOf(3))); /// none: ok
}
void main() {
diff --git a/tests/language/async_throw_in_catch_test.dart b/tests/language/async_throw_in_catch_test.dart
index b3ecbab..6117a26 100644
--- a/tests/language/async_throw_in_catch_test.dart
+++ b/tests/language/async_throw_in_catch_test.dart
@@ -439,7 +439,7 @@
}
runTest(expectedTrace, fun, [expectedError]) async {
- Tracer tracer = new Tracer(expectedTrace);
+ Tracer tracer = new Tracer(expectedTrace, expectedTrace);
try {
await fun(tracer);
} catch (error) {
diff --git a/tests/language/await_postfix_expr_test.dart b/tests/language/await_postfix_expr_test.dart
new file mode 100644
index 0000000..fbec643
--- /dev/null
+++ b/tests/language/await_postfix_expr_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+
+post0(a) async {
+ return await a++;
+}
+
+post1(a) async {
+ return await a++ + await a++;
+}
+
+pref0(a) async {
+ return await ++a;
+}
+
+pref1(a) async {
+ return await ++a + await ++a;
+}
+
+sum(a) async {
+ var s = 0;
+ for (int i = 0; i < a.length; /* nothing */) {
+ s += a[await i++];
+ }
+ return s;
+}
+
+test() async {
+ Expect.equals(10, await post0(10));
+ Expect.equals(21, await post1(10));
+ Expect.equals(11, await pref0(10));
+ Expect.equals(23, await pref1(10));
+ Expect.equals(10, await sum([1, 2, 3, 4]));
+}
+
+main() {
+ asyncStart();
+ test().then((_) {
+ asyncEnd();
+ });
+}
diff --git a/tests/language/language.status b/tests/language/language.status
index 98c131d..194f526 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -43,12 +43,9 @@
[ $compiler == none || ($compiler == dart2dart && $builder_tag != new_backend) ]
async_throw_in_catch_test: Crash, RuntimeError # Issue 21404 or it could be a test error
-asyncstar_throw_in_catch_test: RuntimeError # Issue 21404
[ $compiler == none && ($runtime == drt || $runtime == dartium|| $runtime == ContentShellOnAndroid) ]
async_throw_in_catch_test: Timeout, Fail # Issue 21404 or it could be a test error
-asyncstar_throw_in_catch_test: RuntimeError # Issue 21404
-
[ $compiler == none && $runtime == vm ]
class_keyword_test/02: MissingCompileTimeError # Issue 13627
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index a74ffd94..cc4d7a4 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -63,7 +63,6 @@
mixin_super_constructor_named_test/01: MissingCompileTimeError # Issue 19576
mixin_super_constructor_positionals_test/01: MissingCompileTimeError # Issue 19576
reify_typevar_static_test/00: MissingCompileTimeError # Issue 21565
-reify_typevar_static_test/01: MissingCompileTimeError # Issue 21565
# Please add new failing tests before this line.
# Section below is for invalid tests.
diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status
index bf70695..9694935 100644
--- a/tests/language/language_analyzer2.status
+++ b/tests/language/language_analyzer2.status
@@ -25,7 +25,7 @@
external_test/25: Fail
constructor_duplicate_final_test/03: Fail
reify_typevar_static_test/00: MissingCompileTimeError # Issue 21565
-reify_typevar_static_test/01: MissingCompileTimeError # Issue 21565
+regress_22700_test: Crash # Issue 22700
# Please add new failing tests before this line.
# Section below is for invalid tests.
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index aead0c9..122beb3 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -11,6 +11,8 @@
await_for_test: Skip # Jsshell does not provide periodic timers, Issue 7728
[ $compiler == dart2js || $compiler == dart2dart ]
+regress_22700_test: CompileTimeError # Issue 22700
+reify_typevar_static_test/01: CompileTimeError # Issue 22700
symbol_literal_test/*: Fail # Issue 21825
constructor_duplicate_final_test/01: Fail # Issue 13363
constructor_duplicate_final_test/02: Fail # Issue 13363
@@ -53,6 +55,7 @@
vm/*: Skip # Issue 12699
[ $compiler == dart2js && $checked ]
+await_postfix_expr_test: Fail # Issue 22742
type_variable_bounds_test/02: Fail # Issue 12702
type_variable_bounds2_test/01: Fail # Issue 12702
type_variable_bounds2_test/04: Fail # Issue 12702
@@ -182,7 +185,6 @@
label_test: Skip
[ $compiler == dart2dart && $builder_tag == new_backend ]
-asyncstar_throw_in_catch_test: RuntimeError # Issue 21404
async_await_syntax_test/a03a: CompileTimeError # Issue 21404
async_await_syntax_test/a03b: CompileTimeError # Issue 21404
async_await_syntax_test/a11d: CompileTimeError # Issue 21404
diff --git a/tests/language/regress_22666_test.dart b/tests/language/regress_22666_test.dart
new file mode 100644
index 0000000..bd19d39
--- /dev/null
+++ b/tests/language/regress_22666_test.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+class A extends Object with LinkedListEntry<A> { }
+
+main() => new A();
diff --git a/tests/language/regress_22700_test.dart b/tests/language/regress_22700_test.dart
new file mode 100644
index 0000000..0be42f3
--- /dev/null
+++ b/tests/language/regress_22700_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+class WrapT<T> {
+ Type get type => T;
+}
+
+printAndCheck(t) {
+ print(t);
+ Expect.equals(String, t);
+}
+
+class MyClass<T> {
+ factory MyClass.works() {
+ Type t = new WrapT<T>().type;
+ printAndCheck(t);
+ }
+
+ factory MyClass.works2() {
+ printAndCheck(T);
+ }
+}
+
+main() {
+ new MyClass<String>.works();
+ new MyClass<String>.works2();
+}
diff --git a/tests/language/regress_22719_test.dart b/tests/language/regress_22719_test.dart
new file mode 100644
index 0000000..e6cf84d
--- /dev/null
+++ b/tests/language/regress_22719_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+import 'package:expect/expect.dart';
+
+abstract class A {
+}
+
+abstract class B extends Object with IterableMixin<int> {
+ Iterator<int> get iterator;
+}
+
+abstract class C extends A with IterableMixin<int> implements B {
+ final list = [1,2,3,4,5];
+ Iterator<int> get iterator => list.iterator;
+}
+
+class D extends C {
+}
+
+void main() {
+ var d = new D();
+ var expected = 1;
+ for(var i in d) {
+ Expect.equals(expected, i);
+ expected += 1;
+ }
+}
+
diff --git a/tests/language/regress_22728_test.dart b/tests/language/regress_22728_test.dart
new file mode 100644
index 0000000..b4f32c5
--- /dev/null
+++ b/tests/language/regress_22728_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+bool assertsChecked() {
+ bool checked = false;
+ try {
+ assert(false);
+ } on AssertionError catch (error) {
+ checked = true;
+ }
+ return checked;
+}
+
+main() async {
+ bool fault = false;
+ try {
+ assert(await false);
+ } on AssertionError catch (error) {
+ fault = true;
+ }
+ Expect.equals(assertsChecked(), fault);
+}
diff --git a/tests/language/reify_typevar_static_test.dart b/tests/language/reify_typevar_static_test.dart
index 05d9a39..ed510f0 100644
--- a/tests/language/reify_typevar_static_test.dart
+++ b/tests/language/reify_typevar_static_test.dart
@@ -12,7 +12,7 @@
b ? T : /// 00: compile-time error
null;
factory C.factoryConstructor(bool b) => new C(
- b ? T : /// 01: compile-time error
+ b ? T : /// 01: ok
null);
C.redirectingConstructor(bool b) : this(
b ? T : /// 02: ok
diff --git a/tests/language/vm/regress_22693_vm_test.dart b/tests/language/vm/regress_22693_vm_test.dart
new file mode 100644
index 0000000..1cd07f9
--- /dev/null
+++ b/tests/language/vm/regress_22693_vm_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// Test location summary for Uint32 multiplication.
+// VMOptions=--optimization-counter-threshold=10 --no-use-osr
+
+const MASK = 0xFFFFFFFF;
+
+uint32Mul(x, y) => (x * y) & MASK;
+
+main() {
+ for (var i = 0; i < 20; i++) uint32Mul((1 << 63) - 1, 1);
+}
diff --git a/tests/lib/mirrors/enum_test.dart b/tests/lib/mirrors/enum_test.dart
index 7b265a0..815d167 100644
--- a/tests/lib/mirrors/enum_test.dart
+++ b/tests/lib/mirrors/enum_test.dart
@@ -46,6 +46,7 @@
'Variable(s(SPADES) in s(Suite), static, final)',
'Variable(s(HEARTS) in s(Suite), static, final)',
'Variable(s(values) in s(Suite), static, final)',
+ 'Method(s(hashCode) in s(Suite), getter)',
'Method(s(toString) in s(Suite))'],
reflectClass(Suite).declarations.values
.where((d) => !d.isPrivate).map(stringify));
diff --git a/tools/VERSION b/tools/VERSION
index 9f2b03f..fa3745a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 9
PATCH 0
PRERELEASE 10
-PRERELEASE_PATCH 4
+PRERELEASE_PATCH 5