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

Side by Side Diff: pkg/dart_scanner/lib/io.dart

Issue 2631503002: Modify scanner and parser to be standalone packages. (Closed)
Patch Set: Created 3 years, 11 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
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, 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.md file.
4 4
5 library source_file_provider; 5 import 'dart:async' show
6 Future;
6 7
7 import 'dart:async'; 8 import 'dart:io' show
8 import 'dart:convert'; 9 File,
9 import 'dart:io'; 10 RandomAccessFile;
10 import 'dart:math' as math;
11 import 'dart:typed_data';
12 11
13 import '../compiler.dart' as api show Diagnostic; 12 import 'dart:typed_data' show
14 import '../compiler_new.dart' as api; 13 Uint8List;
15 import '../compiler_new.dart';
16 import 'colors.dart' as colors;
17 import 'dart2js.dart' show AbortLeg;
18 import 'filenames.dart';
19 import 'io/source_file.dart';
20 import 'util/uri_extras.dart';
21 14
22 abstract class SourceFileProvider implements CompilerInput { 15 List<int> readBytesFromFileSync(Uri uri) {
23 bool isWindows = (Platform.operatingSystem == 'windows'); 16 RandomAccessFile file = new File.fromUri(uri).openSync();
24 Uri cwd = currentDirectory; 17 Uint8List list;
25 Map<Uri, SourceFile> sourceFiles = <Uri, SourceFile>{}; 18 try {
26 int dartCharactersRead = 0; 19 int length = file.lengthSync();
27 20 // +1 to have a 0 terminated list, see [Scanner].
28 Future<String> readStringFromUri(Uri resourceUri) { 21 list = new Uint8List(length + 1);
29 return readUtf8BytesFromUri(resourceUri).then(UTF8.decode); 22 file.readIntoSync(list, 0, length);
23 } finally {
24 file.closeSync();
30 } 25 }
31 26 return list;
32 Future<List<int>> readUtf8BytesFromUri(Uri resourceUri) {
33 if (resourceUri.scheme == 'file') {
34 return _readFromFile(resourceUri);
35 } else if (resourceUri.scheme == 'http' || resourceUri.scheme == 'https') {
36 return _readFromHttp(resourceUri);
37 } else {
38 throw new ArgumentError("Unknown scheme in uri '$resourceUri'");
39 }
40 }
41
42 Future<List<int>> _readFromFile(Uri resourceUri) {
43 assert(resourceUri.scheme == 'file');
44 List<int> source;
45 try {
46 source = readAll(resourceUri.toFilePath());
47 } on FileSystemException catch (ex) {
48 String message = ex.osError?.message;
49 String detail = message != null ? ' ($message)' : '';
50 return new Future.error(
51 "Error reading '${relativizeUri(resourceUri)}' $detail");
52 }
53 dartCharactersRead += source.length;
54 sourceFiles[resourceUri] = new CachingUtf8BytesSourceFile(
55 resourceUri, relativizeUri(resourceUri), source);
56 return new Future.value(source);
57 }
58
59 Future<List<int>> _readFromHttp(Uri resourceUri) {
60 assert(resourceUri.scheme == 'http');
61 HttpClient client = new HttpClient();
62 return client
63 .getUrl(resourceUri)
64 .then((HttpClientRequest request) => request.close())
65 .then((HttpClientResponse response) {
66 if (response.statusCode != HttpStatus.OK) {
67 String msg = 'Failure getting $resourceUri: '
68 '${response.statusCode} ${response.reasonPhrase}';
69 throw msg;
70 }
71 return response.toList();
72 }).then((List<List<int>> splitContent) {
73 int totalLength = splitContent.fold(0, (int old, List list) {
74 return old + list.length;
75 });
76 Uint8List result = new Uint8List(totalLength);
77 int offset = 0;
78 for (List<int> contentPart in splitContent) {
79 result.setRange(offset, offset + contentPart.length, contentPart);
80 offset += contentPart.length;
81 }
82 dartCharactersRead += totalLength;
83 sourceFiles[resourceUri] = new CachingUtf8BytesSourceFile(
84 resourceUri, resourceUri.toString(), result);
85 return result;
86 });
87 }
88
89 // TODO(johnniwinther): Remove this when no longer needed for the old compiler
90 // API.
91 Future/*<List<int> | String>*/ call(Uri resourceUri) => throw "unimplemented";
92
93 relativizeUri(Uri uri) => relativize(cwd, uri, isWindows);
94
95 SourceFile getSourceFile(Uri resourceUri) {
96 return sourceFiles[resourceUri];
97 }
98 } 27 }
99 28
100 List<int> readAll(String filename) { 29 Future<List<int>> readBytesFromFile(Uri uri) async {
101 var file = (new File(filename)).openSync(); 30 RandomAccessFile file = await new File.fromUri(uri).open();
102 var length = file.lengthSync(); 31 Uint8List list;
103 // +1 to have a 0 terminated list, see [Scanner]. 32 try {
104 var buffer = new Uint8List(length + 1); 33 int length = await file.length();
105 file.readIntoSync(buffer, 0, length); 34 // +1 to have a 0 terminated list, see [Scanner].
106 file.closeSync(); 35 list = new Uint8List(length + 1);
107 return buffer; 36 int read = await file.readInto(list);
37 if (read != length) {
38 throw "Error reading file: ${uri}";
39 }
40 } finally {
41 await file.close();
42 }
43 return list;
108 } 44 }
109
110 class CompilerSourceFileProvider extends SourceFileProvider {
111 // TODO(johnniwinther): Remove this when no longer needed for the old compiler
112 // API.
113 Future<List<int>> call(Uri resourceUri) => readFromUri(resourceUri);
114
115 @override
116 Future readFromUri(Uri uri) => readUtf8BytesFromUri(uri);
117 }
118
119 class FormattingDiagnosticHandler implements CompilerDiagnostics {
120 final SourceFileProvider provider;
121 bool showWarnings = true;
122 bool showHints = true;
123 bool verbose = false;
124 bool isAborting = false;
125 bool enableColors = false;
126 bool throwOnError = false;
127 int throwOnErrorCount = 0;
128 api.Diagnostic lastKind = null;
129 int fatalCount = 0;
130
131 final int FATAL = api.Diagnostic.CRASH.ordinal | api.Diagnostic.ERROR.ordinal;
132 final int INFO =
133 api.Diagnostic.INFO.ordinal | api.Diagnostic.VERBOSE_INFO.ordinal;
134
135 FormattingDiagnosticHandler([SourceFileProvider provider])
136 : this.provider =
137 (provider == null) ? new CompilerSourceFileProvider() : provider;
138
139 void info(var message, [api.Diagnostic kind = api.Diagnostic.VERBOSE_INFO]) {
140 if (!verbose && kind == api.Diagnostic.VERBOSE_INFO) return;
141 if (enableColors) {
142 print('${colors.green("Info:")} $message');
143 } else {
144 print('Info: $message');
145 }
146 }
147
148 /// Adds [kind] specific prefix to [message].
149 String prefixMessage(String message, api.Diagnostic kind) {
150 switch (kind) {
151 case api.Diagnostic.ERROR:
152 return 'Error: $message';
153 case api.Diagnostic.WARNING:
154 return 'Warning: $message';
155 case api.Diagnostic.HINT:
156 return 'Hint: $message';
157 case api.Diagnostic.CRASH:
158 return 'Internal Error: $message';
159 case api.Diagnostic.INFO:
160 case api.Diagnostic.VERBOSE_INFO:
161 return 'Info: $message';
162 }
163 throw 'Unexpected diagnostic kind: $kind (${kind.ordinal})';
164 }
165
166 @override
167 void report(var code, Uri uri, int begin, int end, String message,
168 api.Diagnostic kind) {
169 if (isAborting) return;
170 isAborting = (kind == api.Diagnostic.CRASH);
171
172 bool fatal = (kind.ordinal & FATAL) != 0;
173 bool isInfo = (kind.ordinal & INFO) != 0;
174 if (isInfo && uri == null && kind != api.Diagnostic.INFO) {
175 info(message, kind);
176 return;
177 }
178
179 message = prefixMessage(message, kind);
180
181 // [lastKind] records the previous non-INFO kind we saw.
182 // This is used to suppress info about a warning when warnings are
183 // suppressed, and similar for hints.
184 if (kind != api.Diagnostic.INFO) {
185 lastKind = kind;
186 }
187 var color;
188 if (kind == api.Diagnostic.ERROR) {
189 color = colors.red;
190 } else if (kind == api.Diagnostic.WARNING) {
191 if (!showWarnings) return;
192 color = colors.magenta;
193 } else if (kind == api.Diagnostic.HINT) {
194 if (!showHints) return;
195 color = colors.cyan;
196 } else if (kind == api.Diagnostic.CRASH) {
197 color = colors.red;
198 } else if (kind == api.Diagnostic.INFO) {
199 if (lastKind == api.Diagnostic.WARNING && !showWarnings) return;
200 if (lastKind == api.Diagnostic.HINT && !showHints) return;
201 color = colors.green;
202 } else {
203 throw 'Unknown kind: $kind (${kind.ordinal})';
204 }
205 if (!enableColors) {
206 color = (x) => x;
207 }
208 if (uri == null) {
209 print('${color(message)}');
210 } else {
211 SourceFile file = provider.sourceFiles[uri];
212 if (file != null) {
213 print(file.getLocationMessage(color(message), begin, end,
214 colorize: color));
215 } else {
216 String position = end - begin > 0 ? '@$begin+${end - begin}' : '';
217 print('${provider.relativizeUri(uri)}$position:\n'
218 '${color(message)}');
219 }
220 }
221 if (fatal && ++fatalCount >= throwOnErrorCount && throwOnError) {
222 isAborting = true;
223 throw new AbortLeg(message);
224 }
225 }
226
227 // TODO(johnniwinther): Remove this when no longer needed for the old compiler
228 // API.
229 void call(Uri uri, int begin, int end, String message, api.Diagnostic kind) {
230 return report(null, uri, begin, end, message, kind);
231 }
232 }
233
234 typedef void MessageCallback(String message);
235
236 class RandomAccessFileOutputProvider implements CompilerOutput {
237 final Uri out;
238 final Uri sourceMapOut;
239 final Uri resolutionOutput;
240 final MessageCallback onInfo;
241 final MessageCallback onFailure;
242
243 int totalCharactersWritten = 0;
244 List<String> allOutputFiles = new List<String>();
245
246 RandomAccessFileOutputProvider(this.out, this.sourceMapOut,
247 {this.onInfo, this.onFailure, this.resolutionOutput});
248
249 static Uri computePrecompiledUri(Uri out) {
250 String extension = 'precompiled.js';
251 String outPath = out.path;
252 if (outPath.endsWith('.js')) {
253 outPath = outPath.substring(0, outPath.length - 3);
254 return out.resolve('$outPath.$extension');
255 } else {
256 return out.resolve(extension);
257 }
258 }
259
260 EventSink<String> call(String name, String extension) {
261 return createEventSink(name, extension);
262 }
263
264 EventSink<String> createEventSink(String name, String extension) {
265 Uri uri;
266 bool isPrimaryOutput = false;
267 // TODO (johnniwinther, sigurdm): Make a better interface for
268 // output-providers.
269 if (extension == "deferred_map") {
270 uri = out.resolve(name);
271 } else if (name == '') {
272 if (extension == 'js' || extension == 'dart') {
273 isPrimaryOutput = true;
274 uri = out;
275 } else if (extension == 'precompiled.js') {
276 uri = computePrecompiledUri(out);
277 onInfo("File ($uri) is compatible with header"
278 " \"Content-Security-Policy: script-src 'self'\"");
279 } else if (extension == 'js.map' || extension == 'dart.map') {
280 uri = sourceMapOut;
281 } else if (extension == 'info.json') {
282 String outName = out.path.substring(out.path.lastIndexOf('/') + 1);
283 uri = out.resolve('$outName.$extension');
284 } else if (extension == 'data') {
285 if (resolutionOutput == null) {
286 onFailure('Serialization target unspecified.');
287 }
288 uri = resolutionOutput;
289 } else {
290 onFailure('Unknown extension: $extension');
291 }
292 } else {
293 uri = out.resolve('$name.$extension');
294 }
295
296 if (uri.scheme != 'file') {
297 onFailure('Unhandled scheme ${uri.scheme} in $uri.');
298 }
299
300 RandomAccessFile output;
301 try {
302 output = new File(uri.toFilePath()).openSync(mode: FileMode.WRITE);
303 } on FileSystemException catch (e) {
304 onFailure('$e');
305 }
306
307 allOutputFiles.add(relativize(currentDirectory, uri, Platform.isWindows));
308
309 int charactersWritten = 0;
310
311 writeStringSync(String data) {
312 // Write the data in chunks of 8kb, otherwise we risk running OOM.
313 int chunkSize = 8 * 1024;
314
315 int offset = 0;
316 while (offset < data.length) {
317 output.writeStringSync(
318 data.substring(offset, math.min(offset + chunkSize, data.length)));
319 offset += chunkSize;
320 }
321 charactersWritten += data.length;
322 }
323
324 onDone() {
325 output.closeSync();
326 if (isPrimaryOutput) {
327 totalCharactersWritten += charactersWritten;
328 }
329 }
330
331 return new _EventSinkWrapper(writeStringSync, onDone);
332 }
333 }
334
335 class _EventSinkWrapper extends EventSink<String> {
336 var onAdd, onClose;
337
338 _EventSinkWrapper(this.onAdd, this.onClose);
339
340 void add(String data) => onAdd(data);
341
342 void addError(error, [StackTrace stackTrace]) => throw error;
343
344 void close() => onClose();
345 }
346
347 /// Adapter to integrate dart2js in bazel.
348 ///
349 /// To handle bazel's special layout:
350 ///
351 /// * We specify a .packages configuration file that expands packages to their
352 /// corresponding bazel location. This way there is no need to create a pub
353 /// cache prior to invoking dart2js.
354 ///
355 /// * We provide an implicit mapping that can make all urls relative to the
356 /// bazel root.
357 /// To the compiler, URIs look like:
358 /// file:///bazel-root/a/b/c.dart
359 ///
360 /// even though in the file system the file is located at:
361 /// file:///path/to/the/actual/bazel/root/a/b/c.dart
362 ///
363 /// This mapping serves two purposes:
364 /// - It makes compiler results independent of the machine layout, which
365 /// enables us to share results across bazel runs and across machines.
366 ///
367 /// - It hides the distinction between generated and source files. That way
368 /// we can use the standard package-resolution mechanism and ignore the
369 /// internals of how files are organized within bazel.
370 ///
371 /// When invoking the compiler, bazel will use `package:` and
372 /// `file:///bazel-root/` URIs to specify entrypoints.
373 ///
374 /// The mapping is specified using search paths relative to the current
375 /// directory. When this provider looks up a file, the bazel-root folder is
376 /// replaced by the first directory in the search path containing the file, if
377 /// any. For example, given the search path ".,bazel-bin/", and a URL
378 /// of the form `file:///bazel-root/a/b.dart`, this provider will check if the
379 /// file exists under "./a/b.dart", then check under "bazel-bin/a/b.dart". If
380 /// none of the paths matches, it will attempt to load the file from
381 /// `/bazel-root/a/b.dart` which will likely fail.
382 class BazelInputProvider extends SourceFileProvider {
383 final List<Uri> dirs;
384
385 BazelInputProvider(List<String> searchPaths)
386 : dirs = searchPaths.map(_resolve).toList();
387
388 static _resolve(String path) => currentDirectory.resolve(path);
389
390 @override
391 Future readFromUri(Uri uri) async {
392 var resolvedUri = uri;
393 var path = uri.path;
394 if (path.startsWith('/bazel-root')) {
395 path = path.substring('/bazel-root/'.length);
396 for (var dir in dirs) {
397 var file = dir.resolve(path);
398 if (await new File.fromUri(file).exists()) {
399 resolvedUri = file;
400 break;
401 }
402 }
403 }
404 var result = await readUtf8BytesFromUri(resolvedUri);
405 sourceFiles[uri] = sourceFiles[resolvedUri];
406 return result;
407 }
408 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698