blob: b939d6a34fc96b1b5867bcc023bd4263d2db8a72 [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.
package com.google.dart.compiler;
import com.google.common.base.Objects;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.google.common.io.CharStreams;
import com.google.common.io.Closeables;
import com.google.dart.compiler.CommandLineOptions.CompilerOptions;
import com.google.dart.compiler.LibraryDeps.Dependency;
import com.google.dart.compiler.UnitTestBatchRunner.Invocation;
import com.google.dart.compiler.ast.DartDirective;
import com.google.dart.compiler.ast.DartLibraryDirective;
import com.google.dart.compiler.ast.DartNode;
import com.google.dart.compiler.ast.DartPartOfDirective;
import com.google.dart.compiler.ast.DartToSourceVisitor;
import com.google.dart.compiler.ast.DartUnit;
import com.google.dart.compiler.ast.LibraryExport;
import com.google.dart.compiler.ast.LibraryNode;
import com.google.dart.compiler.ast.LibraryUnit;
import com.google.dart.compiler.ast.Modifiers;
import com.google.dart.compiler.common.SourceInfo;
import com.google.dart.compiler.metrics.CompilerMetrics;
import com.google.dart.compiler.metrics.DartEventType;
import com.google.dart.compiler.metrics.JvmMetrics;
import com.google.dart.compiler.metrics.Tracer;
import com.google.dart.compiler.metrics.Tracer.TraceEvent;
import com.google.dart.compiler.parser.DartParser;
import com.google.dart.compiler.resolver.CompileTimeConstantAnalyzer;
import com.google.dart.compiler.resolver.CoreTypeProvider;
import com.google.dart.compiler.resolver.CoreTypeProviderImplementation;
import com.google.dart.compiler.resolver.Element;
import com.google.dart.compiler.resolver.ElementKind;
import com.google.dart.compiler.resolver.Elements;
import com.google.dart.compiler.resolver.LibraryElement;
import com.google.dart.compiler.resolver.MemberBuilder;
import com.google.dart.compiler.resolver.MethodElement;
import com.google.dart.compiler.resolver.Resolver;
import com.google.dart.compiler.resolver.ResolverErrorCode;
import com.google.dart.compiler.resolver.SupertypeResolver;
import com.google.dart.compiler.resolver.TopLevelElementBuilder;
import com.google.dart.compiler.type.TypeAnalyzer;
import com.google.dart.compiler.util.DefaultTextOutput;
import com.google.dart.compiler.util.apache.StringUtils;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Entry point for the Dart compiler.
*/
public class DartCompiler {
static final int RESULT_OK = 0;
static final int RESULT_WARNINGS = 1;
static final int RESULT_ERRORS = 2;
static final int RESULT_OTHER = 127;
static class Result {
final int code;
final String message;
public Result(int code, String message) {
this.code = code;
this.message = message;
}
Result merge(Result other) {
if (other.code > code) {
return other;
}
return this;
}
}
public static final String EXTENSION_DEPS = "deps";
public static final String EXTENSION_LOG = "log";
public static final String EXTENSION_TIMESTAMP = "timestamp";
public static final String CORELIB_URL_SPEC = "dart:core";
public static final String MAIN_ENTRY_POINT_NAME = "main";
private static class Compiler {
private final LibrarySource app;
private final List<LibrarySource> embeddedLibraries = new ArrayList<LibrarySource>();
private final DartCompilerMainContext context;
private final CompilerConfiguration config;
private final Map<URI, LibraryUnit> libraries = new LinkedHashMap<URI, LibraryUnit>();
private CoreTypeProvider typeProvider;
private final boolean incremental;
private final List<DartCompilationPhase> phases;
private final LibrarySource coreLibrarySource;
private Compiler(LibrarySource app, List<LibrarySource> embedded, CompilerConfiguration config,
DartCompilerMainContext context) {
this.app = app;
this.config = config;
this.phases = config.getPhases();
this.context = context;
for (LibrarySource library : embedded) {
if (PackageLibraryManager.isDartSpec(library.getName())) {
LibrarySource foundLibrary = context.getSystemLibraryFor(library.getName());
assert(foundLibrary != null);
embeddedLibraries.add(foundLibrary);
} else {
embeddedLibraries.add(library);
}
}
coreLibrarySource = context.getSystemLibraryFor(CORELIB_URL_SPEC);
assert(coreLibrarySource != null);
embeddedLibraries.add(coreLibrarySource);
incremental = config.incremental();
}
void addResolvedLibraries(Map<URI, LibraryUnit> resolvedLibraries) {
libraries.putAll(resolvedLibraries);
}
Map<URI, LibraryUnit> getLibraries() {
return libraries;
}
private void compile() {
TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE) : null;
try {
updateAndResolve();
if (!config.resolveDespiteParseErrors() && context.getErrorCount() > 0) {
return;
}
compileLibraries();
} catch (IOException e) {
context.onError(new DartCompilationError(app, DartCompilerErrorCode.IO, e.getMessage()));
} finally {
Tracer.end(logEvent);
}
}
/**
* Update the current application and any referenced libraries and resolve
* them.
*
* @return a {@link LibraryUnit}, maybe <code>null</code>
* @throws IOException on IO errors - the caller must log this if it cares
*/
private LibraryUnit updateAndResolve() throws IOException {
TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.UPDATE_RESOLVE) : null;
CompilerMetrics compilerMetrics = context.getCompilerMetrics();
if (compilerMetrics != null) {
compilerMetrics.startUpdateAndResolveTime();
}
try {
LibraryUnit library = updateLibraries(app);
importEmbeddedLibraries();
parseOutOfDateFiles();
if (incremental) {
addOutOfDateDeps();
}
if (!config.resolveDespiteParseErrors() && (context.getErrorCount() > 0)) {
return library;
}
buildLibraryScopes();
LibraryUnit corelibUnit = updateLibraries(coreLibrarySource);
typeProvider = new CoreTypeProviderImplementation(corelibUnit.getElement().getScope(),
context);
resolveLibraries();
validateLibraryDirectives();
return library;
} finally {
if(compilerMetrics != null) {
compilerMetrics.endUpdateAndResolveTime();
}
Tracer.end(logEvent);
}
}
/**
* This method reads all libraries. They will be populated from some combination of fully-parsed
* and diet-parser compilation units.
*/
private void parseOutOfDateFiles() throws IOException {
TraceEvent logEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.PARSE_OUTOFDATE) : null;
CompilerMetrics compilerMetrics = context.getCompilerMetrics();
long parseStart = compilerMetrics != null ? CompilerMetrics.getCPUTime() : 0;
try {
final Set<String> topLevelSymbolsDiff = Sets.newHashSet();
for (LibraryUnit lib : getLibrariesToProcess()) {
LibrarySource libSrc = lib.getSource();
LibraryNode selfSourcePath = lib.getSelfSourcePath();
// Load the existing DEPS, or create an empty one.
LibraryDeps deps = lib.getDeps(context);
Set<String> newUnitPaths = Sets.newHashSet();
// Parse each compilation unit.
for (LibraryNode sourcePathNode : lib.getSourcePaths()) {
String relPath = sourcePathNode.getText();
newUnitPaths.add(relPath);
// Prepare DartSource for "#source" unit.
final DartSource dartSrc = libSrc.getSourceFor(relPath);
if (dartSrc == null || !dartSrc.exists()) {
continue;
}
if (!incremental
|| PackageLibraryManager.isDartUri(libSrc.getUri())
|| isSourceOutOfDate(dartSrc)) {
DartUnit unit = parse(dartSrc, lib.getPrefixes(), false);
// If we just parsed unit of library, report problems.
if (sourcePathNode == selfSourcePath) {
// report "#import" problems
for (LibraryNode importPathNode : lib.getImportPaths()) {
LibrarySource dep = getImportSource(libSrc, importPathNode);
if (dep == null) {
reportMissingSource(context, libSrc, importPathNode);
}
}
// report "#source" problems
for (LibraryNode checkSourcePathNode : lib.getSourcePaths()) {
String checkRelPath = checkSourcePathNode.getText();
final DartSource checkSource = libSrc.getSourceFor(checkRelPath);
if (checkSource == null || !checkSource.exists()) {
reportMissingSource(context, libSrc, checkSourcePathNode);
}
}
}
// Process unit, if exists.
if (unit != null) {
if (sourcePathNode == selfSourcePath) {
lib.setSelfDartUnit(unit);
}
// Replace unit within the library.
lib.putUnit(unit);
context.setFilesHaveChanged();
// Include into top-level symbols diff from current units, already existed or new.
{
LibraryDeps.Source source = deps.getSource(relPath);
Set<String> newTopSymbols = unit.getTopDeclarationNames();
if (source != null) {
Set<String> oldTopSymbols = source.getTopSymbols();
SetView<String> diff0 = Sets.symmetricDifference(oldTopSymbols, newTopSymbols);
topLevelSymbolsDiff.addAll(diff0);
} else {
topLevelSymbolsDiff.addAll(newTopSymbols);
}
}
}
} else {
DartUnit dietUnit = parse(dartSrc, lib.getPrefixes(), true);
if (dietUnit != null) {
if (sourcePathNode == selfSourcePath) {
lib.setSelfDartUnit(dietUnit);
}
lib.putUnit(dietUnit);
}
}
}
// Include into top-level symbols diff from units which disappeared since last compiling.
{
Set<String> oldUnitPaths = deps.getUnitPaths();
Set<String> disappearedUnitPaths = Sets.difference(oldUnitPaths, newUnitPaths);
for (String relPath : disappearedUnitPaths) {
LibraryDeps.Source source = deps.getSource(relPath);
if (source != null) {
Set<String> oldTopSymbols = source.getTopSymbols();
topLevelSymbolsDiff.addAll(oldTopSymbols);
}
}
}
}
// Parse units, which potentially depend on the difference in top-level symbols.
if (!topLevelSymbolsDiff.isEmpty()) {
context.setFilesHaveChanged();
for (LibraryUnit lib : getLibrariesToProcess()) {
LibrarySource libSrc = lib.getSource();
LibraryNode selfSourcePath = lib.getSelfSourcePath();
LibraryDeps deps = lib.getDeps(context);
for (LibraryNode libNode : lib.getSourcePaths()) {
String relPath = libNode.getText();
// Prepare source dependency.
LibraryDeps.Source source = deps.getSource(relPath);
if (source == null) {
continue;
}
// Check re-compilation conditions.
if (source.shouldRecompileOnAnyTopLevelChange()
|| !Sets.intersection(source.getAllSymbols(), topLevelSymbolsDiff).isEmpty()
|| !Sets.intersection(source.getHoles(), topLevelSymbolsDiff).isEmpty()) {
DartSource dartSrc = libSrc.getSourceFor(relPath);
if (dartSrc == null || !dartSrc.exists()) {
continue;
}
DartUnit unit = parse(dartSrc, lib.getPrefixes(), false);
if (unit != null) {
if (libNode == selfSourcePath) {
lib.setSelfDartUnit(unit);
} else {
lib.putUnit(unit);
}
}
}
}
}
}
} finally {
if (compilerMetrics != null) {
compilerMetrics.addParseWallTimeNano(CompilerMetrics.getCPUTime() - parseStart);
}
Tracer.end(logEvent);
}
}
Collection<LibraryUnit> getLibrariesToProcess() {
return libraries.values();
}
/**
* This method reads the embedded library sources, making sure they are added
* to the list of libraries to compile. It then adds the libraries as imports
* of all libraries. The import is without prefix.
*/
private void importEmbeddedLibraries() throws IOException {
TraceEvent importEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.IMPORT_EMBEDDED_LIBRARIES) : null;
try {
for (LibrarySource embedded : embeddedLibraries) {
updateLibraries(embedded);
}
for (LibraryUnit lib : getLibrariesToProcess()) {
for (LibrarySource embedded : embeddedLibraries) {
LibraryUnit imp = libraries.get(embedded.getUri());
// Check that the current library is not the embedded library, and
// that the current library does not already import the embedded
// library.
if (lib != imp && !lib.hasImport(imp)) {
lib.addImport(imp, null);
}
}
}
} finally {
Tracer.end(importEvent);
}
}
/**
* This method reads a library source and sets it up with its imports. When it
* completes, it is guaranteed that {@link Compiler#libraries} will be completely populated.
*/
private LibraryUnit updateLibraries(LibrarySource libSrc) throws IOException {
TraceEvent updateEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.UPDATE_LIBRARIES, "name",
libSrc.getName()) : null;
try {
// Avoid cycles.
LibraryUnit lib = libraries.get(libSrc.getUri());
if (lib != null) {
return lib;
}
lib = context.getLibraryUnit(libSrc);
// If we could not find the library, continue. The context will report
// the error at the end.
if (lib == null) {
return null;
}
libraries.put(libSrc.getUri(), lib);
// Update dependencies.
for (LibraryNode libNode : lib.getImportPaths()) {
LibrarySource dep = getImportSource(libSrc, libNode);
if (dep != null) {
LibraryUnit importedLib = updateLibraries(dep);
lib.addImport(importedLib, libNode);
if (libNode.isExported()) {
lib.addExport(importedLib, libNode);
}
}
}
for (LibraryNode libNode : lib.getExportPaths()) {
LibrarySource dep = getImportSource(libSrc, libNode);
if (dep != null) {
lib.addExport(updateLibraries(dep), libNode);
}
}
return lib;
} finally {
Tracer.end(updateEvent);
}
}
/**
* @return the {@link LibrarySource} referenced in the "#import" from "libSrc". May be
* <code>null</code> if invalid URI or not existing library.
*/
private LibrarySource getImportSource(LibrarySource libSrc, LibraryNode libNode)
throws IOException {
String libSpec = libNode.getText();
LibrarySource dep;
try {
if (PackageLibraryManager.isDartSpec(libSpec)) {
dep = context.getSystemLibraryFor(libSpec);
} else {
dep = libSrc.getImportFor(libSpec);
}
} catch (Throwable e) {
return null;
}
if (dep == null || !dep.exists()) {
return null;
}
return dep;
}
/**
* Determines whether the given source is out-of-date with respect to its artifacts.
*/
private boolean isSourceOutOfDate(DartSource dartSrc) {
TraceEvent logEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.IS_SOURCE_OUTOFDATE, "src",
dartSrc.getName()) : null;
try {
// If incremental compilation is disabled, just return true to force all
// units to be recompiled.
if (!incremental) {
return true;
}
TraceEvent timestampEvent =
Tracer.canTrace() ? Tracer.start(
DartEventType.TIMESTAMP_OUTOFDATE,
"src",
dartSrc.getName()) : null;
try {
return context.isOutOfDate(dartSrc, dartSrc, EXTENSION_TIMESTAMP);
} finally {
Tracer.end(timestampEvent);
}
} finally {
Tracer.end(logEvent);
}
}
/**
* Build scopes for the given libraries.
*/
private void buildLibraryScopes() {
TraceEvent logEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.BUILD_LIB_SCOPES) : null;
try {
Collection<LibraryUnit> libs = getLibrariesToProcess();
// Build the class elements declared in the sources of a library.
// Loop can be parallelized.
for (LibraryUnit lib : libs) {
new TopLevelElementBuilder().exec(lib, context);
}
// The library scope can then be constructed, containing types declared
// in the library, and types declared in the imports. Loop can be parallelized.
for (LibraryUnit lib : libs) {
new TopLevelElementBuilder().fillInLibraryScope(lib, context);
}
} finally {
Tracer.end(logEvent);
}
}
/**
* Parses compilation units that are out-of-date with respect to their dependencies.
*/
private void addOutOfDateDeps() throws IOException {
TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.ADD_OUTOFDATE) : null;
try {
boolean filesHaveChanged = false;
for (LibraryUnit lib : getLibrariesToProcess()) {
// Load the existing DEPS, or create an empty one.
LibraryDeps deps = lib.getDeps(context);
// Prepare all top-level symbols.
Set<String> oldTopLevelSymbols = Sets.newHashSet();
for (LibraryDeps.Source source : deps.getSources()) {
oldTopLevelSymbols.addAll(source.getTopSymbols());
}
// Parse units that are out-of-date with respect to their dependencies.
for (DartUnit unit : lib.getUnits()) {
if (unit.isDiet()) {
String relPath = ((DartSource) unit.getSourceInfo().getSource()).getRelativePath();
LibraryDeps.Source source = deps.getSource(relPath);
if (isUnitOutOfDate(lib, source)) {
filesHaveChanged = true;
DartSource dartSrc = lib.getSource().getSourceFor(relPath);
if (dartSrc != null && dartSrc.exists()) {
unit = parse(dartSrc, lib.getPrefixes(), false);
if (unit != null) {
lib.putUnit(unit);
}
}
}
}
}
}
if (filesHaveChanged) {
context.setFilesHaveChanged();
}
} finally {
Tracer.end(logEvent);
}
}
/**
* Determines whether the given dependencies are out-of-date.
*/
private boolean isUnitOutOfDate(LibraryUnit lib, LibraryDeps.Source source) {
// If we don't have dependency information, then we can not be sure that nothing changed.
if (source == null) {
return true;
}
// Check all dependencies.
for (Dependency dep : source.getDeps()) {
LibraryUnit depLib = libraries.get(dep.getLibUri());
if (depLib == null) {
return true;
}
// Prepare unit.
DartUnit depUnit = depLib.getUnit(dep.getUnitName());
if (depUnit == null) {
return true;
}
// May be unit modified.
if (depUnit.getSourceInfo().getSource().getLastModified() != dep.getLastModified()) {
return true;
}
}
// No changed dependencies.
return false;
}
/**
* Resolve all libraries. Assume that all library scopes are already built.
*/
private void resolveLibraries() {
TraceEvent logEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.RESOLVE_LIBRARIES) : null;
try {
// TODO(jgw): Optimization: Skip work for libraries that have nothing to
// compile.
// Resolve super class chain, and build the member elements. Both passes
// need the library scope to be setup. Each for loop can be
// parallelized.
for (LibraryUnit lib : getLibrariesToProcess()) {
for (DartUnit unit : lib.getUnits()) {
// These two method calls can be parallelized.
new SupertypeResolver().exec(unit, context, getTypeProvider());
new MemberBuilder().exec(unit, context, getTypeProvider());
}
}
} finally {
Tracer.end(logEvent);
}
}
private void validateLibraryDirectives() {
for (LibraryUnit lib : getLibrariesToProcess()) {
// don't need to validate system libraries
if (PackageLibraryManager.isDartUri(lib.getSource().getUri())) {
continue;
}
// check for #source uniqueness
{
Set<URI> includedSourceUris = Sets.newHashSet();
for (LibraryNode sourceNode : lib.getSourcePaths()) {
String path = sourceNode.getText();
DartSource source = lib.getSource().getSourceFor(path);
if (source != null) {
URI uri = source.getUri();
if (includedSourceUris.contains(uri)) {
context.onError(new DartCompilationError(sourceNode.getSourceInfo(),
DartCompilerErrorCode.UNIT_WAS_ALREADY_INCLUDED, uri));
}
includedSourceUris.add(uri);
}
}
}
// Validate imports.
boolean hasIO = false;
boolean hasHTML = false;
for (LibraryNode importNode : lib.getImportPaths()) {
String libSpec = importNode.getText();
hasIO |= "dart:io".equals(libSpec);
hasHTML |= "dart:html".equals(libSpec);
// "dart:mirrors" are not done yet
if ("dart:mirrors".equals(libSpec)) {
context.onError(new DartCompilationError(importNode,
DartCompilerErrorCode.MIRRORS_NOT_FULLY_IMPLEMENTED));
}
// validate console/web mix
if (hasIO && hasHTML) {
context.onError(new DartCompilationError(importNode.getSourceInfo(),
DartCompilerErrorCode.CONSOLE_WEB_MIX));
}
}
// check that each exported library has a library directive
for (LibraryExport libraryExport : lib.getExports()) {
LibraryUnit exportedLibrary = libraryExport.getLibrary();
String exportedLibraryName = getLibraryName(exportedLibrary);
// no => error
if (exportedLibraryName == null) {
SourceInfo info = findExportDirective(lib, exportedLibrary);
if (info != null) {
Source expSource = exportedLibrary.getSelfDartUnit().getSourceInfo().getSource();
context.onError(new DartCompilationError(info,
DartCompilerErrorCode.MISSING_LIBRARY_DIRECTIVE_EXPORT,
((DartSource) expSource).getRelativePath()));
}
}
}
// check that each imported library has a library directive
Map<String, LibraryUnit> nameToImportedLibrary = Maps.newHashMap();
for (LibraryUnit importedLib : lib.getImportedLibraries()) {
if (PackageLibraryManager.isDartUri(importedLib.getSource().getUri())) {
// system libraries are always valid
continue;
}
// get the dart unit corresponding to this library
DartUnit unit = importedLib.getSelfDartUnit();
if (unit == null || unit.isDiet()) {
// don't need to check a unit that hasn't changed
continue;
}
// find imported library name
String importedLibraryName = getLibraryName(importedLib);
// no name => error
if (importedLibraryName == null) {
SourceInfo info = findImportDirective(lib, importedLib);
if (info != null) {
context.onError(new DartCompilationError(info,
DartCompilerErrorCode.MISSING_LIBRARY_DIRECTIVE_IMPORT,
((DartSource) unit.getSourceInfo().getSource()).getRelativePath()));
}
}
// has already library with such name => error
if (importedLibraryName != null) {
LibraryUnit prevLibraryWithSameName = nameToImportedLibrary.get(importedLibraryName);
if (prevLibraryWithSameName != null) {
SourceInfo info = findImportDirective(lib, importedLib);
if (info != null) {
Source prevSource = prevLibraryWithSameName.getSelfDartUnit().getSourceInfo().getSource();
context.onError(new DartCompilationError(info,
DartCompilerErrorCode.DUPLICATE_IMPORTED_LIBRARY_NAME,
importedLibraryName,
((DartSource) prevSource).getRelativePath()));
}
} else {
nameToImportedLibrary.put(importedLibraryName, importedLib);
}
}
}
// check that all sourced units have no directives
for (DartUnit unit : lib.getUnits()) {
// don't need to check a unit that hasn't changed
if (unit.isDiet()) {
continue;
}
// ignore library unit
DartSource unitSource = (DartSource) unit.getSourceInfo().getSource();
if (isLibrarySelfUnit(lib, unitSource)) {
continue;
}
// analyze directives
List<DartDirective> directives = unit.getDirectives();
if (directives.isEmpty()) {
context.onError(new DartCompilationError(unitSource,
DartCompilerErrorCode.MISSING_PART_OF_DIRECTIVE, lib.getName()));
} else if (directives.size() == 1 && directives.get(0) instanceof DartPartOfDirective) {
DartPartOfDirective directive = (DartPartOfDirective) directives.get(0);
String dirName = directive.getLibraryName();
if (!Objects.equal(dirName, lib.getName())) {
context.onError(new DartCompilationError(directive,
DartCompilerErrorCode.WRONG_PART_OF_NAME, lib.getName(), dirName));
}
} else {
context.onError(new DartCompilationError(directives.get(0),
DartCompilerErrorCode.ILLEGAL_DIRECTIVES_IN_SOURCED_UNIT,
Elements.getRelativeSourcePath(unitSource, lib.getSource())));
}
}
}
}
/**
* @return the name of the given {@link LibraryUnit} specified in {@link DartLibraryDirective}.
*/
private static String getLibraryName(LibraryUnit libraryUnit) {
DartUnit unit = libraryUnit.getSelfDartUnit();
for (DartDirective directive : unit.getDirectives()) {
if (directive instanceof DartLibraryDirective) {
return ((DartLibraryDirective) directive).getLibraryName();
}
}
return null;
}
/**
* @return the {@link SourceInfo} of the import directive in "lib" for "importedLib".
*/
private static SourceInfo findImportDirective(LibraryUnit lib, LibraryUnit importedLib) {
for (LibraryNode importPath : lib.getImportPaths()) {
if (importPath.getText().equals(importedLib.getSelfSourcePath().getText())) {
return importPath.getSourceInfo();
}
}
return null;
}
/**
* @return the {@link SourceInfo} of the export directive in "lib" for "importedLib".
*/
private static SourceInfo findExportDirective(LibraryUnit lib, LibraryUnit importedLib) {
for (LibraryNode exportPath : lib.getExportPaths()) {
if (exportPath.getText().equals(importedLib.getSelfSourcePath().getText())) {
return exportPath.getSourceInfo();
}
}
return null;
}
private static boolean isLibrarySelfUnit(LibraryUnit lib, DartSource unitSource) {
String unitRelativePath = unitSource.getRelativePath();
for (LibraryNode sourceNode : lib.getSourcePaths()) {
if (unitRelativePath.equals(sourceNode.getText())) {
if (sourceNode == lib.getSelfSourcePath()) {
return true;
}
return false;
}
}
return false;
}
private void setEntryPoint() {
LibraryUnit lib = context.getAppLibraryUnit();
lib.setEntryNode(new LibraryNode(MAIN_ENTRY_POINT_NAME));
// this ensures that if we find it, it's a top-level static element
Element element = lib.getElement().lookupLocalElement(MAIN_ENTRY_POINT_NAME);
switch (ElementKind.of(element)) {
case NONE:
// this is ok, it might just be a library
break;
case METHOD:
MethodElement methodElement = (MethodElement) element;
Modifiers modifiers = methodElement.getModifiers();
if (modifiers.isGetter()) {
context.onError(new DartCompilationError(element,
DartCompilerErrorCode.ENTRY_POINT_METHOD_MAY_NOT_BE_GETTER, MAIN_ENTRY_POINT_NAME));
} else if (modifiers.isSetter()) {
context.onError(new DartCompilationError(element,
DartCompilerErrorCode.ENTRY_POINT_METHOD_MAY_NOT_BE_SETTER, MAIN_ENTRY_POINT_NAME));
} else if (methodElement.getParameters().size() > 0) {
context.onError(new DartCompilationError(element,
DartCompilerErrorCode.ENTRY_POINT_METHOD_CANNOT_HAVE_PARAMETERS,
MAIN_ENTRY_POINT_NAME));
} else {
lib.getElement().setEntryPoint(methodElement);
}
break;
default:
context.onError(new DartCompilationError(element,
ResolverErrorCode.NOT_A_STATIC_METHOD, MAIN_ENTRY_POINT_NAME));
break;
}
}
private void compileLibraries() throws IOException {
TraceEvent logEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE_LIBRARIES) : null;
CompilerMetrics compilerMetrics = context.getCompilerMetrics();
if (compilerMetrics != null) {
compilerMetrics.startCompileLibrariesTime();
}
try {
// Set entry point
setEntryPoint();
// The two following for loops can be parallelized.
for (LibraryUnit lib : getLibrariesToProcess()) {
boolean persist = false;
// Compile all the units in this library.
for (DartCompilationPhase phase : phases) {
// Run all compiler phases including AST simplification and symbol
// resolution. This must run in serial.
for (DartUnit unit : lib.getUnits()) {
// Don't compile diet units.
if (unit.isDiet()) {
continue;
}
unit = phase.exec(unit, context, getTypeProvider());
if (!config.resolveDespiteParseErrors() && context.getErrorCount() > 0) {
return;
}
}
}
for (DartUnit unit : lib.getUnits()) {
if (unit.isDiet()) {
continue;
}
updateAnalysisTimestamp(unit);
// To help support the IDE, notify the listener that this unit is compiled.
context.unitCompiled(unit);
// Update deps.
lib.getDeps(context).update(context, unit);
// We analyzed something, so we need to persist the deps.
persist = true;
}
// Persist the DEPS file.
if (persist) {
lib.writeDeps(context);
}
}
} finally {
if (compilerMetrics != null) {
compilerMetrics.endCompileLibrariesTime();
}
Tracer.end(logEvent);
}
}
private void updateAnalysisTimestamp(DartUnit unit) throws IOException {
// Update timestamp.
Writer writer =
context.getArtifactWriter(unit.getSourceInfo().getSource(), "", EXTENSION_TIMESTAMP);
String timestampData = String.format("%d\n", System.currentTimeMillis());
writer.write(timestampData);
writer.close();
}
DartUnit parse(DartSource dartSrc, Set<String> libraryPrefixes, boolean diet) throws IOException {
TraceEvent parseEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.PARSE, "src", dartSrc.getName()) : null;
CompilerMetrics compilerMetrics = context.getCompilerMetrics();
long parseStart = compilerMetrics != null ? CompilerMetrics.getThreadTime() : 0;
Reader r = dartSrc.getSourceReader();
String srcCode;
boolean failed = true;
try {
try {
srcCode = CharStreams.toString(r);
failed = false;
} finally {
Closeables.close(r, failed);
}
DartParser parser = new DartParser(dartSrc, srcCode, diet, libraryPrefixes, context,
context.getCompilerMetrics());
DartUnit unit = parser.parseUnit();
if (compilerMetrics != null) {
compilerMetrics.addParseTimeNano(CompilerMetrics.getThreadTime() - parseStart);
}
if (!config.resolveDespiteParseErrors() && context.getErrorCount() > 0) {
// We don't return this unit, so no more processing expected for it.
context.unitCompiled(unit);
return null;
}
return unit;
} finally {
Tracer.end(parseEvent);
}
}
private void reportMissingSource(DartCompilerContext context,
LibrarySource libSrc,
LibraryNode libNode) {
if (libNode != null && StringUtils.startsWith(libNode.getText(), "dart-ext:")) {
return;
}
DartCompilationError event = new DartCompilationError(libNode,
DartCompilerErrorCode.MISSING_SOURCE,
libNode.getText());
event.setSource(libSrc);
context.onError(event);
}
CoreTypeProvider getTypeProvider() {
typeProvider.getClass(); // Quick null check.
return typeProvider;
}
}
/**
* Provides cached parse and resolution results during selective compilation
*/
public abstract static class SelectiveCache {
/**
* Answer the cached resolved libraries
*
* @return a mapping (not <code>null</code>) of library source URI to cached {@link LibraryUnit}
*/
public abstract Map<URI, LibraryUnit> getResolvedLibraries();
/**
* Answer the cached unresolved {@link DartUnit} for the specified source
*
* @param dartSrc the source (not <code>null</code>)
* @return the cached unit or <code>null</code> if it is not cached
*/
public abstract DartUnit getUnresolvedDartUnit(DartSource dartSrc);
}
/**
* Selectively compile a library. Use supplied libraries and ASTs when available.
* This allows programming tools to provide customized ASTs for code that is currently being
* edited, and may not compile correctly.
*/
static class SelectiveCompiler extends Compiler {
private final SelectiveCache selectiveCache;
private Collection<LibraryUnit> librariesToProcess;
private SelectiveCompiler(LibrarySource app, SelectiveCache selectiveCache,
CompilerConfiguration config, DartCompilerMainContext context) {
super(app, Collections.<LibrarySource>emptyList(), config, context);
this.selectiveCache = selectiveCache;
addResolvedLibraries(selectiveCache.getResolvedLibraries());
}
@Override
Collection<LibraryUnit> getLibrariesToProcess() {
if (librariesToProcess == null) {
librariesToProcess = new ArrayList<LibraryUnit>();
librariesToProcess.addAll(super.getLibrariesToProcess());
librariesToProcess.removeAll(selectiveCache.getResolvedLibraries().values());
}
return librariesToProcess;
}
@Override
DartUnit parse(DartSource dartSrc, Set<String> prefixes, boolean diet) throws IOException {
DartUnit parsedUnit = selectiveCache.getUnresolvedDartUnit(dartSrc);
if (parsedUnit != null) {
return parsedUnit;
}
return super.parse(dartSrc, prefixes, diet);
}
}
private static CompilerOptions processCommandLineOptions(String[] args) {
CmdLineParser cmdLineParser = null;
CompilerOptions compilerOptions = null;
try {
compilerOptions = new CompilerOptions();
cmdLineParser = CommandLineOptions.parse(args, compilerOptions);
if (args.length == 0 || compilerOptions.showHelp()) {
showUsage(cmdLineParser, System.err);
System.exit(1);
}
} catch (CmdLineException e) {
System.err.println(e.getLocalizedMessage());
showUsage(cmdLineParser, System.err);
System.exit(1);
}
assert compilerOptions != null;
return compilerOptions;
}
public static void main(final String[] topArgs) {
Tracer.init();
CompilerOptions topCompilerOptions = processCommandLineOptions(topArgs);
Result result = null;
try {
// configure UTF-8 output
System.setOut(new PrintStream(System.out, true, "UTF-8"));
System.setErr(new PrintStream(System.err, true, "UTF-8"));
if (topCompilerOptions.showVersion()) {
showVersion(topCompilerOptions);
System.exit(RESULT_OK);
}
if (topCompilerOptions.shouldBatch()) {
if (topArgs.length > 1) {
System.err.println("(Extra arguments specified with -batch ignored.)");
}
result = UnitTestBatchRunner.runAsBatch(topArgs, new Invocation() {
@Override
public Result invoke(String[] lineArgs) throws Throwable {
List<String> allArgs = new ArrayList<String>();
for (String arg: topArgs) {
if (!arg.equals("-batch")) {
allArgs.add(arg);
}
}
for (String arg: lineArgs) {
allArgs.add(arg);
}
CompilerOptions compilerOptions = processCommandLineOptions(
allArgs.toArray(new String[allArgs.size()]));
if (compilerOptions.shouldBatch()) {
System.err.println("-batch ignored: Already in batch mode.");
}
return compilerMain(compilerOptions);
}
});
} else {
System.out.println("WARNING: This command is now deprecated, please use dartanalyzer instead.");
System.out.println("For more info, see www.dartlang.org/tools/analyzer.");
System.out.println();
result = compilerMain(topCompilerOptions);
}
} catch (Throwable t) {
t.printStackTrace();
crash();
}
// exit
{
int exitCode = result.code;
if (!topCompilerOptions.extendedExitCode()) {
if (exitCode == RESULT_ERRORS) {
exitCode = 1;
} else {
exitCode = 0;
}
}
System.exit(exitCode);
}
}
/**
* Invoke the compiler to build single application.
*
* @param compilerOptions parsed command line arguments
* @return the result as integer when <code>0</code> means clean; <code>1</code> there were
* warnings; <code>2</code> there were errors; <code>127</code> other problems or
* exceptions.
*/
public static Result compilerMain(CompilerOptions compilerOptions) throws IOException {
List<String> sourceFiles = compilerOptions.getSourceFiles();
if (sourceFiles.size() == 0) {
System.err.println("dart_analyzer: no source files were specified.");
showUsage(null, System.err);
return new Result(RESULT_OTHER, null);
}
File sourceFile = new File(sourceFiles.get(0));
if (!sourceFile.exists()) {
System.err.println("dart_analyzer: file not found: " + sourceFile);
showUsage(null, System.err);
return new Result(RESULT_OTHER, null);
}
CompilerConfiguration config = new DefaultCompilerConfiguration(compilerOptions);
config.getPackageLibraryManager().setPackageRoots(Arrays.asList(new File[]{compilerOptions.getPackageRoot()}));
return compilerMain(sourceFile, config);
}
/**
* Invoke the compiler to build single application.
*
* @param sourceFile file passed on the command line to build
* @param config compiler configuration built from parsed command line options
*/
public static Result compilerMain(File sourceFile, CompilerConfiguration config)
throws IOException {
Result result = compileApp(sourceFile, config);
String errorMessage = result.message;
if (errorMessage != null) {
System.err.println(errorMessage);
return result;
}
TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.WRITE_METRICS) : null;
try {
maybeShowMetrics(config);
} finally {
Tracer.end(logEvent);
}
return result;
}
public static void crash() {
// Our test scripts look for 253 to signal a "crash".
System.exit(253);
}
private static void showUsage(CmdLineParser cmdLineParser, PrintStream out) {
out.println("Usage: dart_analyzer [<options>] <dart-script> [script-arguments]");
out.println("Available options:");
if (cmdLineParser == null) {
cmdLineParser = new CmdLineParser(new CompilerOptions());
}
cmdLineParser.printUsage(out);
}
private static void maybeShowMetrics(CompilerConfiguration config) {
CompilerMetrics compilerMetrics = config.getCompilerMetrics();
if (compilerMetrics != null) {
compilerMetrics.write(System.out);
}
JvmMetrics.maybeWriteJvmMetrics(System.out, config.getJvmMetricOptions());
}
/**
* Treats the <code>sourceFile</code> as the top level library and generates compiled output by
* linking the dart source in this file with all libraries referenced with <code>#import</code>
* statements.
*
* @return the result as integer when <code>0</code> means clean; <code>1</code> there were
* warnings; <code>2</code> there were errors; <code>127</code> other problems or
* exceptions.
*/
public static Result compileApp(File sourceFile, CompilerConfiguration config) throws IOException {
TraceEvent logEvent =
Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE_APP, "src", sourceFile.toString())
: null;
try {
File outputDirectory = config.getOutputDirectory();
DefaultDartArtifactProvider provider = new DefaultDartArtifactProvider(outputDirectory);
// Compile the Dart application and its dependencies.
PackageLibraryManager libraryManager = config.getPackageLibraryManager();
final LibrarySource lib = new UrlLibrarySource(sourceFile.toURI(),libraryManager);
DefaultDartCompilerListener listener;
if (config.getCompilerOptions().showSourceFromAst()) {
listener = new DefaultDartCompilerListener(config.printErrorFormat()) {
@Override
public void unitCompiled(DartUnit unit) {
if (unit.getLibrary() != null) {
if (unit.getLibrary().getSource() == lib) {
DefaultTextOutput output = new DefaultTextOutput(false);
unit.accept(new DartToSourceVisitor(output));
System.out.println(output.toString());
}
}
}
};
} else {
listener = new DefaultDartCompilerListener(config.printErrorFormat());
}
return compileLib(lib, config, provider, listener);
} finally {
Tracer.end(logEvent);
}
}
/**
* Compiles the given library, translating all its source files, and those
* of its imported libraries, transitively.
*
* @param lib The library to be compiled (not <code>null</code>)
* @param config The compiler configuration specifying the compilation phases
* @param provider A mechanism for specifying where code should be generated
* @param listener An object notified when compilation errors occur
*/
public static Result compileLib(LibrarySource lib, CompilerConfiguration config,
DartArtifactProvider provider, DartCompilerListener listener) throws IOException {
return compileLib(lib, Collections.<LibrarySource>emptyList(), config, provider, listener);
}
/**
* Same method as above, but also takes a list of libraries that should be
* implicitly imported by all libraries. These libraries are provided by the embedder.
*/
public static Result compileLib(LibrarySource lib,
List<LibrarySource> embeddedLibraries,
CompilerConfiguration config,
DartArtifactProvider provider,
DartCompilerListener listener) throws IOException {
DartCompilerMainContext context = new DartCompilerMainContext(lib, provider, listener,
config);
new Compiler(lib, embeddedLibraries, config, context).compile();
int errorCount = context.getErrorCount();
if (config.typeErrorsAreFatal()) {
errorCount += context.getTypeErrorCount();
}
if (config.warningsAreFatal()) {
errorCount += context.getWarningCount();
}
if (errorCount > 0) {
return new Result(RESULT_ERRORS, "Compilation failed with " + errorCount
+ (errorCount == 1 ? " problem." : " problems."));
}
if (!context.getFilesHaveChanged()) {
return null;
}
// Write checking log.
{
Writer writer = provider.getArtifactWriter(lib, "", EXTENSION_LOG);
boolean threw = true;
try {
writer.write(String.format("Checked %s and found:%n", lib.getName()));
writer.write(String.format(" no load/resolution errors%n"));
writer.write(String.format(" %s type errors%n", context.getTypeErrorCount()));
threw = false;
} finally {
Closeables.close(writer, threw);
}
}
{
int resultCode = RESULT_OK;
if (context.getWarningCount() != 0) {
resultCode = RESULT_WARNINGS;
}
return new Result(resultCode, null);
}
}
/**
* Analyzes the given library and all its transitive dependencies.
*
* @param lib The library to be analyzed
* @param parsedUnits A collection of unresolved ASTs that should be used
* instead of parsing the associated source from storage. Intended for
* IDE use when modified buffers must be analyzed. AST nodes in the map may be
* ignored if not referenced by {@code lib}. (May be null.)
* @param config The compiler configuration (phases will not be used), but resolution and
* type-analysis will be invoked
* @param provider A mechanism for specifying where code should be generated
* @param listener An object notified when compilation errors occur
* @throws NullPointerException if any of the arguments except {@code parsedUnits}
* are {@code null}
* @throws IOException on IO errors, which are not logged
*/
public static LibraryUnit analyzeLibrary(LibrarySource lib, final Map<URI, DartUnit> parsedUnits,
CompilerConfiguration config, DartArtifactProvider provider, DartCompilerListener listener)
throws IOException {
final PackageLibraryManager manager = config.getPackageLibraryManager();
final HashMap<URI, LibraryUnit> resolvedLibs = new HashMap<URI, LibraryUnit>();
SelectiveCache selectiveCache = new SelectiveCache() {
@Override
public Map<URI, LibraryUnit> getResolvedLibraries() {
return resolvedLibs;
}
@Override
public DartUnit getUnresolvedDartUnit(DartSource dartSrc) {
if (parsedUnits == null) {
return null;
}
URI srcUri = dartSrc.getUri();
DartUnit parsedUnit = parsedUnits.remove(srcUri);
if (parsedUnit != null) {
return parsedUnit;
}
URI fileUri = manager.resolveDartUri(srcUri);
return parsedUnits.remove(fileUri);
}
};
Map<URI, LibraryUnit> libraryUnit = analyzeLibraries(lib, selectiveCache, config,
provider, listener, false);
return libraryUnit != null ? libraryUnit.get(lib.getUri()) : null;
}
/**
* Analyzes the given library and all its transitive dependencies.
*
* @param lib The library to be analyzed
* @param selectiveCache Provides cached parse and resolution results
* during selective compilation (not <code>null</code>)
* @param config The compiler configuration (phases and backends
* will not be used), but resolution and type-analysis will be invoked
* @param provider A mechanism for specifying where code should be generated
* @param listener An object notified when compilation errors occur
* @param resolveAllNewLibs <code>true</code> if all new libraries should be resolved
* or false if only the library specified by the "lib" parameter should be resolved
* @throws NullPointerException if any of the arguments except {@code parsedUnits}
* are {@code null}
* @throws IOException on IO errors, which are not logged
*/
public static Map<URI, LibraryUnit> analyzeLibraries(LibrarySource lib,
SelectiveCache selectiveCache, CompilerConfiguration config,
DartArtifactProvider provider, DartCompilerListener listener,
boolean resolveAllNewLibs) throws IOException {
lib.getClass(); // Quick null check.
provider.getClass(); // Quick null check.
listener.getClass(); // Quick null check.
Map<URI, LibraryUnit> resolvedLibs = selectiveCache.getResolvedLibraries();
DartCompilerMainContext context = new DartCompilerMainContext(lib, provider, listener, config);
Compiler compiler = new SelectiveCompiler(lib, selectiveCache, config, context);
LibraryUnit topLibUnit = compiler.updateAndResolve();
if (topLibUnit == null) {
return null;
}
Map<URI, LibraryUnit> librariesToResolve;
librariesToResolve = new HashMap<URI, LibraryUnit>();
if (resolveAllNewLibs) {
librariesToResolve.putAll(compiler.getLibraries());
}
librariesToResolve.put(topLibUnit.getSource().getUri(), topLibUnit);
DartCompilationPhase[] phases = {
new Resolver.Phase(),
new CompileTimeConstantAnalyzer.Phase(),
new TypeAnalyzer()};
Map<URI, LibraryUnit> newLibraries = Maps.newHashMap();
for (Entry<URI, LibraryUnit> entry : librariesToResolve.entrySet()) {
URI libUri = entry.getKey();
LibraryUnit libUnit = entry.getValue();
if (!resolvedLibs.containsKey(libUri) && libUnit != null) {
newLibraries.put(libUri, libUnit);
for (DartCompilationPhase phase : phases) {
// Run phase on all units, because "const" phase expects to have fully resolved library.
for (DartUnit unit : libUnit.getUnits()) {
if (unit.isDiet()) {
continue;
}
unit = phase.exec(unit, context, compiler.getTypeProvider());
}
}
// To help support the IDE, notify the listener that these unit were compiled.
for (DartUnit unit : libUnit.getUnits()) {
context.unitCompiled(unit);
}
}
}
return newLibraries;
}
/**
* Re-analyzes source code after a modification. The modification is described by a SourceDelta.
*
* @param delta what has changed
* @param enclosingLibrary the library in which the change occurred
* @param interestStart beginning of interest area (as character offset from the beginning of the
* source file after the change.
* @param interestLength length of interest area
* @return a node which covers the entire interest area.
*/
public static DartNode analyzeDelta(SourceDelta delta,
LibraryElement enclosingLibrary,
LibraryElement coreLibrary,
DartNode interestNode,
int interestStart,
int interestLength,
CompilerConfiguration config,
DartCompilerListener listener) throws IOException {
DeltaAnalyzer analyzer = new DeltaAnalyzer(delta, enclosingLibrary, coreLibrary,
interestNode, interestStart, interestLength,
config, listener);
return analyzer.analyze();
}
public static LibraryUnit findLibrary(LibraryUnit libraryUnit, String uri,
Set<LibraryElement> seen) {
if (seen.contains(libraryUnit.getElement())) {
return null;
}
seen.add(libraryUnit.getElement());
if (uri.equals(libraryUnit.getName())) {
return libraryUnit;
}
for (LibraryNode src : libraryUnit.getSourcePaths()) {
if (src.getText().equals(uri)) {
return libraryUnit;
}
}
for (LibraryUnit importedLibrary : libraryUnit.getImportedLibraries()) {
LibraryUnit unit = findLibrary(importedLibrary, uri, seen);
if (unit != null) {
return unit;
}
}
return null;
}
public static LibraryUnit getCoreLib(LibraryUnit libraryUnit) {
LibraryUnit coreLib = findLibrary(libraryUnit, "dart.core", new HashSet<LibraryElement>());
if (coreLib == null) {
coreLib = findLibrary(libraryUnit, "dart:core", new HashSet<LibraryElement>());
}
return coreLib;
}
private static void showVersion(CompilerOptions options) {
String version = getSdkVersion(options);
if (version == null) {
version = "<unkown>";
}
System.out.println("dart_analyzer version " + version);
}
/**
* @return the numeric revision of SDK, may be <code>null</code> if cannot find.
*/
private static String getSdkVersion(CompilerOptions options) {
try {
File sdkPath = options.getDartSdkPath();
File revisionFile = new File(sdkPath, "version");
if (revisionFile.exists() && revisionFile.isFile()) {
BufferedReader br = new BufferedReader(new FileReader(revisionFile));
try {
return br.readLine();
} finally {
br.close();
}
}
} catch (Throwable e) {
}
return null;
}
}