ecbd1874bf
1. Change dependency tracking from API version on unit to just unit last modified time. 2. Track sets of top-level and all declared symbols in units, recompile in case of possible conflict. 3. Track units with TypeErrorCode.CANNOT_BE_RESOLVED, recompile if any unit changes top-level symbols. 4. Better tests with source code directly in test method. R=zundel@google.com BUG= TEST= Review URL: https://chromiumcodereview.appspot.com//9148026 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@3799 260f80e4-7a28-3924-810f-c04153c831b5
236 lines
7.9 KiB
Java
236 lines
7.9 KiB
Java
// 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 java.io.BufferedReader;
|
|
import java.io.BufferedWriter;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileReader;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.Reader;
|
|
import java.io.Writer;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
|
|
/**
|
|
* A default implementation of {@link DartArtifactProvider} specifying
|
|
* generated files be placed in the same directory as source files.
|
|
*/
|
|
public class DefaultDartArtifactProvider extends DartArtifactProvider {
|
|
|
|
private final File outputDirectory;
|
|
|
|
public DefaultDartArtifactProvider() {
|
|
this(new File("out"));
|
|
}
|
|
|
|
public DefaultDartArtifactProvider(File outputDirectory) {
|
|
this.outputDirectory = outputDirectory;
|
|
}
|
|
|
|
@Override
|
|
public Reader getArtifactReader(Source source, String part, String extension)
|
|
throws IOException {
|
|
if (SystemLibraryManager.isDartUri(source.getUri())) {
|
|
DartSource bundledSource = getBundledArtifact(source, source, part, extension);
|
|
if (bundledSource != null) {
|
|
Reader reader = null;
|
|
try {
|
|
reader = bundledSource.getSourceReader();
|
|
} catch (FileNotFoundException e) {
|
|
/* thrown if file doesn't exist, which is fine */
|
|
}
|
|
if (reader != null) {
|
|
return new BufferedReader(reader);
|
|
}
|
|
}
|
|
}
|
|
File file = getArtifactFile(source, part, extension);
|
|
if (!file.exists()) {
|
|
return null;
|
|
}
|
|
return new BufferedReader(new FileReader(file));
|
|
}
|
|
|
|
@Override
|
|
public URI getArtifactUri(Source source, String part, String extension) {
|
|
try {
|
|
return new URI("file", getArtifactFile(source, part, extension).getPath(), null);
|
|
} catch (URISyntaxException e) {
|
|
throw new IllegalArgumentException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Writer getArtifactWriter(Source source, String part, String extension) throws IOException {
|
|
return new BufferedWriter(new FileWriter(makeDirectories(getArtifactFile(source, part,
|
|
extension))));
|
|
}
|
|
|
|
@Override
|
|
public boolean isOutOfDate(Source source, Source base, String extension) {
|
|
if (SystemLibraryManager.isDartUri(base.getUri())) {
|
|
Source bundledSource = getBundledArtifact(source, base, "", extension);
|
|
if (bundledSource != null && bundledSource.exists()) {
|
|
// Note: Artifacts bundled with sources are always up to date
|
|
return false;
|
|
}
|
|
}
|
|
File artifactFile = getArtifactFile(base, "", extension);
|
|
return !artifactFile.exists() || artifactFile.lastModified() < source.getLastModified();
|
|
}
|
|
|
|
// TODO(jbrosenberg): remove 'source' argument from this method, it's not used
|
|
protected DartSource getBundledArtifact(Source source, Source base, String part, String extension) {
|
|
LibrarySource library;
|
|
URI relativeUri;
|
|
if (base instanceof LibrarySource) {
|
|
library = (LibrarySource) base;
|
|
relativeUri = library.getUri().resolve(".").normalize().relativize(base.getUri());
|
|
} else if (base instanceof DartSource){
|
|
library = ((DartSource) base).getLibrary();
|
|
String name = base.getName();
|
|
URI nameUri = URI.create(name).normalize();
|
|
relativeUri = library.getUri().resolve(".").normalize().relativize(nameUri);
|
|
} else {
|
|
throw new AssertionError(base.getClass().getName());
|
|
}
|
|
|
|
DartSource bundledSource;
|
|
if (!relativeUri.isAbsolute()) {
|
|
bundledSource = library.getSourceFor(fullname(relativeUri.getPath(), part, extension));
|
|
} else {
|
|
bundledSource = null;
|
|
}
|
|
return bundledSource;
|
|
}
|
|
|
|
/**
|
|
* Answer the artifact file associated with the specified source. Only one
|
|
* artifact may be associated with the given extension.
|
|
*
|
|
* @param source the source file (not <code>null</code>)
|
|
* @param part a component of the source file to get a reader for (may be empty).
|
|
* @param extension the file extension for this artifact (not
|
|
* <code>null</code>, not empty)
|
|
* @return the artifact file (not <code>null</code>)
|
|
*/
|
|
protected File getArtifactFile(Source source, String part, String extension) {
|
|
String name = source.getName();
|
|
name = URI.create(name).normalize().toString();
|
|
name = normalizeArtifactName(name);
|
|
File file = new File(outputDirectory, fullname(name, part, extension));
|
|
return file;
|
|
}
|
|
|
|
/*
|
|
* Removes extraneous punctuation and file path syntax.
|
|
*/
|
|
private String normalizeArtifactName(String name) {
|
|
/**
|
|
* For efficiency, String operations are replaced with a single pass over
|
|
* the character array data.
|
|
*
|
|
* Note: This is a refactor of a previous version which used repeated calls
|
|
* to String.replace(), which turns out to be unnecessarily expensive. This
|
|
* particular method has been identified as being called a large number of
|
|
* times, thus the need for the micro-optimization here.
|
|
*
|
|
* This is the original logic being implemented here:
|
|
*
|
|
* <code>
|
|
* name = name.replace("//", File.separator);
|
|
* name = name.replace(":", "");
|
|
* name = name.replace("!", "");
|
|
* name = name.replace("..", "_");
|
|
* </code>
|
|
*
|
|
* Please update the above if the logic being implemented ever changes.
|
|
*
|
|
* TODO(jbrosenberg): Figure out a better way such that this normalization
|
|
* is no longer needed in the first place. Source objects could be built
|
|
* from pre-normalized prefixes, etc. Or if it can be called less often,
|
|
* then use pre-compiled Patterns and Matchers instead.
|
|
*/
|
|
boolean lastCharWasSlash = false;
|
|
boolean lastCharWasPeriod = false;
|
|
boolean madeChanges = false;
|
|
int nameLen = name.length();
|
|
char[] newName = new char[nameLen];
|
|
int idx = 0;
|
|
for (char ch : name.toCharArray()) {
|
|
if (lastCharWasPeriod && ch != '.') {
|
|
// didn't get a second period, so append the one we did get
|
|
newName[idx++] = '.';
|
|
lastCharWasPeriod = false;
|
|
} else if (lastCharWasSlash && ch != '/') {
|
|
// didn't get a second slash, so append the one we did get
|
|
newName[idx++] = '/';
|
|
lastCharWasSlash = false;
|
|
}
|
|
|
|
switch (ch) {
|
|
case ':':
|
|
case '!':
|
|
// replace ':'s and '!'s with empty string
|
|
madeChanges = true;
|
|
break;
|
|
case '/':
|
|
if (lastCharWasSlash) {
|
|
// got a second slash, replace with File.separatorChar
|
|
madeChanges = true;
|
|
newName[idx++] = File.separatorChar;
|
|
lastCharWasSlash = false;
|
|
} else {
|
|
lastCharWasSlash = true;
|
|
}
|
|
break;
|
|
case '.':
|
|
if (lastCharWasPeriod) {
|
|
// got a second period, replace with a '_'
|
|
madeChanges = true;
|
|
newName[idx++] = ('_');
|
|
lastCharWasPeriod = false;
|
|
} else {
|
|
lastCharWasPeriod = true;
|
|
}
|
|
break;
|
|
default:
|
|
newName[idx++] = ch;
|
|
lastCharWasSlash = false;
|
|
lastCharWasPeriod = false;
|
|
}
|
|
}
|
|
if (lastCharWasPeriod) {
|
|
// didn't get a final second period, so append the one we did get
|
|
newName[idx++] = '.';
|
|
} else if (lastCharWasSlash) {
|
|
// didn't get a final second slash, so append the one we did get
|
|
newName[idx++] = '/';
|
|
}
|
|
|
|
if (madeChanges) {
|
|
name = new String(newName, 0, idx);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
private File makeDirectories(File file) {
|
|
file.getParentFile().mkdirs();
|
|
return file;
|
|
}
|
|
|
|
private String fullname(String name, String part, String extension) {
|
|
if (part.isEmpty()) {
|
|
return name + "." + extension;
|
|
} else {
|
|
return name + "$" + part + "." + extension;
|
|
}
|
|
}
|
|
}
|