| // 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.collect.Lists; |
| 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.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 { |
| |
| 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 NamedPlaceHolderLibrarySource implements LibrarySource { |
| private final String name; |
| |
| public NamedPlaceHolderLibrarySource(String name) { |
| this.name = name; |
| } |
| |
| @Override |
| public boolean exists() { |
| throw new AssertionError(); |
| } |
| |
| @Override |
| public long getLastModified() { |
| throw new AssertionError(); |
| } |
| |
| @Override |
| public String getName() { |
| return name; |
| } |
| |
| @Override |
| public Reader getSourceReader() { |
| throw new AssertionError(); |
| } |
| |
| @Override |
| public String getUniqueIdentifier() { |
| throw new AssertionError(); |
| } |
| |
| @Override |
| public URI getUri() { |
| throw new AssertionError(); |
| } |
| |
| @Override |
| public LibrarySource getImportFor(String relPath) { |
| return null; |
| } |
| |
| @Override |
| public DartSource getSourceFor(String relPath) { |
| return null; |
| } |
| } |
| |
| 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; |
| if (PackageLibraryManager.isDartSpec(libSpec)) { |
| dep = context.getSystemLibraryFor(libSpec); |
| } else { |
| dep = libSrc.getImportFor(libSpec); |
| } |
| 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(); |
| String prefix = importNode.getPrefix(); |
| 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 import prefix |
| if (DartParser.PSEUDO_KEYWORDS_SET.contains(prefix)) { |
| context.onError(new DartCompilationError(importNode.getSourceInfo(), |
| ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_IMPORT_PREFIX, prefix)); |
| } |
| // validate console/web mix |
| if (hasIO && hasHTML) { |
| context.onError(new DartCompilationError(importNode.getSourceInfo(), |
| DartCompilerErrorCode.CONSOLE_WEB_MIX)); |
| } |
| } |
| |
| // check that each imported library has a library directive |
| 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; |
| } |
| |
| boolean foundLibraryDirective = false; |
| for (DartDirective directive : unit.getDirectives()) { |
| if (directive instanceof DartLibraryDirective) { |
| foundLibraryDirective = true; |
| break; |
| } |
| } |
| if (!foundLibraryDirective) { |
| // find the imported path node (which corresponds to the import directive node) |
| SourceInfo info = null; |
| for (LibraryNode importPath : lib.getImportPaths()) { |
| if (importPath.getText().equals(importedLib.getSelfSourcePath().getText())) { |
| info = importPath.getSourceInfo(); |
| break; |
| } |
| } |
| if (info != null) { |
| context.onError(new DartCompilationError(info, |
| DartCompilerErrorCode.MISSING_LIBRARY_DIRECTIVE, |
| ((DartSource) unit.getSourceInfo().getSource()).getRelativePath())); |
| } |
| } |
| } |
| |
| // check that all sourced units have no directives |
| for (DartUnit unit : lib.getUnits()) { |
| if (unit.isDiet()) { |
| // don't need to check a unit that hasn't changed |
| continue; |
| } |
| if (invalidDirectivesForPart(unit.getDirectives())) { |
| // find corresponding source node for this unit |
| for (LibraryNode sourceNode : lib.getSourcePaths()) { |
| if (sourceNode == lib.getSelfSourcePath()) { |
| // skip the special synthetic selfSourcePath node |
| continue; |
| } |
| DartSource dartSource = (DartSource) unit.getSourceInfo().getSource(); |
| // check for directives |
| if (dartSource.getRelativePath().equals(sourceNode.getText())) { |
| context.onError(new DartCompilationError(unit.getDirectives().get(0), |
| DartCompilerErrorCode.ILLEGAL_DIRECTIVES_IN_SOURCED_UNIT, |
| Elements.getRelativeSourcePath(dartSource, lib.getSource()))); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private boolean invalidDirectivesForPart(List<DartDirective> directives) { |
| int size = directives.size(); |
| if (size == 1) { |
| return !(directives.get(0) instanceof DartPartOfDirective); |
| } |
| return size > 0; |
| } |
| |
| 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); |
| boolean result = false; |
| try { |
| if (topCompilerOptions.showVersion()) { |
| showVersion(topCompilerOptions); |
| System.exit(0); |
| } |
| if (topCompilerOptions.shouldBatch()) { |
| if (topArgs.length > 1) { |
| System.err.println("(Extra arguments specified with -batch ignored.)"); |
| } |
| UnitTestBatchRunner.runAsBatch(topArgs, new Invocation() { |
| @Override |
| public boolean 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 { |
| result = compilerMain(topCompilerOptions); |
| } |
| } catch (Throwable t) { |
| t.printStackTrace(); |
| crash(); |
| } |
| if (!result) { |
| System.exit(1); |
| } |
| } |
| |
| /** |
| * Invoke the compiler to build single application. |
| * |
| * @param compilerOptions parsed command line arguments |
| * |
| * @return <code> true</code> on success, <code>false</code> on failure. |
| */ |
| public static boolean 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 false; |
| } |
| |
| File sourceFile = new File(sourceFiles.get(0)); |
| if (!sourceFile.exists()) { |
| System.err.println("dart_analyzer: file not found: " + sourceFile); |
| showUsage(null, System.err); |
| return false; |
| } |
| |
| 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 |
| * |
| * @return <code> true</code> on success, <code>false</code> on failure. |
| */ |
| public static boolean compilerMain(File sourceFile, CompilerConfiguration config) |
| throws IOException { |
| String errorMessage = compileApp(sourceFile, config); |
| if (errorMessage != null) { |
| System.err.println(errorMessage); |
| return false; |
| } |
| |
| TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.WRITE_METRICS) : null; |
| try { |
| maybeShowMetrics(config); |
| } finally { |
| Tracer.end(logEvent); |
| } |
| return true; |
| } |
| |
| 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. |
| */ |
| public static String 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()); |
| } |
| String errorString = compileLib(lib, config, provider, listener); |
| return errorString; |
| } 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 String 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 String compileLib(LibrarySource lib, |
| List<LibrarySource> embeddedLibraries, |
| CompilerConfiguration config, |
| DartArtifactProvider provider, |
| DartCompilerListener listener) throws IOException { |
| DartCompilerMainContext context = new DartCompilerMainContext(lib, provider, listener, |
| config); |
| if (config.getCompilerOptions().shouldExposeCoreImpl()) { |
| if (embeddedLibraries == null) { |
| embeddedLibraries = Lists.newArrayList(); |
| } |
| // use a place-holder LibrarySource instance, to be replaced when embedded |
| // in the compiler, where the dart uri can be resolved. |
| embeddedLibraries.add(new NamedPlaceHolderLibrarySource("dart:core")); |
| } |
| |
| 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 "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); |
| } |
| } |
| return 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) { |
| return findLibrary(libraryUnit, "dart:core", new HashSet<LibraryElement>()); |
| } |
| |
| 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; |
| } |
| } |