diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart index 706b38290e01..77a5aad95326 100644 --- a/pkg/analysis_server/lib/src/analysis_server.dart +++ b/pkg/analysis_server/lib/src/analysis_server.dart @@ -585,15 +585,17 @@ class AnalysisServer { return units; } // add a unit for each unit/library combination - Source unitSource = contextSource.source; - List librarySources = context.getLibrariesContaining(unitSource); - for (Source librarySource in librarySources) { - CompilationUnit unit = - context.resolveCompilationUnit2(unitSource, librarySource); - if (unit != null) { - units.add(unit); + runWithWorkingCacheSize(context, () { + Source unitSource = contextSource.source; + List librarySources = context.getLibrariesContaining(unitSource); + for (Source librarySource in librarySources) { + CompilationUnit unit = + context.resolveCompilationUnit2(unitSource, librarySource); + if (unit != null) { + units.add(unit); + } } - } + }); // done return units; } @@ -1183,7 +1185,9 @@ class AnalysisServer { return null; } // if library has been already resolved, resolve unit - return context.resolveCompilationUnit2(source, librarySource); + return runWithWorkingCacheSize(context, () { + return context.resolveCompilationUnit2(source, librarySource); + }); } /** diff --git a/pkg/analysis_server/lib/src/operation/operation_analysis.dart b/pkg/analysis_server/lib/src/operation/operation_analysis.dart index 243384fd5ba6..7f0fe1a84bb1 100644 --- a/pkg/analysis_server/lib/src/operation/operation_analysis.dart +++ b/pkg/analysis_server/lib/src/operation/operation_analysis.dart @@ -19,6 +19,24 @@ import 'package:analyzer/src/generated/error.dart'; import 'package:analyzer/src/generated/html.dart'; import 'package:analyzer/src/generated/source.dart'; +/** + * Runs the given function [f] with the working cache size in [context]. + * Returns the result of [f] invocation. + */ +runWithWorkingCacheSize(AnalysisContext context, f()) { + int currentCacheSize = context.analysisOptions.cacheSize; + if (currentCacheSize < PerformAnalysisOperation.WORKING_CACHE_SIZE) { + setCacheSize(context, PerformAnalysisOperation.WORKING_CACHE_SIZE); + try { + return f(); + } finally { + setCacheSize(context, currentCacheSize); + } + } else { + return f(); + } +} + /** * Schedules indexing of the given [file] using the resolved [dartUnit]. */ @@ -151,6 +169,16 @@ void sendAnalysisNotificationOverrides( }); } +/** + * Sets the cache size in the given [context] to the given value. + */ +void setCacheSize(AnalysisContext context, int cacheSize) { + AnalysisOptionsImpl options = + new AnalysisOptionsImpl.con1(context.analysisOptions); + options.cacheSize = cacheSize; + context.analysisOptions = options; +} + /** * Runs the given notification producing function [f], catching exceptions. */ @@ -207,13 +235,13 @@ class PerformAnalysisOperation extends ServerOperation { // sendStatusNotification(context.toString(), taskDescription); // }); if (!isContinue) { - _setCacheSize(WORKING_CACHE_SIZE); + setCacheSize(context, WORKING_CACHE_SIZE); } // prepare results AnalysisResult result = context.performAnalysisTask(); List notices = result.changeNotices; if (notices == null) { - _setCacheSize(IDLE_CACHE_SIZE); + setCacheSize(context, IDLE_CACHE_SIZE); server.sendContextAnalysisDoneNotifications( context, AnalysisDoneReason.COMPLETE); return; @@ -245,13 +273,6 @@ class PerformAnalysisOperation extends ServerOperation { } } - void _setCacheSize(int cacheSize) { - AnalysisOptionsImpl options = - new AnalysisOptionsImpl.con1(context.analysisOptions); - options.cacheSize = cacheSize; - context.analysisOptions = options; - } - void _updateIndex(AnalysisServer server, List notices) { if (server.index == null) { return; diff --git a/pkg/analysis_server/lib/src/services/index/store/codec.dart b/pkg/analysis_server/lib/src/services/index/store/codec.dart index 0283594d1e3a..f9190f084188 100644 --- a/pkg/analysis_server/lib/src/services/index/store/codec.dart +++ b/pkg/analysis_server/lib/src/services/index/store/codec.dart @@ -81,21 +81,17 @@ class ElementCodec { for (Source unitSource in unitSources) { List libSources = context.getLibrariesContaining(unitSource); for (Source libSource in libSources) { - LibraryElement libraryElement = context.getLibraryElement(libSource); - if (libraryElement == null) { + CompilationUnitElement unitElement = + context.getCompilationUnitElement(unitSource, libSource); + if (unitElement == null) { return null; } if (kindId == ElementKind.LIBRARY.ordinal) { - return libraryElement; + return unitElement.library; } else if (kindId == ElementKind.COMPILATION_UNIT.ordinal) { - for (CompilationUnitElement unit in libraryElement.units) { - if (unit.source.fullName == filePath) { - return unit; - } - } - return null; + return unitElement; } else { - Element element = libraryElement.getElementAt(offset); + Element element = unitElement.getElementAt(offset); if (element == null) { return null; } diff --git a/pkg/analyzer/lib/instrumentation/instrumentation.dart b/pkg/analyzer/lib/instrumentation/instrumentation.dart index 83b863594997..ccb21491e2e0 100644 --- a/pkg/analyzer/lib/instrumentation/instrumentation.dart +++ b/pkg/analyzer/lib/instrumentation/instrumentation.dart @@ -4,6 +4,8 @@ library instrumentation; +import 'dart:convert'; + /** * A container with analysis performance constants. */ @@ -62,6 +64,8 @@ class InstrumentationService { static const String TAG_PERFORMANCE = 'Perf'; static const String TAG_REQUEST = 'Req'; static const String TAG_RESPONSE = 'Res'; + static const String TAG_SUBPROCESS_START = 'SPStart'; + static const String TAG_SUBPROCESS_RESULT = 'SPResult'; static const String TAG_VERSION = 'Ver'; static const String TAG_WATCH_EVENT = 'Watch'; @@ -71,6 +75,11 @@ class InstrumentationService { */ InstrumentationServer _instrumentationServer; + /** + * Counter used to generate unique ID's for [logSubprocessStart]. + */ + int _subprocessCounter = 0; + /** * Initialize a newly created instrumentation service to comunicate with the * given [instrumentationServer]. @@ -190,6 +199,43 @@ class InstrumentationService { _log(TAG_RESPONSE, response); } + /** + * Log the result of executing a subprocess. [subprocessId] should be the + * unique IDreturned by [logSubprocessStart]. + */ + void logSubprocessResult( + int subprocessId, int exitCode, String stdout, String stderr) { + if (_instrumentationServer != null) { + _instrumentationServer.log(_join([ + TAG_SUBPROCESS_RESULT, + subprocessId.toString(), + exitCode.toString(), + JSON.encode(stdout), + JSON.encode(stderr) + ])); + } + } + + /** + * Log that the given subprocess is about to be executed. Returns a unique + * identifier that can be used to identify the subprocess for later log + * entries. + */ + int logSubprocessStart( + String executablePath, List arguments, String workingDirectory) { + int subprocessId = _subprocessCounter++; + if (_instrumentationServer != null) { + _instrumentationServer.log(_join([ + TAG_SUBPROCESS_START, + subprocessId.toString(), + executablePath, + workingDirectory, + JSON.encode(arguments) + ])); + } + return subprocessId; + } + /** * Signal that the client has started analysis server. * This method should be invoked exactly one time. diff --git a/pkg/analyzer/lib/source/pub_package_map_provider.dart b/pkg/analyzer/lib/source/pub_package_map_provider.dart index e16fbefac6ee..a78d4ad37293 100644 --- a/pkg/analyzer/lib/source/pub_package_map_provider.dart +++ b/pkg/analyzer/lib/source/pub_package_map_provider.dart @@ -156,8 +156,15 @@ class PubPackageMapProvider implements PackageMapProvider { * Run pub list to determine the packages and input files. */ io.ProcessResult _runPubListDefault(Folder folder) { - return io.Process.runSync(sdk.pubExecutable.getAbsolutePath(), [ - PUB_LIST_COMMAND - ], workingDirectory: folder.path); + String executablePath = sdk.pubExecutable.getAbsolutePath(); + List arguments = [PUB_LIST_COMMAND]; + String workingDirectory = folder.path; + int subprocessId = AnalysisEngine.instance.instrumentationService + .logSubprocessStart(executablePath, arguments, workingDirectory); + io.ProcessResult result = io.Process.runSync(executablePath, arguments, + workingDirectory: workingDirectory); + AnalysisEngine.instance.instrumentationService.logSubprocessResult( + subprocessId, result.exitCode, result.stdout, result.stderr); + return result; } } diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart index 9c94c9284016..861b1ea2f5e6 100644 --- a/pkg/analyzer/lib/src/generated/element.dart +++ b/pkg/analyzer/lib/src/generated/element.dart @@ -1212,6 +1212,11 @@ abstract class CompilationUnitElement implements Element, UriReferencedElement { */ List get types; + /** + * Return the element at the given [offset], maybe `null` if no such element. + */ + Element getElementAt(int offset); + /** * Return the enum defined in this compilation unit that has the given [name], * or `null` if this compilation unit does not define an enum with the given @@ -1279,6 +1284,11 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl List _variables = TopLevelVariableElementImpl.EMPTY_ARRAY; + /** + * A map from offsets to elements of this unit at these offsets. + */ + final Map _offsetToElementMap = new HashMap(); + /** * Initialize a newly created compilation unit element to have the given * [name]. @@ -1400,6 +1410,13 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl @override accept(ElementVisitor visitor) => visitor.visitCompilationUnitElement(this); + /** + * This method is invoked after this unit was incrementally resolved. + */ + void afterIncrementalResolution() { + _offsetToElementMap.clear(); + } + @override void appendTo(StringBuffer buffer) { if (source == null) { @@ -1450,6 +1467,14 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl return null; } + @override + Element getElementAt(int offset) { + if (_offsetToElementMap.isEmpty) { + accept(new _BuildOffsetToElementMap(_offsetToElementMap)); + } + return _offsetToElementMap[offset]; + } + @override ClassElement getEnum(String enumName) { for (ClassElement enumDeclaration in _enums) { @@ -6765,11 +6790,6 @@ abstract class LibraryElement implements Element { */ List get visibleLibraries; - /** - * Return the element at the given [offset], maybe `null` if no such element. - */ - Element getElementAt(int offset); - /** * Return a list containing all of the imports that share the given [prefix], * or an empty array if there are no such imports. @@ -6839,11 +6859,6 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement { */ FunctionElement _loadLibraryFunction; - /** - * A map from offsets to elements of this library at these offsets. - */ - final Map _offsetToElementMap = new HashMap(); - /** * The export [Namespace] of this library, `null` if it has not been * computed yet. @@ -7119,13 +7134,6 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement { @override accept(ElementVisitor visitor) => visitor.visitLibraryElement(this); - /** - * This method is invoked after this library was incrementally resolved. - */ - void afterIncrementalResolution() { - _offsetToElementMap.clear(); - } - @override ElementImpl getChild(String identifier) { if ((_definingCompilationUnit as CompilationUnitElementImpl).identifier == @@ -7150,14 +7158,6 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement { return null; } - @override - Element getElementAt(int offset) { - if (_offsetToElementMap.isEmpty) { - accept(new _BuildOffsetToElementMap(_offsetToElementMap)); - } - return _offsetToElementMap[offset]; - } - @override List getImportsWithPrefix(PrefixElement prefixElement) { int count = _imports.length; diff --git a/pkg/analyzer/lib/src/generated/element_handle.dart b/pkg/analyzer/lib/src/generated/element_handle.dart index 4c5acae3b40f..0cb42ece90c3 100644 --- a/pkg/analyzer/lib/src/generated/element_handle.dart +++ b/pkg/analyzer/lib/src/generated/element_handle.dart @@ -218,6 +218,11 @@ class CompilationUnitElementHandle extends ElementHandle @override int get uriOffset => actualElement.uriOffset; + @override + Element getElementAt(int offset) { + return actualElement.getElementAt(offset); + } + @override ClassElement getEnum(String enumName) => actualElement.getEnum(enumName); @@ -802,9 +807,6 @@ class LibraryElementHandle extends ElementHandle implements LibraryElement { @override List get visibleLibraries => actualElement.visibleLibraries; - @override - Element getElementAt(int offset) => actualElement.getElementAt(offset); - @override List getImportsWithPrefix(PrefixElement prefixElement) => actualElement.getImportsWithPrefix(prefixElement); diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart index 1e9b61ddca6f..531ac3bc396d 100644 --- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart +++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart @@ -788,7 +788,7 @@ class IncrementalResolver { /** * The element of the compilation unit being resolved. */ - final CompilationUnitElement _definingUnit; + final CompilationUnitElementImpl _definingUnit; /** * The context the compilation unit being resolved in. @@ -887,8 +887,8 @@ class IncrementalResolver { _generateLints(rootNode); // update entry errors _updateEntry(); - // notify library - _definingLibrary.afterIncrementalResolution(); + // notify unit + _definingUnit.afterIncrementalResolution(); // OK return true; } finally { diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index d9cc380a9492..ad791347e13e 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -4566,14 +4566,14 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { } // ignore places where the element is not actually used if (node.parent is TypeName) { - AstNode parent2 = node.parent.parent; - if (parent2 is IsExpression) { - return; - } - // We need to instantiate/extend/implement a class to actually use it. - // OTOH, function type aliases are used to define closure structures. - if (parent2 is VariableDeclarationList && element is ClassElement) { - return; + if (element is ClassElement) { + AstNode parent2 = node.parent.parent; + if (parent2 is IsExpression) { + return; + } + if (parent2 is VariableDeclarationList) { + return; + } } } // OK diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart index 7b173af9d20a..6afb5dcb2e44 100644 --- a/pkg/analyzer/test/generated/resolver_test.dart +++ b/pkg/analyzer/test/generated/resolver_test.dart @@ -3529,6 +3529,20 @@ main() { verify([source]); } + void test_unusedElement_functionTypeAlias_isUsed_isExpression() { + enableUnusedElement = true; + Source source = addSource(r''' +typedef _F(a, b); +main(f) { + if (f is _F) { + print('F'); + } +}'''); + resolve(source); + assertNoErrors(source); + verify([source]); + } + void test_unusedElement_functionTypeAlias_isUsed_reference() { enableUnusedElement = true; Source source = addSource(r''' diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart index 4aaa293a517a..ac68fbd45f82 100644 --- a/pkg/compiler/lib/src/closure.dart +++ b/pkg/compiler/lib/src/closure.dart @@ -1069,16 +1069,12 @@ class ClosureTranslator extends Visitor { inTryStatement = oldInTryStatement; } - visitForIn(ForIn node) { - if (node.awaitToken != null) { - // An `await for` loop is enclosed in an implicit try-finally. - bool oldInTryStatement = inTryStatement; - inTryStatement = true; - visitLoop(node); - inTryStatement = oldInTryStatement; - } else { - visitLoop(node); - } + visitAsyncForIn(AsyncForIn node) { + // An `await for` loop is enclosed in an implicit try-finally. + bool oldInTryStatement = inTryStatement; + inTryStatement = true; + visitLoop(node); + inTryStatement = oldInTryStatement; } } diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index 5bd9aeede472..6bbcd29a28ca 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart @@ -530,6 +530,9 @@ class ResolutionCallbacks { /// Register that [node] is a call to `assert`. void onAssert(Send node, Registry registry) {} + /// Register that an 'await for' has been seen. + void onAsyncForIn(AsyncForIn node, Registry registry) {} + /// Called during resolution to notify to the backend that the /// program uses string interpolation. void onStringInterpolation(Registry registry) {} diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart index 289b230a7c35..a433c4665ed9 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart @@ -417,7 +417,12 @@ abstract class IrBuilderVisitor extends SemanticVisitor closureScope: getClosureScopeForNode(node)); } - visitForIn(ast.ForIn node) { + visitAsyncForIn(ast.AsyncForIn node) { + // await for is not yet implemented. + return giveup(node, 'await for'); + } + + visitSyncForIn(ast.SyncForIn node) { // [node.declaredIdentifier] can be either an [ast.VariableDefinitions] // (defining a new local variable) or a send designating some existing // variable. diff --git a/pkg/compiler/lib/src/dart_backend/backend_ast_to_frontend_ast.dart b/pkg/compiler/lib/src/dart_backend/backend_ast_to_frontend_ast.dart index 8e29ca084fa4..b15a4e2fbb01 100644 --- a/pkg/compiler/lib/src/dart_backend/backend_ast_to_frontend_ast.dart +++ b/pkg/compiler/lib/src/dart_backend/backend_ast_to_frontend_ast.dart @@ -775,11 +775,10 @@ class TreePrinter { } else { left = makeVariableDeclarations(stmt.leftHandValue); } - return new tree.ForIn( + return new tree.SyncForIn( left, makeExpression(stmt.expression), makeStatement(stmt.body, shortIf: shortIf), - awaitToken, forToken, inToken); } else if (stmt is FunctionDeclaration) { diff --git a/pkg/compiler/lib/src/inferrer/inferrer_visitor.dart b/pkg/compiler/lib/src/inferrer/inferrer_visitor.dart index a969baab43bb..889827e9b86c 100644 --- a/pkg/compiler/lib/src/inferrer/inferrer_visitor.dart +++ b/pkg/compiler/lib/src/inferrer/inferrer_visitor.dart @@ -269,6 +269,8 @@ class ArgumentsTypes extends IterableMixin { assert(this.named.values.every((T type) => type != null)); } + ArgumentsTypes.empty() : positional = const [], named = const {}; + int get length => positional.length + named.length; Iterator get iterator => new ArgumentsTypesIterator(this); @@ -713,7 +715,9 @@ abstract class InferrerVisitor T visitDynamicSend(Send node); - T visitForIn(ForIn node); + T visitAsyncForIn(AsyncForIn node); + + T visitSyncForIn(SyncForIn node); T visitReturn(Return node); diff --git a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart index d740569c0a62..d58c45d71b61 100644 --- a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart +++ b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart @@ -1189,7 +1189,7 @@ class SimpleTypeInferrerVisitor // Erroneous elements may be unresolved, for example missing getters. if (Elements.isUnresolved(element)) return types.dynamicType; // TODO(herhut): should we follow redirecting constructors here? We would - // need to pay attention of the constructor is pointing to an erroneous + // need to pay attention if the constructor is pointing to an erroneous // element. return inferrer.registerCalledElement( node, selector, outermostElement, element, arguments, @@ -1329,18 +1329,12 @@ class SimpleTypeInferrerVisitor return null; } - T visitForIn(ast.ForIn node) { - T expressionType = visit(node.expression); - Selector iteratorSelector = elements.getIteratorSelector(node); - Selector currentSelector = elements.getCurrentSelector(node); - Selector moveNextSelector = elements.getMoveNextSelector(node); - - T iteratorType = - handleDynamicSend(node, iteratorSelector, expressionType, null); - handleDynamicSend(node, moveNextSelector, - iteratorType, new ArgumentsTypes([], null)); - T currentType = - handleDynamicSend(node, currentSelector, iteratorType, null); + T handleForInLoop(ast.ForIn node, T iteratorType, Selector currentSelector, + Selector moveNextSelector) { + handleDynamicSend(node, moveNextSelector, iteratorType, + new ArgumentsTypes.empty()); + T currentType = handleDynamicSend(node, currentSelector, iteratorType, + new ArgumentsTypes.empty()); if (node.expression.isThis()) { // Any reasonable implementation of an iterator would expose @@ -1366,4 +1360,35 @@ class SimpleTypeInferrerVisitor visit(node.body); }); } + + T visitAsyncForIn(ast.AsyncForIn node) { + T expressionType = visit(node.expression); + + Selector currentSelector = elements.getCurrentSelector(node); + Selector moveNextSelector = elements.getMoveNextSelector(node); + + js.JavaScriptBackend backend = compiler.backend; + Element ctor = backend.getStreamIteratorConstructor(); + + /// Synthesize a call to the [StreamIterator] constructor. + T iteratorType = handleStaticSend(node, null, ctor, + new ArgumentsTypes([expressionType], + null)); + + return handleForInLoop(node, iteratorType, currentSelector, + moveNextSelector); + } + + T visitSyncForIn(ast.SyncForIn node) { + T expressionType = visit(node.expression); + Selector iteratorSelector = elements.getIteratorSelector(node); + Selector currentSelector = elements.getCurrentSelector(node); + Selector moveNextSelector = elements.getMoveNextSelector(node); + + T iteratorType = handleDynamicSend(node, iteratorSelector, expressionType, + new ArgumentsTypes.empty()); + + return handleForInLoop(node, iteratorType, currentSelector, + moveNextSelector); + } } diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart index b87a8926bfad..dac18f95c18e 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -674,7 +674,7 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation { } bool get isSynthesized { - // Some calls do not have a corresponding node, for example + // Some calls do not have a corresponding selector, for example // fowarding factory constructors, or synthesized super // constructor calls. We synthesize these calls but do // not create a selector for them. diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart index 6d1ead580bb1..94c62a45a1e2 100644 --- a/pkg/compiler/lib/src/js_backend/backend.dart +++ b/pkg/compiler/lib/src/js_backend/backend.dart @@ -2680,6 +2680,11 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks { registerBackendStaticInvocation(backend.assertMethod, registry); } + void onAsyncForIn(AsyncForIn node, Registry registry) { + registerBackendStaticInvocation(backend.getStreamIteratorConstructor(), + registry); + } + void onStringInterpolation(Registry registry) { assert(registry.isForResolution); registerBackendStaticInvocation( diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart index 652387b9ab14..da697606a998 100644 --- a/pkg/compiler/lib/src/resolution/members.dart +++ b/pkg/compiler/lib/src/resolution/members.dart @@ -3598,8 +3598,21 @@ class ResolverVisitor extends MappingVisitor { registry.registerDynamicInvocation(selector); } - visitForIn(ForIn node) { - LibraryElement library = enclosingElement.library; + visitAsyncForIn(AsyncForIn node) { + registry.registerAsyncForIn(node); + registry.setCurrentSelector(node, compiler.currentSelector); + registry.registerDynamicGetter(compiler.currentSelector); + registry.setMoveNextSelector(node, compiler.moveNextSelector); + registry.registerDynamicInvocation(compiler.moveNextSelector); + + visit(node.expression); + + Scope blockScope = new BlockScope(scope); + visitForInDeclaredIdentifierIn(node.declaredIdentifier, node, blockScope); + visitLoopBodyIn(node, node.body, blockScope); + } + + visitSyncForIn(SyncForIn node) { registry.setIteratorSelector(node, compiler.iteratorSelector); registry.registerDynamicGetter(compiler.iteratorSelector); registry.setCurrentSelector(node, compiler.currentSelector); @@ -3608,8 +3621,15 @@ class ResolverVisitor extends MappingVisitor { registry.registerDynamicInvocation(compiler.moveNextSelector); visit(node.expression); + Scope blockScope = new BlockScope(scope); - Node declaration = node.declaredIdentifier; + visitForInDeclaredIdentifierIn(node.declaredIdentifier, node, blockScope); + visitLoopBodyIn(node, node.body, blockScope); + } + + visitForInDeclaredIdentifierIn(Node declaration, ForIn node, + Scope blockScope) { + LibraryElement library = enclosingElement.library; bool oldAllowFinalWithoutInitializer = allowFinalWithoutInitializer; allowFinalWithoutInitializer = true; @@ -3659,7 +3679,6 @@ class ResolverVisitor extends MappingVisitor { // loopVariable may be null if it could not be resolved. registry.setForInVariable(node, loopVariable); } - visitLoopBodyIn(node, node.body, blockScope); } visitLabel(Label node) { diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart index 15082856f66d..8b37559d0bff 100644 --- a/pkg/compiler/lib/src/resolution/registry.dart +++ b/pkg/compiler/lib/src/resolution/registry.dart @@ -372,4 +372,8 @@ class ResolutionRegistry extends Registry { void registerAsyncMarker(FunctionElement element) { backend.registerAsyncMarker(element, world, this); } + + void registerAsyncForIn(AsyncForIn node) { + backend.resolutionCallbacks.onAsyncForIn(node, this); + } } diff --git a/pkg/compiler/lib/src/scanner/listener.dart b/pkg/compiler/lib/src/scanner/listener.dart index 0ef1f5db15af..568577a86c18 100644 --- a/pkg/compiler/lib/src/scanner/listener.dart +++ b/pkg/compiler/lib/src/scanner/listener.dart @@ -2161,8 +2161,13 @@ class NodeListener extends ElementListener { Statement body = popNode(); Expression expression = popNode(); Node declaredIdentifier = popNode(); - pushNode(new ForIn(declaredIdentifier, expression, body, - awaitToken, forToken, inKeyword)); + if (awaitToken == null) { + pushNode(new SyncForIn(declaredIdentifier, expression, body, + forToken, inKeyword)); + } else { + pushNode(new AsyncForIn(declaredIdentifier, expression, body, awaitToken, + forToken, inKeyword)); + } } void endMetadataStar(int count, bool forParameter) { diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart index 195b1889b24f..189b9b8a2c93 100644 --- a/pkg/compiler/lib/src/ssa/builder.dart +++ b/pkg/compiler/lib/src/ssa/builder.dart @@ -5424,8 +5424,7 @@ class SsaBuilder extends NewResolvedVisitor { return new JumpHandler(this, element); } - buildAsyncForIn(ast.ForIn node) { - assert(node.isAsync); + visitAsyncForIn(ast.AsyncForIn node) { // The async-for is implemented with a StreamIterator. HInstruction streamIterator; @@ -5491,11 +5490,7 @@ class SsaBuilder extends NewResolvedVisitor { }); } - visitForIn(ast.ForIn node) { - if (node.isAsync) { - return buildAsyncForIn(node); - } - + visitSyncForIn(ast.SyncForIn node) { // Generate a structure equivalent to: // Iterator $iter = .iterator; // while ($iter.moveNext()) { diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index fd28580862f0..e5a6c7004327 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -2914,7 +2914,8 @@ class LoopTypeVisitor extends ast.Visitor { int visitWhile(ast.While node) => HLoopBlockInformation.WHILE_LOOP; int visitFor(ast.For node) => HLoopBlockInformation.FOR_LOOP; int visitDoWhile(ast.DoWhile node) => HLoopBlockInformation.DO_WHILE_LOOP; - int visitForIn(ast.ForIn node) => HLoopBlockInformation.FOR_IN_LOOP; + int visitAsyncForIn(ast.AsyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; + int visitSyncForIn(ast.SyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; int visitSwitchStatement(ast.SwitchStatement node) => HLoopBlockInformation.SWITCH_CONTINUE_LOOP; } diff --git a/pkg/compiler/lib/src/tree/nodes.dart b/pkg/compiler/lib/src/tree/nodes.dart index 2c22466f0d57..d6222eb6c3a6 100644 --- a/pkg/compiler/lib/src/tree/nodes.dart +++ b/pkg/compiler/lib/src/tree/nodes.dart @@ -9,6 +9,7 @@ abstract class Visitor { R visitNode(Node node); + R visitAsyncForIn(AsyncForIn node) => visitLoop(node); R visitAsyncModifier(AsyncModifier node) => visitNode(node); R visitAwait(Await node) => visitExpression(node); R visitBlock(Block node) => visitStatement(node); @@ -28,7 +29,6 @@ abstract class Visitor { R visitExpression(Expression node) => visitNode(node); R visitExpressionStatement(ExpressionStatement node) => visitStatement(node); R visitFor(For node) => visitLoop(node); - R visitForIn(ForIn node) => visitLoop(node); R visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node); R visitFunctionExpression(FunctionExpression node) => visitExpression(node); R visitGotoStatement(GotoStatement node) => visitStatement(node); @@ -50,6 +50,7 @@ abstract class Visitor { R visitLiteralNull(LiteralNull node) => visitLiteral(node); R visitLiteralString(LiteralString node) => visitStringNode(node); R visitStringJuxtaposition(StringJuxtaposition node) => visitStringNode(node); + R visitSyncForIn(SyncForIn node) => visitLoop(node); R visitLoop(Loop node) => visitStatement(node); R visitMetadata(Metadata node) => visitNode(node); R visitMixinApplication(MixinApplication node) => visitNode(node); @@ -165,6 +166,8 @@ abstract class Node extends NullTreeElementMixin implements Spannable { Expression asExpression() => null; ExpressionStatement asExpressionStatement() => null; For asFor() => null; + SyncForIn asSyncForIn() => null; + AsyncForIn asAsyncForIn() => null; ForIn asForIn() => null; FunctionDeclaration asFunctionDeclaration() => null; FunctionExpression asFunctionExpression() => null; @@ -1775,25 +1778,31 @@ class ContinueStatement extends GotoStatement { accept(Visitor visitor) => visitor.visitContinueStatement(this); } -class ForIn extends Loop with StoredTreeElementMixin { +abstract class ForIn extends Loop { final Node declaredIdentifier; final Expression expression; - final Token awaitToken; final Token forToken; final Token inToken; ForIn(this.declaredIdentifier, this.expression, - Statement body, this.awaitToken, this.forToken, this.inToken) + Statement body, this.forToken, this.inToken) : super(body); - bool get isAsync => awaitToken != null; - Expression get condition => null; ForIn asForIn() => this; - accept(Visitor visitor) => visitor.visitForIn(this); + Token getEndToken() => body.getEndToken(); +} + +class SyncForIn extends ForIn with StoredTreeElementMixin { + SyncForIn(declaredIdentifier, expression, Statement body, forToken, inToken) + : super(declaredIdentifier, expression, body, forToken, inToken); + + SyncForIn asSyncForIn() => this; + + accept(Visitor visitor) => visitor.visitSyncForIn(this); visitChildren(Visitor visitor) { declaredIdentifier.accept(visitor); @@ -1801,9 +1810,27 @@ class ForIn extends Loop with StoredTreeElementMixin { body.accept(visitor); } - Token getBeginToken() => awaitToken != null ? awaitToken : forToken; + Token getBeginToken() => forToken; +} - Token getEndToken() => body.getEndToken(); +class AsyncForIn extends ForIn with StoredTreeElementMixin { + final Token awaitToken; + + AsyncForIn(declaredIdentifier, expression, + Statement body, this.awaitToken, forToken, inToken) + : super(declaredIdentifier, expression, body, forToken, inToken); + + AsyncForIn asAsyncForIn() => this; + + accept(Visitor visitor) => visitor.visitAsyncForIn(this); + + visitChildren(Visitor visitor) { + declaredIdentifier.accept(visitor); + expression.accept(visitor); + body.accept(visitor); + } + + Token getBeginToken() => awaitToken; } class Label extends Node { diff --git a/pkg/compiler/lib/src/tree/prettyprint.dart b/pkg/compiler/lib/src/tree/prettyprint.dart index 624848047815..f01963b663a7 100644 --- a/pkg/compiler/lib/src/tree/prettyprint.dart +++ b/pkg/compiler/lib/src/tree/prettyprint.dart @@ -190,8 +190,12 @@ class PrettyPrinter extends Indentation implements Visitor { visitNodeWithChildren(node, "For"); } - visitForIn(ForIn node) { - openNode(node, "ForIn", {'await': node.awaitToken}); + visitAsyncForIn(AsyncForIn node) { + openNode(node, "AsyncForIn"); + } + + visitSyncForIn(SyncForIn node) { + openNode(node, "ForIn"); node.visitChildren(this); closeNode(); } diff --git a/pkg/compiler/lib/src/tree/unparser.dart b/pkg/compiler/lib/src/tree/unparser.dart index d7dbda7c7dc0..5325f1377dff 100644 --- a/pkg/compiler/lib/src/tree/unparser.dart +++ b/pkg/compiler/lib/src/tree/unparser.dart @@ -623,11 +623,23 @@ class Unparser extends Indentation implements Visitor { visitGotoStatement(node); } - visitForIn(ForIn node) { - if (node.awaitToken != null) { - write(node.awaitToken.value); - write(' '); - } + visitAsyncForIn(AsyncForIn node) { + write(node.awaitToken.value); + write(' '); + write(node.forToken.value); + space(); + write('('); + visit(node.declaredIdentifier); + write(' '); + addToken(node.inToken); + visit(node.expression); + write(')'); + space(); + visit(node.body); + } + + + visitSyncForIn(SyncForIn node) { write(node.forToken.value); space(); write('('); diff --git a/pkg/compiler/lib/src/typechecker.dart b/pkg/compiler/lib/src/typechecker.dart index 04a7a432fff6..078086c823de 100644 --- a/pkg/compiler/lib/src/typechecker.dart +++ b/pkg/compiler/lib/src/typechecker.dart @@ -1719,7 +1719,13 @@ class TypeCheckerVisitor extends Visitor { return const StatementType(); } - visitForIn(ForIn node) { + visitAsyncForIn(AsyncForIn node) { + analyze(node.expression); + analyze(node.body); + return const StatementType(); + } + + visitSyncForIn(SyncForIn node) { analyze(node.expression); analyze(node.body); return const StatementType(); diff --git a/pkg/compiler/lib/src/use_unused_api.dart b/pkg/compiler/lib/src/use_unused_api.dart index 8edad0b5c186..538a0e2612bf 100644 --- a/pkg/compiler/lib/src/use_unused_api.dart +++ b/pkg/compiler/lib/src/use_unused_api.dart @@ -90,6 +90,7 @@ void useConstant(constants.ConstantValue constant, void useNode(tree.Node node) { node ..asAsyncModifier() + ..asAsyncForIn() ..asAwait() ..asBreakStatement() ..asCascade() @@ -126,6 +127,7 @@ void useNode(tree.Node node) { ..asStringNode() ..asSwitchCase() ..asSwitchStatement() + ..asSyncForIn() ..asTryStatement() ..asTypeAnnotation() ..asTypeVariable() diff --git a/tools/VERSION b/tools/VERSION index c7af6239919d..82d7fecc86ee 100644 --- a/tools/VERSION +++ b/tools/VERSION @@ -28,4 +28,4 @@ MAJOR 1 MINOR 10 PATCH 0 PRERELEASE 1 -PRERELEASE_PATCH 1 +PRERELEASE_PATCH 2