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 get.handler; | 5 library analysis_server.src.get_handler; |
| 6 | 6 |
| 7 import 'dart:collection'; | |
| 7 import 'dart:convert'; | 8 import 'dart:convert'; |
| 8 import 'dart:io'; | 9 import 'dart:io'; |
| 9 import 'dart:math'; | 10 import 'dart:math'; |
| 10 | 11 |
| 11 import 'package:analysis_server/src/analysis_server.dart'; | 12 import 'package:analysis_server/src/analysis_server.dart'; |
| 12 import 'package:analysis_server/src/domain_completion.dart'; | 13 import 'package:analysis_server/src/domain_completion.dart'; |
| 14 import 'package:analysis_server/src/domain_execution.dart'; | |
| 15 import 'package:analysis_server/src/operation/operation.dart'; | |
| 16 import 'package:analysis_server/src/operation/operation_analysis.dart'; | |
| 17 import 'package:analysis_server/src/operation/operation_queue.dart'; | |
| 18 import 'package:analysis_server/src/protocol.dart' hide Element; | |
| 13 import 'package:analysis_server/src/socket_server.dart'; | 19 import 'package:analysis_server/src/socket_server.dart'; |
| 20 import 'package:analysis_server/src/status/ast_writer.dart'; | |
| 21 import 'package:analysis_server/src/status/element_writer.dart'; | |
| 14 import 'package:analyzer/file_system/file_system.dart'; | 22 import 'package:analyzer/file_system/file_system.dart'; |
| 15 import 'package:analyzer/instrumentation/instrumentation.dart'; | 23 import 'package:analyzer/src/generated/ast.dart'; |
| 24 import 'package:analyzer/src/generated/element.dart'; | |
| 16 import 'package:analyzer/src/generated/engine.dart'; | 25 import 'package:analyzer/src/generated/engine.dart'; |
| 17 import 'package:analyzer/src/generated/java_engine.dart'; | 26 import 'package:analyzer/src/generated/java_engine.dart'; |
| 18 import 'package:analyzer/src/generated/source.dart'; | 27 import 'package:analyzer/src/generated/source.dart'; |
| 19 | 28 |
| 20 import 'analysis_server.dart'; | 29 import 'analysis_server.dart'; |
| 21 | 30 |
| 22 /** | 31 /** |
| 32 * A function that can be used to generate HTML output into the given [buffer]. | |
| 33 * The HTML that is generated must be valid (special characters must already be | |
| 34 * encoded). | |
| 35 */ | |
| 36 typedef void HtmlGenerator(StringBuffer buffer); | |
| 37 | |
| 38 /** | |
| 23 * Instances of the class [GetHandler] handle GET requests. | 39 * Instances of the class [GetHandler] handle GET requests. |
| 24 */ | 40 */ |
| 25 class GetHandler { | 41 class GetHandler { |
| 26 /** | 42 /** |
| 27 * The path used to request the status of the analysis server as a whole. | 43 * The path used to request the status of the analysis server as a whole. |
| 28 */ | 44 */ |
| 29 static const String STATUS_PATH = '/status'; | 45 static const String STATUS_PATH = '/status'; |
| 30 | 46 |
| 31 /** | 47 /** |
| 48 * The path used to request information about a element model. | |
| 49 */ | |
| 50 static const String AST_PATH = '/ast'; | |
| 51 | |
| 52 /** | |
| 32 * The path used to request information about the cache entry corresponding | 53 * The path used to request information about the cache entry corresponding |
| 33 * to a single file. | 54 * to a single file. |
| 34 */ | 55 */ |
| 35 static const String CACHE_ENTRY_PATH = '/cache_entry'; | 56 static const String CACHE_ENTRY_PATH = '/cache_entry'; |
| 36 | 57 |
| 37 /** | 58 /** |
| 59 * The path used to request information about the cache entry corresponding | |
| 60 * to a single file. | |
| 61 */ | |
| 62 static const String CACHE_ENTRY2_PATH = '/cache_entry2'; | |
| 63 | |
| 64 /** | |
| 38 * The path used to request the list of source files in a certain cache | 65 * The path used to request the list of source files in a certain cache |
| 39 * state. | 66 * state. |
| 40 */ | 67 */ |
| 41 static const String CACHE_STATE_PATH = '/cache_state'; | 68 static const String CACHE_STATE_PATH = '/cache_state'; |
| 42 | 69 |
| 43 /** | 70 /** |
| 44 * The path used to request code completion information. | 71 * The path used to request code completion information. |
| 45 */ | 72 */ |
| 46 static const String COMPLETION_PATH = '/completion'; | 73 static const String COMPLETION_PATH = '/completion'; |
| 47 | 74 |
| 48 /** | 75 /** |
| 76 * The path used to request information about a specific context. | |
| 77 */ | |
| 78 static const String CONTEXT_PATH = '/context'; | |
| 79 | |
| 80 /** | |
| 81 * The path used to request information about a element model. | |
| 82 */ | |
| 83 static const String ELEMENT_PATH = '/element'; | |
| 84 | |
| 85 /** | |
| 49 * The path used to request an overlay contents. | 86 * The path used to request an overlay contents. |
| 50 */ | 87 */ |
| 51 static const String OVERLAY_PATH = '/overlay'; | 88 static const String OVERLAY_PATH = '/overlay'; |
| 52 | 89 |
| 53 /** | 90 /** |
| 54 * The path used to request overlays information. | 91 * The path used to request overlays information. |
| 55 */ | 92 */ |
| 56 static const String OVERLAYS_PATH = '/overlays'; | 93 static const String OVERLAYS_PATH = '/overlays'; |
| 57 | 94 |
| 58 /** | 95 /** |
| 59 * Query parameter used to represent the cache state to search for, when | 96 * Query parameter used to represent the cache state to search for, when |
| 60 * accessing [CACHE_STATE_PATH]. | 97 * accessing [CACHE_STATE_PATH]. |
| 61 */ | 98 */ |
| 62 static const String STATE_QUERY_PARAM = 'state'; | 99 static const String STATE_QUERY_PARAM = 'state'; |
| 63 | 100 |
| 64 /** | 101 /** |
| 65 * Query parameter used to represent the context to search for, when | 102 * Query parameter used to represent the context to search for, when |
| 66 * accessing [CACHE_ENTRY_PATH] or [CACHE_STATE_PATH]. | 103 * accessing [CACHE_ENTRY_PATH] or [CACHE_STATE_PATH]. |
| 67 */ | 104 */ |
| 68 static const String CONTEXT_QUERY_PARAM = 'context'; | 105 static const String CONTEXT_QUERY_PARAM = 'context'; |
| 69 | 106 |
| 70 /** | 107 /** |
| 71 * Query parameter used to represent the descriptor to search for, when | 108 * Query parameter used to represent the descriptor to search for, when |
| 72 * accessing [CACHE_STATE_PATH]. | 109 * accessing [CACHE_STATE_PATH]. |
| 73 */ | 110 */ |
| 74 static const String DESCRIPTOR_QUERY_PARAM = 'descriptor'; | 111 static const String DESCRIPTOR_QUERY_PARAM = 'descriptor'; |
| 75 | 112 |
| 76 /** | 113 /** |
| 114 * Query parameter used to represent the index in the [_overlayContents]. | |
| 115 */ | |
| 116 static const String ID_PARAM = 'id'; | |
| 117 | |
| 118 /** | |
| 77 * Query parameter used to represent the source to search for, when accessing | 119 * Query parameter used to represent the source to search for, when accessing |
| 78 * [CACHE_ENTRY_PATH]. | 120 * [CACHE_ENTRY_PATH]. |
| 79 */ | 121 */ |
| 80 static const String SOURCE_QUERY_PARAM = 'entry'; | 122 static const String SOURCE_QUERY_PARAM = 'entry'; |
| 81 | 123 |
| 82 /** | 124 /** |
| 83 * The socket server whose status is to be reported on. | 125 * The socket server whose status is to be reported on. |
| 84 */ | 126 */ |
| 85 SocketServer _server; | 127 SocketServer _server; |
| 86 | 128 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 99 */ | 141 */ |
| 100 GetHandler(this._server, this._printBuffer); | 142 GetHandler(this._server, this._printBuffer); |
| 101 | 143 |
| 102 /** | 144 /** |
| 103 * Handle a GET request received by the HTTP server. | 145 * Handle a GET request received by the HTTP server. |
| 104 */ | 146 */ |
| 105 void handleGetRequest(HttpRequest request) { | 147 void handleGetRequest(HttpRequest request) { |
| 106 String path = request.uri.path; | 148 String path = request.uri.path; |
| 107 if (path == STATUS_PATH) { | 149 if (path == STATUS_PATH) { |
| 108 _returnServerStatus(request); | 150 _returnServerStatus(request); |
| 151 } else if (path == '/status2') { | |
| 152 _returnServerStatus2(request); | |
| 153 } else if (path == AST_PATH) { | |
| 154 _returnAst(request); | |
| 109 } else if (path == CACHE_STATE_PATH) { | 155 } else if (path == CACHE_STATE_PATH) { |
| 110 _returnCacheState(request); | 156 _returnCacheState(request); |
| 111 } else if (path == CACHE_ENTRY_PATH) { | 157 } else if (path == CACHE_ENTRY_PATH) { |
| 112 _returnCacheEntry(request); | 158 _returnCacheEntry(request); |
| 159 } else if (path == CACHE_ENTRY2_PATH) { | |
| 160 _returnCacheEntry2(request); | |
| 113 } else if (path == COMPLETION_PATH) { | 161 } else if (path == COMPLETION_PATH) { |
| 114 _returnCompletionInfo(request); | 162 _returnCompletionInfo(request); |
| 163 } else if (path == CONTEXT_PATH) { | |
| 164 _returnContextInfo(request); | |
| 165 } else if (path == ELEMENT_PATH) { | |
| 166 _returnElement(request); | |
| 115 } else if (path == OVERLAY_PATH) { | 167 } else if (path == OVERLAY_PATH) { |
| 116 _returnOverlayContents(request); | 168 _returnOverlayContents(request); |
| 117 } else if (path == OVERLAYS_PATH) { | 169 } else if (path == OVERLAYS_PATH) { |
| 118 _returnOverlaysInfo(request); | 170 _returnOverlaysInfo(request); |
| 119 } else { | 171 } else { |
| 120 _returnUnknownRequest(request); | 172 _returnUnknownRequest(request); |
| 121 } | 173 } |
| 122 } | 174 } |
| 123 | 175 |
| 124 /** | 176 /** |
| 177 * Return the folder being managed by the given [analysisServer] that matches | |
| 178 * the given [contextFilter], or `null` if there is none. | |
| 179 */ | |
| 180 Folder _findFolder(AnalysisServer analysisServer, String contextFilter) { | |
| 181 return analysisServer.folderMap.keys.firstWhere( | |
| 182 (Folder folder) => folder.path == contextFilter, | |
| 183 orElse: () => null); | |
| 184 } | |
| 185 | |
| 186 /** | |
| 187 * Return `true` if the given analysis [context] has at least one entry with | |
| 188 * an exception. | |
| 189 */ | |
| 190 bool _hasException(AnalysisContextImpl context) { | |
| 191 bool hasException = false; | |
| 192 context.visitCacheItems( | |
| 193 (Source source, SourceEntry sourceEntry, DataDescriptor rowDesc, | |
| 194 CacheState state) { | |
| 195 if (sourceEntry.exception != null) { | |
| 196 hasException = true; | |
| 197 } | |
| 198 }); | |
| 199 return hasException; | |
| 200 } | |
| 201 | |
| 202 Folder _keyForValue(Map<Folder, AnalysisContext> folderMap, | |
| 203 AnalysisContext context) { | |
| 204 for (Folder folder in folderMap.keys) { | |
| 205 if (folderMap[folder] == context) { | |
| 206 return folder; | |
| 207 } | |
| 208 } | |
| 209 return null; | |
| 210 } | |
| 211 | |
| 212 /** | |
| 125 * Create a link to [path] with query parameters [params], with inner HTML | 213 * Create a link to [path] with query parameters [params], with inner HTML |
| 126 * [innerHtml]. | 214 * [innerHtml]. If [hasError] is `true`, then the link will have the class |
| 215 * 'error'. | |
| 127 */ | 216 */ |
| 128 String _makeLink(String path, Map<String, String> params, String innerHtml) { | 217 String _makeLink(String path, Map<String, String> params, String innerHtml, |
| 218 [bool hasError = false]) { | |
| 129 Uri uri = new Uri(path: path, queryParameters: params); | 219 Uri uri = new Uri(path: path, queryParameters: params); |
| 130 return '<a href="${HTML_ESCAPE.convert(uri.toString())}">$innerHtml</a>'; | 220 String href = HTML_ESCAPE.convert(uri.toString()); |
| 221 String classAttribute = hasError ? ' class="error"' : ''; | |
| 222 return '<a href="$href"$classAttribute>$innerHtml</a>'; | |
| 131 } | 223 } |
| 132 | 224 |
| 133 /** | 225 /** |
| 134 * Generate a table showing the cache values corresponding to the given | 226 * Generate a table showing the cache values corresponding to the given |
| 135 * [descriptors], using [getState] to get the cache state corresponding to | 227 * [descriptors], using [getState] to get the cache state corresponding to |
| 136 * each descriptor, and [getValue] to get the cached value corresponding to | 228 * each descriptor, and [getValue] to get the cached value corresponding to |
| 137 * each descriptor. Append the resulting HTML to [response]. | 229 * each descriptor. Append the resulting HTML to [response]. |
| 138 */ | 230 */ |
| 139 void _outputDescriptorTable(HttpResponse response, | 231 void _outputDescriptorTable(HttpResponse response, |
| 140 List<DataDescriptor> descriptors, CacheState getState(DataDescriptor), dyn amic | 232 List<DataDescriptor> descriptors, CacheState getState(DataDescriptor), dyn amic |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 173 } | 265 } |
| 174 response.write('</ul>'); | 266 response.write('</ul>'); |
| 175 } else { | 267 } else { |
| 176 response.write(HTML_ESCAPE.convert(value.toString())); | 268 response.write(HTML_ESCAPE.convert(value.toString())); |
| 177 response.write( | 269 response.write( |
| 178 ' <i>(${HTML_ESCAPE.convert(value.runtimeType.toString())})</i>'); | 270 ' <i>(${HTML_ESCAPE.convert(value.runtimeType.toString())})</i>'); |
| 179 } | 271 } |
| 180 } | 272 } |
| 181 | 273 |
| 182 /** | 274 /** |
| 275 * Return a response containing information about an AST structure. | |
| 276 */ | |
| 277 void _returnAst(HttpRequest request) { | |
| 278 AnalysisServer analysisServer = _server.analysisServer; | |
| 279 if (analysisServer == null) { | |
| 280 return _returnFailure(request, 'Analysis server not running'); | |
| 281 } | |
| 282 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; | |
| 283 if (contextFilter == null) { | |
| 284 return _returnFailure( | |
| 285 request, | |
| 286 'Query parameter $CONTEXT_QUERY_PARAM required'); | |
| 287 } | |
| 288 Folder folder = _findFolder(analysisServer, contextFilter); | |
| 289 if (folder == null) { | |
| 290 return _returnFailure(request, 'Invalid context: $contextFilter'); | |
| 291 } | |
| 292 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM]; | |
| 293 if (sourceUri == null) { | |
| 294 return _returnFailure( | |
| 295 request, | |
| 296 'Query parameter $SOURCE_QUERY_PARAM required'); | |
| 297 } | |
| 298 | |
| 299 AnalysisContextImpl context = analysisServer.folderMap[folder]; | |
| 300 | |
| 301 _writeResponse(request, (StringBuffer buffer) { | |
| 302 _writePage( | |
| 303 buffer, | |
| 304 'Analysis Server - AST Structure', | |
| 305 ['Context: $contextFilter', 'File: $sourceUri'], | |
| 306 (HttpResponse) { | |
| 307 Source source = context.sourceFactory.forUri(sourceUri); | |
| 308 if (source == null) { | |
| 309 buffer.write('<p>Not found.</p>'); | |
| 310 return; | |
| 311 } | |
| 312 SourceEntry entry = context.getReadableSourceEntryOrNull(source); | |
| 313 if (entry == null) { | |
| 314 buffer.write('<p>Not found.</p>'); | |
| 315 return; | |
| 316 } | |
| 317 CompilationUnit ast = (entry as DartEntry).anyParsedCompilationUnit; | |
| 318 if (ast == null) { | |
| 319 buffer.write('<p>null</p>'); | |
| 320 return; | |
| 321 } | |
| 322 ast.accept(new AstWriter(buffer)); | |
| 323 }); | |
| 324 }); | |
| 325 } | |
| 326 | |
| 327 /** | |
| 183 * Return a response containing information about a single source file in the | 328 * Return a response containing information about a single source file in the |
| 184 * cache. | 329 * cache. |
| 185 */ | 330 */ |
| 186 void _returnCacheEntry(HttpRequest request) { | 331 void _returnCacheEntry(HttpRequest request) { |
| 187 // Figure out which context is being searched for. | 332 // Figure out which context is being searched for. |
| 188 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; | 333 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; |
| 189 if (contextFilter == null) { | 334 if (contextFilter == null) { |
| 190 return _returnFailure( | 335 return _returnFailure( |
| 191 request, | 336 request, |
| 192 'Query parameter $CONTEXT_QUERY_PARAM required'); | 337 'Query parameter $CONTEXT_QUERY_PARAM required'); |
| 193 } | 338 } |
| 194 | 339 |
| 195 // Figure out which CacheEntry is being searched for. | 340 // Figure out which CacheEntry is being searched for. |
| 196 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM]; | 341 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM]; |
| 197 if (sourceUri == null) { | 342 if (sourceUri == null) { |
| 198 return _returnFailure( | 343 return _returnFailure( |
| 199 request, | 344 request, |
| 200 'Query parameter $SOURCE_QUERY_PARAM required'); | 345 'Query parameter $SOURCE_QUERY_PARAM required'); |
| 201 } | 346 } |
| 202 | 347 |
| 203 AnalysisServer analysisServer = _server.analysisServer; | 348 AnalysisServer analysisServer = _server.analysisServer; |
| 204 if (analysisServer == null) { | 349 if (analysisServer == null) { |
| 205 return _returnFailure(request, 'Analysis server not running'); | 350 return _returnFailure(request, 'Analysis server not running'); |
| 206 } | 351 } |
| 352 | |
| 207 HttpResponse response = request.response; | 353 HttpResponse response = request.response; |
| 208 response.statusCode = HttpStatus.OK; | 354 response.statusCode = HttpStatus.OK; |
| 209 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); | 355 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); |
| 210 response.write('<html>'); | 356 response.write('<html>'); |
| 211 response.write('<head>'); | 357 response.write('<head>'); |
| 212 response.write('<title>Dart Analysis Server - Search result</title>'); | 358 response.write('<title>Dart Analysis Server - Search result</title>'); |
| 213 response.write('</head>'); | 359 response.write('</head>'); |
| 214 response.write('<body>'); | 360 response.write('<body>'); |
| 215 response.write('<h1>'); | 361 response.write('<h1>'); |
| 216 response.write('File ${HTML_ESCAPE.convert(sourceUri)}'); | 362 response.write('File ${HTML_ESCAPE.convert(sourceUri)}'); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 246 entry.libraryDescriptors, | 392 entry.libraryDescriptors, |
| 247 (DataDescriptor descriptor) => | 393 (DataDescriptor descriptor) => |
| 248 entry.getStateInLibrary(descriptor, librarySource), | 394 entry.getStateInLibrary(descriptor, librarySource), |
| 249 (DataDescriptor descriptor) => | 395 (DataDescriptor descriptor) => |
| 250 entry.getValueInLibrary(descriptor, librarySource)); | 396 entry.getValueInLibrary(descriptor, librarySource)); |
| 251 } | 397 } |
| 252 } | 398 } |
| 253 }); | 399 }); |
| 254 response.write('</body>'); | 400 response.write('</body>'); |
| 255 response.write('</html>'); | 401 response.write('</html>'); |
| 402 | |
| 256 response.close(); | 403 response.close(); |
| 257 } | 404 } |
| 258 | 405 |
| 259 /** | 406 /** |
| 407 * Return a response containing information about a single source file in the | |
| 408 * cache. | |
| 409 */ | |
| 410 void _returnCacheEntry2(HttpRequest request) { | |
| 411 AnalysisServer analysisServer = _server.analysisServer; | |
| 412 if (analysisServer == null) { | |
| 413 return _returnFailure(request, 'Analysis server not running'); | |
| 414 } | |
| 415 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; | |
| 416 if (contextFilter == null) { | |
| 417 return _returnFailure( | |
| 418 request, | |
| 419 'Query parameter $CONTEXT_QUERY_PARAM required'); | |
| 420 } | |
| 421 Folder folder = _findFolder(analysisServer, contextFilter); | |
| 422 if (folder == null) { | |
| 423 return _returnFailure(request, 'Invalid context: $contextFilter'); | |
| 424 } | |
| 425 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM]; | |
| 426 if (sourceUri == null) { | |
| 427 return _returnFailure( | |
| 428 request, | |
| 429 'Query parameter $SOURCE_QUERY_PARAM required'); | |
| 430 } | |
| 431 | |
| 432 AnalysisContextImpl context = analysisServer.folderMap[folder]; | |
| 433 | |
| 434 _writeResponse(request, (StringBuffer buffer) { | |
| 435 _writePage( | |
| 436 buffer, | |
| 437 'Analysis Server - Cache Entry', | |
| 438 ['Context: $contextFilter', 'File: $sourceUri'], | |
| 439 (HttpResponse) { | |
| 440 Source source = context.sourceFactory.forUri(sourceUri); | |
| 441 if (source == null) { | |
| 442 buffer.write('<p>Not found.</p>'); | |
| 443 return; | |
| 444 } | |
| 445 SourceEntry entry = context.getReadableSourceEntryOrNull(source); | |
| 446 if (entry == null) { | |
| 447 buffer.write('<p>Not found.</p>'); | |
| 448 return; | |
| 449 } | |
| 450 Map<String, String> linkParameters = <String, String>{ | |
| 451 CONTEXT_QUERY_PARAM: folder.path, | |
| 452 SOURCE_QUERY_PARAM: source.uri.toString() | |
| 453 }; | |
| 454 buffer.write('<h3>Library Independent</h3>'); | |
| 455 _writeDescriptorTable( | |
| 456 buffer, | |
| 457 entry.descriptors, | |
| 458 entry.getState, | |
| 459 entry.getValue, | |
| 460 linkParameters); | |
| 461 if (entry is DartEntry) { | |
| 462 for (Source librarySource in entry.containingLibraries) { | |
| 463 String libraryName = HTML_ESCAPE.convert(librarySource.fullName); | |
| 464 buffer.write('<h3>In library $libraryName:</h3>'); | |
| 465 _writeDescriptorTable( | |
| 466 buffer, | |
| 467 entry.libraryDescriptors, | |
| 468 (DataDescriptor descriptor) => | |
| 469 entry.getStateInLibrary(descriptor, librarySource), | |
| 470 (DataDescriptor descriptor) => | |
| 471 entry.getValueInLibrary(descriptor, librarySource), | |
| 472 linkParameters); | |
| 473 } | |
| 474 } | |
| 475 if (entry.exception != null) { | |
| 476 buffer.write('<h3>Exception</h3>'); | |
| 477 _writeException(buffer, entry.exception); | |
| 478 } | |
| 479 }); | |
| 480 }); | |
| 481 } | |
| 482 | |
| 483 /** | |
| 260 * Return a response indicating the set of source files in a certain cache | 484 * Return a response indicating the set of source files in a certain cache |
| 261 * state. | 485 * state. |
| 262 */ | 486 */ |
| 263 void _returnCacheState(HttpRequest request) { | 487 void _returnCacheState(HttpRequest request) { |
| 264 // Figure out what CacheState is being searched for. | 488 // Figure out what CacheState is being searched for. |
| 265 String stateQueryParam = request.uri.queryParameters[STATE_QUERY_PARAM]; | 489 String stateQueryParam = request.uri.queryParameters[STATE_QUERY_PARAM]; |
| 266 if (stateQueryParam == null) { | 490 if (stateQueryParam == null) { |
| 267 return _returnFailure( | 491 return _returnFailure( |
| 268 request, | 492 request, |
| 269 'Query parameter $STATE_QUERY_PARAM required'); | 493 'Query parameter $STATE_QUERY_PARAM required'); |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 369 response.write('<a href="?refresh=5">?refresh=5</a>'); | 593 response.write('<a href="?refresh=5">?refresh=5</a>'); |
| 370 response.write(' to refresh every 5 seconds</p>'); | 594 response.write(' to refresh every 5 seconds</p>'); |
| 371 response.write('<p>and '); | 595 response.write('<p>and '); |
| 372 response.write('<a href="?maxCount=50">?maxCount=50</a>'); | 596 response.write('<a href="?maxCount=50">?maxCount=50</a>'); |
| 373 response.write(' to keep the last 50 performance measurements</p>'); | 597 response.write(' to keep the last 50 performance measurements</p>'); |
| 374 response.write('</body>'); | 598 response.write('</body>'); |
| 375 response.write('</html>'); | 599 response.write('</html>'); |
| 376 response.close(); | 600 response.close(); |
| 377 } | 601 } |
| 378 | 602 |
| 603 /** | |
| 604 * Return a response containing information about a single source file in the | |
| 605 * cache. | |
| 606 */ | |
| 607 void _returnContextInfo(HttpRequest request) { | |
| 608 AnalysisServer analysisServer = _server.analysisServer; | |
| 609 if (analysisServer == null) { | |
| 610 return _returnFailure(request, 'Analysis server not running'); | |
| 611 } | |
| 612 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; | |
| 613 if (contextFilter == null) { | |
| 614 return _returnFailure( | |
| 615 request, | |
| 616 'Query parameter $CONTEXT_QUERY_PARAM required'); | |
| 617 } | |
| 618 Folder folder = _findFolder(analysisServer, contextFilter); | |
| 619 if (folder == null) { | |
| 620 return _returnFailure(request, 'Invalid context: $contextFilter'); | |
| 621 } | |
| 622 | |
| 623 List<String> priorityNames; | |
| 624 List<String> explicitNames = <String>[]; | |
| 625 List<String> implicitNames = <String>[]; | |
| 626 Map<String, String> links = new HashMap<String, String>(); | |
| 627 List<CaughtException> exceptions = <CaughtException>[]; | |
| 628 Map<String, int> overlayMap = new HashMap<String, int>(); | |
| 629 AnalysisContextImpl context = analysisServer.folderMap[folder]; | |
| 630 priorityNames = | |
| 631 context.prioritySources.map((Source source) => source.fullName).toList() ; | |
| 632 context.visitCacheItems( | |
| 633 (Source source, SourceEntry sourceEntry, DataDescriptor rowDesc, | |
| 634 CacheState state) { | |
| 635 String sourceName = source.fullName; | |
| 636 if (!links.containsKey(sourceName)) { | |
| 637 CaughtException exception = sourceEntry.exception; | |
| 638 if (exception != null) { | |
| 639 exceptions.add(exception); | |
| 640 } | |
| 641 String link = _makeLink(CACHE_ENTRY2_PATH, { | |
| 642 CONTEXT_QUERY_PARAM: folder.path, | |
| 643 SOURCE_QUERY_PARAM: source.uri.toString() | |
| 644 }, sourceName, exception != null); | |
| 645 if (sourceEntry.explicitlyAdded) { | |
| 646 explicitNames.add(sourceName); | |
| 647 } else { | |
| 648 implicitNames.add(sourceName); | |
| 649 } | |
| 650 links[sourceName] = link; | |
| 651 } | |
| 652 }); | |
| 653 _overlayContents.clear(); | |
| 654 int count = 0; | |
| 655 context.visitContentCache((Source source, int stamp, String contents) { | |
| 656 count++; | |
| 657 overlayMap[source.fullName] = count; | |
| 658 _overlayContents[count] = contents; | |
| 659 }); | |
| 660 explicitNames.sort(); | |
| 661 implicitNames.sort(); | |
| 662 | |
| 663 void _writeFiles(StringBuffer buffer, String title, List<String> fileNames) | |
| 664 { | |
| 665 buffer.write('<h3>$title</h3>'); | |
| 666 if (fileNames == null || fileNames.isEmpty) { | |
| 667 buffer.write('<p>None</p>'); | |
| 668 } else { | |
| 669 buffer.write('<table style="width: 100%">'); | |
| 670 for (String fileName in fileNames) { | |
| 671 buffer.write('<tr><td>'); | |
| 672 buffer.write(links[fileName]); | |
| 673 buffer.write('</td><td>'); | |
| 674 if (overlayMap.containsKey(fileName)) { | |
| 675 buffer.write(_makeLink(OVERLAY_PATH, { | |
| 676 ID_PARAM: overlayMap[fileName].toString() | |
| 677 }, 'overlay')); | |
| 678 } | |
| 679 buffer.write('</td></tr>'); | |
| 680 } | |
| 681 buffer.write('</table>'); | |
| 682 } | |
| 683 } | |
| 684 | |
| 685 _writeResponse(request, (StringBuffer buffer) { | |
| 686 _writePage( | |
| 687 buffer, | |
| 688 'Analysis Server - Context', | |
| 689 ['Context: $contextFilter'], | |
| 690 (StringBuffer buffer) { | |
| 691 List headerRowText = ['Context']; | |
| 692 headerRowText.addAll(CacheState.values); | |
| 693 buffer.write('<h3>Summary</h3>'); | |
| 694 buffer.write('<table>'); | |
| 695 _writeRow2(buffer, headerRowText, header: true); | |
| 696 AnalysisContextStatistics statistics = context.statistics; | |
| 697 statistics.cacheRows.forEach((AnalysisContextStatistics_CacheRow row) { | |
| 698 List rowText = [row.name]; | |
| 699 for (CacheState state in CacheState.values) { | |
| 700 String text = row.getCount(state).toString(); | |
| 701 Map<String, String> params = <String, String>{ | |
| 702 STATE_QUERY_PARAM: state.toString(), | |
| 703 CONTEXT_QUERY_PARAM: folder.path, | |
| 704 DESCRIPTOR_QUERY_PARAM: row.name | |
| 705 }; | |
| 706 rowText.add(_makeLink(CACHE_STATE_PATH, params, text)); | |
| 707 } | |
| 708 _writeRow2(buffer, rowText, classes: [null, "right"]); | |
| 709 }); | |
| 710 buffer.write('</table>'); | |
| 711 | |
| 712 _writeFiles(buffer, 'Priority Files', priorityNames); | |
| 713 _writeFiles(buffer, 'Explicitly Analyzed Files', explicitNames); | |
| 714 _writeFiles(buffer, 'Implicitly Analyzed Files', implicitNames); | |
| 715 | |
| 716 buffer.write('<h3>Exceptions</h3>'); | |
| 717 if (exceptions.isEmpty) { | |
| 718 buffer.write('<p>None</p>'); | |
| 719 } else { | |
| 720 exceptions.forEach((CaughtException exception) { | |
| 721 _writeException(buffer, exception); | |
| 722 }); | |
| 723 } | |
| 724 }); | |
| 725 }); | |
| 726 } | |
| 727 | |
| 728 /** | |
| 729 * Return a response containing information about an element structure. | |
| 730 */ | |
| 731 void _returnElement(HttpRequest request) { | |
| 732 AnalysisServer analysisServer = _server.analysisServer; | |
| 733 if (analysisServer == null) { | |
| 734 return _returnFailure(request, 'Analysis server not running'); | |
| 735 } | |
| 736 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; | |
| 737 if (contextFilter == null) { | |
| 738 return _returnFailure( | |
| 739 request, | |
| 740 'Query parameter $CONTEXT_QUERY_PARAM required'); | |
| 741 } | |
| 742 Folder folder = _findFolder(analysisServer, contextFilter); | |
| 743 if (folder == null) { | |
| 744 return _returnFailure(request, 'Invalid context: $contextFilter'); | |
| 745 } | |
| 746 String sourceUri = request.uri.queryParameters[SOURCE_QUERY_PARAM]; | |
| 747 if (sourceUri == null) { | |
| 748 return _returnFailure( | |
| 749 request, | |
| 750 'Query parameter $SOURCE_QUERY_PARAM required'); | |
| 751 } | |
| 752 | |
| 753 AnalysisContextImpl context = analysisServer.folderMap[folder]; | |
| 754 | |
| 755 _writeResponse(request, (StringBuffer buffer) { | |
| 756 _writePage( | |
| 757 buffer, | |
| 758 'Analysis Server - Element Model', | |
| 759 ['Context: $contextFilter', 'File: $sourceUri'], | |
| 760 (HttpResponse) { | |
| 761 Source source = context.sourceFactory.forUri(sourceUri); | |
| 762 if (source == null) { | |
| 763 buffer.write('<p>Not found.</p>'); | |
| 764 return; | |
| 765 } | |
| 766 SourceEntry entry = context.getReadableSourceEntryOrNull(source); | |
| 767 if (entry == null) { | |
| 768 buffer.write('<p>Not found.</p>'); | |
| 769 return; | |
| 770 } | |
| 771 LibraryElement element = entry.getValue(DartEntry.ELEMENT); | |
| 772 if (element == null) { | |
| 773 buffer.write('<p>null</p>'); | |
| 774 return; | |
| 775 } | |
| 776 element.accept(new ElementWriter(buffer)); | |
| 777 }); | |
| 778 }); | |
| 779 } | |
| 780 | |
| 379 void _returnFailure(HttpRequest request, String message) { | 781 void _returnFailure(HttpRequest request, String message) { |
| 380 HttpResponse response = request.response; | 782 HttpResponse response = request.response; |
| 381 response.statusCode = HttpStatus.OK; | 783 response.statusCode = HttpStatus.OK; |
| 382 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); | 784 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); |
| 383 response.write('<html>'); | 785 response.write('<html>'); |
| 384 response.write('<head>'); | 786 response.write('<head>'); |
| 385 response.write('<title>Dart Analysis Server - Failure</title>'); | 787 response.write('<title>Dart Analysis Server - Failure</title>'); |
| 386 response.write('</head>'); | 788 response.write('</head>'); |
| 387 response.write('<body>'); | 789 response.write('<body>'); |
| 388 response.write(HTML_ESCAPE.convert(message)); | 790 response.write(HTML_ESCAPE.convert(message)); |
| 389 response.write('</body>'); | 791 response.write('</body>'); |
| 390 response.write('</html>'); | 792 response.write('</html>'); |
| 391 response.close(); | 793 response.close(); |
| 392 } | 794 } |
| 393 | 795 |
| 394 void _returnOverlayContents(HttpRequest request) { | 796 void _returnOverlayContents(HttpRequest request) { |
| 395 String idString = request.requestedUri.queryParameters['id']; | 797 String idString = request.requestedUri.queryParameters[ID_PARAM]; |
| 396 int id = int.parse(idString); | 798 int id = int.parse(idString); |
| 397 String contents = _overlayContents[id]; | 799 String contents = _overlayContents[id]; |
| 398 HttpResponse response = request.response; | 800 HttpResponse response = request.response; |
| 399 response.statusCode = HttpStatus.OK; | 801 response.statusCode = HttpStatus.OK; |
| 400 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); | 802 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); |
| 401 response.write('<html>'); | 803 response.write('<html>'); |
| 402 response.write('<head>'); | 804 response.write('<head>'); |
| 403 response.write('<title>Dart Analysis Server - Overlay</title>'); | 805 response.write('<title>Dart Analysis Server - Overlay</title>'); |
| 404 response.write('</head>'); | 806 response.write('</head>'); |
| 405 response.write('<body>'); | 807 response.write('<body>'); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 531 }; | 933 }; |
| 532 rowText.add(_makeLink(CACHE_STATE_PATH, params, text)); | 934 rowText.add(_makeLink(CACHE_STATE_PATH, params, text)); |
| 533 } | 935 } |
| 534 _writeRow(response, rowText, classes: [null, "right"]); | 936 _writeRow(response, rowText, classes: [null, "right"]); |
| 535 }); | 937 }); |
| 536 response.write('</table>'); | 938 response.write('</table>'); |
| 537 List<CaughtException> exceptions = statistics.exceptions; | 939 List<CaughtException> exceptions = statistics.exceptions; |
| 538 if (!exceptions.isEmpty) { | 940 if (!exceptions.isEmpty) { |
| 539 response.write('<h2>Exceptions</h2>'); | 941 response.write('<h2>Exceptions</h2>'); |
| 540 exceptions.forEach((CaughtException exception) { | 942 exceptions.forEach((CaughtException exception) { |
| 541 response.write('<p>${exception.exception}</p>'); | 943 StringBuffer buffer = new StringBuffer(); |
| 944 _writeException(buffer, exception); | |
| 945 response.write(buffer.toString()); | |
| 542 }); | 946 }); |
| 543 } | 947 } |
| 544 }); | 948 }); |
| 545 } | 949 } |
| 546 response.write('<h1>Most recent strings printed by analysis server</h2>'); | 950 response.write('<h1>Most recent strings printed by analysis server</h2>'); |
| 547 response.write('<pre>'); | 951 response.write('<pre>'); |
| 548 response.write(HTML_ESCAPE.convert(_printBuffer.join('\n'))); | 952 response.write(HTML_ESCAPE.convert(_printBuffer.join('\n'))); |
| 549 response.write('</pre>'); | 953 response.write('</pre>'); |
| 550 response.write('</body>'); | 954 response.write('</body>'); |
| 551 response.write('</html>'); | 955 response.write('</html>'); |
| 552 response.close(); | 956 response.close(); |
| 553 } | 957 } |
| 554 | 958 |
| 555 /** | 959 /** |
| 960 * Return a response indicating the status of the analysis server. | |
| 961 */ | |
| 962 void _returnServerStatus2(HttpRequest request) { | |
| 963 _writeResponse(request, (StringBuffer buffer) { | |
| 964 _writePage(buffer, 'Analysis Server - Status', [], (StringBuffer buffer) { | |
| 965 if (_writeServerStatus(buffer)) { | |
| 966 _writeAnalysisStatus(buffer); | |
| 967 _writeEditStatus(buffer); | |
| 968 _writeExecutionStatus(buffer); | |
| 969 _writeRecentOutput(buffer); | |
| 970 } | |
| 971 }); | |
| 972 }); | |
| 973 } | |
| 974 | |
| 975 /** | |
| 556 * Return an error in response to an unrecognized request received by the HTTP | 976 * Return an error in response to an unrecognized request received by the HTTP |
| 557 * server. | 977 * server. |
| 558 */ | 978 */ |
| 559 void _returnUnknownRequest(HttpRequest request) { | 979 void _returnUnknownRequest(HttpRequest request) { |
| 560 HttpResponse response = request.response; | 980 HttpResponse response = request.response; |
| 561 response.statusCode = HttpStatus.NOT_FOUND; | 981 response.statusCode = HttpStatus.NOT_FOUND; |
| 562 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); | 982 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); |
| 563 response.write('<html>'); | 983 response.write('<html>'); |
| 564 response.write('<head>'); | 984 response.write('<head>'); |
| 565 response.write('<title>Dart Analysis Server - Page Not Found</title>'); | 985 response.write('<title>Dart Analysis Server - Page Not Found</title>'); |
| 566 response.write('</head>'); | 986 response.write('</head>'); |
| 567 response.write('<body>'); | 987 response.write('<body>'); |
| 568 response.write('<h1>Page Not Found</h1>'); | 988 response.write('<h1>Page Not Found</h1>'); |
| 569 response.write('<p>Try one of these links instead:</p>'); | 989 response.write('<p>Try one of these links instead:</p>'); |
| 570 response.write('<ul>'); | 990 response.write('<ul>'); |
| 571 response.write('<li><a href="$STATUS_PATH">Server Status</a></li>'); | 991 response.write('<li><a href="$STATUS_PATH">Server Status</a></li>'); |
| 572 response.write('<li><a href="$COMPLETION_PATH">Completion Stats</a></li>'); | 992 response.write('<li><a href="$COMPLETION_PATH">Completion Stats</a></li>'); |
| 573 response.write('<ul>'); | 993 response.write('<ul>'); |
| 574 response.write('</body>'); | 994 response.write('</body>'); |
| 575 response.write('</html>'); | 995 response.write('</html>'); |
| 576 response.close(); | 996 response.close(); |
| 577 } | 997 } |
| 578 | 998 |
| 579 /** | 999 /** |
| 1000 * Return a two digit decimal representation of the given integer [value]. | |
|
Paul Berry
2015/01/20 17:28:11
Nit: this function will produce nonsense if the in
Brian Wilkerson
2015/01/20 17:46:34
Done.
| |
| 1001 */ | |
| 1002 String _twoDigit(int value) { | |
| 1003 if (value < 10) { | |
| 1004 return '0$value'; | |
| 1005 } | |
| 1006 return value.toString(); | |
| 1007 } | |
| 1008 | |
| 1009 /** | |
| 1010 * Write the status of the analysis domain (on the main status page) to the | |
| 1011 * given [buffer] object. | |
| 1012 */ | |
| 1013 void _writeAnalysisStatus(StringBuffer buffer) { | |
| 1014 AnalysisServer analysisServer = _server.analysisServer; | |
| 1015 Map<Folder, AnalysisContext> folderMap = analysisServer.folderMap; | |
| 1016 List<Folder> folders = folderMap.keys.toList(); | |
| 1017 folders.sort( | |
| 1018 (Folder first, Folder second) => first.shortName.compareTo(second.shortN ame)); | |
| 1019 AnalysisOptionsImpl options = | |
| 1020 analysisServer.contextDirectoryManager.defaultOptions; | |
| 1021 ServerOperationQueue operationQueue = analysisServer.operationQueue; | |
| 1022 | |
| 1023 buffer.write('<h3>Analysis Domain</h3>'); | |
| 1024 _writeTwoColumns(buffer, (StringBuffer buffer) { | |
| 1025 if (operationQueue.isEmpty) { | |
| 1026 buffer.write('<p>Status: Done analyzing</p>'); | |
| 1027 } else { | |
| 1028 ServerOperation operation = operationQueue.peek(); | |
| 1029 if (operation is PerformAnalysisOperation) { | |
| 1030 Folder folder = _keyForValue(folderMap, operation.context); | |
| 1031 if (folder == null) { | |
| 1032 buffer.write('<p>Status: Analyzing in unmapped context</p>'); | |
| 1033 } else { | |
| 1034 buffer.write('<p>Status: Analyzing in ${folder.path}</p>'); | |
| 1035 } | |
| 1036 } else { | |
| 1037 buffer.write('<p>Status: Analyzing</p>'); | |
| 1038 } | |
| 1039 } | |
| 1040 | |
| 1041 buffer.write('<p><b>Analysis Contexts</b></p>'); | |
| 1042 buffer.write('<p>'); | |
| 1043 bool first = true; | |
| 1044 folders.forEach((Folder folder) { | |
| 1045 if (first) { | |
| 1046 first = false; | |
| 1047 } else { | |
| 1048 buffer.write('<br>'); | |
| 1049 } | |
| 1050 String key = folder.shortName; | |
| 1051 buffer.write(_makeLink(CONTEXT_PATH, { | |
| 1052 CONTEXT_QUERY_PARAM: folder.path | |
| 1053 }, key, _hasException(folderMap[folder]))); | |
| 1054 }); | |
| 1055 buffer.write('</p>'); | |
| 1056 | |
| 1057 buffer.write('<p><b>Options</b></p>'); | |
| 1058 buffer.write('<p>'); | |
| 1059 _writeOption( | |
| 1060 buffer, | |
| 1061 'Analyze functon bodies', | |
| 1062 options.analyzeFunctionBodies); | |
| 1063 _writeOption(buffer, 'Cache size', options.cacheSize); | |
| 1064 _writeOption(buffer, 'Generate hints', options.hint); | |
| 1065 _writeOption(buffer, 'Generate dart2js hints', options.dart2jsHint); | |
| 1066 _writeOption(buffer, 'Generate SDK errors', options.generateSdkErrors); | |
| 1067 _writeOption(buffer, 'Incremental resolution', options.incremental); | |
| 1068 _writeOption( | |
| 1069 buffer, | |
| 1070 'Incremental resolution with API changes', | |
| 1071 options.incrementalApi); | |
| 1072 _writeOption( | |
| 1073 buffer, | |
| 1074 'Preserve comments', | |
| 1075 options.preserveComments, | |
| 1076 last: true); | |
| 1077 buffer.write('</p>'); | |
| 1078 }, (StringBuffer buffer) { | |
| 1079 _writeSubscriptionMap( | |
| 1080 buffer, | |
| 1081 AnalysisService.VALUES, | |
| 1082 analysisServer.analysisServices); | |
| 1083 }); | |
| 1084 } | |
| 1085 | |
| 1086 /** | |
| 580 * Append code completion information. | 1087 * Append code completion information. |
| 581 */ | 1088 */ |
| 582 void _writeCompletionInfo(HttpResponse response, maxCount) { | 1089 void _writeCompletionInfo(HttpResponse response, maxCount) { |
| 583 response.write('<h1>Code Completion</h1>'); | 1090 response.write('<h1>Code Completion</h1>'); |
| 584 AnalysisServer analysisServer = _server.analysisServer; | 1091 AnalysisServer analysisServer = _server.analysisServer; |
| 585 if (analysisServer == null) { | 1092 if (analysisServer == null) { |
| 586 response.write('<p>Not running</p>'); | 1093 response.write('<p>Not running</p>'); |
| 587 return; | 1094 return; |
| 588 } | 1095 } |
| 589 CompletionDomainHandler handler = analysisServer.handlers.firstWhere( | 1096 CompletionDomainHandler handler = analysisServer.handlers.firstWhere( |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 655 ' ', | 1162 ' ', |
| 656 performance.suggestionCount, | 1163 performance.suggestionCount, |
| 657 ' ', | 1164 ' ', |
| 658 performance.snippet]); | 1165 performance.snippet]); |
| 659 }); | 1166 }); |
| 660 response.write('</table>'); | 1167 response.write('</table>'); |
| 661 } | 1168 } |
| 662 } | 1169 } |
| 663 | 1170 |
| 664 /** | 1171 /** |
| 1172 * Generate a table showing the cache values corresponding to the given | |
| 1173 * [descriptors], using [getState] to get the cache state corresponding to | |
| 1174 * each descriptor, and [getValue] to get the cached value corresponding to | |
| 1175 * each descriptor. Append the resulting HTML to the given [buffer]. The | |
| 1176 * [linkParameters] will be used if the value is too large to be displayed on | |
| 1177 * the current page and needs to be linked to a separate page. | |
| 1178 */ | |
| 1179 void _writeDescriptorTable(StringBuffer buffer, | |
| 1180 List<DataDescriptor> descriptors, CacheState getState(DataDescriptor), dyn amic | |
| 1181 getValue(DataDescriptor), Map<String, String> linkParameters) { | |
| 1182 buffer.write('<dl>'); | |
| 1183 for (DataDescriptor descriptor in descriptors) { | |
| 1184 String descriptorName = HTML_ESCAPE.convert(descriptor.toString()); | |
| 1185 String descriptorState = | |
| 1186 HTML_ESCAPE.convert(getState(descriptor).toString()); | |
| 1187 buffer.write('<dt>$descriptorName ($descriptorState)</dt><dd>'); | |
| 1188 try { | |
| 1189 _writeValueAsHtml(buffer, getValue(descriptor), linkParameters); | |
| 1190 } catch (exception) { | |
| 1191 buffer.write('(${HTML_ESCAPE.convert(exception.toString())})'); | |
| 1192 } | |
| 1193 buffer.write('</dd>'); | |
| 1194 } | |
| 1195 buffer.write('</dl>'); | |
| 1196 } | |
| 1197 | |
| 1198 /** | |
| 1199 * Write the status of the edit domain (on the main status page) to the given | |
| 1200 * [buffer]. | |
| 1201 */ | |
| 1202 void _writeEditStatus(StringBuffer buffer) { | |
| 1203 buffer.write('<h3>Edit Domain</h3>'); | |
| 1204 _writeTwoColumns(buffer, (StringBuffer buffer) { | |
| 1205 buffer.write('<p><b>Performance Data</b></p>'); | |
| 1206 buffer.write('<p>'); | |
| 1207 buffer.write(_makeLink(COMPLETION_PATH, {}, 'Completion data')); | |
| 1208 buffer.write('</p>'); | |
| 1209 }, (StringBuffer buffer) { | |
| 1210 }); | |
| 1211 } | |
| 1212 | |
| 1213 /** | |
| 1214 * Write a representation of the given [caughtException] to the given | |
| 1215 * [buffer]. If [isCause] is `true`, then the exception was a cause for | |
| 1216 * another exception. | |
| 1217 */ | |
| 1218 void _writeException(StringBuffer buffer, CaughtException caughtException, | |
| 1219 {bool isCause: false}) { | |
| 1220 Object exception = caughtException.exception; | |
| 1221 | |
| 1222 if (exception is AnalysisException) { | |
| 1223 buffer.write('<p>'); | |
| 1224 if (isCause) { | |
| 1225 buffer.write('Caused by '); | |
| 1226 } | |
| 1227 buffer.write(exception.message); | |
| 1228 buffer.write('</p>'); | |
| 1229 _writeStackTrace(buffer, caughtException.stackTrace); | |
| 1230 CaughtException cause = exception.cause; | |
| 1231 if (cause != null) { | |
| 1232 buffer.write('<blockquote>'); | |
| 1233 _writeException(buffer, cause, isCause: true); | |
| 1234 buffer.write('</blockquote>'); | |
| 1235 } | |
| 1236 } else { | |
| 1237 buffer.write('<p>'); | |
| 1238 if (isCause) { | |
| 1239 buffer.write('Caused by '); | |
| 1240 } | |
| 1241 buffer.write(exception.toString()); | |
| 1242 buffer.write('<p>'); | |
| 1243 _writeStackTrace(buffer, caughtException.stackTrace); | |
| 1244 } | |
| 1245 } | |
| 1246 | |
| 1247 /** | |
| 1248 * Write the status of the execution domain (on the main status page) to the | |
| 1249 * given [buffer]. | |
| 1250 */ | |
| 1251 void _writeExecutionStatus(StringBuffer buffer) { | |
| 1252 AnalysisServer analysisServer = _server.analysisServer; | |
| 1253 ExecutionDomainHandler handler = analysisServer.handlers.firstWhere( | |
| 1254 (RequestHandler handler) => handler is ExecutionDomainHandler, | |
| 1255 orElse: () => null); | |
| 1256 Set<ExecutionService> services = new Set<ExecutionService>(); | |
| 1257 if (handler.onFileAnalyzed != null) { | |
| 1258 services.add(ExecutionService.LAUNCH_DATA); | |
| 1259 } | |
| 1260 | |
| 1261 if (handler != null) { | |
| 1262 buffer.write('<h3>Execution Domain</h3>'); | |
| 1263 _writeTwoColumns(buffer, (StringBuffer buffer) { | |
| 1264 _writeSubscriptionList(buffer, ExecutionService.VALUES, services); | |
| 1265 }, (StringBuffer buffer) { | |
| 1266 }); | |
| 1267 } | |
| 1268 } | |
| 1269 | |
| 1270 /** | |
| 1271 * Write a representation of an analysis option with the given [name] and | |
| 1272 * [value] to the given [buffer]. The option should be separated from other | |
| 1273 * options unless the [last] flag is true, indicating that this is the last | |
| 1274 * option in the list of options. | |
| 1275 */ | |
| 1276 void _writeOption(StringBuffer buffer, String name, Object value, {bool last: | |
| 1277 false}) { | |
| 1278 buffer.write(name); | |
| 1279 buffer.write(' = '); | |
| 1280 buffer.write(value.toString()); | |
| 1281 if (!last) { | |
| 1282 buffer.write('<br>'); | |
| 1283 } | |
| 1284 } | |
| 1285 | |
| 1286 /** | |
| 1287 * Write a standard HTML page to the given [buffer]. The page will have the | |
| 1288 * given [title] and a body that is generated by the given [body] generator. | |
| 1289 */ | |
| 1290 void _writePage(StringBuffer buffer, String title, List<String> subtitles, | |
| 1291 HtmlGenerator body) { | |
| 1292 DateTime now = new DateTime.now(); | |
| 1293 String date = "${now.month}/${now.day}/${now.year}"; | |
| 1294 String time = | |
| 1295 "${now.hour}:${_twoDigit(now.minute)}:${_twoDigit(now.second)}.${now.mil lisecond}"; | |
| 1296 | |
| 1297 buffer.write('<!DOCTYPE html>'); | |
| 1298 buffer.write('<html>'); | |
| 1299 buffer.write('<head>'); | |
| 1300 buffer.write('<meta charset="utf-8">'); | |
| 1301 buffer.write( | |
| 1302 '<meta name="viewport" content="width=device-width, initial-scale=1.0">' ); | |
| 1303 buffer.write('<title>$title</title>'); | |
| 1304 buffer.write('<style>'); | |
| 1305 buffer.write('a {color: #0000DD; text-decoration: none;}'); | |
| 1306 buffer.write('a:link.error {background-color: #FFEEEE;}'); | |
| 1307 buffer.write('a:visited.error {background-color: #FFEEEE;}'); | |
| 1308 buffer.write('a:hover.error {background-color: #FFEEEE;}'); | |
| 1309 buffer.write('a:active.error {background-color: #FFEEEE;}'); | |
| 1310 buffer.write( | |
| 1311 'h3 {background-color: #DDDDDD; margin-top: 0em; margin-bottom: 0em;}'); | |
| 1312 buffer.write('p {margin-top: 0.5em; margin-bottom: 0.5em;}'); | |
| 1313 // response.write('span.error {text-decoration-line: underline; text-decorati on-color: red; text-decoration-style: wavy;}'); | |
| 1314 buffer.write( | |
| 1315 'table.column {border: 0px solid black; width: 100%; table-layout: fixed ;}'); | |
| 1316 buffer.write('td.column {vertical-align: top; width: 50%;}'); | |
| 1317 buffer.write('td.right {text-align: right;}'); | |
| 1318 buffer.write('</style>'); | |
| 1319 buffer.write('</head>'); | |
| 1320 | |
| 1321 buffer.write('<body>'); | |
| 1322 buffer.write( | |
| 1323 '<h2>$title <small><small>(as of $time on $date)</small></small></h2>'); | |
| 1324 if (subtitles != null && subtitles.isNotEmpty) { | |
| 1325 buffer.write('<blockquote>'); | |
| 1326 bool first = true; | |
| 1327 for (String subtitle in subtitles) { | |
| 1328 if (first) { | |
| 1329 first = false; | |
| 1330 } else { | |
| 1331 buffer.write('<br>'); | |
| 1332 } | |
| 1333 buffer.write('<b>'); | |
| 1334 buffer.write(subtitle); | |
| 1335 buffer.write('</b>'); | |
| 1336 } | |
| 1337 buffer.write('</blockquote>'); | |
| 1338 } | |
| 1339 try { | |
| 1340 body(buffer); | |
| 1341 } catch (exception, stackTrace) { | |
| 1342 buffer.write('<h3>Exception while creating page</h3>'); | |
| 1343 _writeException(buffer, new CaughtException(exception, stackTrace)); | |
| 1344 } | |
| 1345 buffer.write('</body>'); | |
| 1346 buffer.write('</html>'); | |
| 1347 } | |
| 1348 | |
| 1349 /** | |
| 1350 * Write the recent output section (on the main status page) to the given | |
| 1351 * [buffer] object. | |
| 1352 */ | |
| 1353 void _writeRecentOutput(StringBuffer buffer) { | |
| 1354 buffer.write('<h3>Recent Output</h3>'); | |
| 1355 String output = HTML_ESCAPE.convert(_printBuffer.join('\n')); | |
| 1356 if (output.isEmpty) { | |
| 1357 buffer.write('<i>none</i>'); | |
| 1358 } else { | |
| 1359 buffer.write('<pre>'); | |
| 1360 buffer.write(output); | |
| 1361 buffer.write('</pre>'); | |
| 1362 } | |
| 1363 } | |
| 1364 | |
| 1365 void _writeResponse(HttpRequest request, HtmlGenerator writePage) { | |
| 1366 HttpResponse response = request.response; | |
| 1367 response.statusCode = HttpStatus.OK; | |
| 1368 response.headers.add(HttpHeaders.CONTENT_TYPE, "text/html"); | |
| 1369 try { | |
| 1370 StringBuffer buffer = new StringBuffer(); | |
| 1371 try { | |
| 1372 writePage(buffer); | |
| 1373 } catch (exception, stackTrace) { | |
| 1374 buffer.clear(); | |
| 1375 _writePage(buffer, 'Internal Exception', [], (StringBuffer buffer) { | |
| 1376 _writeException(buffer, new CaughtException(exception, stackTrace)); | |
| 1377 }); | |
| 1378 } | |
| 1379 response.write(buffer.toString()); | |
| 1380 } finally { | |
| 1381 response.close(); | |
| 1382 } | |
| 1383 } | |
| 1384 | |
| 1385 /** | |
| 665 * Write a single row within a table to the given [response] object. The row | 1386 * Write a single row within a table to the given [response] object. The row |
| 666 * will have one cell for each of the [columns], and will be a header row if | 1387 * will have one cell for each of the [columns], and will be a header row if |
| 667 * [header] is `true`. | 1388 * [header] is `true`. |
| 668 */ | 1389 */ |
| 669 void _writeRow(HttpResponse response, List<Object> columns, {bool header: | 1390 void _writeRow(HttpResponse response, List<Object> columns, {bool header: |
| 670 false, List<String> classes}) { | 1391 false, List<String> classes}) { |
| 671 response.write('<tr>'); | 1392 response.write('<tr>'); |
| 672 int count = columns.length; | 1393 int count = columns.length; |
| 673 int maxClassIndex = classes == null ? 0 : classes.length - 1; | 1394 int maxClassIndex = classes == null ? 0 : classes.length - 1; |
| 674 for (int i = 0; i < count; i++) { | 1395 for (int i = 0; i < count; i++) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 687 response.write(columns[i]); | 1408 response.write(columns[i]); |
| 688 if (header) { | 1409 if (header) { |
| 689 response.write('</th>'); | 1410 response.write('</th>'); |
| 690 } else { | 1411 } else { |
| 691 response.write('</td>'); | 1412 response.write('</td>'); |
| 692 } | 1413 } |
| 693 } | 1414 } |
| 694 | 1415 |
| 695 response.write('</tr>'); | 1416 response.write('</tr>'); |
| 696 } | 1417 } |
| 1418 | |
| 1419 /** | |
| 1420 * Write a single row within a table to the given [buffer]. The row will have | |
| 1421 * one cell for each of the [columns], and will be a header row if [header] is | |
| 1422 * `true`. | |
| 1423 */ | |
| 1424 void _writeRow2(StringBuffer buffer, List<Object> columns, {bool header: | |
| 1425 false, List<String> classes}) { | |
| 1426 buffer.write('<tr>'); | |
| 1427 int count = columns.length; | |
| 1428 int maxClassIndex = classes == null ? 0 : classes.length - 1; | |
| 1429 for (int i = 0; i < count; i++) { | |
| 1430 String classAttribute = ''; | |
| 1431 if (classes != null) { | |
| 1432 String className = classes[min(i, maxClassIndex)]; | |
| 1433 if (className != null) { | |
| 1434 classAttribute = ' class="$className"'; | |
| 1435 } | |
| 1436 } | |
| 1437 if (header) { | |
| 1438 buffer.write('<th$classAttribute>'); | |
| 1439 } else { | |
| 1440 buffer.write('<td$classAttribute>'); | |
| 1441 } | |
| 1442 buffer.write(columns[i]); | |
| 1443 if (header) { | |
| 1444 buffer.write('</th>'); | |
| 1445 } else { | |
| 1446 buffer.write('</td>'); | |
| 1447 } | |
| 1448 } | |
| 1449 buffer.write('</tr>'); | |
| 1450 } | |
| 1451 | |
| 1452 /** | |
| 1453 * Write the status of the service domain (on the main status page) to the | |
| 1454 * given [response] object. | |
| 1455 */ | |
| 1456 bool _writeServerStatus(StringBuffer buffer) { | |
| 1457 AnalysisServer analysisServer = _server.analysisServer; | |
| 1458 Set<ServerService> services = analysisServer.serverServices; | |
| 1459 | |
| 1460 buffer.write('<h3>Server Domain</h3>'); | |
| 1461 _writeTwoColumns(buffer, (StringBuffer buffer) { | |
| 1462 if (analysisServer == null) { | |
| 1463 buffer.write('Status: <span style="color:red">Not running</span>'); | |
| 1464 return false; | |
| 1465 } | |
| 1466 buffer.write('<p>'); | |
| 1467 buffer.write('Status: Running<br>'); | |
| 1468 buffer.write('Instrumentation: '); | |
| 1469 if (AnalysisEngine.instance.instrumentationService.isActive) { | |
| 1470 buffer.write('<span style="color:red">Active</span>'); | |
| 1471 } else { | |
| 1472 buffer.write('Inactive'); | |
| 1473 } | |
| 1474 buffer.write('<br>'); | |
| 1475 buffer.write('Version: '); | |
| 1476 buffer.write(AnalysisServer.VERSION); | |
| 1477 buffer.write('</p>'); | |
| 1478 }, (StringBuffer buffer) { | |
| 1479 _writeSubscriptionList(buffer, ServerService.VALUES, services); | |
| 1480 }); | |
| 1481 return true; | |
| 1482 } | |
| 1483 | |
| 1484 /** | |
| 1485 * Write a representation of the given [stackTrace] to the given [buffer]. | |
| 1486 */ | |
| 1487 void _writeStackTrace(StringBuffer buffer, StackTrace stackTrace) { | |
| 1488 if (stackTrace != null) { | |
| 1489 String trace = stackTrace.toString().replaceAll('#', '<br>#'); | |
| 1490 if (trace.startsWith('<br>#')) { | |
| 1491 trace = trace.substring(4); | |
| 1492 } | |
| 1493 buffer.write('<p>'); | |
| 1494 buffer.write(trace); | |
| 1495 buffer.write('</p>'); | |
| 1496 } | |
| 1497 } | |
| 1498 | |
| 1499 /** | |
| 1500 * Given a [service] that could be subscribed to and a set of the services | |
| 1501 * that are actually subscribed to ([subscribedServices]), write a | |
| 1502 * representation of the service to the given [buffer]. | |
| 1503 */ | |
| 1504 void _writeSubscriptionInList(StringBuffer buffer, Enum service, | |
| 1505 Set<Enum> subscribedServices) { | |
| 1506 if (subscribedServices.contains(service)) { | |
| 1507 buffer.write('<code>+ </code>'); | |
| 1508 } else { | |
| 1509 buffer.write('<code>- </code>'); | |
| 1510 } | |
| 1511 buffer.write(service.name); | |
| 1512 buffer.write('<br>'); | |
| 1513 } | |
| 1514 | |
| 1515 /** | |
| 1516 * Given a [service] that could be subscribed to and a set of paths that are | |
| 1517 * subscribed to the services ([subscribedPaths]), write a representation of | |
| 1518 * the service to the given [buffer]. | |
| 1519 */ | |
| 1520 void _writeSubscriptionInMap(StringBuffer buffer, Enum service, | |
| 1521 Set<String> subscribedPaths) { | |
| 1522 buffer.write('<p>'); | |
| 1523 buffer.write(service.name); | |
| 1524 buffer.write('</p>'); | |
| 1525 if (subscribedPaths == null || subscribedPaths.isEmpty) { | |
| 1526 buffer.write('none'); | |
| 1527 } else { | |
| 1528 List<String> paths = subscribedPaths.toList(); | |
| 1529 paths.sort(); | |
| 1530 for (String path in paths) { | |
| 1531 buffer.write('<p>'); | |
| 1532 buffer.write(path); | |
| 1533 buffer.write('</p>'); | |
| 1534 } | |
| 1535 } | |
| 1536 } | |
| 1537 | |
| 1538 /** | |
| 1539 * Given a list containing all of the services that can be subscribed to in a | |
| 1540 * single domain ([allServices]) and a set of the services that are actually | |
| 1541 * subscribed to ([subscribedServices]), write a representation of the | |
| 1542 * subscriptions to the given [buffer]. | |
| 1543 */ | |
| 1544 void _writeSubscriptionList(StringBuffer buffer, List<Enum> allServices, | |
| 1545 Set<Enum> subscribedServices) { | |
| 1546 buffer.write('<p><b>Subscriptions</b></p>'); | |
| 1547 buffer.write('<p>'); | |
| 1548 for (Enum service in allServices) { | |
| 1549 _writeSubscriptionInList(buffer, service, subscribedServices); | |
| 1550 } | |
| 1551 buffer.write('</p>'); | |
| 1552 } | |
| 1553 | |
| 1554 /** | |
| 1555 * Given a list containing all of the services that can be subscribed to in a | |
| 1556 * single domain ([allServices]) and a set of the services that are actually | |
| 1557 * subscribed to ([subscribedServices]), write a representation of the | |
| 1558 * subscriptions to the given [buffer]. | |
| 1559 */ | |
| 1560 void _writeSubscriptionMap(StringBuffer buffer, List<Enum> allServices, | |
| 1561 Map<Enum, Set<String>> subscribedServices) { | |
| 1562 buffer.write('<p><b>Subscriptions</b></p>'); | |
| 1563 for (Enum service in allServices) { | |
| 1564 _writeSubscriptionInMap(buffer, service, subscribedServices[service]); | |
| 1565 } | |
| 1566 } | |
| 1567 | |
| 1568 /** | |
| 1569 * Write two columns of information to the given [buffer], where the | |
| 1570 * [leftColumn] and [rightColumn] functions are used to generate the content | |
| 1571 * of those columns. | |
| 1572 */ | |
| 1573 void _writeTwoColumns(StringBuffer buffer, HtmlGenerator leftColumn, | |
| 1574 HtmlGenerator rightColumn) { | |
| 1575 buffer.write( | |
| 1576 '<table class="column"><tr class="column"><td class="column">'); | |
| 1577 leftColumn(buffer); | |
| 1578 buffer.write('</td><td class="column">'); | |
| 1579 rightColumn(buffer); | |
| 1580 buffer.write('</td></tr></table>'); | |
| 1581 } | |
| 1582 | |
| 1583 /** | |
| 1584 * Render the given [value] as HTML and append it to the given [buffer]. The | |
| 1585 * [linkParameters] will be used if the value is too large to be displayed on | |
| 1586 * the current page and needs to be linked to a separate page. | |
| 1587 */ | |
| 1588 void _writeValueAsHtml(StringBuffer buffer, Object value, Map<String, | |
| 1589 String> linkParameters) { | |
| 1590 if (value == null) { | |
| 1591 buffer.write('<i>null</i>'); | |
| 1592 } else if (value is String) { | |
| 1593 buffer.write('<pre>${HTML_ESCAPE.convert(value)}</pre>'); | |
| 1594 } else if (value is List) { | |
| 1595 buffer.write('List containing ${value.length} entries'); | |
| 1596 buffer.write('<ul>'); | |
| 1597 for (var entry in value) { | |
| 1598 buffer.write('<li>'); | |
| 1599 _writeValueAsHtml(buffer, entry, linkParameters); | |
| 1600 buffer.write('</li>'); | |
| 1601 } | |
| 1602 buffer.write('</ul>'); | |
| 1603 } else if (value is AstNode) { | |
| 1604 String link = | |
| 1605 _makeLink(AST_PATH, linkParameters, value.runtimeType.toString()); | |
| 1606 buffer.write('<i>$link</i>'); | |
| 1607 } else if (value is Element) { | |
| 1608 String link = | |
| 1609 _makeLink(ELEMENT_PATH, linkParameters, value.runtimeType.toString()); | |
| 1610 buffer.write('<i>$link</i>'); | |
| 1611 } else { | |
| 1612 buffer.write(HTML_ESCAPE.convert(value.toString())); | |
| 1613 buffer.write(' <i>(${value.runtimeType.toString()})</i>'); | |
| 1614 } | |
| 1615 } | |
| 697 } | 1616 } |
| OLD | NEW |