blob: bcdf2e46aca1c26af89ef7adbbf6246b8df24f68 [file] [log] [blame]
// Copyright (c) 2011, 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.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.dart.compiler.ast.DartUnit;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Represents a library's dependencies artifact.
*/
public class LibraryDeps {
private static final String VERSION = "v00001";
/**
* Each dependency record contains the library in which it was found, name of the unit in this
* library and last-modified timestamp. Any change in the timestamp of the target dependency will
* force a recompile of the associated compilation unit.
*/
public static class Dependency {
private final URI libUri;
private final String unitName;
private final long lastModified;
public Dependency(URI libUri, String unitName, long lastModified) {
this.libUri = libUri;
this.unitName = unitName;
this.lastModified = lastModified;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Dependency) {
Dependency dep = (Dependency) obj;
return Objects.equal(libUri, dep.libUri) && Objects.equal(unitName, dep.unitName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(libUri, unitName);
}
public URI getLibUri() {
return libUri;
}
public String getUnitName() {
return unitName;
}
public long getLastModified() {
return lastModified;
}
}
public static class Source {
private final Set<Dependency> deps = Sets.newHashSet();
private final Set<String> topSymbols = Sets.newHashSet();
private final Set<String> allSymbols = Sets.newHashSet();
private final Set<String> holes = Sets.newHashSet();
private boolean shouldRecompileOnAnyTopLevelChange = false;
/**
* @return the {@link Set} of {@link Dependency}s.
*/
public Set<Dependency> getDeps() {
return deps;
}
/**
* @return the names of top-level elements, such as methods and classes.
*/
public Set<String> getTopSymbols() {
return topSymbols;
}
/**
* @return the names of all elements in unit, such as names of local variables, fields, etc.
*/
public Set<String> getAllSymbols() {
return allSymbols;
}
/**
* @return the names of functions, which are invoked without qualifier. So, declaration or
* removing function with such name on top-level should cause recompiling.
*/
public Set<String> getHoles() {
return holes;
}
/**
* @return <code>true</code> if this unit should be recompiled on any change in the set of
* top-level symbols. Typically unit has compilation errors, which potentially may be
* fixed, so we should recompile this unit.
*/
public boolean shouldRecompileOnAnyTopLevelChange() {
return shouldRecompileOnAnyTopLevelChange;
}
/**
* Adds new {@link Dependency}.
*/
public void addDep(Dependency dep) {
deps.add(dep);
}
/**
* Adds symbol to the {@link Set} of top symbols.
*/
public void addTopSymbol(String symbol) {
if (!Strings.isNullOrEmpty(symbol)) {
topSymbols.add(symbol);
}
}
/**
* Adds symbol to the {@link Set} of all symbols.
*/
public void addAllSymbol(String symbol) {
allSymbols.add(symbol);
}
/**
* Adds new hole for {@link #getHoles()}.
*/
public void addHole(String hole) {
holes.add(hole);
}
}
public static LibraryDeps fromReader(Reader reader) {
try {
return fromReaderEx(reader);
} catch (Throwable e) {
return null;
}
}
private static LibraryDeps fromReaderEx(Reader reader) throws Exception {
LibraryDeps deps = new LibraryDeps();
BufferedReader buf = new BufferedReader(reader);
// Check version.
{
String line = buf.readLine();
if (!Objects.equal(line, VERSION)) {
return deps;
}
}
// Read units dependencies.
String relPath;
while (null != (relPath = buf.readLine())) {
Source source = new Source();
// Read flags.
source.shouldRecompileOnAnyTopLevelChange = Boolean.parseBoolean(buf.readLine());
// Read top symbols.
{
String line = buf.readLine();
Iterable<String> topSymbols = Splitter.on(' ').omitEmptyStrings().split(line);
Iterables.addAll(source.topSymbols, topSymbols);
}
// Read all symbols.
{
String line = buf.readLine();
Iterable<String> allSymbols = Splitter.on(' ').omitEmptyStrings().split(line);
Iterables.addAll(source.allSymbols, allSymbols);
}
// Read holes.
{
String line = buf.readLine();
Iterable<String> holes = Splitter.on(' ').omitEmptyStrings().split(line);
Iterables.addAll(source.holes, holes);
}
// Read dependencies.
while (true) {
String line = buf.readLine();
// Blank line: next unit.
if (line.length() == 0) {
break;
}
// Parse line.
String[] parts = line.split(" ");
source.deps.add(new Dependency(new URI(parts[0]), parts[1], Long.parseLong(parts[2])));
}
// Remember dependencies for current unit.
deps.sources.put(relPath, source);
}
return deps;
}
private final Map<String, Source> sources = Maps.newHashMap();
public LibraryDeps() {
}
/**
* @return the relative paths of all units with remembered dependencies.
*/
public Set<String> getUnitPaths() {
return sources.keySet();
}
/**
* @return all {@link Source} descriptions for all units in this library.
*/
public Iterable<Source> getSources() {
return sources.values();
}
/**
* @return the {@link Source} description of the unit with given path.
*/
public Source getSource(String relPath) {
return sources.get(relPath);
}
/**
* Remembers {@link Dependency}s of the unit with given path.
*/
public void putSource(String relPath, Source source) {
sources.put(relPath, source);
}
/**
* Update the library dependencies to reflect this unit's classes.
*/
public void update(DartCompilerMainContext context, DartUnit unit) {
Source source = new Source();
DartSource unitSource = (DartSource) unit.getSourceInfo().getSource();
String relPath = unitSource.getRelativePath();
putSource(relPath, source);
// Remember dependencies.
LibraryDepsVisitor.exec(unit, source);
// Fill Source with symbols.
for (String name : unit.getDeclarationNames()) {
source.addAllSymbol(name);
}
for (String name : unit.getTopDeclarationNames()) {
source.addTopSymbol(name);
}
// Analyze errors and see if any of them should force recompilation.
List<DartCompilationError> sourceErrors = context.getSourceErrors(unitSource);
for (DartCompilationError error : sourceErrors) {
if (error.getErrorCode().needsRecompilation()) {
source.shouldRecompileOnAnyTopLevelChange = true;
break;
}
}
}
public void write(Writer writer) throws IOException {
// Write version.
writer.write(VERSION);
writer.write('\n');
// Write entries.
for (Entry<String, Source> entry : sources.entrySet()) {
String relPath = entry.getKey();
Source source = entry.getValue();
// Unit name.
writer.write(relPath);
writer.write('\n');
// Flags.
writer.write(Boolean.toString(source.shouldRecompileOnAnyTopLevelChange));
writer.write('\n');
// Write top symbols.
for (String symbol : source.topSymbols) {
writer.write(symbol);
writer.write(' ');
}
writer.write('\n');
// Write all symbols.
for (String symbol : source.allSymbols) {
writer.write(symbol);
writer.write(' ');
}
writer.write('\n');
// Write holes.
for (String hole : source.holes) {
writer.write(hole);
writer.write(' ');
}
writer.write('\n');
// Write dependencies.
for (Dependency dep : source.deps) {
writer.write(dep.libUri.toString());
writer.write(' ');
writer.write(dep.unitName);
writer.write(' ');
writer.write(Long.toString(dep.lastModified));
writer.write('\n');
}
// Empty line after each unit.
writer.write('\n');
}
}
}