Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analysis.server; | 5 library analysis.server; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/analysis_logger.dart'; | 9 import 'package:analysis_server/src/analysis_logger.dart'; |
| 10 import 'package:analysis_server/src/channel.dart'; | 10 import 'package:analysis_server/src/channel.dart'; |
| 11 import 'package:analysis_server/src/constants.dart'; | 11 import 'package:analysis_server/src/constants.dart'; |
| 12 import 'package:analysis_server/src/context_directory_manager.dart'; | 12 import 'package:analysis_server/src/context_directory_manager.dart'; |
| 13 import 'package:analysis_server/src/domain_analysis.dart'; | 13 import 'package:analysis_server/src/domain_analysis.dart'; |
| 14 import 'package:analysis_server/src/operation/operation.dart'; | |
| 15 import 'package:analysis_server/src/operation/operation_queue.dart'; | |
| 14 import 'package:analysis_server/src/protocol.dart'; | 16 import 'package:analysis_server/src/protocol.dart'; |
| 15 import 'package:analysis_server/src/resource.dart'; | 17 import 'package:analysis_server/src/resource.dart'; |
| 16 import 'package:analyzer/src/generated/ast.dart'; | 18 import 'package:analyzer/src/generated/ast.dart'; |
| 17 import 'package:analyzer/src/generated/engine.dart'; | 19 import 'package:analyzer/src/generated/engine.dart'; |
| 18 import 'package:analyzer/src/generated/error.dart'; | 20 import 'package:analyzer/src/generated/error.dart'; |
| 19 import 'package:analyzer/src/generated/java_core.dart'; | 21 import 'package:analyzer/src/generated/java_core.dart'; |
| 20 import 'package:analyzer/src/generated/source.dart'; | 22 import 'package:analyzer/src/generated/source.dart'; |
| 21 import 'package:analyzer/src/generated/sdk.dart'; | 23 import 'package:analyzer/src/generated/sdk.dart'; |
| 22 import 'package:analyzer/src/generated/sdk_io.dart'; | 24 import 'package:analyzer/src/generated/sdk_io.dart'; |
| 23 import 'package:analyzer/src/generated/source_io.dart'; | 25 import 'package:analyzer/src/generated/source_io.dart'; |
| 24 import 'package:analysis_server/src/computers.dart'; | 26 import 'package:analysis_server/src/computers.dart'; |
| 25 | 27 |
| 26 | 28 |
| 27 /** | 29 /** |
| 28 * An instance of [DirectoryBasedDartSdk] that is shared between | 30 * An instance of [DirectoryBasedDartSdk] that is shared between |
| 29 * [AnalysisServer] instances to improve performance. | 31 * [AnalysisServer] instances to improve performance. |
| 30 */ | 32 */ |
| 31 final DirectoryBasedDartSdk SHARED_SDK = DirectoryBasedDartSdk.defaultSdk; | 33 final DirectoryBasedDartSdk SHARED_SDK = DirectoryBasedDartSdk.defaultSdk; |
| 32 | 34 |
| 33 class AnalysisServerContextDirectoryManager extends ContextDirectoryManager { | 35 class AnalysisServerContextDirectoryManager extends ContextDirectoryManager { |
| 34 final AnalysisServer analysisServer; | 36 final AnalysisServer analysisServer; |
| 35 | 37 |
| 36 AnalysisServerContextDirectoryManager(this.analysisServer, ResourceProvider re sourceProvider) | 38 AnalysisServerContextDirectoryManager(this.analysisServer, ResourceProvider re sourceProvider) |
| 37 : super(resourceProvider); | 39 : super(resourceProvider); |
| 38 | 40 |
| 39 void addContext(Folder folder, File pubspecFile) { | 41 void addContext(Folder folder, File pubspecFile) { |
| 40 ContextDirectory contextDirectory = new ContextDirectory( | 42 ContextDirectory contextDirectory = new ContextDirectory( |
| 41 analysisServer.defaultSdk, folder, pubspecFile); | 43 analysisServer.defaultSdk, folder, pubspecFile); |
| 42 analysisServer.folderMap[folder] = contextDirectory; | 44 analysisServer.folderMap[folder] = contextDirectory; |
| 43 analysisServer.addContextToWorkQueue(contextDirectory.context); | 45 analysisServer.schedulePerformAnalysisOperation(contextDirectory.context, fa lse); |
| 44 } | 46 } |
| 45 | 47 |
| 46 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) { | 48 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) { |
| 47 analysisServer.folderMap[contextFolder].context.applyChanges(changeSet); | 49 analysisServer.folderMap[contextFolder].context.applyChanges(changeSet); |
| 48 } | 50 } |
| 49 } | 51 } |
| 50 | 52 |
| 51 /** | 53 /** |
| 52 * Instances of the class [AnalysisServer] implement a server that listens on a | 54 * Instances of the class [AnalysisServer] implement a server that listens on a |
| 53 * [CommunicationChannel] for analysis requests and process them. | 55 * [CommunicationChannel] for analysis requests and process them. |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 71 * discard any tasks it finds on [contextWorkQueue]. | 73 * discard any tasks it finds on [contextWorkQueue]. |
| 72 */ | 74 */ |
| 73 bool running; | 75 bool running; |
| 74 | 76 |
| 75 /** | 77 /** |
| 76 * A list of the request handlers used to handle the requests sent to this | 78 * A list of the request handlers used to handle the requests sent to this |
| 77 * server. | 79 * server. |
| 78 */ | 80 */ |
| 79 List<RequestHandler> handlers; | 81 List<RequestHandler> handlers; |
| 80 | 82 |
| 81 // TODO(scheglov) remove once setAnalysisRoots() is completely implemented | |
| 82 // /** | |
| 83 // * A table mapping context id's to the analysis contexts associated with the m. | |
| 84 // */ | |
| 85 // final Map<String, AnalysisContext> contextMap = new Map<String, AnalysisCont ext>(); | |
| 86 // | |
| 87 // /** | |
| 88 // * A table mapping analysis contexts to the context id's associated with the m. | |
| 89 // */ | |
| 90 // final Map<AnalysisContext, String> contextIdMap = new Map<AnalysisContext, S tring>(); | |
| 91 | |
| 92 /** | 83 /** |
| 93 * The current default [DartSdk]. | 84 * The current default [DartSdk]. |
| 94 */ | 85 */ |
| 95 DartSdk defaultSdk = SHARED_SDK; | 86 DartSdk defaultSdk = SHARED_SDK; |
| 96 | 87 |
| 97 /** | 88 /** |
| 98 * A table mapping [Folder]s to the [ContextDirectory]s associated with them. | 89 * A table mapping [Folder]s to the [ContextDirectory]s associated with them. |
| 99 */ | 90 */ |
| 100 final Map<Folder, ContextDirectory> folderMap = <Folder, ContextDirectory>{}; | 91 final Map<Folder, ContextDirectory> folderMap = <Folder, ContextDirectory>{}; |
| 101 | 92 |
| 102 /** | 93 /** |
| 103 * The context identifier used in the last status notification. | 94 * The context identifier used in the last status notification. |
| 104 */ | 95 */ |
| 105 String lastStatusNotificationContextId = null; | 96 String lastStatusNotificationContextId = null; |
| 106 | 97 |
| 107 /** | 98 final ServerOperationQueue operationQueue = new ServerOperationQueue(); |
| 108 * A list of the analysis contexts for which analysis work needs to be | |
| 109 * performed. | |
| 110 * | |
| 111 * Invariant: when this list is non-empty, there is exactly one pending call | |
|
Paul Berry
2014/06/02 16:04:41
Why is this comment being deleted? Is your intent
scheglov
2014/06/02 17:56:59
Restored for the operation queue field.
| |
| 112 * to [performTask] on the event queue. When this list is empty, there are | |
| 113 * no calls to [performTask] on the event queue. | |
| 114 */ | |
| 115 final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>(); | |
| 116 | 99 |
| 117 /** | 100 /** |
| 118 * A set of the [ServerService]s to send notifications for. | 101 * A set of the [ServerService]s to send notifications for. |
| 119 */ | 102 */ |
| 120 Set<ServerService> serverServices = new Set<ServerService>(); | 103 Set<ServerService> serverServices = new Set<ServerService>(); |
| 121 | 104 |
| 122 /** | 105 /** |
| 123 * A table mapping [AnalysisService]s to the file paths for which these | 106 * A table mapping [AnalysisService]s to the file paths for which these |
| 124 * notifications should be sent. | 107 * notifications should be sent. |
| 125 */ | 108 */ |
| 126 Map<AnalysisService, Set<String>> analysisServices = <AnalysisService, Set<Str ing>>{}; | 109 Map<AnalysisService, Set<String>> analysisServices = <AnalysisService, Set<Str ing>>{}; |
| 127 | 110 |
| 128 /** | 111 /** |
| 129 * Initialize a newly created server to receive requests from and send | 112 * Initialize a newly created server to receive requests from and send |
| 130 * responses to the given [channel]. | 113 * responses to the given [channel]. |
| 131 */ | 114 */ |
| 132 AnalysisServer(this.channel, ResourceProvider resourceProvider) { | 115 AnalysisServer(this.channel, ResourceProvider resourceProvider) { |
| 133 contextDirectoryManager = new AnalysisServerContextDirectoryManager(this, re sourceProvider); | 116 contextDirectoryManager = new AnalysisServerContextDirectoryManager(this, re sourceProvider); |
| 134 AnalysisEngine.instance.logger = new AnalysisLogger(); | 117 AnalysisEngine.instance.logger = new AnalysisLogger(); |
| 135 running = true; | 118 running = true; |
| 136 Notification notification = new Notification(NOTIFICATION_CONNECTED); | 119 Notification notification = new Notification(NOTIFICATION_CONNECTED); |
| 137 channel.sendNotification(notification); | 120 channel.sendNotification(notification); |
| 138 channel.listen(handleRequest, onDone: done, onError: error); | 121 channel.listen(handleRequest, onDone: done, onError: error); |
| 139 } | 122 } |
| 140 | 123 |
| 141 /** | 124 /** |
| 142 * If [running] is true, add the given [context] to the list of analysis | 125 * Schedules analysis of the given context. |
| 143 * contexts for which analysis work needs to be performed, and ensure that | 126 * |
| 144 * the work will be performed. | 127 * [isContinue] is `true` if the new operation is continuation of analysis of |
| 128 * the same context which was analyzed before. | |
| 145 */ | 129 */ |
| 146 void addContextToWorkQueue(AnalysisContext context) { | 130 void schedulePerformAnalysisOperation(AnalysisContext context, bool isContinue ) { |
| 147 if (!running) { | 131 // bool isPriority = priorityContexts.contains(contextId); |
| 148 return; | 132 // TODO(scheglov) support for priority sources |
| 149 } | 133 bool isPriority = false; |
| 150 if (!contextWorkQueue.contains(context)) { | 134 operationQueue.add(new PerformAnalysisOperation(context, isPriority, isConti nue)); |
| 151 contextWorkQueue.add(context); | 135 _scheduleTask(); |
|
Brian Wilkerson
2014/06/02 14:32:18
I believe that this will cause the event queue to
Paul Berry
2014/06/02 16:04:41
I'm not sure we even need a closure. I think it i
| |
| 152 if (contextWorkQueue.length == 1) { | |
| 153 // Work queue was previously empty, so schedule analysis. | |
| 154 _scheduleTask(); | |
| 155 } | |
| 156 } | |
| 157 } | 136 } |
| 158 | 137 |
| 159 /** | 138 /** |
| 160 * The socket from which requests are being read has been closed. | 139 * The socket from which requests are being read has been closed. |
| 161 */ | 140 */ |
| 162 void done() { | 141 void done() { |
| 163 running = false; | 142 running = false; |
| 164 } | 143 } |
| 165 | 144 |
| 166 /** | 145 /** |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 185 } | 164 } |
| 186 } on RequestFailure catch (exception) { | 165 } on RequestFailure catch (exception) { |
| 187 channel.sendResponse(exception.response); | 166 channel.sendResponse(exception.response); |
| 188 return; | 167 return; |
| 189 } | 168 } |
| 190 } | 169 } |
| 191 channel.sendResponse(new Response.unknownRequest(request)); | 170 channel.sendResponse(new Response.unknownRequest(request)); |
| 192 } | 171 } |
| 193 | 172 |
| 194 /** | 173 /** |
| 195 * Perform the next available task. If a request was received that has not yet | 174 * Perform the next available [ServerOperation]. |
| 196 * been performed, perform it next. Otherwise, look for some analysis that | |
| 197 * needs to be done and do that. Otherwise, do nothing. | |
| 198 */ | 175 */ |
| 199 void performTask() { | 176 void performTask() { |
| 200 if (!running) { | 177 if (!running) { |
| 201 // An error has occurred, or the connection to the client has been | |
| 202 // closed, since performTask() was scheduled on the event queue. So | |
| 203 // don't do any analysis. Instead clear the work queue. | |
| 204 contextWorkQueue.clear(); | |
|
Paul Berry
2014/06/02 16:04:41
Why are we getting rid of this code? Was it incor
scheglov
2014/06/02 17:56:59
Restored.
| |
| 205 } | |
| 206 if (contextWorkQueue.isEmpty) { | |
| 207 // Nothing to do. | |
| 208 return; | 178 return; |
| 209 } | 179 } |
| 210 // | 180 // prepare next operation |
| 211 // Look for a context that has work to be done and then perform one task. | 181 ServerOperation operation = operationQueue.take(); |
| 212 // | 182 if (operation == null) { |
| 213 List<ChangeNotice> notices = null; | 183 return; |
| 214 // String contextId; | 184 } |
| 185 // perform the operation | |
| 215 try { | 186 try { |
| 216 AnalysisContext context = contextWorkQueue[0]; | 187 operation.perform(this); |
| 217 // contextId = contextIdMap[context]; | 188 } catch (e) { |
| 218 // TODO(danrubel): Replace with context identifier or similar | 189 // TODO(scheglov) decide how to handle exceptions |
| 219 sendStatusNotification(context.toString()); | |
| 220 AnalysisResult result = context.performAnalysisTask(); | |
| 221 notices = result.changeNotices; | |
| 222 } finally { | |
|
Paul Berry
2014/06/02 16:04:41
Why are we dropping this logic? I believe that sc
scheglov
2014/06/02 17:56:59
Restored in performTask/performOperation.
| |
| 223 if (notices == null) { | |
| 224 // Either we have no more work to do for this context, or there was an | |
| 225 // unhandled exception trying to perform the analysis. In either case, | |
| 226 // remove the context form the work queue so we won't try to do more | |
| 227 // analysis on it. | |
| 228 contextWorkQueue.removeAt(0); | |
| 229 } | |
| 230 // | |
| 231 // Schedule this method to be run again if there is any more work to be | |
| 232 // done. | |
| 233 // | |
| 234 if (!contextWorkQueue.isEmpty) { | |
| 235 _scheduleTask(); | |
| 236 } | |
| 237 } | 190 } |
| 191 // schedule this method again | |
| 192 _scheduleTask(); | |
|
Brian Wilkerson
2014/06/02 14:32:18
This method should only get scheduled if the queue
scheglov
2014/06/02 17:56:59
Done.
| |
| 193 } | |
| 194 | |
| 195 /** | |
| 196 * Perform analysis in the given [AnalysisContext]. | |
| 197 */ | |
| 198 void internalPerformAnalysis(AnalysisContext context) { | |
|
Brian Wilkerson
2014/06/02 14:32:18
I would prefer a style in which the code to perfor
scheglov
2014/06/02 17:56:59
Me too, but I'm afraid we have to expose too much
Brian Wilkerson
2014/06/02 18:21:03
I'd like to start with doing it the better way unt
| |
| 199 // prepare results | |
| 200 AnalysisResult result = context.performAnalysisTask(); | |
| 201 List<ChangeNotice> notices = result.changeNotices; | |
| 202 if (notices == null) { | |
| 203 return; | |
| 204 } | |
| 205 // TODO(scheglov) remember known sources | |
| 206 // TODO(scheglov) index units | |
| 207 // TODO(scheglov) schedule notifications | |
| 238 if (notices != null) { | 208 if (notices != null) { |
| 239 sendNotices(notices); | 209 sendNotices(notices); |
| 240 } else { | 210 } else { |
| 241 sendStatusNotification(null); | 211 sendStatusNotification(null); |
| 242 } | 212 } |
| 213 // schedule analysis again | |
| 214 schedulePerformAnalysisOperation(context, true); | |
|
Brian Wilkerson
2014/06/02 14:32:18
We need to take the "continuation" flag into accou
scheglov
2014/06/02 17:56:59
We do use it - PerformAnalysisOperation returns di
Brian Wilkerson
2014/06/02 18:21:03
I meant, if we are continuing analysis of a contex
scheglov
2014/06/02 18:32:25
Well, it is added to the tail of the ANALYSIS_CONT
| |
| 243 } | 215 } |
| 244 | 216 |
| 245 /** | 217 /** |
| 246 * Send the information in the given list of notices back to the client. | 218 * Send the information in the given list of notices back to the client. |
| 247 */ | 219 */ |
| 248 void sendNotices(List<ChangeNotice> notices) { | 220 void sendNotices(List<ChangeNotice> notices) { |
| 249 for (int i = 0; i < notices.length; i++) { | 221 for (int i = 0; i < notices.length; i++) { |
| 250 ChangeNotice notice = notices[i]; | 222 ChangeNotice notice = notices[i]; |
| 251 Source source = notice.source; | 223 Source source = notice.source; |
| 252 CompilationUnit dartUnit = notice.compilationUnit; | 224 CompilationUnit dartUnit = notice.compilationUnit; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 changes.forEach((file, change) { | 306 changes.forEach((file, change) { |
| 335 AnalysisContext analysisContext = _getAnalysisContext(file); | 307 AnalysisContext analysisContext = _getAnalysisContext(file); |
| 336 if (analysisContext != null) { | 308 if (analysisContext != null) { |
| 337 Source source = _getSource(file); | 309 Source source = _getSource(file); |
| 338 if (change.offset == null) { | 310 if (change.offset == null) { |
| 339 analysisContext.setContents(source, change.content); | 311 analysisContext.setContents(source, change.content); |
| 340 } else { | 312 } else { |
| 341 analysisContext.setChangedContents(source, change.content, | 313 analysisContext.setChangedContents(source, change.content, |
| 342 change.offset, change.oldLength, change.newLength); | 314 change.offset, change.oldLength, change.newLength); |
| 343 } | 315 } |
| 344 addContextToWorkQueue(analysisContext); | 316 schedulePerformAnalysisOperation(analysisContext, false); |
| 345 } | 317 } |
| 346 }); | 318 }); |
| 347 } | 319 } |
| 348 | 320 |
| 349 /** | 321 /** |
| 350 * Implementation for `analysis.setSubscriptions`. | 322 * Implementation for `analysis.setSubscriptions`. |
| 351 */ | 323 */ |
| 352 void setAnalysisSubscriptions(Map<AnalysisService, Set<String>> subscriptions) { | 324 void setAnalysisSubscriptions(Map<AnalysisService, Set<String>> subscriptions) { |
| 353 // send notifications for already analyzed sources | 325 // send notifications for already analyzed sources |
| 354 subscriptions.forEach((service, Set<String> newFiles) { | 326 subscriptions.forEach((service, Set<String> newFiles) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 411 return null; | 383 return null; |
| 412 } | 384 } |
| 413 // get a resolved unit | 385 // get a resolved unit |
| 414 return context.getResolvedCompilationUnit2(unitSource, librarySources[0]); | 386 return context.getResolvedCompilationUnit2(unitSource, librarySources[0]); |
| 415 } | 387 } |
| 416 | 388 |
| 417 /** | 389 /** |
| 418 * Return `true` if all tasks are finished in this [AnalysisServer]. | 390 * Return `true` if all tasks are finished in this [AnalysisServer]. |
| 419 */ | 391 */ |
| 420 bool test_areTasksFinished() { | 392 bool test_areTasksFinished() { |
| 421 return contextWorkQueue.isEmpty; | 393 return operationQueue.isEmpty; |
| 422 } | 394 } |
| 423 | 395 |
| 424 static Map<String, Object> errorToJson(AnalysisError analysisError) { | 396 static Map<String, Object> errorToJson(AnalysisError analysisError) { |
| 425 // TODO(paulberry): move this function into the AnalysisError class. | 397 // TODO(paulberry): move this function into the AnalysisError class. |
| 426 ErrorCode errorCode = analysisError.errorCode; | 398 ErrorCode errorCode = analysisError.errorCode; |
| 427 Map<String, Object> result = { | 399 Map<String, Object> result = { |
| 428 'file': analysisError.source.fullName, | 400 'file': analysisError.source.fullName, |
| 429 // TODO(scheglov) add Enum.fullName ? | 401 // TODO(scheglov) add Enum.fullName ? |
| 430 'errorCode': '${errorCode.runtimeType}.${(errorCode as Enum).name}', | 402 'errorCode': '${errorCode.runtimeType}.${(errorCode as Enum).name}', |
| 431 'offset': analysisError.offset, | 403 'offset': analysisError.offset, |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 516 /** | 488 /** |
| 517 * An enumeration of the services provided by the server domain. | 489 * An enumeration of the services provided by the server domain. |
| 518 */ | 490 */ |
| 519 class ServerService extends Enum2<ServerService> { | 491 class ServerService extends Enum2<ServerService> { |
| 520 static const ServerService STATUS = const ServerService('STATUS', 0); | 492 static const ServerService STATUS = const ServerService('STATUS', 0); |
| 521 | 493 |
| 522 static const List<ServerService> VALUES = const [STATUS]; | 494 static const List<ServerService> VALUES = const [STATUS]; |
| 523 | 495 |
| 524 const ServerService(String name, int ordinal) : super(name, ordinal); | 496 const ServerService(String name, int ordinal) : super(name, ordinal); |
| 525 } | 497 } |
| OLD | NEW |