OLD | NEW |
---|---|
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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 import 'dart:async'; | 5 import 'dart:async'; |
6 | 6 |
7 import 'package:kernel/target/targets.dart'; | |
8 import 'package:kernel/target/vm_fasta.dart'; | |
7 import 'package:front_end/compiler_options.dart'; | 9 import 'package:front_end/compiler_options.dart'; |
8 import 'package:front_end/file_system.dart'; | 10 import 'package:front_end/file_system.dart'; |
9 import 'package:front_end/src/fasta/translate_uri.dart'; | 11 import 'package:front_end/src/fasta/translate_uri.dart'; |
12 import 'package:front_end/src/fasta/ticker.dart'; | |
13 import 'package:front_end/src/fasta/errors.dart'; | |
10 import 'package:front_end/src/base/performace_logger.dart'; | 14 import 'package:front_end/src/base/performace_logger.dart'; |
11 import 'package:front_end/src/incremental/byte_store.dart'; | 15 import 'package:front_end/src/incremental/byte_store.dart'; |
12 import 'package:front_end/src/simple_error.dart'; | 16 import 'package:front_end/src/simple_error.dart'; |
13 import 'package:package_config/packages_file.dart' as package_config; | 17 import 'package:package_config/packages_file.dart' as package_config; |
14 import 'package:kernel/kernel.dart' show Program, loadProgramFromBytes; | 18 import 'package:kernel/kernel.dart' |
19 show Program, loadProgramFromBytes, CanonicalName; | |
15 | 20 |
16 /// Wrapper around [CompilerOptions] which exposes the options in a form useful | 21 /// All options needed for the front end implementation. |
17 /// to the front end implementation. | 22 /// |
23 /// This includes: all of [CompilerOptions] in a form useful to the | |
24 /// implementation, default values for options that were not provided, | |
25 /// and information derived from how the compiler was invoked (like the | |
26 /// entry-points given to the compiler and whether a modular or whole-program | |
27 /// API was used). | |
18 /// | 28 /// |
19 /// The intent is that the front end should immediately wrap any incoming | 29 /// The intent is that the front end should immediately wrap any incoming |
20 /// [CompilerOptions] object in this class before doing further processing, and | 30 /// [CompilerOptions] object in this class before doing further processing, and |
21 /// should thereafter access all options via the wrapper. This ensures that | 31 /// should thereafter access all options via the wrapper. This ensures that |
22 /// options are interpreted in a consistent way and that data derived from | 32 /// options are interpreted in a consistent way and that data derived from |
23 /// options is not unnecessarily recomputed. | 33 /// options is not unnecessarily recomputed. |
24 class ProcessedOptions { | 34 class ProcessedOptions { |
25 /// The raw [CompilerOptions] which this class wraps. | 35 /// The raw [CompilerOptions] which this class wraps. |
26 final CompilerOptions _raw; | 36 final CompilerOptions _raw; |
27 | 37 |
28 /// The package map derived from the options, or `null` if the package map has | 38 /// The package map derived from the options, or `null` if the package map has |
29 /// not been computed yet. | 39 /// not been computed yet. |
30 Map<String, Uri> _packages; | 40 Map<String, Uri> _packages; |
31 | 41 |
32 /// The object that knows how to resolve "package:" and "dart:" URIs, | 42 /// The object that knows how to resolve "package:" and "dart:" URIs, |
33 /// or `null` if it has not been computed yet. | 43 /// or `null` if it has not been computed yet. |
34 TranslateUri _uriTranslator; | 44 TranslateUri _uriTranslator; |
35 | 45 |
36 /// The SDK summary, or `null` if it has not been read yet. | 46 /// The SDK summary, or `null` if it has not been read yet. |
47 /// | |
48 /// A summary, also referred to as "outline" internally, is a [Program] where | |
49 /// all method bodies are left out. In essence, it contains just API | |
50 /// signatures and constants. When strong-mode is enabled, the summary already | |
51 /// includes inferred types. | |
37 Program _sdkSummaryProgram; | 52 Program _sdkSummaryProgram; |
38 | 53 |
39 /// The summary for each uri in `options.inputSummaries`. | 54 /// The summary for each uri in `options.inputSummaries`. |
55 /// | |
56 /// A summary, also referred to as "outline" internally, is a [Program] where | |
57 /// all method bodies are left out. In essence, it contains just API | |
58 /// signatures and constants. When strong-mode is enabled, the summary already | |
59 /// includes inferred types. | |
40 List<Program> _inputSummariesPrograms; | 60 List<Program> _inputSummariesPrograms; |
41 | 61 |
62 /// Other programs that are meant to be linked and compiled with the input | |
63 /// sources. | |
64 List<Program> _linkedDependencies; | |
65 | |
42 /// The location of the SDK, or `null` if the location hasn't been determined | 66 /// The location of the SDK, or `null` if the location hasn't been determined |
43 /// yet. | 67 /// yet. |
44 Uri _sdkRoot; | 68 Uri _sdkRoot; |
45 | |
46 Uri get sdkRoot => _sdkRoot ??= _normalizeSdkRoot(); | 69 Uri get sdkRoot => _sdkRoot ??= _normalizeSdkRoot(); |
47 | 70 |
71 Uri _sdkSummary; | |
72 Uri get sdkSummary => _sdkSummary ??= _computeSdkSummaryUri(); | |
73 | |
74 Ticker _ticker; | |
75 Ticker get ticker => _ticker ??= new Ticker(isVerbose: _raw.verbose); | |
76 | |
77 bool get verbose => _raw.verbose; | |
78 | |
79 bool get verify => _raw.verify; | |
80 | |
81 bool get debugDump => _raw.debugDump; | |
82 | |
83 /// Like [CompilerOptions.chaseDependencies] but with the appropriate default | |
84 /// value filled in. | |
85 bool get chaseDependencies => _raw.chaseDependencies ?? !modularApi; | |
86 | |
87 /// Whether the compiler was invoked with a modular API. | |
88 /// | |
89 /// Used to determine the default behavior for [chaseDependencies]. | |
90 final bool modularApi; | |
Paul Berry
2017/07/04 15:12:14
Nit: consider making this private. Any reference
Siggi Cherem (dart-lang)
2017/07/05 18:42:12
Done.
| |
91 | |
92 /// The entry-points provided to the compiler. | |
93 final List<Uri> inputs; | |
94 | |
48 /// Initializes a [ProcessedOptions] object wrapping the given [rawOptions]. | 95 /// Initializes a [ProcessedOptions] object wrapping the given [rawOptions]. |
49 ProcessedOptions(CompilerOptions rawOptions) : this._raw = rawOptions; | 96 ProcessedOptions(CompilerOptions rawOptions, |
97 [this.modularApi = false, this.inputs = const []]) | |
98 : this._raw = rawOptions; | |
50 | 99 |
51 /// The logger to report compilation progress. | 100 /// The logger to report compilation progress. |
52 PerformanceLog get logger { | 101 PerformanceLog get logger { |
53 return _raw.logger; | 102 return _raw.logger; |
54 } | 103 } |
55 | 104 |
56 /// The byte storage to get and put serialized data. | 105 /// The byte storage to get and put serialized data. |
57 ByteStore get byteStore { | 106 ByteStore get byteStore { |
58 return _raw.byteStore; | 107 return _raw.byteStore; |
59 } | 108 } |
60 | 109 |
110 void reportError(String message) { | |
111 _raw.onError(new SimpleError(message)); | |
112 } | |
113 | |
61 /// Runs various validations checks on the input options. For instance, | 114 /// Runs various validations checks on the input options. For instance, |
62 /// if an option is a path to a file, it checks that the file exists. | 115 /// if an option is a path to a file, it checks that the file exists. |
63 Future<bool> validateOptions() async { | 116 Future<bool> validateOptions() async { |
64 var fs = _raw.fileSystem; | 117 for (var source in inputs) { |
65 var root = _raw.sdkRoot; | 118 if (source.scheme == 'file' && |
119 !await fileSystem.entityForUri(source).exists()) { | |
120 reportError("Entry-point file not found: $source"); | |
121 return false; | |
122 } | |
123 } | |
66 | 124 |
67 bool _report(String msg) { | 125 if (_raw.sdkRoot != null && |
68 _raw.onError(new SimpleError(msg)); | 126 !await fileSystem.entityForUri(sdkRoot).exists()) { |
127 reportError("SDK root directory not found: ${sdkRoot}"); | |
69 return false; | 128 return false; |
70 } | 129 } |
71 | 130 |
72 if (root != null && !await fs.entityForUri(root).exists()) { | 131 var summary = sdkSummary; |
73 return _report("SDK root directory not found: ${_raw.sdkRoot}"); | 132 if (summary != null && !await fileSystem.entityForUri(summary).exists()) { |
133 reportError("SDK summary not found: ${summary}"); | |
134 return false; | |
74 } | 135 } |
75 | 136 |
76 var summary = _raw.sdkSummary; | 137 if (compileSdk && summary != null) { |
77 if (summary != null && !await fs.entityForUri(summary).exists()) { | 138 reportError( |
78 return _report("SDK summary not found: ${_raw.sdkSummary}"); | 139 "The compileSdk and sdkSummary options are mutually exclusive"); |
140 return false; | |
79 } | 141 } |
80 | |
81 // TODO(sigmund): add checks for options that are meant to be disjoint (like | |
82 // sdkRoot and sdkSummary). | |
83 return true; | 142 return true; |
84 } | 143 } |
85 | 144 |
86 /// Determine whether to generate code for the SDK when compiling a | 145 /// Determine whether to generate code for the SDK when compiling a |
87 /// whole-program. | 146 /// whole-program. |
88 bool get compileSdk => _raw.compileSdk; | 147 bool get compileSdk => _raw.compileSdk; |
89 | 148 |
149 FileSystem _fileSystem; | |
150 | |
90 /// Get the [FileSystem] which should be used by the front end to access | 151 /// Get the [FileSystem] which should be used by the front end to access |
91 /// files. | 152 /// files. |
92 /// | 153 /// |
93 /// If the client supplied roots using [CompilerOptions.multiRoots], the | 154 /// If the client supplied roots using [CompilerOptions.multiRoots], the |
94 /// returned [FileSystem] will automatically perform the appropriate mapping. | 155 /// returned [FileSystem] will automatically perform the appropriate mapping. |
95 FileSystem get fileSystem { | 156 FileSystem get fileSystem => _fileSystem ??= _createFileSystem(); |
96 // TODO(paulberry): support multiRoots. | |
97 assert(_raw.multiRoots.isEmpty); | |
98 return _raw.fileSystem; | |
99 } | |
100 | 157 |
101 /// Whether to interpret Dart sources in strong-mode. | 158 /// Whether to interpret Dart sources in strong-mode. |
102 bool get strongMode => _raw.strongMode; | 159 bool get strongMode => _raw.strongMode; |
103 | 160 |
104 /// Get an outline program that summarizes the SDK. | 161 Target _target; |
105 Future<Program> get sdkSummaryProgram async { | 162 Target get target => _target ??= |
163 _raw.target ?? new VmFastaTarget(new TargetFlags(strongMode: strongMode)); | |
164 | |
165 /// Get an outline program that summarizes the SDK, if any. | |
166 Future<Program> loadSdkSummary(CanonicalName nameRoot) async { | |
106 if (_sdkSummaryProgram == null) { | 167 if (_sdkSummaryProgram == null) { |
107 if (_raw.sdkSummary == null) return null; | 168 if (sdkSummary == null) return null; |
108 _sdkSummaryProgram = await _loadProgram(_raw.sdkSummary); | 169 var bytes = await fileSystem.entityForUri(sdkSummary).readAsBytes(); |
170 _sdkSummaryProgram = loadProgram(bytes, nameRoot); | |
109 } | 171 } |
110 return _sdkSummaryProgram; | 172 return _sdkSummaryProgram; |
111 } | 173 } |
112 | 174 |
113 /// Get the summary programs for each of the underlying `inputSummaries` | 175 /// Get the summary programs for each of the underlying `inputSummaries` |
114 /// provided via [CompilerOptions]. | 176 /// provided via [CompilerOptions]. |
115 Future<List<Program>> get inputSummariesPrograms async { | 177 Future<List<Program>> loadInputSummaries(CanonicalName nameRoot) async { |
116 if (_inputSummariesPrograms == null) { | 178 if (_inputSummariesPrograms == null) { |
117 var uris = _raw.inputSummaries; | 179 var uris = _raw.inputSummaries; |
118 if (uris == null || uris.isEmpty) return const <Program>[]; | 180 if (uris == null || uris.isEmpty) return const <Program>[]; |
119 _inputSummariesPrograms = await Future.wait(uris.map(_loadProgram)); | 181 var allBytes = await Future |
182 .wait(uris.map((uri) => fileSystem.entityForUri(uri).readAsBytes())); | |
183 _inputSummariesPrograms = | |
184 allBytes.map((bytes) => loadProgram(bytes, nameRoot)).toList(); | |
120 } | 185 } |
121 return _inputSummariesPrograms; | 186 return _inputSummariesPrograms; |
122 } | 187 } |
123 | 188 |
124 Future<Program> _loadProgram(Uri uri) async { | 189 /// Load each of the [CompilerOptions.linkedDependencies] programs. |
125 var bytes = await fileSystem.entityForUri(uri).readAsBytes(); | 190 Future<List<Program>> loadLinkDependencies(CanonicalName nameRoot) async { |
126 return loadProgramFromBytes(bytes)..unbindCanonicalNames(); | 191 if (_linkedDependencies == null) { |
192 var uris = _raw.linkedDependencies; | |
193 if (uris == null || uris.isEmpty) return const <Program>[]; | |
194 var allBytes = await Future | |
195 .wait(uris.map((uri) => fileSystem.entityForUri(uri).readAsBytes())); | |
196 _linkedDependencies = | |
197 allBytes.map((bytes) => loadProgram(bytes, nameRoot)).toList(); | |
198 } | |
199 return _linkedDependencies; | |
200 } | |
201 | |
202 /// Helper to load a .dill file from [uri] using the existing [nameRoot]. | |
203 Program loadProgram(List<int> bytes, CanonicalName nameRoot) { | |
204 return loadProgramFromBytes(bytes, new Program(nameRoot: nameRoot)); | |
127 } | 205 } |
128 | 206 |
129 /// Get the [TranslateUri] which resolves "package:" and "dart:" URIs. | 207 /// Get the [TranslateUri] which resolves "package:" and "dart:" URIs. |
130 /// | 208 /// |
131 /// This is an asynchronous method since file system operations may be | 209 /// This is an asynchronous method since file system operations may be |
132 /// required to locate/read the packages file as well as SDK metadata. | 210 /// required to locate/read the packages file as well as SDK metadata. |
133 Future<TranslateUri> getUriTranslator() async { | 211 Future<TranslateUri> getUriTranslator() async { |
134 if (_uriTranslator == null) { | 212 if (_uriTranslator == null) { |
135 await _getPackages(); | 213 await _getPackages(); |
136 // TODO(scheglov) Load SDK libraries from whatever format we decide. | 214 // TODO(scheglov) Load SDK libraries from whatever format we decide. |
137 // TODO(scheglov) Remove the field "_raw.dartLibraries". | 215 // TODO(scheglov) Remove the field "_raw.dartLibraries". |
138 _uriTranslator = new TranslateUri( | 216 var libraries = _raw.dartLibraries ?? await _parseLibraries(); |
139 _packages, _raw.dartLibraries, const <String, List<Uri>>{}); | 217 _uriTranslator = |
140 _uriTranslator.dartLibraries.addAll(_raw.dartLibraries); | 218 new TranslateUri(_packages, libraries, const <String, List<Uri>>{}); |
219 ticker.logMs("Read packages file"); | |
141 } | 220 } |
142 return _uriTranslator; | 221 return _uriTranslator; |
143 } | 222 } |
144 | 223 |
224 Future<Map<String, Uri>> _parseLibraries() async { | |
225 Uri librariesJson = _raw.sdkRoot?.resolve("lib/libraries.json"); | |
226 return await computeLibraries(fileSystem, librariesJson); | |
227 } | |
228 | |
145 /// Get the package map which maps package names to URIs. | 229 /// Get the package map which maps package names to URIs. |
146 /// | 230 /// |
147 /// This is an asynchronous getter since file system operations may be | 231 /// This is an asynchronous getter since file system operations may be |
148 /// required to locate/read the packages file. | 232 /// required to locate/read the packages file. |
149 Future<Map<String, Uri>> _getPackages() async { | 233 Future<Map<String, Uri>> _getPackages() async { |
150 if (_packages == null) { | 234 if (_packages == null) { |
151 if (_raw.packagesFileUri == null) { | 235 if (_raw.packagesFileUri == null) { |
152 throw new UnimplementedError(); // TODO(paulberry): search for .packages | 236 throw new UnimplementedError(); // TODO(paulberry): search for .packages |
153 } else if (_raw.packagesFileUri.path.isEmpty) { | 237 } else if (_raw.packagesFileUri.path.isEmpty) { |
154 _packages = {}; | 238 _packages = {}; |
(...skipping 14 matching lines...) Expand all Loading... | |
169 // If an SDK summary location was provided, the SDK itself should not be | 253 // If an SDK summary location was provided, the SDK itself should not be |
170 // needed. | 254 // needed. |
171 assert(_raw.sdkSummary == null); | 255 assert(_raw.sdkSummary == null); |
172 if (_raw.sdkRoot == null) { | 256 if (_raw.sdkRoot == null) { |
173 // TODO(paulberry): implement the algorithm for finding the SDK | 257 // TODO(paulberry): implement the algorithm for finding the SDK |
174 // automagically. | 258 // automagically. |
175 throw new UnimplementedError(); | 259 throw new UnimplementedError(); |
176 } | 260 } |
177 var root = _raw.sdkRoot; | 261 var root = _raw.sdkRoot; |
178 if (!root.path.endsWith('/')) { | 262 if (!root.path.endsWith('/')) { |
179 root = root.replace(path: _sdkRoot.path + '/'); | 263 root = root.replace(path: root.path + '/'); |
180 } | 264 } |
181 return root; | 265 return root; |
182 } | 266 } |
267 | |
268 /// Get or infer the location of the SDK summary. | |
269 Uri _computeSdkSummaryUri() { | |
270 if (_raw.sdkSummary != null) return _raw.sdkSummary; | |
271 | |
272 // Infer based on the sdkRoot, but only when `compileSdk` is false, | |
273 // otherwise the default intent was to compile the sdk from sources and not | |
274 // to load an sdk summary file. | |
275 if (_raw.compileSdk) return null; | |
276 return sdkRoot.resolve('outline.dill'); | |
277 } | |
278 | |
279 /// Create a [FileSystem] specific to the current options. | |
280 /// | |
281 /// If [chaseDependencies] is false, the resulting file system will be | |
282 /// hermetic. | |
283 FileSystem _createFileSystem() { | |
284 var result = _raw.fileSystem; | |
285 if (!chaseDependencies) { | |
286 var allInputs = inputs.toSet(); | |
287 allInputs.addAll(_raw.inputSummaries); | |
288 allInputs.addAll(_raw.linkedDependencies); | |
289 | |
290 if (sdkSummary != null) allInputs.add(sdkSummary); | |
291 | |
292 if (_raw.sdkRoot != null) { | |
293 // TODO(sigmund): refine this, we should be more explicit about when | |
294 // sdkRoot and libraries.json are allowed to be used. | |
295 allInputs.add(sdkRoot); | |
296 allInputs.add(sdkRoot.resolve("lib/libraries.json")); | |
297 } | |
298 | |
299 /// Note: Searching the file-system for the package-config is not | |
300 /// supported in hermetic builds. | |
301 if (_raw.packagesFileUri != null) allInputs.add(_raw.packagesFileUri); | |
302 result = new HermeticFileSystem(allInputs, result); | |
303 } | |
304 // TODO(paulberry): support multiRoots. | |
305 assert(_raw.multiRoots.isEmpty); | |
306 return result; | |
307 } | |
183 } | 308 } |
309 | |
310 /// A [FileSystem] that only allows access to files that have been explicitly | |
311 /// whitelisted. | |
312 class HermeticFileSystem implements FileSystem { | |
313 final Set<Uri> includedFiles; | |
314 final FileSystem _realFileSystem; | |
315 | |
316 HermeticFileSystem(this.includedFiles, this._realFileSystem); | |
317 | |
318 FileSystemEntity entityForUri(Uri uri) { | |
319 if (includedFiles.contains(uri)) return _realFileSystem.entityForUri(uri); | |
320 return inputError( | |
321 null, | |
322 -1, | |
323 'Invalid access to $uri: ' | |
324 'the file is accessed in a modular hermetic build ' | |
325 '(where chaseDependencies is false), but it was not ' | |
Paul Berry
2017/07/04 15:12:14
Is this going to be a user-visible error message?
Siggi Cherem (dart-lang)
2017/07/05 18:42:11
Good point. removed.
| |
326 'explicitly listed as an input.'); | |
327 } | |
328 } | |
OLD | NEW |