| 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 '../../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 tasks.addAll([ | |
| 82 userHandlerTask = new leg.GenericTask('Diagnostic handler', this), | |
| 83 userProviderTask = new leg.GenericTask('Input provider', this), | |
| 84 ]); | |
| 85 if (!libraryRoot.path.endsWith("/")) { | |
| 86 throw new ArgumentError("libraryRoot must end with a /"); | |
| 87 } | |
| 88 if (packageRoot != null && !packageRoot.path.endsWith("/")) { | |
| 89 throw new ArgumentError("packageRoot must end with a /"); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 static String extractStringOption(List<String> options, | |
| 94 String prefix, | |
| 95 String defaultValue) { | |
| 96 for (String option in options) { | |
| 97 if (option.startsWith(prefix)) { | |
| 98 return option.substring(prefix.length); | |
| 99 } | |
| 100 } | |
| 101 return defaultValue; | |
| 102 } | |
| 103 | |
| 104 static Uri extractUriOption(List<String> options, String prefix) { | |
| 105 var option = extractStringOption(options, prefix, null); | |
| 106 return (option == null) ? null : Uri.parse(option); | |
| 107 } | |
| 108 | |
| 109 // CSV: Comma separated values. | |
| 110 static List<String> extractCsvOption(List<String> options, String prefix) { | |
| 111 for (String option in options) { | |
| 112 if (option.startsWith(prefix)) { | |
| 113 return option.substring(prefix.length).split(','); | |
| 114 } | |
| 115 } | |
| 116 return const <String>[]; | |
| 117 } | |
| 118 | |
| 119 static Set<String> getAllowedLibraryCategories(List<String> options) { | |
| 120 var result = extractCsvOption(options, '--categories='); | |
| 121 if (result.isEmpty) { | |
| 122 result = ['Client']; | |
| 123 } | |
| 124 result.add('Shared'); | |
| 125 result.add('Internal'); | |
| 126 return new Set<String>.from(result); | |
| 127 } | |
| 128 | |
| 129 static bool hasOption(List<String> options, String option) { | |
| 130 return options.indexOf(option) >= 0; | |
| 131 } | |
| 132 | |
| 133 // TODO(johnniwinther): Merge better with [translateDartUri] when | |
| 134 // [scanBuiltinLibrary] is removed. | |
| 135 String lookupLibraryPath(String dartLibraryName) { | |
| 136 LibraryInfo info = LIBRARIES[dartLibraryName]; | |
| 137 if (info == null) return null; | |
| 138 if (!info.isDart2jsLibrary) return null; | |
| 139 if (!allowedLibraryCategories.contains(info.category)) return null; | |
| 140 String path = info.dart2jsPath; | |
| 141 if (path == null) { | |
| 142 path = info.path; | |
| 143 } | |
| 144 return "lib/$path"; | |
| 145 } | |
| 146 | |
| 147 String lookupPatchPath(String dartLibraryName) { | |
| 148 LibraryInfo info = LIBRARIES[dartLibraryName]; | |
| 149 if (info == null) return null; | |
| 150 if (!info.isDart2jsLibrary) return null; | |
| 151 String path = info.dart2jsPatchPath; | |
| 152 if (path == null) return null; | |
| 153 return "lib/$path"; | |
| 154 } | |
| 155 | |
| 156 void log(message) { | |
| 157 handler(null, null, null, message, api.Diagnostic.VERBOSE_INFO); | |
| 158 } | |
| 159 | |
| 160 /// See [leg.Compiler.translateResolvedUri]. | |
| 161 Uri translateResolvedUri(elements.LibraryElement importingLibrary, | |
| 162 Uri resolvedUri, tree.Node node) { | |
| 163 if (resolvedUri.scheme == 'dart') { | |
| 164 return translateDartUri(importingLibrary, resolvedUri, node); | |
| 165 } | |
| 166 return resolvedUri; | |
| 167 } | |
| 168 | |
| 169 /** | |
| 170 * Reads the script designated by [readableUri]. | |
| 171 */ | |
| 172 Future<leg.Script> readScript(leg.Spannable node, Uri readableUri) { | |
| 173 if (!readableUri.isAbsolute) { | |
| 174 if (node == null) node = leg.NO_LOCATION_SPANNABLE; | |
| 175 internalError(node, | |
| 176 'Relative uri $readableUri provided to readScript(Uri).'); | |
| 177 } | |
| 178 | |
| 179 // We need to store the current element since we are reporting read errors | |
| 180 // asynchronously and therefore need to restore the current element for | |
| 181 // [node] to be valid. | |
| 182 elements.Element element = currentElement; | |
| 183 void reportReadError(exception) { | |
| 184 withCurrentElement(element, () { | |
| 185 reportError(node, | |
| 186 leg.MessageKind.READ_SCRIPT_ERROR, | |
| 187 {'uri': readableUri, 'exception': exception}); | |
| 188 }); | |
| 189 } | |
| 190 | |
| 191 Uri resourceUri = translateUri(node, readableUri); | |
| 192 // TODO(johnniwinther): Wrap the result from [provider] in a specialized | |
| 193 // [Future] to ensure that we never execute an asynchronous action without | |
| 194 // setting up the current element of the compiler. | |
| 195 return new Future.sync(() => callUserProvider(resourceUri)).then((data) { | |
| 196 SourceFile sourceFile; | |
| 197 String resourceUriString = resourceUri.toString(); | |
| 198 if (data is List<int>) { | |
| 199 sourceFile = new Utf8BytesSourceFile(resourceUriString, data); | |
| 200 } else if (data is String) { | |
| 201 sourceFile = new StringSourceFile(resourceUriString, data); | |
| 202 } else { | |
| 203 String message = "Expected a 'String' or a 'List<int>' from the input " | |
| 204 "provider, but got: ${Error.safeToString(data)}."; | |
| 205 reportReadError(message); | |
| 206 } | |
| 207 // We use [readableUri] as the URI for the script since need to preserve | |
| 208 // the scheme in the script because [Script.uri] is used for resolving | |
| 209 // relative URIs mentioned in the script. See the comment on | |
| 210 // [LibraryLoader] for more details. | |
| 211 return new leg.Script(readableUri, resourceUri, sourceFile); | |
| 212 }).catchError((error) { | |
| 213 reportReadError(error); | |
| 214 return null; | |
| 215 }); | |
| 216 } | |
| 217 | |
| 218 /** | |
| 219 * Translates a readable URI into a resource URI. | |
| 220 * | |
| 221 * See [LibraryLoader] for terminology on URIs. | |
| 222 */ | |
| 223 Uri translateUri(leg.Spannable node, Uri readableUri) { | |
| 224 switch (readableUri.scheme) { | |
| 225 case 'package': return translatePackageUri(node, readableUri); | |
| 226 default: return readableUri; | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 Uri translateDartUri(elements.LibraryElement importingLibrary, | |
| 231 Uri resolvedUri, tree.Node node) { | |
| 232 LibraryInfo libraryInfo = LIBRARIES[resolvedUri.path]; | |
| 233 String path = lookupLibraryPath(resolvedUri.path); | |
| 234 if (libraryInfo != null && | |
| 235 libraryInfo.category == "Internal") { | |
| 236 bool allowInternalLibraryAccess = false; | |
| 237 if (importingLibrary != null) { | |
| 238 if (importingLibrary.isPlatformLibrary || importingLibrary.isPatch) { | |
| 239 allowInternalLibraryAccess = true; | |
| 240 } else if (importingLibrary.canonicalUri.path.contains( | |
| 241 'dart/tests/compiler/dart2js_native')) { | |
| 242 allowInternalLibraryAccess = true; | |
| 243 } | |
| 244 } | |
| 245 if (!allowInternalLibraryAccess) { | |
| 246 if (importingLibrary != null) { | |
| 247 reportError( | |
| 248 node, | |
| 249 leg.MessageKind.INTERNAL_LIBRARY_FROM, | |
| 250 {'resolvedUri': resolvedUri, | |
| 251 'importingUri': importingLibrary.canonicalUri}); | |
| 252 } else { | |
| 253 reportError( | |
| 254 node, | |
| 255 leg.MessageKind.INTERNAL_LIBRARY, | |
| 256 {'resolvedUri': resolvedUri}); | |
| 257 } | |
| 258 } | |
| 259 } | |
| 260 if (path == null) { | |
| 261 reportError(node, leg.MessageKind.LIBRARY_NOT_FOUND, | |
| 262 {'resolvedUri': resolvedUri}); | |
| 263 return null; | |
| 264 } | |
| 265 if (resolvedUri.path == 'html' || | |
| 266 resolvedUri.path == 'io') { | |
| 267 // TODO(ahe): Get rid of mockableLibraryUsed when test.dart | |
| 268 // supports this use case better. | |
| 269 mockableLibraryUsed = true; | |
| 270 } | |
| 271 return libraryRoot.resolve(path); | |
| 272 } | |
| 273 | |
| 274 Uri resolvePatchUri(String dartLibraryPath) { | |
| 275 String patchPath = lookupPatchPath(dartLibraryPath); | |
| 276 if (patchPath == null) return null; | |
| 277 return libraryRoot.resolve(patchPath); | |
| 278 } | |
| 279 | |
| 280 Uri translatePackageUri(leg.Spannable node, Uri uri) { | |
| 281 if (packageRoot == null) { | |
| 282 reportFatalError( | |
| 283 node, leg.MessageKind.PACKAGE_ROOT_NOT_SET, {'uri': uri}); | |
| 284 } | |
| 285 return packageRoot.resolve(uri.path); | |
| 286 } | |
| 287 | |
| 288 Future<bool> run(Uri uri) { | |
| 289 log('Allowed library categories: $allowedLibraryCategories'); | |
| 290 return super.run(uri).then((bool success) { | |
| 291 int cumulated = 0; | |
| 292 for (final task in tasks) { | |
| 293 int elapsed = task.timing; | |
| 294 if (elapsed != 0) { | |
| 295 cumulated += elapsed; | |
| 296 log('${task.name} took ${elapsed}msec'); | |
| 297 } | |
| 298 } | |
| 299 int total = totalCompileTime.elapsedMilliseconds; | |
| 300 log('Total compile-time ${total}msec;' | |
| 301 ' unaccounted ${total - cumulated}msec'); | |
| 302 return success; | |
| 303 }); | |
| 304 } | |
| 305 | |
| 306 void reportDiagnostic(leg.Spannable node, | |
| 307 leg.Message message, | |
| 308 api.Diagnostic kind) { | |
| 309 leg.SourceSpan span = spanFromSpannable(node); | |
| 310 if (identical(kind, api.Diagnostic.ERROR) | |
| 311 || identical(kind, api.Diagnostic.CRASH)) { | |
| 312 compilationFailed = true; | |
| 313 } | |
| 314 // [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For | |
| 315 // instance in the [Types] constructor in typechecker.dart. | |
| 316 if (span == null || span.uri == null) { | |
| 317 callUserHandler(null, null, null, '$message', kind); | |
| 318 } else { | |
| 319 callUserHandler( | |
| 320 translateUri(null, span.uri), span.begin, span.end, '$message', kind); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 bool get isMockCompilation { | |
| 325 return mockableLibraryUsed | |
| 326 && (options.indexOf('--allow-mock-compilation') != -1); | |
| 327 } | |
| 328 | |
| 329 void callUserHandler(Uri uri, int begin, int end, | |
| 330 String message, api.Diagnostic kind) { | |
| 331 try { | |
| 332 userHandlerTask.measure(() { | |
| 333 handler(uri, begin, end, message, kind); | |
| 334 }); | |
| 335 } catch (ex, s) { | |
| 336 diagnoseCrashInUserCode( | |
| 337 'Uncaught exception in diagnostic handler', ex, s); | |
| 338 rethrow; | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 Future callUserProvider(Uri uri) { | |
| 343 try { | |
| 344 return userProviderTask.measure(() => provider(uri)); | |
| 345 } catch (ex, s) { | |
| 346 diagnoseCrashInUserCode('Uncaught exception in input provider', ex, s); | |
| 347 rethrow; | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 void diagnoseCrashInUserCode(String message, exception, stackTrace) { | |
| 352 hasCrashed = true; | |
| 353 print('$message: ${tryToString(exception)}'); | |
| 354 print(tryToString(stackTrace)); | |
| 355 } | |
| 356 | |
| 357 fromEnvironment(String name) => environment[name]; | |
| 358 } | |
| OLD | NEW |