blob: ddf776eefdb1d3d51c54c0cde3a499a310438da2 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.compiler_base;
import 'dart:async' show Future;
import '../compiler_new.dart' as api;
import 'backend_strategy.dart';
import 'common/names.dart' show Selectors;
import 'common/names.dart' show Uris;
import 'common/resolution.dart'
show
ParsingContext,
Resolution,
ResolutionWorkItem,
ResolutionImpact,
Target;
import 'common/tasks.dart' show CompilerTask, GenericTask, Measurer;
import 'common/work.dart' show WorkItem;
import 'common.dart';
import 'compile_time_constants.dart';
import 'constants/values.dart';
import 'common_elements.dart' show CommonElements, ElementEnvironment;
import 'deferred_load.dart' show DeferredLoadTask, OutputUnitData;
import 'diagnostics/code_location.dart';
import 'diagnostics/diagnostic_listener.dart' show DiagnosticReporter;
import 'diagnostics/invariant.dart' show REPORT_EXCESS_RESOLUTION;
import 'diagnostics/messages.dart' show Message, MessageTemplate;
import 'dump_info.dart' show DumpInfoTask;
import 'elements/elements.dart';
import 'elements/entities.dart';
import 'elements/resolution_types.dart' show ResolutionDartType, Types;
import 'enqueue.dart' show Enqueuer, EnqueueTask, ResolutionEnqueuer;
import 'environment.dart';
import 'frontend_strategy.dart';
import 'id_generator.dart';
import 'io/source_information.dart' show SourceInformation;
import 'io/source_file.dart' show Binary;
import 'js_backend/backend.dart' show JavaScriptBackend;
import 'js_backend/element_strategy.dart' show ElementBackendStrategy;
import 'kernel/kernel_backend_strategy.dart';
import 'kernel/kernel_strategy.dart';
import 'library_loader.dart'
show
ElementScanner,
LibraryLoader,
LibraryLoaderTask,
LoadedLibraries,
ScriptLoader;
import 'mirrors_used.dart' show MirrorUsageAnalyzerTask;
import 'null_compiler_output.dart' show NullCompilerOutput, NullSink;
import 'options.dart' show CompilerOptions, DiagnosticOptions;
import 'parser/diet_parser_task.dart' show DietParserTask;
import 'parser/parser_task.dart' show ParserTask;
import 'patch_parser.dart' show PatchParserTask;
import 'resolution/resolution.dart' show ResolverTask;
import 'resolution/resolution_strategy.dart';
import 'resolved_uri_translator.dart';
import 'scanner/scanner_task.dart' show ScannerTask;
import 'script.dart' show Script;
import 'serialization/task.dart' show SerializationTask;
import 'ssa/nodes.dart' show HInstruction;
import 'package:front_end/src/fasta/scanner.dart' show StringToken, Token;
import 'tokens/token_map.dart' show TokenMap;
import 'tree/tree.dart' show Node, TypeAnnotation;
import 'typechecker.dart' show TypeCheckerTask;
import 'types/types.dart' show GlobalTypeInferenceTask;
import 'universe/selector.dart' show Selector;
import 'universe/world_builder.dart'
show ResolutionWorldBuilder, CodegenWorldBuilder;
import 'universe/use.dart' show StaticUse, TypeUse;
import 'universe/world_impact.dart'
show ImpactStrategy, WorldImpact, WorldImpactBuilderImpl;
import 'util/util.dart' show Link;
import 'world.dart' show ClosedWorld, ClosedWorldRefiner;
typedef CompilerDiagnosticReporter MakeReporterFunction(
Compiler compiler, CompilerOptions options);
abstract class Compiler {
Measurer get measurer;
api.CompilerInput get provider;
final IdGenerator idGenerator = new IdGenerator();
FrontendStrategy frontendStrategy;
BackendStrategy backendStrategy;
CompilerDiagnosticReporter _reporter;
CompilerResolution _resolution;
Map<Entity, WorldImpact> _impactCache;
ImpactCacheDeleter _impactCacheDeleter;
ParsingContext _parsingContext;
ImpactStrategy impactStrategy = const ImpactStrategy();
/**
* Map from token to the first preceding comment token.
*/
final TokenMap commentMap = new TokenMap();
/// Options provided from command-line arguments.
final CompilerOptions options;
/**
* If true, stop compilation after type inference is complete. Used for
* debugging and testing purposes only.
*/
bool stopAfterTypeInference = false;
/// Output provider from user of Compiler API.
api.CompilerOutput _outputProvider;
api.CompilerOutput get outputProvider => _outputProvider;
List<Uri> librariesToAnalyzeWhenRun;
ResolvedUriTranslator get resolvedUriTranslator;
Uri mainLibraryUri;
ClosedWorld backendClosedWorldForTesting;
DiagnosticReporter get reporter => _reporter;
Resolution get resolution => _resolution;
Map<Entity, WorldImpact> get impactCache => _impactCache;
ImpactCacheDeleter get impactCacheDeleter => _impactCacheDeleter;
ParsingContext get parsingContext => _parsingContext;
// TODO(zarah): Remove this map and incorporate compile-time errors
// in the model.
/// Tracks elements with compile-time errors.
final Map<Entity, List<DiagnosticMessage>> elementsWithCompileTimeErrors =
new Map<Entity, List<DiagnosticMessage>>();
final Environment environment;
// TODO(sigmund): delete once we migrate the rest of the compiler to use
// `environment` directly.
@deprecated
fromEnvironment(String name) => environment.valueOf(name);
Entity get currentElement => _reporter.currentElement;
List<CompilerTask> tasks;
ScannerTask scanner;
DietParserTask dietParser;
ParserTask parser;
PatchParserTask patchParser;
LibraryLoaderTask libraryLoader;
SerializationTask serialization;
ResolverTask resolver;
TypeCheckerTask checker;
GlobalTypeInferenceTask globalInference;
JavaScriptBackend backend;
CodegenWorldBuilder _codegenWorldBuilder;
GenericTask selfTask;
/// The constant environment for the frontend interpretation of compile-time
/// constants.
ConstantEnvironment constants;
EnqueueTask enqueuer;
DeferredLoadTask deferredLoadTask;
MirrorUsageAnalyzerTask mirrorUsageAnalyzerTask;
DumpInfoTask dumpInfoTask;
bool get hasCrashed => _reporter.hasCrashed;
Progress progress = const Progress();
static const int PHASE_SCANNING = 0;
static const int PHASE_RESOLVING = 1;
static const int PHASE_DONE_RESOLVING = 2;
static const int PHASE_COMPILING = 3;
int phase;
bool compilationFailed = false;
Compiler(
{CompilerOptions options,
api.CompilerOutput outputProvider,
this.environment: const _EmptyEnvironment(),
MakeReporterFunction makeReporter})
: this.options = options {
CompilerTask kernelFrontEndTask;
selfTask = new GenericTask('self', measurer);
_outputProvider = new _CompilerOutput(this, outputProvider);
if (makeReporter != null) {
_reporter = makeReporter(this, options);
} else {
_reporter = new CompilerDiagnosticReporter(this, options);
}
if (options.useKernel) {
kernelFrontEndTask = new GenericTask('Front end', measurer);
frontendStrategy = new KernelFrontEndStrategy(kernelFrontEndTask, options,
reporter, environment, options.kernelInitializedCompilerState);
backendStrategy = new KernelBackendStrategy(this);
_impactCache = <Entity, WorldImpact>{};
_impactCacheDeleter = new _MapImpactCacheDeleter(_impactCache);
} else {
frontendStrategy = new ResolutionFrontEndStrategy(this);
backendStrategy = new ElementBackendStrategy(this);
_resolution = createResolution();
_impactCache = _resolution._worldImpactCache;
_impactCacheDeleter = _resolution;
}
if (options.verbose) {
progress = new ProgressImpl(_reporter);
}
backend = createBackend();
enqueuer = backend.makeEnqueuer();
tasks = [
dietParser = new DietParserTask(idGenerator, backend, reporter, measurer),
scanner = createScannerTask(),
serialization = new SerializationTask(this),
patchParser = new PatchParserTask(this),
libraryLoader = frontendStrategy.createLibraryLoader(
resolvedUriTranslator,
options.compileOnly
? new _NoScriptLoader(this)
: new _ScriptLoader(this),
provider,
new _ElementScanner(scanner),
serialization,
resolvePatchUri,
patchParser,
environment,
reporter,
measurer),
parser = new ParserTask(this),
resolver = createResolverTask(),
checker = new TypeCheckerTask(this),
globalInference = new GlobalTypeInferenceTask(this),
constants = backend.constantCompilerTask,
deferredLoadTask = frontendStrategy.createDeferredLoadTask(this),
mirrorUsageAnalyzerTask = new MirrorUsageAnalyzerTask(this),
// [enqueuer] is created earlier because it contains the resolution world
// objects needed by other tasks.
enqueuer,
dumpInfoTask = new DumpInfoTask(this),
selfTask,
];
if (options.useKernel) tasks.add(kernelFrontEndTask);
if (options.resolveOnly) {
serialization.supportSerialization = true;
}
_parsingContext =
new ParsingContext(reporter, parser, scanner, patchParser, backend);
tasks.addAll(backend.tasks);
}
/// Creates the backend.
///
/// Override this to mock the backend for testing.
JavaScriptBackend createBackend() {
return new JavaScriptBackend(this,
generateSourceMap: options.generateSourceMap,
useStartupEmitter: options.useStartupEmitter,
useMultiSourceInfo: options.useMultiSourceInfo,
useNewSourceInfo: options.useNewSourceInfo);
}
/// Creates the scanner task.
///
/// Override this to mock the scanner for testing.
ScannerTask createScannerTask() =>
new ScannerTask(dietParser, reporter, measurer,
preserveComments: options.preserveComments, commentMap: commentMap);
/// Creates the resolution object.
///
/// Override this to mock resolution for testing.
Resolution createResolution() => new CompilerResolution(this);
/// Creates the resolver task.
///
/// Override this to mock the resolver for testing.
ResolverTask createResolverTask() {
return new ResolverTask(resolution, backend.constantCompilerTask, measurer);
}
ResolutionWorldBuilder get resolutionWorldBuilder =>
enqueuer.resolution.worldBuilder;
CodegenWorldBuilder get codegenWorldBuilder {
assert(
_codegenWorldBuilder != null,
failedAt(NO_LOCATION_SPANNABLE,
"CodegenWorldBuilder has not been created yet."));
return _codegenWorldBuilder;
}
bool get analyzeAll => options.analyzeAll || compileAll;
bool get compileAll => false;
bool get disableTypeInference =>
options.disableTypeInference || compilationFailed;
// Compiles the dart script at [uri].
//
// The resulting future will complete with true if the compilation
// succeeded.
Future<bool> run(Uri uri) => selfTask.measureSubtask("Compiler.run", () {
measurer.startWallClock();
return new Future.sync(() => runInternal(uri))
.catchError((error) => _reporter.onError(uri, error))
.whenComplete(() {
measurer.stopWallClock();
}).then((_) {
return !compilationFailed;
});
});
/// Compute the set of distinct import chains to the library at [uri] within
/// [loadedLibraries].
///
/// The chains are strings of the form
///
/// <main-uri> => <intermediate-uri1> => <intermediate-uri2> => <uri>
///
Set<String> computeImportChainsFor(LoadedLibraries loadedLibraries, Uri uri) {
// TODO(johnniwinther): Move computation of dependencies to the library
// loader.
Set<String> importChains = new Set<String>();
// The maximum number of full imports chains to process.
final int chainLimit = 10000;
// The maximum number of imports chains to show.
final int compactChainLimit = options.verbose ? 20 : 10;
int chainCount = 0;
loadedLibraries.forEachImportChain(uri,
callback: (Link<Uri> importChainReversed) {
Link<CodeLocation> compactImportChain = const Link<CodeLocation>();
CodeLocation currentCodeLocation =
new UriLocation(importChainReversed.head);
compactImportChain = compactImportChain.prepend(currentCodeLocation);
for (Link<Uri> link = importChainReversed.tail;
!link.isEmpty;
link = link.tail) {
Uri uri = link.head;
if (!currentCodeLocation.inSameLocation(uri)) {
currentCodeLocation =
options.verbose ? new UriLocation(uri) : new CodeLocation(uri);
compactImportChain = compactImportChain.prepend(currentCodeLocation);
}
}
String importChain = compactImportChain.map((CodeLocation codeLocation) {
return codeLocation.relativize(
(loadedLibraries.rootLibrary as LibraryElement).canonicalUri);
}).join(' => ');
if (!importChains.contains(importChain)) {
if (importChains.length > compactChainLimit) {
importChains.add('...');
return false;
} else {
importChains.add(importChain);
}
}
chainCount++;
if (chainCount > chainLimit) {
// Assume there are more import chains.
importChains.add('...');
return false;
}
return true;
});
return importChains;
}
/// This method is called when all new libraries loaded through
/// [LibraryLoader.loadLibrary] has been loaded and their imports/exports
/// have been computed.
///
/// [loadedLibraries] contains the newly loaded libraries.
///
/// The method returns a [Future] allowing for the loading of additional
/// libraries.
LoadedLibraries processLoadedLibraries(LoadedLibraries loadedLibraries) {
loadedLibraries.forEachLibrary((LibraryEntity library) {
backend.setAnnotations(library);
});
// TODO(efortuna, sigmund): These validation steps should be done in the
// front end for the Kernel path since Kernel doesn't have the notion of
// imports (everything has already been resolved). (See
// https://github.com/dart-lang/sdk/issues/29368)
if (!options.useKernel) {
for (Uri uri in resolvedUriTranslator.disallowedLibraryUris) {
if (loadedLibraries.containsLibrary(uri)) {
Set<String> importChains =
computeImportChainsFor(loadedLibraries, uri);
reporter.reportInfo(
NO_LOCATION_SPANNABLE, MessageKind.DISALLOWED_LIBRARY_IMPORT, {
'uri': uri,
'importChain': importChains
.join(MessageTemplate.DISALLOWED_LIBRARY_IMPORT_PADDING)
});
}
}
if (loadedLibraries.containsLibrary(Uris.dart_core)) {
bool importsMirrorsLibrary =
loadedLibraries.containsLibrary(Uris.dart_mirrors);
if (importsMirrorsLibrary && !backend.supportsReflection) {
Set<String> importChains =
computeImportChainsFor(loadedLibraries, Uris.dart_mirrors);
reporter.reportErrorMessage(NO_LOCATION_SPANNABLE,
MessageKind.MIRRORS_LIBRARY_NOT_SUPPORT_BY_BACKEND, {
'importChain': importChains
.join(MessageTemplate.MIRRORS_NOT_SUPPORTED_BY_BACKEND_PADDING)
});
} else if (importsMirrorsLibrary &&
!options.enableExperimentalMirrors) {
Set<String> importChains =
computeImportChainsFor(loadedLibraries, Uris.dart_mirrors);
reporter.reportWarningMessage(
NO_LOCATION_SPANNABLE, MessageKind.IMPORT_EXPERIMENTAL_MIRRORS, {
'importChain': importChains
.join(MessageTemplate.IMPORT_EXPERIMENTAL_MIRRORS_PADDING)
});
}
}
} else {
if (loadedLibraries.containsLibrary(Uris.dart_mirrors)) {
reporter.reportWarningMessage(NO_LOCATION_SPANNABLE,
MessageKind.MIRRORS_LIBRARY_NOT_SUPPORT_WITH_CFE);
}
}
backend.onLibrariesLoaded(frontendStrategy.commonElements, loadedLibraries);
return loadedLibraries;
}
/**
* Get an [Uri] pointing to a patch for the dart: library with
* the given path. Returns null if there is no patch.
*/
Uri resolvePatchUri(String dartLibraryPath);
Future runInternal(Uri uri) async {
mainLibraryUri = uri;
// TODO(ahe): This prevents memory leaks when invoking the compiler
// multiple times. Implement a better mechanism where we can store
// such caches in the compiler and get access to them through a
// suitably maintained static reference to the current compiler.
StringToken.canonicalizer.clear();
Selector.canonicalizedValues.clear();
// The selector objects held in static fields must remain canonical.
for (Selector selector in Selectors.ALL) {
Selector.canonicalizedValues
.putIfAbsent(selector.hashCode, () => <Selector>[])
.add(selector);
}
assert(uri != null || options.analyzeOnly);
// As far as I can tell, this branch is only used by test code.
if (librariesToAnalyzeWhenRun != null) {
await Future.forEach(librariesToAnalyzeWhenRun, (libraryUri) async {
reporter.log('Analyzing $libraryUri (${options.buildId})');
LoadedLibraries loadedLibraries =
await libraryLoader.loadLibrary(libraryUri);
processLoadedLibraries(loadedLibraries);
});
}
LibraryEntity mainApp;
if (uri != null) {
if (options.analyzeOnly) {
reporter.log('Analyzing $uri (${options.buildId})');
} else {
reporter.log('Compiling $uri (${options.buildId})');
}
LoadedLibraries libraries = await libraryLoader.loadLibrary(uri);
// Note: libraries may be null because of errors trying to find files or
// parse-time errors (when using `package:front_end` as a loader).
if (libraries == null) return;
processLoadedLibraries(libraries);
mainApp = libraries.rootLibrary;
}
compileLoadedLibraries(mainApp);
}
/// Analyze all members of the library in [libraryUri].
///
/// If [skipLibraryWithPartOfTag] is `true`, member analysis is skipped if the
/// library has a `part of` tag, assuming it is a part and not a library.
///
/// This operation assumes an unclosed resolution queue and is only supported
/// when the '--analyze-main' option is used.
Future<LibraryElement> analyzeUri(Uri libraryUri,
{bool skipLibraryWithPartOfTag: true}) async {
phase = PHASE_RESOLVING;
assert(options.analyzeMain);
reporter.log('Analyzing $libraryUri (${options.buildId})');
LoadedLibraries loadedLibraries = await libraryLoader
.loadLibrary(libraryUri, skipFileWithPartOfTag: true);
if (loadedLibraries == null) return null;
processLoadedLibraries(loadedLibraries);
LibraryElement library = loadedLibraries.rootLibrary;
ResolutionEnqueuer resolutionEnqueuer = startResolution();
resolutionEnqueuer.applyImpact(computeImpactForLibrary(library));
emptyQueue(resolutionEnqueuer, onProgress: showResolutionProgress);
resolutionEnqueuer.logSummary(reporter.log);
return library;
}
/// Starts the resolution phase, creating the [ResolutionEnqueuer] if not
/// already created.
///
/// During normal compilation resolution only started once, but through
/// [analyzeUri] resolution is started repeatedly.
ResolutionEnqueuer startResolution() {
ResolutionEnqueuer resolutionEnqueuer;
if (enqueuer.hasResolution) {
resolutionEnqueuer = enqueuer.resolution;
} else {
resolutionEnqueuer = enqueuer.createResolutionEnqueuer();
backend.onResolutionStart();
}
resolutionEnqueuer.addDeferredActions(libraryLoader.pullDeferredActions());
return resolutionEnqueuer;
}
/// Performs the compilation when all libraries have been loaded.
void compileLoadedLibraries(LibraryEntity rootLibrary) =>
selfTask.measureSubtask("Compiler.compileLoadedLibraries", () {
ResolutionEnqueuer resolutionEnqueuer = startResolution();
WorldImpactBuilderImpl mainImpact = new WorldImpactBuilderImpl();
FunctionEntity mainFunction =
frontendStrategy.computeMain(rootLibrary, mainImpact);
if (!options.useKernel) {
// TODO(johnniwinther): Support mirrors usages analysis from dill.
mirrorUsageAnalyzerTask.analyzeUsage(rootLibrary);
}
// In order to see if a library is deferred, we must compute the
// compile-time constants that are metadata. This means adding
// something to the resolution queue. So we cannot wait with
// this until after the resolution queue is processed.
deferredLoadTask.beforeResolution(rootLibrary);
impactStrategy = backend.createImpactStrategy(
supportDeferredLoad: deferredLoadTask.isProgramSplit,
supportDumpInfo: options.dumpInfo,
supportSerialization: serialization.supportSerialization);
phase = PHASE_RESOLVING;
resolutionEnqueuer.applyImpact(mainImpact);
if (options.resolveOnly) {
libraryLoader.libraries.where((LibraryEntity library) {
return !serialization.isDeserialized(library);
}).forEach((LibraryEntity library) {
reporter.log('Enqueuing ${library.canonicalUri}');
resolutionEnqueuer.applyImpact(computeImpactForLibrary(library));
});
} else if (analyzeAll) {
libraryLoader.libraries.forEach((LibraryEntity library) {
reporter.log('Enqueuing ${library.canonicalUri}');
resolutionEnqueuer.applyImpact(computeImpactForLibrary(library));
});
} else if (options.analyzeMain) {
if (rootLibrary != null) {
resolutionEnqueuer
.applyImpact(computeImpactForLibrary(rootLibrary));
}
if (librariesToAnalyzeWhenRun != null) {
for (Uri libraryUri in librariesToAnalyzeWhenRun) {
resolutionEnqueuer.applyImpact(computeImpactForLibrary(
libraryLoader.lookupLibrary(libraryUri)));
}
}
}
if (frontendStrategy.commonElements.mirrorsLibrary != null &&
!options.useKernel) {
// TODO(johnniwinther): Support mirrors from dill.
resolveLibraryMetadata();
}
reporter.log('Resolving...');
processQueue(frontendStrategy.elementEnvironment, resolutionEnqueuer,
mainFunction, libraryLoader.libraries,
onProgress: showResolutionProgress);
backend.onResolutionEnd();
resolutionEnqueuer.logSummary(reporter.log);
_reporter.reportSuppressedMessagesSummary();
if (compilationFailed) {
if (!options.generateCodeWithCompileTimeErrors) {
return;
}
if (mainFunction == null) return;
if (!backend
.enableCodegenWithErrorsIfSupported(NO_LOCATION_SPANNABLE)) {
return;
}
}
if (options.resolveOnly && !compilationFailed) {
reporter.log('Serializing to ${options.resolutionOutput}');
serialization.serializeToSink(
outputProvider.createOutputSink(
'', 'data', api.OutputType.serializationData),
libraryLoader.libraries.where((LibraryEntity library) {
return !serialization.isDeserialized(library);
}));
}
if (options.analyzeOnly) return;
assert(mainFunction != null);
ClosedWorldRefiner closedWorldRefiner = closeResolution(mainFunction);
ClosedWorld closedWorld = closedWorldRefiner.closedWorld;
backendClosedWorldForTesting = closedWorld;
mainFunction = closedWorld.elementEnvironment.mainFunction;
reporter.log('Inferring types...');
globalInference.runGlobalTypeInference(
mainFunction, closedWorld, closedWorldRefiner);
closedWorldRefiner.computeSideEffects();
if (stopAfterTypeInference) return;
reporter.log('Compiling...');
phase = PHASE_COMPILING;
Enqueuer codegenEnqueuer = startCodegen(closedWorld);
if (compileAll) {
libraryLoader.libraries.forEach((LibraryEntity library) {
codegenEnqueuer.applyImpact(computeImpactForLibrary(library));
});
}
processQueue(closedWorld.elementEnvironment, codegenEnqueuer,
mainFunction, libraryLoader.libraries,
onProgress: showCodegenProgress);
codegenEnqueuer.logSummary(reporter.log);
int programSize = backend.assembleProgram(closedWorld);
if (options.dumpInfo) {
dumpInfoTask.reportSize(programSize);
dumpInfoTask.dumpInfo(closedWorld);
}
backend.onCodegenEnd();
checkQueues(resolutionEnqueuer, codegenEnqueuer);
});
Enqueuer startCodegen(ClosedWorld closedWorld) {
Enqueuer codegenEnqueuer = enqueuer.createCodegenEnqueuer(closedWorld);
_codegenWorldBuilder = codegenEnqueuer.worldBuilder;
codegenEnqueuer.applyImpact(backend.onCodegenStart(
closedWorld, _codegenWorldBuilder, backendStrategy.sorter));
return codegenEnqueuer;
}
/// Perform the steps needed to fully end the resolution phase.
ClosedWorldRefiner closeResolution(FunctionEntity mainFunction) {
phase = PHASE_DONE_RESOLVING;
ClosedWorld closedWorld = resolutionWorldBuilder.closeWorld();
ClosedWorldRefiner closedWorldRefiner =
backendStrategy.createClosedWorldRefiner(closedWorld);
// Compute whole-program-knowledge that the backend needs. (This might
// require the information computed in [world.closeWorld].)
backend.onResolutionClosedWorld(closedWorld, closedWorldRefiner);
OutputUnitData result = deferredLoadTask.run(mainFunction, closedWorld);
backend.onDeferredLoadComplete(result);
// TODO(johnniwinther): Move this after rti computation but before
// reflection members computation, and (re-)close the world afterwards.
backendStrategy.closureDataLookup.convertClosures(
enqueuer.resolution.processedEntities, closedWorldRefiner);
return closedWorldRefiner;
}
/// Compute the [WorldImpact] for accessing all elements in [library].
WorldImpact computeImpactForLibrary(LibraryEntity library) {
WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl();
void registerStaticUse(MemberEntity element) {
impactBuilder.registerStaticUse(new StaticUse.directUse(element));
}
void registerStaticElementUse(Element element) {
MemberElement member = element;
impactBuilder.registerStaticUse(new StaticUse.directUse(member));
}
void registerElement(Element element) {
if (element.isClass) {
ClassElement cls = element;
cls.ensureResolved(resolution);
cls.forEachLocalMember(registerStaticElementUse);
impactBuilder.registerTypeUse(new TypeUse.instantiation(cls.rawType));
} else if (element.isTypedef) {
TypedefElement typedef = element;
typedef.ensureResolved(resolution);
} else {
registerStaticElementUse(element);
}
}
if (library is LibraryElement) {
library.implementation.forEachLocalMember(registerElement);
library.imports.forEach((ImportElement import) {
if (import.isDeferred) {
// `import.prefix` and `loadLibrary` may be `null` when the deferred
// import has compile-time errors.
GetterElement loadLibrary = import.prefix?.loadLibrary;
if (loadLibrary != null) {
registerStaticUse(loadLibrary);
}
}
if (serialization.supportSerialization) {
for (MetadataAnnotation metadata in import.metadata) {
metadata.ensureResolved(resolution);
}
}
});
if (serialization.supportSerialization) {
library.exports.forEach((ExportElement export) {
for (MetadataAnnotation metadata in export.metadata) {
metadata.ensureResolved(resolution);
}
});
library.compilationUnits.forEach((CompilationUnitElement unit) {
for (MetadataAnnotation metadata in unit.metadata) {
metadata.ensureResolved(resolution);
}
});
}
} else {
ElementEnvironment elementEnvironment =
frontendStrategy.elementEnvironment;
elementEnvironment.forEachLibraryMember(library, registerStaticUse);
elementEnvironment.forEachClass(library, (ClassEntity cls) {
impactBuilder.registerTypeUse(
new TypeUse.instantiation(elementEnvironment.getRawType(cls)));
elementEnvironment.forEachLocalClassMember(cls, registerStaticUse);
});
}
return impactBuilder;
}
// Resolves metadata on library elements. This is necessary in order to
// resolve metadata classes referenced only from metadata on library tags.
// TODO(ahe): Figure out how to do this lazily.
void resolveLibraryMetadata() {
assert(frontendStrategy.commonElements.mirrorsLibrary != null);
for (LibraryElement library in libraryLoader.libraries) {
if (library.metadata != null) {
for (MetadataAnnotation metadata in library.metadata) {
metadata.ensureResolved(resolution);
}
}
}
}
/**
* Empty the [enqueuer] queue.
*/
void emptyQueue(Enqueuer enqueuer, {void onProgress(Enqueuer enqueuer)}) {
selfTask.measureSubtask("Compiler.emptyQueue", () {
enqueuer.forEach((WorkItem work) {
if (onProgress != null) {
onProgress(enqueuer);
}
reporter.withCurrentElement(
work.element,
() => selfTask.measureSubtask("world.applyImpact", () {
enqueuer.applyImpact(
selfTask.measureSubtask("work.run", () => work.run()),
impactSource: work.element);
}));
});
});
}
void processQueue(ElementEnvironment elementEnvironment, Enqueuer enqueuer,
FunctionEntity mainMethod, Iterable<LibraryEntity> libraries,
{void onProgress(Enqueuer enqueuer)}) {
selfTask.measureSubtask("Compiler.processQueue", () {
enqueuer.open(impactStrategy, mainMethod, libraries);
progress.startPhase();
emptyQueue(enqueuer, onProgress: onProgress);
enqueuer.queueIsClosed = true;
enqueuer.close();
// Notify the impact strategy impacts are no longer needed for this
// enqueuer.
impactStrategy.onImpactUsed(enqueuer.impactUse);
assert(compilationFailed ||
enqueuer.checkNoEnqueuedInvokedInstanceMethods(elementEnvironment));
});
}
/**
* Perform various checks of the queues. This includes checking that
* the queues are empty (nothing was added after we stopped
* processing the queues). Also compute the number of methods that
* were resolved, but not compiled (aka excess resolution).
*/
checkQueues(Enqueuer resolutionEnqueuer, Enqueuer codegenEnqueuer) {
for (Enqueuer enqueuer in [resolutionEnqueuer, codegenEnqueuer]) {
enqueuer.checkQueueIsEmpty();
}
if (!REPORT_EXCESS_RESOLUTION) return;
var resolved = new Set.from(resolutionEnqueuer.processedEntities);
for (MemberEntity e in codegenEnqueuer.processedEntities) {
resolved.remove(e);
}
for (MemberEntity e in new Set.from(resolved)) {
if (e.isField) {
resolved.remove(e);
}
if (e.isConstructor && (e as ConstructorEntity).isGenerativeConstructor) {
resolved.remove(e);
}
if (backend.isTargetSpecificLibrary(e.library)) {
resolved.remove(e);
}
}
reporter.log('Excess resolution work: ${resolved.length}.');
for (MemberEntity e in resolved) {
reporter.reportWarningMessage(e, MessageKind.GENERIC,
{'text': 'Warning: $e resolved but not compiled.'});
}
}
void showResolutionProgress(Enqueuer enqueuer) {
assert(phase == PHASE_RESOLVING, 'Unexpected phase: $phase');
progress.showProgress(
'Resolved ', enqueuer.processedEntities.length, ' elements.');
}
void showCodegenProgress(Enqueuer enqueuer) {
progress.showProgress(
'Compiled ', enqueuer.processedEntities.length, ' methods.');
}
void reportDiagnostic(DiagnosticMessage message,
List<DiagnosticMessage> infos, api.Diagnostic kind);
void reportCrashInUserCode(String message, exception, stackTrace) {
reporter.onCrashInUserCode(message, exception, stackTrace);
}
/// Messages for which compile-time errors are reported but compilation
/// continues regardless.
static const List<MessageKind> BENIGN_ERRORS = const <MessageKind>[
MessageKind.INVALID_METADATA,
MessageKind.INVALID_METADATA_GENERIC,
];
bool markCompilationAsFailed(DiagnosticMessage message, api.Diagnostic kind) {
if (options.testMode) {
// When in test mode, i.e. on the build-bot, we always stop compilation.
return true;
}
if (reporter.options.fatalWarnings) {
return true;
}
return !BENIGN_ERRORS.contains(message.message.kind);
}
void fatalDiagnosticReported(DiagnosticMessage message,
List<DiagnosticMessage> infos, api.Diagnostic kind) {
if (markCompilationAsFailed(message, kind)) {
compilationFailed = true;
}
registerCompileTimeError(currentElement, message);
}
/**
* Reads the script specified by the [readableUri].
*
* See [LibraryLoader] for terminology on URIs.
*/
Future<Script> readScript(Uri readableUri, [Spannable node]) {
throw failedAt(node, 'Compiler.readScript not implemented.');
}
Future<Binary> readBinary(Uri readableUri, [Spannable node]) {
throw failedAt(node, 'Compiler.readBinary not implemented.');
}
Element lookupElementIn(ScopeContainerElement container, String name) {
Element element = container.localLookup(name);
if (element == null) {
throw 'Could not find $name in $container';
}
return element;
}
bool get isMockCompilation => false;
/// Helper for determining whether the current element is declared within
/// 'user code'.
///
/// See [inUserCode] for what defines 'user code'.
bool currentlyInUserCode() {
return inUserCode(currentElement);
}
/// Helper for determining whether [element] is declared within 'user code'.
///
/// What constitutes 'user code' is defined by the URI(s) provided by the
/// entry point(s) of compilation or analysis:
///
/// If an entrypoint URI uses the 'package' scheme then every library from
/// that same package is considered to be in user code. For instance, if
/// an entry point URI is 'package:foo/bar.dart' then every library whose
/// canonical URI starts with 'package:foo/' is in user code.
///
/// If an entrypoint URI uses another scheme than 'package' then every library
/// with that scheme is in user code. For instance, an entry point URI is
/// 'file:///foo.dart' then every library whose canonical URI scheme is
/// 'file' is in user code.
///
/// If [assumeInUserCode] is `true`, [element] is assumed to be in user code
/// if no entrypoints have been set.
bool inUserCode(Entity element, {bool assumeInUserCode: false}) {
if (element == null) return assumeInUserCode;
Uri libraryUri = _uriFromElement(element);
if (libraryUri == null) return false;
Iterable<CodeLocation> userCodeLocations =
computeUserCodeLocations(assumeInUserCode: assumeInUserCode);
return userCodeLocations.any(
(CodeLocation codeLocation) => codeLocation.inSameLocation(libraryUri));
}
Iterable<CodeLocation> computeUserCodeLocations(
{bool assumeInUserCode: false}) {
List<CodeLocation> userCodeLocations = <CodeLocation>[];
if (mainLibraryUri != null) {
userCodeLocations.add(new CodeLocation(mainLibraryUri));
}
if (librariesToAnalyzeWhenRun != null) {
userCodeLocations.addAll(
librariesToAnalyzeWhenRun.map((Uri uri) => new CodeLocation(uri)));
}
if (userCodeLocations.isEmpty && assumeInUserCode) {
// Assume in user code since [mainApp] has not been set yet.
userCodeLocations.add(const AnyLocation());
}
return userCodeLocations;
}
/// Return a canonical URI for the source of [element].
///
/// For a package library with canonical URI 'package:foo/bar/baz.dart' the
/// return URI is 'package:foo'. For non-package libraries the returned URI is
/// the canonical URI of the library itself.
Uri getCanonicalUri(Entity element) {
Uri libraryUri = _uriFromElement(element);
if (libraryUri == null) return null;
if (libraryUri.scheme == 'package') {
int slashPos = libraryUri.path.indexOf('/');
if (slashPos != -1) {
String packageName = libraryUri.path.substring(0, slashPos);
return new Uri(scheme: 'package', path: packageName);
}
}
return libraryUri;
}
Uri _uriFromElement(Entity element) {
if (element is LibraryEntity) {
return element.canonicalUri;
} else if (element is ClassEntity) {
return element.library.canonicalUri;
} else if (element is MemberEntity) {
return element.library.canonicalUri;
} else if (element is Element) {
return element.library.canonicalUri;
}
return null;
}
/// Returns [true] if a compile-time error has been reported for element.
bool elementHasCompileTimeError(Entity element) {
return elementsWithCompileTimeErrors.containsKey(element);
}
/// Associate [element] with a compile-time error [message].
void registerCompileTimeError(Entity element, DiagnosticMessage message) {
// The information is only needed if [generateCodeWithCompileTimeErrors].
if (options.generateCodeWithCompileTimeErrors) {
if (element == null) {
// Record as global error.
// TODO(zarah): Extend element model to represent compile-time
// errors instead of using a map.
element = frontendStrategy.elementEnvironment.mainFunction;
}
elementsWithCompileTimeErrors
.putIfAbsent(element, () => <DiagnosticMessage>[])
.add(message);
}
}
}
class _CompilerOutput implements api.CompilerOutput {
final Compiler _compiler;
final api.CompilerOutput _userOutput;
_CompilerOutput(this._compiler, api.CompilerOutput output)
: this._userOutput = output ?? const NullCompilerOutput();
@override
api.OutputSink createOutputSink(
String name, String extension, api.OutputType type) {
if (_compiler.compilationFailed) {
if (!_compiler.options.generateCodeWithCompileTimeErrors ||
_compiler.options.testMode) {
// Disable output in test mode: The build bot currently uses the time
// stamp of the generated file to determine whether the output is
// up-to-date.
return NullSink.outputProvider(name, extension, type);
}
}
return _userOutput.createOutputSink(name, extension, type);
}
}
/// Information about suppressed warnings and hints for a given library.
class SuppressionInfo {
int warnings = 0;
int hints = 0;
}
class CompilerDiagnosticReporter extends DiagnosticReporter {
final Compiler compiler;
final DiagnosticOptions options;
Entity _currentElement;
bool hasCrashed = false;
/// `true` if the last diagnostic was filtered, in which case the
/// accompanying info message should be filtered as well.
bool lastDiagnosticWasFiltered = false;
/// Map containing information about the warnings and hints that have been
/// suppressed for each library.
Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{};
CompilerDiagnosticReporter(this.compiler, this.options);
Entity get currentElement => _currentElement;
DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
[Map arguments = const {}]) {
SourceSpan span = spanFromSpannable(spannable);
MessageTemplate template = MessageTemplate.TEMPLATES[messageKind];
Message message = template.message(arguments, options.terseDiagnostics);
return new DiagnosticMessage(span, spannable, message);
}
void reportError(DiagnosticMessage message,
[List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR);
}
void reportWarning(DiagnosticMessage message,
[List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING);
}
void reportHint(DiagnosticMessage message,
[List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
reportDiagnosticInternal(message, infos, api.Diagnostic.HINT);
}
@deprecated
void reportInfo(Spannable node, MessageKind messageKind,
[Map arguments = const {}]) {
reportDiagnosticInternal(createMessage(node, messageKind, arguments),
const <DiagnosticMessage>[], api.Diagnostic.INFO);
}
void reportDiagnosticInternal(DiagnosticMessage message,
List<DiagnosticMessage> infos, api.Diagnostic kind) {
if (!options.showAllPackageWarnings &&
message.spannable != NO_LOCATION_SPANNABLE) {
switch (kind) {
case api.Diagnostic.WARNING:
case api.Diagnostic.HINT:
Entity element = elementFromSpannable(message.spannable);
if (!compiler.inUserCode(element, assumeInUserCode: true)) {
Uri uri = compiler.getCanonicalUri(element);
if (options.showPackageWarningsFor(uri)) {
reportDiagnostic(message, infos, kind);
return;
}
SuppressionInfo info = suppressedWarnings.putIfAbsent(
uri, () => new SuppressionInfo());
if (kind == api.Diagnostic.WARNING) {
info.warnings++;
} else {
info.hints++;
}
lastDiagnosticWasFiltered = true;
return;
}
break;
case api.Diagnostic.INFO:
if (lastDiagnosticWasFiltered) {
return;
}
break;
}
}
lastDiagnosticWasFiltered = false;
reportDiagnostic(message, infos, kind);
}
void reportDiagnostic(DiagnosticMessage message,
List<DiagnosticMessage> infos, api.Diagnostic kind) {
compiler.reportDiagnostic(message, infos, kind);
if (kind == api.Diagnostic.ERROR ||
kind == api.Diagnostic.CRASH ||
(options.fatalWarnings && kind == api.Diagnostic.WARNING)) {
Entity errorElement;
if (message.spannable is Entity) {
errorElement = message.spannable;
} else {
errorElement = currentElement;
}
compiler.registerCompileTimeError(errorElement, message);
compiler.fatalDiagnosticReported(message, infos, kind);
}
}
@override
bool get hasReportedError => compiler.compilationFailed;
/**
* Perform an operation, [f], returning the return value from [f]. If an
* error occurs then report it as having occurred during compilation of
* [element]. Can be nested.
*/
withCurrentElement(Entity element, f()) {
Entity old = currentElement;
_currentElement = element;
try {
return f();
} on SpannableAssertionFailure catch (ex) {
if (!hasCrashed) {
reportAssertionFailure(ex);
pleaseReportCrash();
}
hasCrashed = true;
rethrow;
} on StackOverflowError {
// We cannot report anything useful in this case, because we
// do not have enough stack space.
rethrow;
} catch (ex) {
if (hasCrashed) rethrow;
try {
unhandledExceptionOnElement(element);
} catch (doubleFault) {
// Ignoring exceptions in exception handling.
}
rethrow;
} finally {
_currentElement = old;
}
}
void reportAssertionFailure(SpannableAssertionFailure ex) {
String message =
(ex.message != null) ? tryToString(ex.message) : tryToString(ex);
reportDiagnosticInternal(
createMessage(ex.node, MessageKind.GENERIC, {'text': message}),
const <DiagnosticMessage>[],
api.Diagnostic.CRASH);
}
/// Using [frontendStrategy] to compute a [SourceSpan] from spannable using
/// the [currentElement] as context.
SourceSpan _spanFromStrategy(Spannable spannable) {
SourceSpan span;
if (compiler.phase == Compiler.PHASE_COMPILING) {
span =
compiler.backendStrategy.spanFromSpannable(spannable, currentElement);
} else {
span = compiler.frontendStrategy
.spanFromSpannable(spannable, currentElement);
}
if (span != null) return span;
throw 'No error location.';
}
SourceSpan spanFromSpannable(Spannable spannable) {
if (spannable == CURRENT_ELEMENT_SPANNABLE) {
spannable = currentElement;
} else if (spannable == NO_LOCATION_SPANNABLE) {
if (currentElement == null) return null;
spannable = currentElement;
}
if (spannable is SourceSpan) {
return spannable;
} else if (spannable is HInstruction) {
Entity element = spannable.sourceElement;
if (element == null) element = currentElement;
SourceInformation position = spannable.sourceInformation;
if (position != null) return position.sourceSpan;
return _spanFromStrategy(element);
} else {
return _spanFromStrategy(spannable);
}
}
// TODO(johnniwinther): Move this to the parser listeners.
@override
SourceSpan spanFromToken(Token token) {
if (compiler.frontendStrategy is ResolutionFrontEndStrategy) {
ResolutionFrontEndStrategy strategy = compiler.frontendStrategy;
return strategy.spanFromToken(currentElement, token);
}
throw 'No error location.';
}
Element _elementFromHInstruction(HInstruction instruction) {
return instruction.sourceElement is Element
? instruction.sourceElement
: null;
}
internalError(Spannable spannable, reason) {
String message = tryToString(reason);
reportDiagnosticInternal(
createMessage(spannable, MessageKind.GENERIC, {'text': message}),
const <DiagnosticMessage>[],
api.Diagnostic.CRASH);
throw 'Internal Error: $message';
}
void unhandledExceptionOnElement(Entity element) {
if (hasCrashed) return;
hasCrashed = true;
reportDiagnostic(createMessage(element, MessageKind.COMPILER_CRASHED),
const <DiagnosticMessage>[], api.Diagnostic.CRASH);
pleaseReportCrash();
}
void pleaseReportCrash() {
print(MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH]
.message({'buildId': compiler.options.buildId}));
}
/// Finds the approximate [Element] for [node]. [currentElement] is used as
/// the default value.
Entity elementFromSpannable(Spannable node) {
Entity element;
if (node is Entity) {
element = node;
} else if (node is HInstruction) {
element = _elementFromHInstruction(node);
} else if (node is MetadataAnnotation) {
element = node.annotatedElement;
}
return element != null ? element : currentElement;
}
void log(message) {
Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC]
.message({'text': '$message'});
reportDiagnostic(new DiagnosticMessage(null, null, msg),
const <DiagnosticMessage>[], api.Diagnostic.VERBOSE_INFO);
}
String tryToString(object) {
try {
return object.toString();
} catch (_) {
return '<exception in toString()>';
}
}
onError(Uri uri, error) {
try {
if (!hasCrashed) {
hasCrashed = true;
if (error is SpannableAssertionFailure) {
reportAssertionFailure(error);
} else {
reportDiagnostic(
createMessage(
new SourceSpan(uri, 0, 0), MessageKind.COMPILER_CRASHED),
const <DiagnosticMessage>[],
api.Diagnostic.CRASH);
}
pleaseReportCrash();
}
} catch (doubleFault) {
// Ignoring exceptions in exception handling.
}
throw error;
}
@override
void onCrashInUserCode(String message, exception, stackTrace) {
hasCrashed = true;
print('$message: ${tryToString(exception)}');
print(tryToString(stackTrace));
}
void reportSuppressedMessagesSummary() {
if (!options.showAllPackageWarnings && !options.suppressWarnings) {
suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
if (info.warnings == 0) {
kind = MessageKind.HIDDEN_HINTS;
} else if (info.hints == 0) {
kind = MessageKind.HIDDEN_WARNINGS;
}
MessageTemplate template = MessageTemplate.TEMPLATES[kind];
Message message = template.message(
{'warnings': info.warnings, 'hints': info.hints, 'uri': uri},
options.terseDiagnostics);
reportDiagnostic(new DiagnosticMessage(null, null, message),
const <DiagnosticMessage>[], api.Diagnostic.HINT);
});
}
}
}
// TODO(johnniwinther): Move [ResolverTask] here.
class CompilerResolution implements Resolution, ImpactCacheDeleter {
final Compiler _compiler;
final Map<Element, ResolutionImpact> _resolutionImpactCache =
<Element, ResolutionImpact>{};
final Map<Entity, WorldImpact> _worldImpactCache = <Entity, WorldImpact>{};
bool retainCachesForTesting = false;
Types _types;
CompilerResolution(this._compiler) {
_types = new Types(this);
}
@override
DiagnosticReporter get reporter => _compiler.reporter;
@override
ParsingContext get parsingContext => _compiler.parsingContext;
@override
ElementEnvironment get elementEnvironment =>
_compiler.frontendStrategy.elementEnvironment;
@override
CommonElements get commonElements =>
_compiler.frontendStrategy.commonElements;
@override
Types get types => _types;
@override
Target get target => _compiler.backend.target;
@override
ResolverTask get resolver => _compiler.resolver;
@override
ResolutionEnqueuer get enqueuer => _compiler.enqueuer.resolution;
@override
CompilerOptions get options => _compiler.options;
@override
IdGenerator get idGenerator => _compiler.idGenerator;
@override
ConstantEnvironment get constants => _compiler.constants;
@override
MirrorUsageAnalyzerTask get mirrorUsageAnalyzerTask =>
_compiler.mirrorUsageAnalyzerTask;
LibraryElement get coreLibrary =>
_compiler.frontendStrategy.commonElements.coreLibrary;
@override
bool get wasProxyConstantComputedTestingOnly => _proxyConstant != null;
@override
void resolveClass(ClassElement cls) {
_compiler.resolver.resolveClass(cls);
}
@override
void resolveTypedef(TypedefElement typdef) {
_compiler.resolver.resolve(typdef);
}
@override
void resolveMetadataAnnotation(MetadataAnnotation metadataAnnotation) {
_compiler.resolver.resolveMetadataAnnotation(metadataAnnotation);
}
@override
FunctionSignature resolveSignature(FunctionElement function) {
return _compiler.resolver.resolveSignature(function);
}
@override
ResolutionDartType resolveTypeAnnotation(
Element element, TypeAnnotation node) {
return _compiler.resolver.resolveTypeAnnotation(element, node);
}
@override
void ensureResolved(Element element) {
if (_compiler.serialization.isDeserialized(element)) {
return;
}
computeWorldImpact(element);
}
@override
void ensureClassMembers(ClassElement element) {
if (!_compiler.serialization.isDeserialized(element)) {
_compiler.resolver.checkClass(element);
}
}
@override
void registerCompileTimeError(Element element, DiagnosticMessage message) =>
_compiler.registerCompileTimeError(element, message);
@override
bool hasResolvedAst(ExecutableElement element) {
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
if (_compiler.serialization.isDeserialized(element)) {
return _compiler.serialization.hasResolvedAst(element);
}
return hasBeenResolved(element.memberContext.declaration) &&
element.hasResolvedAst;
}
@override
ResolvedAst getResolvedAst(ExecutableElement element) {
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
assert(hasResolvedAst(element),
failedAt(element, "ResolvedAst not available for $element."));
if (_compiler.serialization.isDeserialized(element)) {
return _compiler.serialization.getResolvedAst(element);
}
return element.resolvedAst;
}
@override
ResolvedAst computeResolvedAst(Element element) {
ensureResolved(element);
return getResolvedAst(element);
}
@override
bool hasResolutionImpact(Element element) {
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
if (_compiler.serialization.isDeserialized(element)) {
return _compiler.serialization.hasResolutionImpact(element);
}
return _resolutionImpactCache.containsKey(element);
}
@override
ResolutionImpact getResolutionImpact(Element element) {
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
ResolutionImpact resolutionImpact;
if (_compiler.serialization.isDeserialized(element)) {
resolutionImpact = _compiler.serialization.getResolutionImpact(element);
} else {
resolutionImpact = _resolutionImpactCache[element];
}
assert(resolutionImpact != null,
failedAt(element, "ResolutionImpact not available for $element."));
return resolutionImpact;
}
@override
WorldImpact getWorldImpact(Element element) {
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
WorldImpact worldImpact = _worldImpactCache[element];
assert(worldImpact != null,
failedAt(element, "WorldImpact not computed for $element."));
return worldImpact;
}
@override
WorldImpact computeWorldImpact(Element element) {
return _compiler.selfTask.measureSubtask("Resolution.computeWorldImpact",
() {
assert(
element.impliesType ||
element.isField ||
element.isFunction ||
element.isConstructor ||
element.isGetter ||
element.isSetter,
failedAt(element, 'Unexpected element kind: ${element.kind}'));
// `true ==` prevents analyzer type inference from strengthening element
// to AnalyzableElement which incompatible with some down-cast to ElementX
// uses.
// TODO(29712): Can this be made to work as we expect?
assert(true == element is AnalyzableElement,
failedAt(element, 'Element $element is not analyzable.'));
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
return _worldImpactCache.putIfAbsent(element, () {
assert(_compiler.parser != null);
Node tree = _compiler.parser.parse(element);
assert(!element.isSynthesized || tree == null, failedAt(element));
ResolutionImpact resolutionImpact = _compiler.resolver.resolve(element);
if (_compiler.serialization.supportSerialization ||
retainCachesForTesting) {
// [ResolutionImpact] is currently only used by serialization. The
// enqueuer uses the [WorldImpact] which is always cached.
// TODO(johnniwinther): Align these use cases better; maybe only
// cache [ResolutionImpact] and let the enqueuer transform it into
// a [WorldImpact].
_resolutionImpactCache[element] = resolutionImpact;
}
if (tree != null && !_compiler.options.analyzeSignaturesOnly) {
// TODO(het): don't do this if suppressWarnings is on, currently we
// have to do it because the typechecker also sets types
// Only analyze nodes with a corresponding [TreeElements].
_compiler.checker.check(element);
}
return transformResolutionImpact(element, resolutionImpact);
});
});
}
@override
WorldImpact transformResolutionImpact(
Element element, ResolutionImpact resolutionImpact) {
WorldImpact worldImpact = _compiler.backend.impactTransformer
.transformResolutionImpact(resolutionImpact);
_worldImpactCache[element] = worldImpact;
return worldImpact;
}
@override
void uncacheWorldImpact(covariant Element element) {
assert(element.isDeclaration,
failedAt(element, "Element $element must be the declaration."));
if (retainCachesForTesting) return;
if (_compiler.serialization.isDeserialized(element)) return;
assert(_worldImpactCache[element] != null,
failedAt(element, "WorldImpact not computed for $element."));
_worldImpactCache[element] = const WorldImpact();
_resolutionImpactCache.remove(element);
}
@override
void emptyCache() {
if (retainCachesForTesting) return;
for (Element element in _worldImpactCache.keys) {
_worldImpactCache[element] = const WorldImpact();
}
_resolutionImpactCache.clear();
}
@override
bool hasBeenResolved(Element element) {
return _worldImpactCache.containsKey(element);
}
@override
ResolutionWorkItem createWorkItem(MemberElement element) {
if (_compiler.serialization.isDeserialized(element)) {
return _compiler.serialization.createResolutionWorkItem(element);
} else {
return new ResolutionWorkItem(this, element);
}
}
ConstantValue _proxyConstant;
@override
bool isProxyConstant(ConstantValue value) {
FieldElement field = coreLibrary.find('proxy');
if (field == null) return false;
if (!hasBeenResolved(field)) return false;
if (_proxyConstant == null) {
_proxyConstant = constants
.getConstantValue(resolver.constantCompiler.compileConstant(field));
}
return _proxyConstant == value;
}
@override
void registerClass(ClassEntity cls) {
enqueuer.worldBuilder.registerClass(cls);
}
}
class _MapImpactCacheDeleter implements ImpactCacheDeleter {
final Map<Entity, WorldImpact> _impactCache;
_MapImpactCacheDeleter(this._impactCache);
bool retainCachesForTesting = false;
void uncacheWorldImpact(Entity element) {
if (retainCachesForTesting) return;
_impactCache.remove(element);
}
void emptyCache() {
if (retainCachesForTesting) return;
_impactCache.clear();
}
}
class _ScriptLoader implements ScriptLoader {
Compiler compiler;
_ScriptLoader(this.compiler);
Future<Script> readScript(Uri uri, [Spannable spannable]) =>
compiler.readScript(uri, spannable);
Future<Binary> readBinary(Uri uri, [Spannable spannable]) =>
compiler.readBinary(uri, spannable);
}
/// [ScriptLoader] used to ensure that scripts are not loaded accidentally
/// through the [LibraryLoader] when `CompilerOptions.compileOnly` is `true`.
class _NoScriptLoader implements ScriptLoader {
Compiler compiler;
_NoScriptLoader(this.compiler);
Future<Script> readScript(Uri uri, [Spannable spannable]) {
throw compiler.reporter
.internalError(spannable, "Script loading of '$uri' is not enabled.");
}
Future<Binary> readBinary(Uri uri, [Spannable spannable]) {
throw compiler.reporter
.internalError(spannable, "Script loading of '$uri' is not enabled.");
}
}
class _ElementScanner implements ElementScanner {
ScannerTask scanner;
_ElementScanner(this.scanner);
void scanLibrary(LibraryElement library) => scanner.scanLibrary(library);
void scanUnit(CompilationUnitElement unit) => scanner.scan(unit);
}
class _EmptyEnvironment implements Environment {
const _EmptyEnvironment();
String valueOf(String key) => null;
}
/// Interface for showing progress during compilation.
class Progress {
const Progress();
/// Starts a new phase for which to show progress.
void startPhase() {}
/// Shows progress of the current phase if needed. The shown message is
/// computed as '$prefix$count$suffix'.
void showProgress(String prefix, int count, String suffix) {}
}
/// Progress implementations that prints progress to the [DiagnosticReporter]
/// with 500ms intervals.
class ProgressImpl implements Progress {
final DiagnosticReporter _reporter;
final Stopwatch _stopwatch = new Stopwatch()..start();
ProgressImpl(this._reporter);
void showProgress(String prefix, int count, String suffix) {
if (_stopwatch.elapsedMilliseconds > 500) {
_reporter.log('$prefix$count$suffix');
_stopwatch.reset();
}
}
void startPhase() {
_stopwatch.reset();
}
}