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

Side by Side Diff: pkg/analyzer_experimental/lib/src/services/runtime/coverage_server_impl.dart

Issue 16580018: Code coverage server babysteps. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /// A library for code coverage support for Dart.
pquitslund 2013/06/11 17:18:56 Again. Probably want a header.
scheglov 2013/06/11 19:54:23 Done.
2 library runtime.coverage.server_impl;
3
4 import "dart:io";
5
6 import "package:logging/logging.dart" as log;
7 import "package:pathos/path.dart" as po;
8
9 import 'package:analyzer_experimental/src/generated/source.dart' show Source;
10 import 'package:analyzer_experimental/src/generated/scanner.dart' show StringSca nner;
11 import 'package:analyzer_experimental/src/generated/parser.dart' show Parser;
12 import 'package:analyzer_experimental/src/generated/ast.dart';
13 import 'package:analyzer_experimental/src/generated/engine.dart' show RecordingE rrorListener;
14
15
16 log.Logger logger = log.Logger.root;
17
18 /// Abstract server that listens requests and serves files, may be rewriting the m.
19 abstract class RewriteServer {
20 String _basePath;
21 RewriteServer(this._basePath);
22 void start() {
23 HttpServer.bind("127.0.0.1", 3445).then((HttpServer server) {
24 logger.info('RewriteServer is listening at: ${server.port}.');
25 server.listen((HttpRequest request) {
26 var response = request.response;
27 // prepare path
pquitslund 2013/06/11 17:18:56 prepare -> Prepare (upper case)
scheglov 2013/06/11 19:54:23 Done.
28 String path = _basePath + '/' + request.uri.path;
pquitslund 2013/06/11 17:18:56 var path
scheglov 2013/06/11 19:54:23 Done.
29 path = po.normalize(path);
30 logger.info('[$path] Requested.');
31 // may be we have path content
pquitslund 2013/06/11 17:18:56 Upper case 'may'
scheglov 2013/06/11 19:54:23 Done.
32 {
33 String content = rewritePathContent(path);
34 if (content != null) {
35 logger.info('[$path] Request served by path.');
36 response.write(content);
37 response.close();
38 return;
39 }
40 }
41 // get content from file
pquitslund 2013/06/11 17:18:56 More upper case (and below). Pretty sure the styl
scheglov 2013/06/11 19:54:23 Done.
42 logger.info('[$path] Serving file.');
43 File file = new File(path);
pquitslund 2013/06/11 17:18:56 var file
scheglov 2013/06/11 19:54:23 Done.
44 file.exists().then((bool found) {
45 if (found) {
46 logger.finest('[$path] Found file.');
47 file.readAsString().then((String content) {
48 logger.finest('[$path] Got file content.');
49 var sw = new Stopwatch();
50 sw.start();
51 try {
52 content = rewriteFileContent(path, content);
53 } finally {
54 sw.stop();
55 logger.fine('[$path] Rewritten in ${sw.elapsedMilliseconds} ms.' );
56 }
57 response.write(content);
58 response.close();
59 });
60 } else {
61 logger.severe('[$path] File not found.');
62 response.statusCode = HttpStatus.NOT_FOUND;
63 response.close();
64 }
65 });
66 });
67 });
68 }
69
70 /// Subclasses implement this method to rewrite the provided [code] of the fil e with [path].
71 /// Returns some content or `null` if file content should be requested.
72 String rewritePathContent(String path);
73
74 /// Subclasses implement this method to rewrite the provided [code] of the fil e with [path].
75 String rewriteFileContent(String path, String code);
76 }
77
78 /// Server that rewrites Dart code so that it reports execution of statements an d other nodes.
79 class CoverageServer extends RewriteServer {
80 CoverageServer(String basePath) : super(basePath);
81
82 String rewritePathContent(String path) {
83 if (path.endsWith('__coverage_impl.dart')) {
84 String implPath = po.joinAll([
85 po.dirname(new Options().script),
86 '..', 'lib', 'src', 'services', 'runtime', 'coverage_lib.dart']);
87 return new File(implPath).readAsStringSync();
88 }
89 return null;
90 }
91
92 String rewriteFileContent(String path, String code) {
93 if (po.extension(path).toLowerCase() != '.dart') return code;
94 if (path.contains('packages')) return code;
95 CompilationUnit unit = _parseCode(code);
pquitslund 2013/06/11 17:18:56 Again, drop the type annotation (var unit).
scheglov 2013/06/11 19:54:23 Done.
96 print(unit);
97 var injector = new StringInjector(code);
98 // inject coverage_lib.dart import
99 var directives = unit.directives;
100 if (directives.isNotEmpty && directives[0] is LibraryDirective) {
101 injector.inject(directives[0].end, 'import "__coverage_impl.dart" as __cc; ');
102 } else {
103 throw new Exception('Only single library coverage is implemented.');
104 }
105 // insert touch() invocations
106 unit.accept(new InsertTouchInvocationsVisitor(injector));
107 // done
108 code = injector.code;
109 logger.finest('[$path] Rewritten content\n$code');
110 return code;
111 }
112
113 CompilationUnit _parseCode(String code) {
114 Source source = null;
pquitslund 2013/06/11 17:18:56 And here.
scheglov 2013/06/11 19:54:23 Done.
115 var errorListener = new RecordingErrorListener();
116 var parser = new Parser(source, errorListener);
117 var scanner = new StringScanner(source, code, errorListener);
118 var token = scanner.tokenize();
119 return parser.parseCompilationUnit(token);
120 }
121 }
122
123 /// The visitor that inserts `touch` method invocations.
124 class InsertTouchInvocationsVisitor extends GeneralizingASTVisitor {
125 StringInjector injector;
126 InsertTouchInvocationsVisitor(this.injector);
127 visitStatement(Statement node) {
128 super.visitStatement(node);
129 int offset = node.end;
pquitslund 2013/06/11 17:18:56 And here.
scheglov 2013/06/11 19:54:23 Done.
130 if (node is Block) {
131 offset--;
132 }
133 if (node is Block && node.parent is BlockFunctionBody) return null;
134 injector.inject(offset, '__cc.touch(${node.offset});');
135 return null;
136 }
137 }
138
139 /// Helper for injecting fragments into some existing [String].
140 class StringInjector {
141 String code;
142 int _lastOffset = -1;
143 int _delta = 0;
144 StringInjector(this.code);
145 void inject(int offset, String fragment) {
146 if (offset < _lastOffset) {
147 throw new ArgumentError('Only forward inserts are supported, was $_lastOff set given $offset');
148 }
149 _lastOffset = offset;
150 offset += _delta;
151 code = code.substring(0, offset) + fragment + code.substring(offset);
152 _delta += fragment.length;
153 }
154 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698