| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 library leg_apiimpl; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 import '../compiler.dart' as api; | |
| 10 import 'dart2jslib.dart' as leg; | |
| 11 import 'tree/tree.dart' as tree; | |
| 12 import 'elements/elements.dart' as elements; | |
| 13 import 'package:_internal/libraries.dart'; | |
| 14 import 'source_file.dart'; | |
| 15 | |
| 16 const bool forceIncrementalSupport = | |
| 17 const bool.fromEnvironment('DART2JS_EXPERIMENTAL_INCREMENTAL_SUPPORT'); | |
| 18 | |
| 19 class Compiler extends leg.Compiler { | |
| 20 api.CompilerInputProvider provider; | |
| 21 api.DiagnosticHandler handler; | |
| 22 final Uri libraryRoot; | |
| 23 final Uri packageRoot; | |
| 24 List<String> options; | |
| 25 Map<String, dynamic> environment; | |
| 26 bool mockableLibraryUsed = false; | |
| 27 final Set<String> allowedLibraryCategories; | |
| 28 | |
| 29 leg.GenericTask userHandlerTask; | |
| 30 leg.GenericTask userProviderTask; | |
| 31 | |
| 32 Compiler(this.provider, | |
| 33 api.CompilerOutputProvider outputProvider, | |
| 34 this.handler, | |
| 35 this.libraryRoot, | |
| 36 this.packageRoot, | |
| 37 List<String> options, | |
| 38 this.environment) | |
| 39 : this.options = options, | |
| 40 this.allowedLibraryCategories = getAllowedLibraryCategories(options), | |
| 41 super( | |
| 42 outputProvider: outputProvider, | |
| 43 enableTypeAssertions: hasOption(options, '--enable-checked-mode'), | |
| 44 enableUserAssertions: hasOption(options, '--enable-checked-mode'), | |
| 45 trustTypeAnnotations: | |
| 46 hasOption(options, '--trust-type-annotations'), | |
| 47 enableMinification: hasOption(options, '--minify'), | |
| 48 preserveUris: hasOption(options, '--preserve-uris'), | |
| 49 enableNativeLiveTypeAnalysis: | |
| 50 !hasOption(options, '--disable-native-live-type-analysis'), | |
| 51 emitJavaScript: !(hasOption(options, '--output-type=dart') || | |
| 52 hasOption(options, '--output-type=dart-multi')), | |
| 53 dart2dartMultiFile: hasOption(options, '--output-type=dart-multi'), | |
| 54 generateSourceMap: !hasOption(options, '--no-source-maps'), | |
| 55 analyzeAllFlag: hasOption(options, '--analyze-all'), | |
| 56 analyzeOnly: hasOption(options, '--analyze-only'), | |
| 57 analyzeMain: hasOption(options, '--analyze-main'), | |
| 58 analyzeSignaturesOnly: | |
| 59 hasOption(options, '--analyze-signatures-only'), | |
| 60 strips: extractCsvOption(options, '--force-strip='), | |
| 61 enableConcreteTypeInference: | |
| 62 hasOption(options, '--enable-concrete-type-inference'), | |
| 63 disableTypeInferenceFlag: | |
| 64 hasOption(options, '--disable-type-inference'), | |
| 65 preserveComments: hasOption(options, '--preserve-comments'), | |
| 66 verbose: hasOption(options, '--verbose'), | |
| 67 sourceMapUri: extractUriOption(options, '--source-map='), | |
| 68 outputUri: extractUriOption(options, '--out='), | |
| 69 terseDiagnostics: hasOption(options, '--terse'), | |
| 70 dumpInfo: hasOption(options, '--dump-info'), | |
| 71 buildId: extractStringOption( | |
| 72 options, '--build-id=', | |
| 73 "build number could not be determined"), | |
| 74 showPackageWarnings: | |
| 75 hasOption(options, '--show-package-warnings'), | |
| 76 useContentSecurityPolicy: hasOption(options, '--csp'), | |
| 77 hasIncrementalSupport: | |
| 78 forceIncrementalSupport || | |
| 79 hasOption(options, '--incremental-support'), | |
| 80 suppressWarnings: hasOption(options, '--suppress-warnings'), | |
| 81 enableAsyncAwait: hasOption(options, '--enable-async')) { | |
| 82 tasks.addAll([ | |
| 83 userHandlerTask = new leg.GenericTask('Diagnostic handler', this), | |
| 84 userProviderTask = new leg.GenericTask('Input provider', this), | |
| 85 ]); | |
| 86 if (!libraryRoot.path.endsWith("/")) { | |
| 87 throw new ArgumentError("libraryRoot must end with a /"); | |
| 88 } | |
| 89 if (packageRoot != null && !packageRoot.path.endsWith("/")) { | |
| 90 throw new ArgumentError("packageRoot must end with a /"); | |
| 91 } | |
| 92 if (enableAsyncAwait && !analyzeOnly) { | |
| 93 throw new ArgumentError( | |
| 94 "--enable-async is currently only supported with --analyze-only"); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 static String extractStringOption(List<String> options, | |
| 99 String prefix, | |
| 100 String defaultValue) { | |
| 101 for (String option in options) { | |
| 102 if (option.startsWith(prefix)) { | |
| 103 return option.substring(prefix.length); | |
| 104 } | |
| 105 } | |
| 106 return defaultValue; | |
| 107 } | |
| 108 | |
| 109 static Uri extractUriOption(List<String> options, String prefix) { | |
| 110 var option = extractStringOption(options, prefix, null); | |
| 111 return (option == null) ? null : Uri.parse(option); | |
| 112 } | |
| 113 | |
| 114 // CSV: Comma separated values. | |
| 115 static List<String> extractCsvOption(List<String> options, String prefix) { | |
| 116 for (String option in options) { | |
| 117 if (option.startsWith(prefix)) { | |
| 118 return option.substring(prefix.length).split(','); | |
| 119 } | |
| 120 } | |
| 121 return const <String>[]; | |
| 122 } | |
| 123 | |
| 124 static Set<String> getAllowedLibraryCategories(List<String> options) { | |
| 125 var result = extractCsvOption(options, '--categories='); | |
| 126 if (result.isEmpty) { | |
| 127 result = ['Client']; | |
| 128 } | |
| 129 result.add('Shared'); | |
| 130 result.add('Internal'); | |
| 131 return new Set<String>.from(result); | |
| 132 } | |
| 133 | |
| 134 static bool hasOption(List<String> options, String option) { | |
| 135 return options.indexOf(option) >= 0; | |
| 136 } | |
| 137 | |
| 138 // TODO(johnniwinther): Merge better with [translateDartUri] when | |
| 139 // [scanBuiltinLibrary] is removed. | |
| 140 String lookupLibraryPath(String dartLibraryName) { | |
| 141 LibraryInfo info = LIBRARIES[dartLibraryName]; | |
| 142 if (info == null) return null; | |
| 143 if (!info.isDart2jsLibrary) return null; | |
| 144 if (!allowedLibraryCategories.contains(info.category)) return null; | |
| 145 String path = info.dart2jsPath; | |
| 146 if (path == null) { | |
| 147 path = info.path; | |
| 148 } | |
| 149 return "lib/$path"; | |
| 150 } | |
| 151 | |
| 152 String lookupPatchPath(String dartLibraryName) { | |
| 153 LibraryInfo info = LIBRARIES[dartLibraryName]; | |
| 154 if (info == null) return null; | |
| 155 if (!info.isDart2jsLibrary) return null; | |
| 156 String path = info.dart2jsPatchPath; | |
| 157 if (path == null) return null; | |
| 158 return "lib/$path"; | |
| 159 } | |
| 160 | |
| 161 void log(message) { | |
| 162 handler(null, null, null, message, api.Diagnostic.VERBOSE_INFO); | |
| 163 } | |
| 164 | |
| 165 /// See [leg.Compiler.translateResolvedUri]. | |
| 166 Uri translateResolvedUri(elements.LibraryElement importingLibrary, | |
| 167 Uri resolvedUri, tree.Node node) { | |
| 168 if (resolvedUri.scheme == 'dart') { | |
| 169 return translateDartUri(importingLibrary, resolvedUri, node); | |
| 170 } | |
| 171 return resolvedUri; | |
| 172 } | |
| 173 | |
| 174 /** | |
| 175 * Reads the script designated by [readableUri]. | |
| 176 */ | |
| 177 Future<leg.Script> readScript(leg.Spannable node, Uri readableUri) { | |
| 178 if (!readableUri.isAbsolute) { | |
| 179 if (node == null) node = leg.NO_LOCATION_SPANNABLE; | |
| 180 internalError(node, | |
| 181 'Relative uri $readableUri provided to readScript(Uri).'); | |
| 182 } | |
| 183 | |
| 184 // We need to store the current element since we are reporting read errors | |
| 185 // asynchronously and therefore need to restore the current element for | |
| 186 // [node] to be valid. | |
| 187 elements.Element element = currentElement; | |
| 188 void reportReadError(exception) { | |
| 189 withCurrentElement(element, () { | |
| 190 reportError(node, | |
| 191 leg.MessageKind.READ_SCRIPT_ERROR, | |
| 192 {'uri': readableUri, 'exception': exception}); | |
| 193 }); | |
| 194 } | |
| 195 | |
| 196 Uri resourceUri = translateUri(node, readableUri); | |
| 197 // TODO(johnniwinther): Wrap the result from [provider] in a specialized | |
| 198 // [Future] to ensure that we never execute an asynchronous action without | |
| 199 // setting up the current element of the compiler. | |
| 200 return new Future.sync(() => callUserProvider(resourceUri)).then((data) { | |
| 201 SourceFile sourceFile; | |
| 202 String resourceUriString = resourceUri.toString(); | |
| 203 if (data is List<int>) { | |
| 204 sourceFile = new Utf8BytesSourceFile(resourceUriString, data); | |
| 205 } else if (data is String) { | |
| 206 sourceFile = new StringSourceFile(resourceUriString, data); | |
| 207 } else { | |
| 208 String message = "Expected a 'String' or a 'List<int>' from the input " | |
| 209 "provider, but got: ${Error.safeToString(data)}."; | |
| 210 reportReadError(message); | |
| 211 } | |
| 212 // We use [readableUri] as the URI for the script since need to preserve | |
| 213 // the scheme in the script because [Script.uri] is used for resolving | |
| 214 // relative URIs mentioned in the script. See the comment on | |
| 215 // [LibraryLoader] for more details. | |
| 216 return new leg.Script(readableUri, resourceUri, sourceFile); | |
| 217 }).catchError((error) { | |
| 218 reportReadError(error); | |
| 219 return null; | |
| 220 }); | |
| 221 } | |
| 222 | |
| 223 /** | |
| 224 * Translates a readable URI into a resource URI. | |
| 225 * | |
| 226 * See [LibraryLoader] for terminology on URIs. | |
| 227 */ | |
| 228 Uri translateUri(leg.Spannable node, Uri readableUri) { | |
| 229 switch (readableUri.scheme) { | |
| 230 case 'package': return translatePackageUri(node, readableUri); | |
| 231 default: return readableUri; | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 Uri translateDartUri(elements.LibraryElement importingLibrary, | |
| 236 Uri resolvedUri, tree.Node node) { | |
| 237 LibraryInfo libraryInfo = LIBRARIES[resolvedUri.path]; | |
| 238 String path = lookupLibraryPath(resolvedUri.path); | |
| 239 if (libraryInfo != null && | |
| 240 libraryInfo.category == "Internal") { | |
| 241 bool allowInternalLibraryAccess = false; | |
| 242 if (importingLibrary != null) { | |
| 243 if (importingLibrary.isPlatformLibrary || importingLibrary.isPatch) { | |
| 244 allowInternalLibraryAccess = true; | |
| 245 } else if (importingLibrary.canonicalUri.path.contains( | |
| 246 'dart/tests/compiler/dart2js_native')) { | |
| 247 allowInternalLibraryAccess = true; | |
| 248 } | |
| 249 } | |
| 250 if (!allowInternalLibraryAccess) { | |
| 251 if (importingLibrary != null) { | |
| 252 reportError( | |
| 253 node, | |
| 254 leg.MessageKind.INTERNAL_LIBRARY_FROM, | |
| 255 {'resolvedUri': resolvedUri, | |
| 256 'importingUri': importingLibrary.canonicalUri}); | |
| 257 } else { | |
| 258 reportError( | |
| 259 node, | |
| 260 leg.MessageKind.INTERNAL_LIBRARY, | |
| 261 {'resolvedUri': resolvedUri}); | |
| 262 } | |
| 263 } | |
| 264 } | |
| 265 if (path == null) { | |
| 266 reportError(node, leg.MessageKind.LIBRARY_NOT_FOUND, | |
| 267 {'resolvedUri': resolvedUri}); | |
| 268 return null; | |
| 269 } | |
| 270 if (resolvedUri.path == 'html' || | |
| 271 resolvedUri.path == 'io') { | |
| 272 // TODO(ahe): Get rid of mockableLibraryUsed when test.dart | |
| 273 // supports this use case better. | |
| 274 mockableLibraryUsed = true; | |
| 275 } | |
| 276 return libraryRoot.resolve(path); | |
| 277 } | |
| 278 | |
| 279 Uri resolvePatchUri(String dartLibraryPath) { | |
| 280 String patchPath = lookupPatchPath(dartLibraryPath); | |
| 281 if (patchPath == null) return null; | |
| 282 return libraryRoot.resolve(patchPath); | |
| 283 } | |
| 284 | |
| 285 Uri translatePackageUri(leg.Spannable node, Uri uri) { | |
| 286 if (packageRoot == null) { | |
| 287 reportFatalError( | |
| 288 node, leg.MessageKind.PACKAGE_ROOT_NOT_SET, {'uri': uri}); | |
| 289 } | |
| 290 return packageRoot.resolve(uri.path); | |
| 291 } | |
| 292 | |
| 293 Future<bool> run(Uri uri) { | |
| 294 log('Allowed library categories: $allowedLibraryCategories'); | |
| 295 return super.run(uri).then((bool success) { | |
| 296 int cumulated = 0; | |
| 297 for (final task in tasks) { | |
| 298 int elapsed = task.timing; | |
| 299 if (elapsed != 0) { | |
| 300 cumulated += elapsed; | |
| 301 log('${task.name} took ${elapsed}msec'); | |
| 302 } | |
| 303 } | |
| 304 int total = totalCompileTime.elapsedMilliseconds; | |
| 305 log('Total compile-time ${total}msec;' | |
| 306 ' unaccounted ${total - cumulated}msec'); | |
| 307 return success; | |
| 308 }); | |
| 309 } | |
| 310 | |
| 311 void reportDiagnostic(leg.Spannable node, | |
| 312 leg.Message message, | |
| 313 api.Diagnostic kind) { | |
| 314 leg.SourceSpan span = spanFromSpannable(node); | |
| 315 if (identical(kind, api.Diagnostic.ERROR) | |
| 316 || identical(kind, api.Diagnostic.CRASH)) { | |
| 317 compilationFailed = true; | |
| 318 } | |
| 319 // [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For | |
| 320 // instance in the [Types] constructor in typechecker.dart. | |
| 321 if (span == null || span.uri == null) { | |
| 322 callUserHandler(null, null, null, '$message', kind); | |
| 323 } else { | |
| 324 callUserHandler( | |
| 325 translateUri(null, span.uri), span.begin, span.end, '$message', kind); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 bool get isMockCompilation { | |
| 330 return mockableLibraryUsed | |
| 331 && (options.indexOf('--allow-mock-compilation') != -1); | |
| 332 } | |
| 333 | |
| 334 void callUserHandler(Uri uri, int begin, int end, | |
| 335 String message, api.Diagnostic kind) { | |
| 336 try { | |
| 337 userHandlerTask.measure(() { | |
| 338 handler(uri, begin, end, message, kind); | |
| 339 }); | |
| 340 } catch (ex, s) { | |
| 341 diagnoseCrashInUserCode( | |
| 342 'Uncaught exception in diagnostic handler', ex, s); | |
| 343 rethrow; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 Future callUserProvider(Uri uri) { | |
| 348 try { | |
| 349 return userProviderTask.measure(() => provider(uri)); | |
| 350 } catch (ex, s) { | |
| 351 diagnoseCrashInUserCode('Uncaught exception in input provider', ex, s); | |
| 352 rethrow; | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 void diagnoseCrashInUserCode(String message, exception, stackTrace) { | |
| 357 hasCrashed = true; | |
| 358 print('$message: ${tryToString(exception)}'); | |
| 359 print(tryToString(stackTrace)); | |
| 360 } | |
| 361 | |
| 362 fromEnvironment(String name) => environment[name]; | |
| 363 } | |
| OLD | NEW |